|
|
@@ -124,7 +124,11 @@ def _body(chain_name: str, views: tuple[_View, ...], business_date: date) -> lis
|
|
|
and not view.formula_text.startswith(CLEAR_MARK)
|
|
|
and view.formula_text not in GATE_FORMULAS
|
|
|
]
|
|
|
- clears = [view.calc_text for view in views if view.formula_text.startswith(CLEAR_MARK) and view.calc_text]
|
|
|
+ clears = [
|
|
|
+ _reflow_topic(view.calc_text)
|
|
|
+ for view in views
|
|
|
+ if view.formula_text.startswith(CLEAR_MARK) and view.calc_text
|
|
|
+ ]
|
|
|
pieces = [_summary(chain_name, views)]
|
|
|
if chain_name == DIM_ORDER:
|
|
|
pieces.extend(_order_sections(rest, clears))
|
|
|
@@ -347,23 +351,69 @@ def _report_block(rows: Sequence[_View], business_date: date) -> str:
|
|
|
(
|
|
|
f"报表:{label}",
|
|
|
f"筛选:{screen}",
|
|
|
- _stacked("期望", names),
|
|
|
+ _expect_line(names),
|
|
|
f"实据:{actual}",
|
|
|
- _stacked("依据", bases or ["见各列登记"]),
|
|
|
+ _basis_line(bases or ["见各列登记"]),
|
|
|
f"结论:{label_text}",
|
|
|
)
|
|
|
)
|
|
|
|
|
|
|
|
|
-def _stacked(label: str, items: Sequence[str]) -> str:
|
|
|
- """多条依据或期望各占一行。用分号拼在一起时,原型路径会挤成一行。"""
|
|
|
+def _expect_line(items: Sequence[str]) -> str:
|
|
|
+ """多个字段写在同一句里,用逗号分开,句号收尾。"""
|
|
|
|
|
|
- kept = [item for item in items if item]
|
|
|
+ kept: list[str] = []
|
|
|
+ for item in items:
|
|
|
+ text = item.strip().rstrip("。").strip()
|
|
|
+ if text and text not in kept:
|
|
|
+ kept.append(text)
|
|
|
if not kept:
|
|
|
- return f"{label}:"
|
|
|
- if len(kept) == 1:
|
|
|
- return f"{label}:{kept[0]}"
|
|
|
- return f"{label}:{kept[0]}\n" + "\n".join(kept[1:])
|
|
|
+ return "期望:"
|
|
|
+ return "期望:" + ",".join(kept) + "。"
|
|
|
+
|
|
|
+
|
|
|
+def _basis_line(items: Sequence[str]) -> str:
|
|
|
+ """同一份原型文件只写一次,后面接它引用的字段。不同文件仍分行。"""
|
|
|
+
|
|
|
+ files: dict[str, list[str]] = {}
|
|
|
+ sequence: list[tuple[str, str]] = []
|
|
|
+ for item in items:
|
|
|
+ text = item.strip()
|
|
|
+ if not text:
|
|
|
+ continue
|
|
|
+ doc, field = _split_basis(text)
|
|
|
+ if not field:
|
|
|
+ sequence.append(("plain", text))
|
|
|
+ continue
|
|
|
+ if doc not in files:
|
|
|
+ files[doc] = []
|
|
|
+ sequence.append(("file", doc))
|
|
|
+ if field not in files[doc]:
|
|
|
+ files[doc].append(field)
|
|
|
+ lines: list[str] = []
|
|
|
+ for kind, key in sequence:
|
|
|
+ if kind == "plain":
|
|
|
+ lines.append(key)
|
|
|
+ continue
|
|
|
+ quoted = "".join(f"「{name}」" for name in files[key])
|
|
|
+ lines.append(f"{key} {quoted}".strip())
|
|
|
+ if not lines:
|
|
|
+ return "依据:"
|
|
|
+ return "依据:" + lines[0] + (("\n" + "\n".join(lines[1:])) if len(lines) > 1 else "")
|
|
|
+
|
|
|
+
|
|
|
+def _split_basis(text: str) -> tuple[str, str]:
|
|
|
+ """从「产品原型 某文件.md 「列名」」里拆出文件和列名。没有列名就不分组。"""
|
|
|
+
|
|
|
+ if "「" not in text or "」" not in text:
|
|
|
+ return "", ""
|
|
|
+ doc, _, rest = text.partition("「")
|
|
|
+ field, _, _tail = rest.partition("」")
|
|
|
+ doc = doc.strip()
|
|
|
+ field = field.strip()
|
|
|
+ if not doc or not field:
|
|
|
+ return "", ""
|
|
|
+ return doc, field
|
|
|
|
|
|
|
|
|
def _formula_names(rows: Sequence[_View]) -> list[str]:
|
|
|
@@ -466,9 +516,9 @@ def format_topic(title: str, lines: Sequence[object]) -> str:
|
|
|
return _lines(
|
|
|
(
|
|
|
f"字段:{title}",
|
|
|
- _stacked("期望", names[:8] or ["该项已登记的公式"]),
|
|
|
+ _expect_line(names or ["该项已登记的公式"]),
|
|
|
f"实据:{actual}",
|
|
|
- _stacked("依据", bases[:6] or ["见各列登记"]),
|
|
|
+ _basis_line(bases or ["见各列登记"]),
|
|
|
f"结论:{label}({counts[VERDICT_CORRECT]}/{len(orders)})",
|
|
|
)
|
|
|
)
|
|
|
@@ -486,9 +536,59 @@ def _clear_title(formula: str) -> str:
|
|
|
return formula
|
|
|
|
|
|
|
|
|
+def _reflow_topic(text: str) -> str:
|
|
|
+ """已落库的字段块再排一次。期望收成一句,同一份原型只留一次文件名。"""
|
|
|
+
|
|
|
+ labels = ("字段:", "期望:", "实据:", "依据:", "结论:")
|
|
|
+ sections: list[tuple[str, list[str]]] = []
|
|
|
+ label = ""
|
|
|
+ parts: list[str] = []
|
|
|
+ for raw in text.splitlines():
|
|
|
+ line = raw.strip()
|
|
|
+ if not line:
|
|
|
+ continue
|
|
|
+ matched = next((item for item in labels if line.startswith(item)), "")
|
|
|
+ if matched:
|
|
|
+ if label:
|
|
|
+ sections.append((label, parts))
|
|
|
+ label = matched
|
|
|
+ rest = line[len(matched) :].strip()
|
|
|
+ parts = [rest] if rest else []
|
|
|
+ continue
|
|
|
+ if label:
|
|
|
+ parts.append(line)
|
|
|
+ if label:
|
|
|
+ sections.append((label, parts))
|
|
|
+ rows: list[str] = []
|
|
|
+ for name, chunks in sections:
|
|
|
+ if name == "期望:":
|
|
|
+ items: list[str] = []
|
|
|
+ for chunk in chunks:
|
|
|
+ items.extend(_split_items(chunk))
|
|
|
+ rows.append(_expect_line(items))
|
|
|
+ continue
|
|
|
+ if name == "依据:":
|
|
|
+ rows.append(_basis_line(chunks))
|
|
|
+ continue
|
|
|
+ body = "\n".join(chunk for chunk in chunks if chunk)
|
|
|
+ rows.append(f"{name}{body}" if body else name)
|
|
|
+ return "\n".join(rows)
|
|
|
+
|
|
|
+
|
|
|
+def _split_items(text: str) -> list[str]:
|
|
|
+ """把已经用逗号或分号连过的字段再拆开,避免重复加标点。"""
|
|
|
+
|
|
|
+ found: list[str] = []
|
|
|
+ for piece in text.replace(";", ",").split(","):
|
|
|
+ item = piece.strip().rstrip("。").strip()
|
|
|
+ if item:
|
|
|
+ found.append(item)
|
|
|
+ return found
|
|
|
+
|
|
|
+
|
|
|
def _index_block(text: str) -> str:
|
|
|
if text.startswith("字段:"):
|
|
|
- return text
|
|
|
+ return _reflow_topic(text)
|
|
|
head, _, rest = text.partition(":")
|
|
|
actual = rest.split("。", 1)[0] if rest else text
|
|
|
detail = "对应报表卡"
|