mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 00:21:58 +00:00
引用分析结论时没写换算的按结论记的格式渲染(显示规则 4 版):[[v:A1.r0.share]]、[[a:A1.top_share]] 取这一列 / 这个数的格式(占比、变化率按百分数,比率的变化按百分点),写作者自己写了换算的照他的;实际用的换算记在引用上,展开整表时每一格也照写。写作目录的具名的数、结果表的行按同一格式写。3 版及以前组装的文档照当年原样显示,复核按记下的版本,不判对不上。pct、pp 两种换算的写法(不是 0 的不写成 0.0);可验证报告按引用上的换算、4 版起按结论记的格式渲染,不认识的显示规则版本(5 版)判无法核对
This commit is contained in:
@@ -596,7 +596,8 @@
|
|||||||
|
|
||||||
const shifted = (d, k) => ({ neg: d.neg, coef: d.coef, exp: d.exp + k })
|
const shifted = (d, k) => ({ neg: d.neg, coef: d.coef, exp: d.exp + k })
|
||||||
|
|
||||||
/** _render_numeric:万 / 亿留两位、去尾零,单位接在后面;pct、比率乘 100 加 %(不接单位);int、.N 改小数位 */
|
/** _render_numeric:万 / 亿留两位、去尾零,单位接在后面;pct、比率乘 100 加 %(不接单位);pp 乘 100 写成「+1.2 个百分点」;
|
||||||
|
* int、.N 改小数位 */
|
||||||
function renderNumeric(d, unit, decimals, fmt, conv) {
|
function renderNumeric(d, unit, decimals, fmt, conv) {
|
||||||
if (adjusted(d) >= MAX_DIGITS) throw new Unshown(T.shownTooLong)
|
if (adjusted(d) >= MAX_DIGITS) throw new Unshown(T.shownTooLong)
|
||||||
const ratio = fmt === 'percent_of_ratio'
|
const ratio = fmt === 'percent_of_ratio'
|
||||||
@@ -609,6 +610,20 @@
|
|||||||
if (unit.includes('%') || unit.includes('%')) throw new Unshown(T.shownPctUnit)
|
if (unit.includes('%') || unit.includes('%')) throw new Unshown(T.shownPctUnit)
|
||||||
return `${digitsOf(shifted(d, 2), decimals != null ? Math.max(decimals - 2, 0) : 1, group, false)}%`
|
return `${digitsOf(shifted(d, 2), decimals != null ? Math.max(decimals - 2, 0) : 1, group, false)}%`
|
||||||
}
|
}
|
||||||
|
if (conv === 'pp') {
|
||||||
|
// 比率的变化按百分点写:一位小数、正数带「+」(0 不带);不是 0 的值写出来成了 0.0 的多给小数位(最多 4 位),
|
||||||
|
// 不写假的 0(同服务端 engine/evidence._render_numeric 的 pp)
|
||||||
|
if (unit.includes('%') || unit.includes('%')) throw new Unshown(T.shownPctUnit)
|
||||||
|
const points = shifted(d, 2)
|
||||||
|
if (isZero(points)) return `${digitsOf(points, 1, group, false)} 个百分点`
|
||||||
|
let places = 1
|
||||||
|
for (; places < 5; places++) {
|
||||||
|
if (!isZero(quantize(points, -places))) break
|
||||||
|
}
|
||||||
|
if (places >= 5) throw new Unshown(T.shownZero)
|
||||||
|
const out = digitsOf(points, places, group, false)
|
||||||
|
return `${out.startsWith('-') ? out : `+${out}`} 个百分点`
|
||||||
|
}
|
||||||
let places
|
let places
|
||||||
if (conv === 'int') places = 0
|
if (conv === 'int') places = 0
|
||||||
else if (typeof conv === 'string' && CONV_PLACES.test(conv)) places = Number(conv.slice(1))
|
else if (typeof conv === 'string' && CONV_PLACES.test(conv)) places = Number(conv.slice(1))
|
||||||
@@ -652,12 +667,13 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* render_cell(shown(raw)):快照里的一格。kind 是查询时记下的列类型,display 是报告文档记下的单元格显示规则版本
|
* render_cell(shown(raw)):快照里的一格。kind 是查询时记下的列类型,display 是报告文档记下的单元格显示规则版本
|
||||||
* (2 版起 ISO 日期时间的 T 换成空格;3 版起标识列的数不分千位)。plain 是引用上记下的「这一格按标识列显示」:
|
* (2 版起 ISO 日期时间的 T 换成空格;3 版起标识列的数不分千位;4 版起引用分析结论时按结论记的格式换算)。
|
||||||
|
* plain 是引用上记下的「这一格按标识列显示」:
|
||||||
* 哪几列是标识列服务端按 SQL 和冻结的数据目录判(cell_format.plain_columns),本页解析不了 SQL,照引用上记的渲染——
|
* 哪几列是标识列服务端按 SQL 和冻结的数据目录判(cell_format.plain_columns),本页解析不了 SQL,照引用上记的渲染——
|
||||||
* 它只决定分不分千位,值本身照样逐位比对。DECIMAL 落成的数字文本按数渲染、不经 float;文字压成一行,竖线换成 ¦
|
* 它只决定分不分千位,值本身照样逐位比对。DECIMAL 落成的数字文本按数渲染、不经 float;文字压成一行,竖线换成 ¦
|
||||||
*/
|
*/
|
||||||
function renderCell(raw, token, conv, kind, display, plain = false) {
|
function renderCell(raw, token, conv, kind, display, plain = false) {
|
||||||
if (display !== 1 && display !== 2 && display !== 3) throw new Unshown(T.shownDisplay(display))
|
if (display !== 1 && display !== 2 && display !== 3 && display !== 4) throw new Unshown(T.shownDisplay(display))
|
||||||
if (!PY_NUMERIC || !ISO_DATETIME) throw new Unshown(T.shownOld)
|
if (!PY_NUMERIC || !ISO_DATETIME) throw new Unshown(T.shownOld)
|
||||||
const fmt = display >= 3 && plain === true ? 'plain' : 'thousands'
|
const fmt = display >= 3 && plain === true ? 'plain' : 'thousands'
|
||||||
let value = raw
|
let value = raw
|
||||||
@@ -1546,6 +1562,12 @@
|
|||||||
/** 引用上记的换算(|万、|pct 这类),没写是 null */
|
/** 引用上记的换算(|万、|pct 这类),没写是 null */
|
||||||
const convOf = (cite) => (own(cite, 'conv') === undefined ? null : cite.conv)
|
const convOf = (cite) => (own(cite, 'conv') === undefined ? null : cite.conv)
|
||||||
|
|
||||||
|
/** 分析结论给这一列 / 这个数记的换算(formats / value_formats:占比、变化率的列 pct,比率的变化 pp),没记是 null */
|
||||||
|
const namedConv = (formats, key) => {
|
||||||
|
const found = isObj(formats) ? formats[key] : null
|
||||||
|
return typeof found === 'string' && found ? found : null
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分析结论的一处引用(checkNumber 已核过工件的哈希和引用链):引用的值等于结论里记的那个值,正文显示的字等于按服务端
|
* 分析结论的一处引用(checkNumber 已核过工件的哈希和引用链):引用的值等于结论里记的那个值,正文显示的字等于按服务端
|
||||||
* 的规则渲染出来的字。判定语按文本格渲染(engine/evidence.render_cell 的 kind=text:压成一行、竖线换成 ¦),具名的数按
|
* 的规则渲染出来的字。判定语按文本格渲染(engine/evidence.render_cell 的 kind=text:压成一行、竖线换成 ¦),具名的数按
|
||||||
@@ -1573,7 +1595,10 @@
|
|||||||
}
|
}
|
||||||
if (!sameValue(cite.value, info.truth)) return { ...info, result: FAIL, reason: T.numAnalysisCell }
|
if (!sameValue(cite.value, info.truth)) return { ...info, result: FAIL, reason: T.numAnalysisCell }
|
||||||
const display = Object.prototype.hasOwnProperty.call(doc, 'cell_display') ? doc.cell_display : 1
|
const display = Object.prototype.hasOwnProperty.call(doc, 'cell_display') ? doc.cell_display : 1
|
||||||
return checkShown(info, seg, () => renderCell(cell.raw, info.truthToken, convOf(cite),
|
// 没写换算、结论的结果表给这一列记了格式(formats:4 版显示规则起)、文档也按 4 版复核的,按它渲染(服务端同样);
|
||||||
|
// 写作者自己写了换算的照他的
|
||||||
|
const conv = convOf(cite) ?? (display >= 4 ? namedConv(table.formats, cell.column) : null)
|
||||||
|
return checkShown(info, seg, () => renderCell(cell.raw, info.truthToken, conv,
|
||||||
columnKind(table, cell.column), display, own(cite, 'plain') === true))
|
columnKind(table, cell.column), display, own(cite, 'plain') === true))
|
||||||
}
|
}
|
||||||
if (loc.key != null) {
|
if (loc.key != null) {
|
||||||
@@ -1584,7 +1609,10 @@
|
|||||||
info.truthToken = tokenAt(tokensOf(art), ['values', loc.key])
|
info.truthToken = tokenAt(tokensOf(art), ['values', loc.key])
|
||||||
if (typeof raw === 'string' && typeof info.truth === 'number') info.truthText = pyStrip(raw)
|
if (typeof raw === 'string' && typeof info.truth === 'number') info.truthText = pyStrip(raw)
|
||||||
if (!sameValue(cite.value, info.truth)) return { ...info, result: FAIL, reason: T.numValueDiffers }
|
if (!sameValue(cite.value, info.truth)) return { ...info, result: FAIL, reason: T.numValueDiffers }
|
||||||
return checkShown(info, seg, () => renderCell(raw, info.truthToken, convOf(cite), null, 1))
|
// 没写换算、结论给这个数记了格式(value_formats;4 版显示规则起)的按它渲染(服务端同样);写作者自己写了换算的照他的
|
||||||
|
const display = Object.prototype.hasOwnProperty.call(doc, 'cell_display') ? doc.cell_display : 1
|
||||||
|
const conv = convOf(cite) ?? (display >= 4 ? namedConv(result.value_formats, loc.key) : null)
|
||||||
|
return checkShown(info, seg, () => renderCell(raw, info.truthToken, conv, null, 1))
|
||||||
}
|
}
|
||||||
if (typeof result.phrase !== 'string' || !result.phrase.trim()) return { ...info, result: FAIL, reason: T.numNoPhrase }
|
if (typeof result.phrase !== 'string' || !result.phrase.trim()) return { ...info, result: FAIL, reason: T.numNoPhrase }
|
||||||
info.truth = result.phrase
|
info.truth = result.phrase
|
||||||
|
|||||||
@@ -91,11 +91,15 @@ ENTITY_SYNTAXES = frozenset({1, 2})
|
|||||||
#: 「没有第 N 行」重新解析得了,会被当成文档被改过(render_mismatch)。另外标识列(编码、编号、分组键,见
|
#: 「没有第 N 行」重新解析得了,会被当成文档被改过(render_mismatch)。另外标识列(编码、编号、分组键,见
|
||||||
#: cell_format.plain_columns)的数不分千位:编码 1203 不写成「1,203」。真有格子因此改了写法的文档记 cell_display: 3,
|
#: cell_format.plain_columns)的数不分千位:编码 1203 不写成「1,203」。真有格子因此改了写法的文档记 cell_display: 3,
|
||||||
#: 引用上记 plain: true(离线核对页解析不了 SQL,照它渲染)
|
#: 引用上记 plain: true(离线核对页解析不了 SQL,照它渲染)
|
||||||
|
#: - 4:引用分析结论的结果表一格、具名的数时没写换算的,按结论给这一列 / 这个数记的格式渲染(占比、变化率按百分数,
|
||||||
|
#: 比率的变化按百分点:0.62 显示成 62.0%,不再照原样写 0.618556701),实际用的换算记在引用上(conv,导出页和可验证报告
|
||||||
|
#: 照它渲染)。3 版及以前组装的文档里的引用照当年原样显示,复核按记下的版本,不因此判对不上
|
||||||
#: 新组装的文档里真有格子因为显示规则改了写法时才记 cell_display(参与内容哈希),记的是渲染得出这些格子的最低一版
|
#: 新组装的文档里真有格子因为显示规则改了写法时才记 cell_display(参与内容哈希),记的是渲染得出这些格子的最低一版
|
||||||
#: (只有日期时间被改写的记 2:标识列没有因此改写法的,3 版的整表截行不改格子的写法),没有的文档和以前一字不差。复核必须按
|
#: (只有日期时间被改写的记 2:标识列没有因此改写法的,3 版的整表截行不改格子的写法,引用分析结论时本来就有换算的记 1);没有的
|
||||||
#: 文档生成时的那一版:老文档存的是带 T 的字,拿新规则重新渲染对不上,会被当成文档被改过(render_mismatch)
|
#: 文档和以前一字不差。复核必须按文档生成时的那一版:老文档存的是带 T 的字,拿新规则重新渲染对不上,会被当成文档被改过
|
||||||
CELL_DISPLAY = 3
|
#: (render_mismatch)
|
||||||
CELL_DISPLAYS = frozenset({1, 2, 3})
|
CELL_DISPLAY = 4
|
||||||
|
CELL_DISPLAYS = frozenset({1, 2, 3, 4})
|
||||||
_ISO_DATETIME = re.compile(r"^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?)$")
|
_ISO_DATETIME = re.compile(r"^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?)$")
|
||||||
|
|
||||||
|
|
||||||
@@ -148,7 +152,7 @@ class Citation(TypedDict, total=False):
|
|||||||
status: Literal["resolved", "unresolved", "mismatch"]
|
status: Literal["resolved", "unresolved", "mismatch"]
|
||||||
reason: str # unresolved 时为什么
|
reason: str # unresolved 时为什么
|
||||||
unknown: bool # 实体:本次运行哪里都没有这个名字(可疑实体,不是写法错了)
|
unknown: bool # 实体:本次运行哪里都没有这个名字(可疑实体,不是写法错了)
|
||||||
conv: str # 换算:万 / 亿 / pct / int / .N
|
conv: str # 换算:万 / 亿 / pct / pp / int / .N(没写换算、按分析结论记的格式渲染的也照记,导出、可验证报告照它渲染)
|
||||||
value: Any
|
value: Any
|
||||||
rendered: str
|
rendered: str
|
||||||
source: dict[str, Any] # 引文:原文所在的检索快照、文档、片段
|
source: dict[str, Any] # 引文:原文所在的检索快照、文档、片段
|
||||||
@@ -573,6 +577,11 @@ MISSING = "—"
|
|||||||
|
|
||||||
_CONV_SCALE = {"万": Decimal(10) ** 4, "亿": Decimal(10) ** 8}
|
_CONV_SCALE = {"万": Decimal(10) ** 4, "亿": Decimal(10) ** 8}
|
||||||
_CONV_PLACES = re.compile(r"^\.([0-6])$")
|
_CONV_PLACES = re.compile(r"^\.([0-6])$")
|
||||||
|
#: 比率的变化按百分点写(0.012 →「+1.2 个百分点」,-0.003 →「-0.3 个百分点」):分析结论的结果表、具名的数里的换算,
|
||||||
|
#: 算子记成 pp(analysis.contract.CONV_POINTS)。正数带「+」(和 pct 一样),不是 0 的值写出来成了 0.0 的多给小数位
|
||||||
|
#: (最多 4 位),不写假的 0
|
||||||
|
_CONV_POINTS = "pp"
|
||||||
|
_POINTS_SUFFIX = " 个百分点"
|
||||||
#: 小数位最多写到多少。负数是取整到十位、百位……(round(x, -2) 的意思)
|
#: 小数位最多写到多少。负数是取整到十位、百位……(round(x, -2) 的意思)
|
||||||
MAX_PLACES = 100
|
MAX_PLACES = 100
|
||||||
#: 整数部分最多多少位。浮点数到头也就 309 位,再大的整数按规则一位位写出来没有意义
|
#: 整数部分最多多少位。浮点数到头也就 309 位,再大的整数按规则一位位写出来没有意义
|
||||||
@@ -648,7 +657,7 @@ def render_number(
|
|||||||
- decimals:固定小数位;不写就按值本来的样子(整数不带小数点)
|
- decimals:固定小数位;不写就按值本来的样子(整数不带小数点)
|
||||||
- 单位直接接在数字后面:45,678.5元、8.7%
|
- 单位直接接在数字后面:45,678.5元、8.7%
|
||||||
- conv 是报告里写的换算:万 / 亿(保留两位、去尾零,单位接在后面:4.57万元)、
|
- conv 是报告里写的换算:万 / 亿(保留两位、去尾零,单位接在后面:4.57万元)、
|
||||||
pct(比率 ×100 加 %)、int(取整)、.N(N 位小数)
|
pct(比率 ×100 加 %)、pp(比率的变化 ×100 写成「+1.2 个百分点」)、int(取整)、.N(N 位小数)
|
||||||
|
|
||||||
值是 None 显示「—」。文本值原样(压成一行),不能换算。
|
值是 None 显示「—」。文本值原样(压成一行),不能换算。
|
||||||
|
|
||||||
@@ -696,13 +705,30 @@ def _render_numeric(d: Decimal, *, unit: str, decimals: int | None, fmt: str, co
|
|||||||
raise RenderError("该指标本身是百分数(单位为 %),不能再换算为 pct")
|
raise RenderError("该指标本身是百分数(单位为 %),不能再换算为 pct")
|
||||||
places = max(decimals - 2, 0) if decimals is not None else 1
|
places = max(decimals - 2, 0) if decimals is not None else 1
|
||||||
return _digits(_ctx(d).multiply(d, 100), places, group=group, zero=rounded) + "%"
|
return _digits(_ctx(d).multiply(d, 100), places, group=group, zero=rounded) + "%"
|
||||||
|
if conv == _CONV_POINTS:
|
||||||
|
# 比率的变化按百分点写:一位小数、正数带「+」(0 不带);不是 0 的值写出来成了 0.0 的多给小数位(最多 4 位),
|
||||||
|
# 不写假的 0。说法照 phrases.points / ratio_change 里已有的百分点写法,只是表格里的数没有「上升 / 下降」可依,
|
||||||
|
# 正负靠符号
|
||||||
|
if "%" in unit or "%" in unit:
|
||||||
|
raise RenderError("该指标本身是百分数(单位为 %),不能再换算为 pp")
|
||||||
|
points = _ctx(d).multiply(d, 100)
|
||||||
|
if points.is_zero():
|
||||||
|
return _digits(points, 1, group=group) + _POINTS_SUFFIX
|
||||||
|
zero = f"{shown} 按 |pp 显示为 0,精度不够:请去掉换算,或改用 |.N 多保留几位小数"
|
||||||
|
for places in range(1, 5):
|
||||||
|
exp = -(places)
|
||||||
|
if points.quantize(Decimal(1).scaleb(exp), context=_ctx(points, places)).is_zero():
|
||||||
|
continue
|
||||||
|
out = _digits(points, places, group=group, zero=zero)
|
||||||
|
return ("+" + out if not out.startswith("-") else out) + _POINTS_SUFFIX
|
||||||
|
raise RenderError(zero)
|
||||||
|
|
||||||
if conv == "int":
|
if conv == "int":
|
||||||
places: int | None = 0
|
places: int | None = 0
|
||||||
elif conv and (m := _CONV_PLACES.match(conv)):
|
elif conv and (m := _CONV_PLACES.match(conv)):
|
||||||
places = int(m.group(1))
|
places = int(m.group(1))
|
||||||
elif conv:
|
elif conv:
|
||||||
raise RenderError(f"无法识别的换算 |{conv},只支持 万 / 亿 / pct / int / .N(N 为 0–6)")
|
raise RenderError(f"无法识别的换算 |{conv},只支持 万 / 亿 / pct / pp / int / .N(N 为 0–6)")
|
||||||
else:
|
else:
|
||||||
rounded = (f"{shown} 按口径卡的小数位({decimals})显示为 0,精度不够:请在口径卡中增加小数位"
|
rounded = (f"{shown} 按口径卡的小数位({decimals})显示为 0,精度不够:请在口径卡中增加小数位"
|
||||||
if decimals is not None else
|
if decimals is not None else
|
||||||
@@ -1464,8 +1490,8 @@ class _Snapshots:
|
|||||||
|
|
||||||
def table(self, alias: str, entry: dict[str, Any]) -> tuple[Any, str | None]:
|
def table(self, alias: str, entry: dict[str, Any]) -> tuple[Any, str | None]:
|
||||||
"""按单元格、整表取的那张表:(表, 取不到的原因)。查询结果是查询快照本身;分析结论是它的结果表(形状同查询快照:
|
"""按单元格、整表取的那张表:(表, 取不到的原因)。查询结果是查询快照本身;分析结论是它的结果表(形状同查询快照:
|
||||||
columns 是稳定的列 id,另带 labels 给人看的列名,整表的表头写它)。结果表另记了列类型(column_types)的照着渲染,
|
columns 是稳定的列 id,另带 labels 给人看的列名,整表的表头写它)。结果表另记了列类型(column_types)、各列的换算
|
||||||
没记的按值认(同老快照)。"""
|
(formats,占比、变化率这类以比率存的列)的照着渲染,没记的按值认(同老快照)。"""
|
||||||
if entry.get("kind") != LEDGER_KIND:
|
if entry.get("kind") != LEDGER_KIND:
|
||||||
return self.get(alias, entry.get("artifact"))
|
return self.get(alias, entry.get("artifact"))
|
||||||
result, why = self.analysis(alias, entry)
|
result, why = self.analysis(alias, entry)
|
||||||
@@ -1476,9 +1502,11 @@ class _Snapshots:
|
|||||||
return None, f"{alias} 没有结果表" + ("(这次分析没有做成)" if result.get("status") == "refused" else "")
|
return None, f"{alias} 没有结果表" + ("(这次分析没有做成)" if result.get("status") == "refused" else "")
|
||||||
labels = table.get("labels")
|
labels = table.get("labels")
|
||||||
types = table.get("column_types")
|
types = table.get("column_types")
|
||||||
|
formats = table.get("formats")
|
||||||
return {"columns": [str(c) for c in table.get("columns") or []], "rows": table["rows"],
|
return {"columns": [str(c) for c in table.get("columns") or []], "rows": table["rows"],
|
||||||
"labels": {str(k): str(v) for k, v in labels.items()} if isinstance(labels, dict) else {},
|
"labels": {str(k): str(v) for k, v in labels.items()} if isinstance(labels, dict) else {},
|
||||||
**({"column_types": types} if isinstance(types, dict) else {})}, None
|
**({"column_types": types} if isinstance(types, dict) else {}),
|
||||||
|
**({"formats": {str(k): str(v) for k, v in formats.items()}} if isinstance(formats, dict) else {})}, None
|
||||||
|
|
||||||
def hits(self, alias: str, artifact: Any) -> tuple[list[Any], str | None]:
|
def hits(self, alias: str, artifact: Any) -> tuple[list[Any], str | None]:
|
||||||
"""检索快照里的命中:(命中列表, 取不到的原因)。"""
|
"""检索快照里的命中:(命中列表, 取不到的原因)。"""
|
||||||
@@ -1565,6 +1593,19 @@ def _noun(entry: dict[str, Any] | None) -> str:
|
|||||||
return "结果表" if (entry or {}).get("kind") == LEDGER_KIND else "查询结果"
|
return "结果表" if (entry or {}).get("kind") == LEDGER_KIND else "查询结果"
|
||||||
|
|
||||||
|
|
||||||
|
def _column_conv(snapshot: Any, column: str, display: int = CELL_DISPLAY) -> str | None:
|
||||||
|
"""分析结论的结果表给这一列记的换算(ResultTable.formats:占比、变化率的列记 pct,比率的变化记 pp)。查询快照没有这一项,
|
||||||
|
一切照原样(None)。按分析结论记的格式显示是 4 版起的显示规则:老文档(3 版及以前)里的引用照当年原样显示,复核不因此
|
||||||
|
判对不上。"""
|
||||||
|
if display < 4:
|
||||||
|
return None
|
||||||
|
formats = snapshot.get("formats") if isinstance(snapshot, dict) else None
|
||||||
|
if not isinstance(formats, dict):
|
||||||
|
return None
|
||||||
|
found = formats.get(column)
|
||||||
|
return found if isinstance(found, str) and found else None
|
||||||
|
|
||||||
|
|
||||||
def _resolve_cell(marker: dict[str, Any], catalog: dict[str, Any], snaps: _Snapshots,
|
def _resolve_cell(marker: dict[str, Any], catalog: dict[str, Any], snaps: _Snapshots,
|
||||||
cells_allowed: bool) -> dict[str, Any]:
|
cells_allowed: bool) -> dict[str, Any]:
|
||||||
ref = marker.get("ref") or ""
|
ref = marker.get("ref") or ""
|
||||||
@@ -1592,7 +1633,14 @@ def _resolve_cell(marker: dict[str, Any], catalog: dict[str, Any], snaps: _Snaps
|
|||||||
raw, column = locate_cell(snapshot, parsed["row"], parsed["column"])
|
raw, column = locate_cell(snapshot, parsed["row"], parsed["column"])
|
||||||
kind = column_kind(snapshot, column)
|
kind = column_kind(snapshot, column)
|
||||||
shown = snaps.shown(raw)
|
shown = snaps.shown(raw)
|
||||||
|
# 分析结论的结果表记了这一列的换算(占比、变化率这类以比率存的列;4 版显示规则起)、写作者没写换算的,按它渲染;
|
||||||
|
# 写作者自己写了换算的照他的。实际用的换算记进引用(导出页、可验证报告照它渲染)
|
||||||
|
conv = conv or _column_conv(snapshot, column, snaps.display)
|
||||||
rendered = render_cell(shown, conv, kind=kind)
|
rendered = render_cell(shown, conv, kind=kind)
|
||||||
|
if conv and not marker.get("conv") and rendered != render_cell(shown, None, kind=kind):
|
||||||
|
# 按分析结论记的格式渲染、真改了写法(4 版起):文档的 cell_display 记成 4,引用上记 conv,
|
||||||
|
# 导出页和可验证报告照引用上的换算渲染
|
||||||
|
snaps.needed = max(snaps.needed, 4)
|
||||||
plain = column in snaps.plain(entry.get("artifact"), snapshot) \
|
plain = column in snaps.plain(entry.get("artifact"), snapshot) \
|
||||||
and render_cell(shown, conv, kind=kind, plain=True) != rendered
|
and render_cell(shown, conv, kind=kind, plain=True) != rendered
|
||||||
if plain:
|
if plain:
|
||||||
@@ -1836,6 +1884,7 @@ def _resolve_analysis(marker: dict[str, Any], catalog: dict[str, Any], snaps: _S
|
|||||||
cells_allowed: bool) -> dict[str, Any]:
|
cells_allowed: bool) -> dict[str, Any]:
|
||||||
"""[[a:A3]] → 判定语:分析结论里系统按模板渲染好的那一整句(判定是平台的算子算的,写作者只引用,00-总方案 D5)。
|
"""[[a:A3]] → 判定语:分析结论里系统按模板渲染好的那一整句(判定是平台的算子算的,写作者只引用,00-总方案 D5)。
|
||||||
[[a:A3.top_share|pct]] → 具名标量:结论 values 里的一项(数一律十进制字符串),按单元格的规则渲染,换算同 [[m:]]。
|
[[a:A3.top_share|pct]] → 具名标量:结论 values 里的一项(数一律十进制字符串),按单元格的规则渲染,换算同 [[m:]]。
|
||||||
|
没写换算、结论给这个数记了格式(value_formats)的按它渲染(0.62 显示成 62.0%,不是 0.62),实际用的换算记进引用。
|
||||||
|
|
||||||
判定语是一个片段(种类 analysis),里面的数和字都是系统写的:组装、复核都按带引用的片段整段涂白,不算裸数字。受管级别的
|
判定语是一个片段(种类 analysis),里面的数和字都是系统写的:组装、复核都按带引用的片段整段涂白,不算裸数字。受管级别的
|
||||||
正式出具、契约没开「单元格引用」时,Agent 现场做的分析解析不了(ANALYSIS_CELLS_REASON),分析卡做的照常解析。"""
|
正式出具、契约没开「单元格引用」时,Agent 现场做的分析解析不了(ANALYSIS_CELLS_REASON),分析卡做的照常解析。"""
|
||||||
@@ -1875,6 +1924,13 @@ def _resolve_analysis(marker: dict[str, Any], catalog: dict[str, Any], snaps: _S
|
|||||||
return _unresolved(marker, alias, LEDGER_KIND, _Reason(
|
return _unresolved(marker, alias, LEDGER_KIND, _Reason(
|
||||||
f"{alias} 中没有名为「{key}」的数", f"{alias} 里没有「{key}」,能引用的数:{names}(写成 [[{MARKER_KIND}:{alias}.键]])"))
|
f"{alias} 中没有名为「{key}」的数", f"{alias} 里没有「{key}」,能引用的数:{names}(写成 [[{MARKER_KIND}:{alias}.键]])"))
|
||||||
raw = values[key]
|
raw = values[key]
|
||||||
|
# 结论里给这个数记了换算(value_formats:占比、变化率这类以比率存的数;4 版显示规则起)而写作者没写换算的,按它渲染;
|
||||||
|
# 写作者自己写了换算的照他的。实际用的换算记进引用(导出页、可验证报告照它渲染)
|
||||||
|
named = result.get("value_formats") if isinstance(result.get("value_formats"), dict) else {}
|
||||||
|
if snaps.display >= 4 and not conv and isinstance(named.get(key), str) and named.get(key):
|
||||||
|
conv = named[key]
|
||||||
|
# 按分析结论记的格式渲染了(4 版起):文档记成 4,引用上记 conv(写了换算才真有换算,写法一定和以前不同)
|
||||||
|
snaps.needed = max(snaps.needed, 4)
|
||||||
try:
|
try:
|
||||||
rendered = render_cell(raw, conv)
|
rendered = render_cell(raw, conv)
|
||||||
except RenderError as e:
|
except RenderError as e:
|
||||||
@@ -4028,7 +4084,8 @@ def _query_prompt(entry: dict[str, Any], snaps: _Snapshots, cells_allowed: bool,
|
|||||||
ANALYSIS_PROMPT = (
|
ANALYSIS_PROMPT = (
|
||||||
"分析结论(系统按平台的方法算好的判定。判定语、具名的数、结果表都只能用引用标记写,系统照结论渲染、逐字核对):\n"
|
"分析结论(系统按平台的方法算好的判定。判定语、具名的数、结果表都只能用引用标记写,系统照结论渲染、逐字核对):\n"
|
||||||
"写法:判定语单独写 [[a:A1]],系统换成整句判定;具名的数写 [[a:A1.键]],换算同指标,比重、变化率这类比率写成 "
|
"写法:判定语单独写 [[a:A1]],系统换成整句判定;具名的数写 [[a:A1.键]],换算同指标,比重、变化率这类比率写成 "
|
||||||
"[[a:A1.top_share|pct]] 显示成百分数;结果表的一格写 [[v:A1.r0.列]](行号从 0 数),要列出多行多列,单独一行写 "
|
"[[a:A1.top_share|pct]] 显示成百分数;比重、变化率不写换算也按百分数显示,比率的变化按百分点([[v:A1.r0.mix]] 显示成「1.2 "
|
||||||
|
"个百分点」);结果表的一格写 [[v:A1.r0.列]](行号从 0 数),要列出多行多列,单独一行写 "
|
||||||
"[[table:A1 cols=列a,列b rows=0-4]];拿它当结论的依据,句末写 [[see:A1]]。"
|
"[[table:A1 cols=列a,列b rows=0-4]];拿它当结论的依据,句末写 [[see:A1]]。"
|
||||||
"「主要来自」「超出常规波动」「呈上升趋势」这类判断已经写在判定语里:引用判定语,不要自己改写判定;"
|
"「主要来自」「超出常规波动」「呈上升趋势」这类判断已经写在判定语里:引用判定语,不要自己改写判定;"
|
||||||
"算不了的照实写算不了的原因。下面没有的编号、键、列不要编造。"
|
"算不了的照实写算不了的原因。下面没有的编号、键、列不要编造。"
|
||||||
@@ -4101,10 +4158,13 @@ def _analysis_prompt(entry: dict[str, Any], catalog: dict[str, Any], snaps: _Sna
|
|||||||
if see_only:
|
if see_only:
|
||||||
return lines
|
return lines
|
||||||
values = result.get("values") if isinstance(result.get("values"), dict) else {}
|
values = result.get("values") if isinstance(result.get("values"), dict) else {}
|
||||||
|
named = result.get("value_formats") if isinstance(result.get("value_formats"), dict) else {}
|
||||||
shown = []
|
shown = []
|
||||||
for key, raw in list(values.items())[:_PROMPT_VALUES]:
|
for key, raw in list(values.items())[:_PROMPT_VALUES]:
|
||||||
try:
|
try:
|
||||||
shown.append(f"{key}={render_cell(raw)}")
|
# 具名的数按结论记的格式写(占比、变化率是百分数):写作者在这里看到的就是引用之后显示的字
|
||||||
|
conv = named.get(key) if isinstance(named.get(key), str) and named.get(key) else None
|
||||||
|
shown.append(f"{key}={render_cell(raw, conv)}")
|
||||||
except RenderError:
|
except RenderError:
|
||||||
shown.append(f"{key}={MISSING}")
|
shown.append(f"{key}={MISSING}")
|
||||||
if shown:
|
if shown:
|
||||||
@@ -4127,12 +4187,15 @@ def _analysis_prompt(entry: dict[str, Any], catalog: dict[str, Any], snaps: _Sna
|
|||||||
top = top if isinstance(top, int) and not isinstance(top, bool) and top > 0 else _TOP_K
|
top = top if isinstance(top, int) and not isinstance(top, bool) and top > 0 else _TOP_K
|
||||||
lines.append(f" 结果表(共 {len(rows)} 段):列 {named}")
|
lines.append(f" 结果表(共 {len(rows)} 段):列 {named}")
|
||||||
plain = snaps.plain(entry.get("artifact"), snapshot)
|
plain = snaps.plain(entry.get("artifact"), snapshot)
|
||||||
|
formats = snapshot.get("formats") if isinstance(snapshot.get("formats"), dict) else {}
|
||||||
for r in range(min(top, len(rows))):
|
for r in range(min(top, len(rows))):
|
||||||
cells = []
|
cells = []
|
||||||
for c in columns:
|
for c in columns:
|
||||||
try:
|
try:
|
||||||
raw, _ = locate_cell(snapshot, r, c)
|
raw, _ = locate_cell(snapshot, r, c)
|
||||||
cells.append(f"{c}={render_cell(snaps.shown(raw), kind=column_kind(snapshot, c), plain=c in plain)}")
|
conv = formats.get(c) if isinstance(formats.get(c), str) and formats.get(c) else None
|
||||||
|
cells.append(f"{c}={render_cell(snaps.shown(raw), conv, kind=column_kind(snapshot, c),
|
||||||
|
plain=c in plain)}")
|
||||||
except (CellError, RenderError):
|
except (CellError, RenderError):
|
||||||
cells.append(f"{c}={MISSING}")
|
cells.append(f"{c}={MISSING}")
|
||||||
lines.append(f" r{r}:" + ",".join(cells))
|
lines.append(f" r{r}:" + ",".join(cells))
|
||||||
|
|||||||
Reference in New Issue
Block a user