feat(investigations): refine report authoring and replay flushing
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
"""Reader-facing text checks for new publications, never historical reads."""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from app.domain.schemas.answer_ui import ReportUiBlock
|
||||
|
||||
|
||||
_execution_text = re.compile(
|
||||
r"\b(?:execution_generation|handling_context|verification_ready|waiting_work_order|"
|
||||
r"waiting_scada|returned_at|verified_through|required_samples|interval_seconds|"
|
||||
r"knowledge_cutoff|batch_sequence|query_complete|evidence_id|action_id|run_id|"
|
||||
r"task_id|source_session_id|approval_status|execution_status|verification_evidence_id|"
|
||||
r"normal_day_count|baseline_method|selection_method|get_task_context|query_scada_readings|"
|
||||
r"run_burst_location|publish_task_report|publish_task_progress)\b|"
|
||||
r"\b(?:status|phase|interval)\s*[:=]|\b(?:mcp|recovery|sha256):|"
|
||||
r"\bskill(?:\.(?:name|version))?\s*[:=]|\bskill\s+[\w-]+\s+v?\d|"
|
||||
r"\b(?:final|interim)/concluded\b|"
|
||||
r"[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}",
|
||||
re.IGNORECASE | re.ASCII,
|
||||
)
|
||||
_source_time = re.compile(
|
||||
r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}|UTC\s*[+-]\s*\d|Asia/Shanghai|上海时间|"
|
||||
r"\d{2}:\d{2}(?::\d{2})?\s*\+\d{2}:\d{2}",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
_unrendered_markup = re.compile(r"`|\*\*|</?[A-Za-z][^>]*>|^\s{0,3}#{1,6}\s", re.MULTILINE)
|
||||
|
||||
|
||||
def validate_report_text(value: str) -> str:
|
||||
if _execution_text.search(value):
|
||||
raise ValueError("请将执行字段和状态码改写为中文业务说明,完整编号保留在结构化引用中")
|
||||
if _source_time.search(value):
|
||||
raise ValueError("请将来源时间转换为当地日期和时刻,正文省略时区名称、偏移和 ISO 时间戳")
|
||||
if _unrendered_markup.search(value):
|
||||
raise ValueError("可见文字使用纯文本;重点与明细使用对应结果组件,不写 Markdown 或 HTML")
|
||||
return value
|
||||
|
||||
|
||||
def validate_report_ui_text(blocks: list[ReportUiBlock]) -> None:
|
||||
for item in blocks:
|
||||
validate_report_text(item.block.fallback_text)
|
||||
for element in item.block.spec.elements.values():
|
||||
props = element.props
|
||||
for key in ("title", "summary", "text", "description", "x_axis_name", "y_axis_name"):
|
||||
if isinstance(props.get(key), str):
|
||||
validate_report_text(props[key])
|
||||
for value in props.get("columns", []):
|
||||
validate_report_text(value)
|
||||
for row in props.get("rows", []):
|
||||
for value in row:
|
||||
validate_report_text(value)
|
||||
for value in props.get("x_data", []):
|
||||
validate_report_text(value)
|
||||
for series in props.get("series", []):
|
||||
validate_report_text(series["name"])
|
||||
|
||||
|
||||
def validate_presentation_command_text(command: dict[str, Any]) -> None:
|
||||
"""Check visible labels without touching geometry, references or timestamps."""
|
||||
if command["action"] == "set_overlay":
|
||||
validate_report_text(command["title"])
|
||||
elif command["action"] == "open_chart":
|
||||
chart = command["chart"]
|
||||
for key in ("title", "x_axis_name", "y_axis_name"):
|
||||
if isinstance(chart.get(key), str):
|
||||
validate_report_text(chart[key])
|
||||
for value in chart["x_data"]:
|
||||
validate_report_text(value)
|
||||
for series in chart["series"]:
|
||||
validate_report_text(series["name"])
|
||||
Reference in New Issue
Block a user