eduevidence 6.2.0 → 6.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +395 -0
- package/README.md +22 -13
- package/README.zh-CN.md +15 -8
- package/SKILL.md +10 -9
- package/benchmarks/evidence-library.json +277 -1
- package/docs/architecture.md +6 -3
- package/docs/j-ev-experimental.md +250 -0
- package/docs/reproducibility.md +138 -0
- package/domains/_neutral/copy/few_shots.json +21 -0
- package/domains/_neutral/copy/framing_lexicon.json +19 -0
- package/domains/_neutral/copy/module_labels.json +5 -0
- package/domains/_neutral/copy/module_labels_footer.json +102 -0
- package/domains/_neutral/copy/module_labels_modules.json +204 -0
- package/domains/_neutral/copy/module_labels_nav.json +126 -0
- package/domains/_neutral/copy/module_labels_summary.json +98 -0
- package/domains/_neutral/copy/module_labels_tables.json +164 -0
- package/domains/_neutral/copy/module_labels_v2.json +90 -0
- package/domains/_neutral/copy/risk_constructs.json +20 -0
- package/domains/_neutral/copy/section_titles.json +66 -0
- package/domains/_neutral/copy/terminology.json +11 -0
- package/domains/check_copy_packs.py +103 -0
- package/domains/education/copy/few_shots.json +22 -0
- package/domains/education/copy/framing_enums.json +167 -0
- package/domains/education/copy/framing_lexicon.json +166 -0
- package/domains/education/copy/module_labels.json +169 -0
- package/domains/education/copy/risk_constructs.json +48 -0
- package/domains/education/copy/section_titles.json +186 -0
- package/domains/education/copy/terminology.json +70 -0
- package/domains/education/manifest.json +1 -1
- package/domains/education/outcome_taxonomy.json +2 -2
- package/domains/manifest.json +1 -1
- package/domains/policy/copy/few_shots.json +22 -0
- package/domains/policy/copy/framing_enums.json +94 -0
- package/domains/policy/copy/framing_lexicon.json +174 -0
- package/domains/policy/copy/module_labels.json +168 -0
- package/domains/policy/copy/risk_constructs.json +33 -0
- package/domains/policy/copy/section_titles.json +186 -0
- package/domains/policy/copy/terminology.json +64 -0
- package/engine/capabilities.py +57 -5
- package/engine/decision_policy.py +88 -17
- package/engine/library_builtin.py +7 -4
- package/engine/tribunal.py +17 -23
- package/engine/versions.py +1 -1
- package/examples/ai-coding-assistant-evidence/EduEvidence_Report.html +4 -4
- package/examples/ai-coding-assistant-evidence/reports-5themes/EduEvidence_Report_academic.html +4 -4
- package/examples/ai-coding-assistant-evidence/reports-5themes/EduEvidence_Report_claude.html +4 -4
- package/examples/ai-coding-assistant-evidence/reports-5themes/EduEvidence_Report_datalab-dark.html +4 -4
- package/examples/ai-coding-assistant-evidence/reports-5themes/EduEvidence_Report_datalab.html +4 -4
- package/examples/ai-coding-assistant-evidence/reports-5themes/EduEvidence_Report_presentation.html +4 -4
- package/examples/spaced-retrieval-practice/EduEvidence_Report.html +2728 -0
- package/examples/spaced-retrieval-practice/report.html +2522 -0
- package/examples/spaced-retrieval-practice/reports-5themes/EduEvidence_Report_academic.html +4 -4
- package/examples/spaced-retrieval-practice/reports-5themes/EduEvidence_Report_claude.html +4 -4
- package/examples/spaced-retrieval-practice/reports-5themes/EduEvidence_Report_datalab-dark.html +4 -4
- package/examples/spaced-retrieval-practice/reports-5themes/EduEvidence_Report_datalab.html +4 -4
- package/examples/spaced-retrieval-practice/reports-5themes/EduEvidence_Report_presentation.html +4 -4
- package/examples/workplace-ai-assistant/EduEvidence_Report.html +2814 -0
- package/examples/workplace-ai-assistant/reports-5themes/EduEvidence_Report_academic.html +36 -36
- package/examples/workplace-ai-assistant/reports-5themes/EduEvidence_Report_claude.html +36 -36
- package/examples/workplace-ai-assistant/reports-5themes/EduEvidence_Report_datalab-dark.html +36 -36
- package/examples/workplace-ai-assistant/reports-5themes/EduEvidence_Report_datalab.html +36 -36
- package/examples/workplace-ai-assistant/reports-5themes/EduEvidence_Report_presentation.html +36 -36
- package/integrations/jev/__init__.py +115 -0
- package/integrations/jev/approval.py +212 -0
- package/integrations/jev/cli.py +84 -0
- package/integrations/jev/config.py +112 -0
- package/integrations/jev/gateway.py +128 -0
- package/integrations/jev/modes.py +38 -0
- package/integrations/jev/tools_classify.py +88 -0
- package/integrations/jev/tools_extract.py +111 -0
- package/integrations/jev/tools_rerank.py +71 -0
- package/integrations/jev/tools_screen.py +87 -0
- package/integrations/jev/tools_verify.py +95 -0
- package/integrations/jev_mcp.py +22 -0
- package/integrations/semantic_decide.py +286 -0
- package/integrations/semdecide_cli.py +55 -0
- package/package.json +9 -1
- package/pyproject.toml +1 -1
- package/references/report-copy-style.md +43 -3
- package/schemas/v2/decision-snapshot.schema.json +20 -9
- package/schemas/v2/intake.schema.json +191 -0
- package/scripts/build_evidence_library.py +15 -5
- package/scripts/dashboard_server.py +13 -2
- package/scripts/intake/__init__.py +31 -0
- package/scripts/intake/__main__.py +18 -0
- package/scripts/intake/background.py +78 -0
- package/scripts/intake/browser.py +79 -0
- package/scripts/intake/cli.py +57 -0
- package/scripts/intake/constants.py +57 -0
- package/scripts/intake/depth.py +53 -0
- package/scripts/intake/enhancements.py +106 -0
- package/scripts/intake/hooks.py +90 -0
- package/scripts/intake/prefs.py +76 -0
- package/scripts/intake/prompts.py +85 -0
- package/scripts/intake/session.py +152 -0
- package/scripts/lint_file_layers.py +126 -0
- package/scripts/orchestrator.py +68 -17
- package/scripts/pre_verdict_gate.py +21 -7
- package/scripts/skill_lint.py +11 -1
- package/scripts/skill_payload.py +3 -3
- package/scripts/test_adversarial_empirical.py +70 -6
- package/skill/agents/evidence-judge.md +49 -7
- package/skill/workflows/experimental-jev.md +170 -0
- package/skill/workflows/intake.md +120 -0
- package/visualization/eduevidence-report/scripts/build_infographics.py +32 -14
- package/visualization/eduevidence-report/scripts/build_report.py +75 -662
- package/visualization/eduevidence-report/scripts/report_copy_pack.py +296 -0
- package/visualization/eduevidence-report/scripts/report_copy_policy_guard.py +47 -0
- package/visualization/eduevidence-report/scripts/zh_labels.py +61 -0
- package/scripts/build_esl_artifacts.py +0 -1921
- package/scripts/build_killer_demo.py +0 -295
- package/scripts/enrich_projects_human_and_lieflat.py +0 -315
- package/scripts/generate_new_projects.py +0 -686
- package/scripts/sync_killer_demo_report.py +0 -270
|
@@ -37,7 +37,7 @@ from typing import Any
|
|
|
37
37
|
from build_charts import build_all as build_chart_specs, effect_outcomes
|
|
38
38
|
from build_figures import build_figure_data, render_figures, render_lieflat_gallery
|
|
39
39
|
from build_infographics import build_all as build_infographics
|
|
40
|
-
from lieflat_engine import REGISTRY as LIEFLAT_REGISTRY,
|
|
40
|
+
from lieflat_engine import REGISTRY as LIEFLAT_REGISTRY, ACADEMIC_FIGURE_KEYS
|
|
41
41
|
from zh_labels import label
|
|
42
42
|
|
|
43
43
|
THEMES_DIR = Path(__file__).resolve().parent.parent / "themes"
|
|
@@ -65,197 +65,21 @@ DATA_ORIGIN_LABELS = {
|
|
|
65
65
|
# Full report is intentionally NOT a fixed 12-chapter template. The template exposes
|
|
66
66
|
# semantic modules; an upstream AI may group them into any 5–7 chapter outline by writing
|
|
67
67
|
# `report_outline.chapters`. If it does not, a six-chapter fallback keeps the report usable.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
# Domain copy packs (domains/<id>/copy/) — UI 词典 + 域文案, see report_copy_pack.py.
|
|
69
|
+
from report_copy_pack import ( # noqa: E402
|
|
70
|
+
FULL_REPORT_MODULES,
|
|
71
|
+
activate_copy_pack,
|
|
72
|
+
brief_block_titles,
|
|
73
|
+
build_ui,
|
|
74
|
+
default_full_report_plan,
|
|
75
|
+
default_leads,
|
|
76
|
+
evidence_detail_label_table,
|
|
77
|
+
frame_enum_table,
|
|
78
|
+
methodology_label_table,
|
|
79
|
+
outcome_group_map,
|
|
80
|
+
scope_field_labels,
|
|
81
|
+
scope_subfield_labels,
|
|
71
82
|
)
|
|
72
|
-
DEFAULT_FULL_REPORT_PLAN = (
|
|
73
|
-
{"key": "decision", "title_zh": "结论、裁决与研究边界", "title_en": "Decision, Adjudication & Research Boundary",
|
|
74
|
-
"modules": ("decision", "scope")},
|
|
75
|
-
{"key": "evidence", "title_zh": "关键证据与结果分离", "title_en": "Key Evidence & Outcome Separation",
|
|
76
|
-
"modules": ("retrieval", "outcomes", "evidence")},
|
|
77
|
-
{"key": "quality", "title_zh": "证据可信度、反证与方法审计", "title_en": "Evidence Quality, Counterevidence & Method Audit",
|
|
78
|
-
"modules": ("quality", "conflicts", "trace")},
|
|
79
|
-
{"key": "action", "title_zh": "适用范围与教学行动", "title_en": "Applicability & Teaching Action",
|
|
80
|
-
"modules": ("applicability", "intervention")},
|
|
81
|
-
{"key": "evaluation", "title_zh": "试点设计、评估与停止条件", "title_en": "Pilot, Evaluation & Stop Conditions",
|
|
82
|
-
"modules": ("evaluation",)},
|
|
83
|
-
{"key": "sources", "title_zh": "来源、溯源与附录", "title_en": "Sources, Traceability & Appendix",
|
|
84
|
-
"modules": ("sources",)},
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
METHODOLOGY_LABELS_ZH = {
|
|
88
|
-
"control_group": "对照组", "randomization": "随机分配", "pre_test": "前测",
|
|
89
|
-
"post_test": "后测", "retention_test": "保持测试", "transfer_test": "迁移测试",
|
|
90
|
-
"sample_bias": "样本偏差", "self_selection": "自我选择偏差",
|
|
91
|
-
"measurement_validity": "测量效度", "confounders": "混杂因素",
|
|
92
|
-
"instructor_effect": "教师效应", "novelty_effect": "新奇效应",
|
|
93
|
-
"tool_version_effect": "工具版本效应", "ai_usage_policy": "AI 使用规则",
|
|
94
|
-
"dropout": "样本流失",
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
FRAME_ENUM_ZH = {
|
|
98
|
-
"teaching_decision": "教学决策",
|
|
99
|
-
"evidence_review": "证据评审",
|
|
100
|
-
"pilot_design": "试点设计",
|
|
101
|
-
"evaluation_design": "评价设计",
|
|
102
|
-
"undergraduate_year_1": "大学一年级",
|
|
103
|
-
"undergraduate_year_2": "大学二年级",
|
|
104
|
-
"undergraduate_year_3": "大学三年级",
|
|
105
|
-
"undergraduate_year_4": "大学四年级",
|
|
106
|
-
"postgraduate": "研究生",
|
|
107
|
-
"high_school": "高中",
|
|
108
|
-
"middle_school": "初中",
|
|
109
|
-
"primary_school": "小学",
|
|
110
|
-
"workplace_adult": "在职成人",
|
|
111
|
-
"computer_science": "计算机科学与技术",
|
|
112
|
-
"information_systems": "信息管理",
|
|
113
|
-
"software_engineering": "软件工程",
|
|
114
|
-
"data_science": "数据科学",
|
|
115
|
-
"mathematics": "数学",
|
|
116
|
-
"medicine": "医学",
|
|
117
|
-
"humanities": "人文",
|
|
118
|
-
"social_sciences": "社会科学",
|
|
119
|
-
"business": "商科",
|
|
120
|
-
"C_programming": "C 语言程序设计",
|
|
121
|
-
"introductory_programming": "程序设计入门",
|
|
122
|
-
"data_structures": "数据结构",
|
|
123
|
-
"algorithms": "算法",
|
|
124
|
-
"database_systems": "数据库系统",
|
|
125
|
-
"machine_learning": "机器学习",
|
|
126
|
-
"academic_writing": "学术写作",
|
|
127
|
-
"tesol": "英语教学",
|
|
128
|
-
"compulsory_core_course": "必修核心课程",
|
|
129
|
-
"elective_course": "选修课程",
|
|
130
|
-
"lecture_lab": "讲授 + 实验课",
|
|
131
|
-
"seminar": "研讨课",
|
|
132
|
-
"online_course": "在线课程",
|
|
133
|
-
"blended_learning": "混合式教学",
|
|
134
|
-
"professional_training": "职业培训",
|
|
135
|
-
"enterprise_customer_support": "企业客户支持",
|
|
136
|
-
"software_development": "软件开发",
|
|
137
|
-
"knowledge_work": "知识型工作",
|
|
138
|
-
"primary": "主要结果",
|
|
139
|
-
"secondary": "次要结果",
|
|
140
|
-
"risk": "风险结果",
|
|
141
|
-
"adopt": "采纳",
|
|
142
|
-
"modify": "修改",
|
|
143
|
-
"terminate": "终止",
|
|
144
|
-
"maintain": "维持",
|
|
145
|
-
"evaluate_impact": "评估影响",
|
|
146
|
-
"rct": "随机对照试验",
|
|
147
|
-
"quasi_experimental": "准实验",
|
|
148
|
-
"observational": "观察性研究",
|
|
149
|
-
"meta_analysis": "元分析",
|
|
150
|
-
"systematic_review": "系统综述",
|
|
151
|
-
"qualitative": "质性研究",
|
|
152
|
-
"mixed_methods": "混合方法",
|
|
153
|
-
"survey": "调查研究",
|
|
154
|
-
"case_study": "案例研究",
|
|
155
|
-
"literature_review": "文献综述",
|
|
156
|
-
"online": "线上",
|
|
157
|
-
"offline": "线下",
|
|
158
|
-
"hybrid": "混合",
|
|
159
|
-
"under_design_pending_evidence_review": "设计中(待证据评审)",
|
|
160
|
-
"human_supervised_customer_support_assistant": "人工监督的客服助手",
|
|
161
|
-
"approved_knowledge_base_and_agent_review": "已批准知识库 + 坐席复核",
|
|
162
|
-
"institutional_reform": "机构改革",
|
|
163
|
-
"enterprise_customer_support_staff": "企业客服人员",
|
|
164
|
-
"autonomous_agents_and_high_stakes_specialist_advice": "自主代理与高风险专业建议",
|
|
165
|
-
"lecture_with_lab_exercises": "讲授 + 实验练习",
|
|
166
|
-
"generative_ai_coding_assistant": "生成式 AI 编程助手",
|
|
167
|
-
"first_programming_course_no_prior_text_based_programming": "首次程序设计课程,无文本编程基础",
|
|
168
|
-
"mixed_ability_large_class_60_students": "混合能力大班(60 人)",
|
|
169
|
-
"weekly_lab_sessions": "每周实验课",
|
|
170
|
-
"one_semester": "一学期",
|
|
171
|
-
"16_weeks_one_semester": "16 周(一学期)",
|
|
172
|
-
"60_students": "60 名学生",
|
|
173
|
-
"TA_supported_two_TAs": "助教支持(2 名助教)",
|
|
174
|
-
"daily": "每日",
|
|
175
|
-
"weekly": "每周",
|
|
176
|
-
"monthly": "每月",
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
FRAME_ENUM_EN = {
|
|
180
|
-
"teaching_decision": "Teaching decision",
|
|
181
|
-
"evidence_review": "Evidence review",
|
|
182
|
-
"pilot_design": "Pilot design",
|
|
183
|
-
"evaluation_design": "Evaluation design",
|
|
184
|
-
"undergraduate_year_1": "First-year undergraduate",
|
|
185
|
-
"undergraduate_year_2": "Second-year undergraduate",
|
|
186
|
-
"undergraduate_year_3": "Third-year undergraduate",
|
|
187
|
-
"undergraduate_year_4": "Fourth-year undergraduate",
|
|
188
|
-
"postgraduate": "Postgraduate",
|
|
189
|
-
"high_school": "High school",
|
|
190
|
-
"middle_school": "Middle school",
|
|
191
|
-
"primary_school": "Primary school",
|
|
192
|
-
"workplace_adult": "Working adults",
|
|
193
|
-
"computer_science": "Computer science",
|
|
194
|
-
"information_systems": "Information systems",
|
|
195
|
-
"software_engineering": "Software engineering",
|
|
196
|
-
"data_science": "Data science",
|
|
197
|
-
"mathematics": "Mathematics",
|
|
198
|
-
"medicine": "Medicine",
|
|
199
|
-
"humanities": "Humanities",
|
|
200
|
-
"social_sciences": "Social sciences",
|
|
201
|
-
"business": "Business",
|
|
202
|
-
"C_programming": "C programming",
|
|
203
|
-
"introductory_programming": "Introductory programming",
|
|
204
|
-
"data_structures": "Data structures",
|
|
205
|
-
"algorithms": "Algorithms",
|
|
206
|
-
"database_systems": "Database systems",
|
|
207
|
-
"machine_learning": "Machine learning",
|
|
208
|
-
"academic_writing": "Academic writing",
|
|
209
|
-
"tesol": "TESOL",
|
|
210
|
-
"compulsory_core_course": "Compulsory core course",
|
|
211
|
-
"elective_course": "Elective course",
|
|
212
|
-
"lecture_lab": "Lecture + lab",
|
|
213
|
-
"seminar": "Seminar",
|
|
214
|
-
"online_course": "Online course",
|
|
215
|
-
"blended_learning": "Blended learning",
|
|
216
|
-
"professional_training": "Professional training",
|
|
217
|
-
"enterprise_customer_support": "Enterprise customer support",
|
|
218
|
-
"software_development": "Software development",
|
|
219
|
-
"knowledge_work": "Knowledge work",
|
|
220
|
-
"primary": "Primary outcomes",
|
|
221
|
-
"secondary": "Secondary outcomes",
|
|
222
|
-
"risk": "Risk outcomes",
|
|
223
|
-
"adopt": "Adopt",
|
|
224
|
-
"modify": "Modify",
|
|
225
|
-
"terminate": "Terminate",
|
|
226
|
-
"maintain": "Maintain",
|
|
227
|
-
"evaluate_impact": "Evaluate impact",
|
|
228
|
-
"rct": "Randomised controlled trial",
|
|
229
|
-
"quasi_experimental": "Quasi-experimental",
|
|
230
|
-
"observational": "Observational",
|
|
231
|
-
"meta_analysis": "Meta-analysis",
|
|
232
|
-
"systematic_review": "Systematic review",
|
|
233
|
-
"qualitative": "Qualitative",
|
|
234
|
-
"mixed_methods": "Mixed methods",
|
|
235
|
-
"survey": "Survey",
|
|
236
|
-
"case_study": "Case study",
|
|
237
|
-
"literature_review": "Literature review",
|
|
238
|
-
"online": "Online",
|
|
239
|
-
"offline": "Offline",
|
|
240
|
-
"hybrid": "Hybrid",
|
|
241
|
-
"under_design_pending_evidence_review": "Under design (pending evidence review)",
|
|
242
|
-
"approved_knowledge_base_and_agent_review": "Approved knowledge base + agent review",
|
|
243
|
-
"institutional_reform": "Institutional reform",
|
|
244
|
-
"enterprise_customer_support_staff": "Customer-support staff",
|
|
245
|
-
"autonomous_agents_and_high_stakes_specialist_advice": "Autonomous agents and high-stakes specialist advice",
|
|
246
|
-
"lecture_with_lab_exercises": "Lecture with lab exercises",
|
|
247
|
-
"generative_ai_coding_assistant": "Generative AI coding assistant",
|
|
248
|
-
"first_programming_course_no_prior_text_based_programming": "First programming course, no prior text-based programming",
|
|
249
|
-
"mixed_ability_large_class_60_students": "Mixed-ability large class (60 students)",
|
|
250
|
-
"weekly_lab_sessions": "Weekly lab sessions",
|
|
251
|
-
"one_semester": "One semester",
|
|
252
|
-
"16_weeks_one_semester": "16 weeks (one semester)",
|
|
253
|
-
"60_students": "60 students",
|
|
254
|
-
"TA_supported_two_TAs": "TA supported (2 TAs)",
|
|
255
|
-
"daily": "Daily",
|
|
256
|
-
"weekly": "Weekly",
|
|
257
|
-
"monthly": "Monthly",
|
|
258
|
-
}
|
|
259
83
|
|
|
260
84
|
DIR_LABEL = {"support": "支持", "contradict": "反驳", "neutral": "中性"}
|
|
261
85
|
DIR_CLASS = {"support": "pos", "contradict": "neg", "neutral": "neu"}
|
|
@@ -266,359 +90,6 @@ EFFECT_CLASS = {"positive": "pos", "negative": "neg", "null": "neu", "neutral":
|
|
|
266
90
|
# 双语 UI 文案
|
|
267
91
|
# ---------------------------------------------------------------------------
|
|
268
92
|
|
|
269
|
-
UI_ZH = {
|
|
270
|
-
"theme_label": "主题",
|
|
271
|
-
"lang_label": "语言",
|
|
272
|
-
"zh": "中文",
|
|
273
|
-
"en": "EN",
|
|
274
|
-
"visual_brief": "可视化摘要",
|
|
275
|
-
"full_report": "完整报告",
|
|
276
|
-
"contents": "目录",
|
|
277
|
-
"collapse_contents": "收起目录",
|
|
278
|
-
"expand_contents": "展开目录",
|
|
279
|
-
"expand_evidence": "查看完整证据",
|
|
280
|
-
"expand_methodology": "查看审计依据",
|
|
281
|
-
"expand_source": "查看来源与溯源",
|
|
282
|
-
"expand_details": "展开完整说明",
|
|
283
|
-
"what_this_means": "这意味着什么",
|
|
284
|
-
"original_title": "原文标题",
|
|
285
|
-
"original_text": "原文",
|
|
286
|
-
"full_report_intro": "结论前置:全部可追溯证据与方法学细节都在这里,关键论证位置穿插有意义的可视化,每个数字都能回查到 result.json。",
|
|
287
|
-
"section_titles": {
|
|
288
|
-
"01": "01 执行决策", "02": "02 结果证据概览", "03": "03 证据矩阵",
|
|
289
|
-
"04": "04 证据裁决", "05": "05 方法学审计", "06": "06 冲突分析",
|
|
290
|
-
"07": "07 主张-证据追溯", "08": "08 适用性", "09": "09 教学干预",
|
|
291
|
-
"10": "10 评价方案", "11": "11 基准测试", "12": "12 来源与溯源",
|
|
292
|
-
},
|
|
293
|
-
"section_leads": {
|
|
294
|
-
"01": "本节先给结论:最终怎么裁决、置信度多高、靠哪几条证据。",
|
|
295
|
-
"02": "一图看清:哪些学习结果有支持证据、哪些被反驳。",
|
|
296
|
-
"03": "每条证据来自哪项研究、测了什么、方向与质量如何;可筛选、可搜索。",
|
|
297
|
-
"04": "证据允许主张什么、不允许主张什么;缺失的关键证据是什么。",
|
|
298
|
-
"05": "研究质量可靠吗?哪些方法学问题让结论打折。",
|
|
299
|
-
"06": "不同研究为何结论不同;分歧出在哪一环。",
|
|
300
|
-
"07": "从结论到证据到原始来源,每一步都可追查。",
|
|
301
|
-
"08": "结论适用于谁、什么课程与结果、需要什么条件。",
|
|
302
|
-
"09": "试点怎么分阶段放开 AI 规则;什么情况必须叫停。",
|
|
303
|
-
"10": "如何验证效果:指标、对照、成功阈值。",
|
|
304
|
-
"11": "EduEvidence 自身基准表现:引用精度与成本。",
|
|
305
|
-
"12": "每篇文献是谁、出自哪里、如何获取。",
|
|
306
|
-
},
|
|
307
|
-
"decision_kpi": ["决策", "置信度", "证据最充分的结果", "最不确定的结果", "主要风险", "来源数量"],
|
|
308
|
-
"summary_title": "一句话结论",
|
|
309
|
-
"summary_question": "问题",
|
|
310
|
-
"summary_evidence": "依据",
|
|
311
|
-
"summary_action": "行动",
|
|
312
|
-
"outcome_table": ["结果类型", "正向效应", "负向效应", "零效应", "证据"],
|
|
313
|
-
"figure1_caption": "图 1. 各结果类型的正向 / 负向 / 零效应证据数量(基于 effect_direction,不等同于 Claim 是否被支持)。",
|
|
314
|
-
"matrix_filter": "筛选 / 搜索",
|
|
315
|
-
"matrix_search_ph": "搜索证据…",
|
|
316
|
-
"matrix_all_dir": "全部效应",
|
|
317
|
-
"matrix_all_outcome": "全部结果",
|
|
318
|
-
"matrix_heads": ["ID", "结果", "效应", "质量", "主张", "来源"],
|
|
319
|
-
"matrix_details": "查看完整证据",
|
|
320
|
-
"matrix_detail_labels": ["研究标题", "研究设计", "人群", "干预", "直接性"],
|
|
321
|
-
"matrix_source_missing": "无可验证来源",
|
|
322
|
-
"hero_action": "建议决策",
|
|
323
|
-
"hero_confidence": "置信度",
|
|
324
|
-
"hero_supported": "最强支持结论",
|
|
325
|
-
"hero_uncertain": "关键不确定性 / 反例",
|
|
326
|
-
"hero_risk": "主要风险",
|
|
327
|
-
"hero_next": "下一步",
|
|
328
|
-
"hero_provenance": "证据 / 来源",
|
|
329
|
-
"outcome_separation_title": "结果分离 · 任务表现 ≠ 学习效果",
|
|
330
|
-
"effect_positive": "正向效应",
|
|
331
|
-
"effect_negative": "负向效应",
|
|
332
|
-
"effect_null": "零效应",
|
|
333
|
-
"outcome_group_task": "任务 / 近端表现",
|
|
334
|
-
"outcome_group_learning": "学习 / 保持 / 迁移",
|
|
335
|
-
"outcome_group_risk": "风险 / 依赖",
|
|
336
|
-
"outcome_group_other": "其他结果",
|
|
337
|
-
"outcome_sep_note": "将不同结果类型分开裁决,避免把训练时更快、更高分直接等同于真正学会。",
|
|
338
|
-
"tribunal_decision": "决策",
|
|
339
|
-
"tribunal_confidence": "置信度",
|
|
340
|
-
"tribunal_can": "可以主张",
|
|
341
|
-
"tribunal_uncertain": "尚不能主张",
|
|
342
|
-
"tribunal_cannot": "被反驳的主张",
|
|
343
|
-
"tribunal_missing": "缺失证据",
|
|
344
|
-
"tribunal_flow": "EvidenceFlow 协议",
|
|
345
|
-
"tribunal_figure": "裁决信息图",
|
|
346
|
-
"method_audit_heads": ["检查项", "状态", "说明"],
|
|
347
|
-
"method_guard": "任务 vs 学习护栏",
|
|
348
|
-
"conflict_verdict": "裁决说明",
|
|
349
|
-
"trace_decision": "决策",
|
|
350
|
-
"trace_claim_prefix": "主张",
|
|
351
|
-
"trace_no_source": "无来源",
|
|
352
|
-
"applicability": ["适用于谁", "适用课程", "适用结果", "适用条件", "目标人群", "目标情境"],
|
|
353
|
-
"intervention_learners": "目标学习者",
|
|
354
|
-
"intervention_duration": "试点时长",
|
|
355
|
-
"intervention_policy": "AI 使用规则",
|
|
356
|
-
"intervention_rule": "AI 规则",
|
|
357
|
-
"intervention_activities": "活动",
|
|
358
|
-
"intervention_check": "结果检查",
|
|
359
|
-
"intervention_stop": "停止条件",
|
|
360
|
-
"intervention_timeline": "干预时间线信息图",
|
|
361
|
-
"evaluation_question": "研究问题",
|
|
362
|
-
"evaluation_measures": ["基线", "后测", "保持测试", "迁移测试"],
|
|
363
|
-
"evaluation_metrics": ["过程指标", "学习指标", "风险指标"],
|
|
364
|
-
"evaluation_threshold": "成功阈值",
|
|
365
|
-
"evaluation_plan": "分析计划",
|
|
366
|
-
"evaluation_figure": "评价设计信息图",
|
|
367
|
-
"benchmark_note": "result.json 未携带 benchmark.baselines,本图不绘制;基准表现见独立基准报告。",
|
|
368
|
-
"sources_title": "来源列表",
|
|
369
|
-
"sources_heads": ["ID", "标题", "年份", "权威级别", "可验证位置"],
|
|
370
|
-
"provenance_title": "Fetch 溯源",
|
|
371
|
-
"provenance_heads": ["来源", "Fetch 方式", "状态", "降级", "时间"],
|
|
372
|
-
"provenance_search": "搜索提供方",
|
|
373
|
-
"provenance_time": "检索时间",
|
|
374
|
-
"provenance_empty": "无逐条 fetch 记录(来源由研究管线直接提供)。",
|
|
375
|
-
"no_data": "无数据。",
|
|
376
|
-
"header_evidence": "证据 ",
|
|
377
|
-
"header_sources": "来源 ",
|
|
378
|
-
"header_mode": "模式:",
|
|
379
|
-
"header_generated": "生成时间:",
|
|
380
|
-
"header_evidence_suffix": " 条",
|
|
381
|
-
"header_sources_suffix": " 个",
|
|
382
|
-
"footer_schema": "Schema",
|
|
383
|
-
"footer_claims": "Claim Binding",
|
|
384
|
-
"footer_numbers": "Numeric Consistency",
|
|
385
|
-
"footer_bilingual": "Bilingual Structure",
|
|
386
|
-
"footer_language": "语言人话化",
|
|
387
|
-
"footer_no_false_precision": "无伪精度",
|
|
388
|
-
"footer_lieflat_bound": "Lieflat 数据溯源",
|
|
389
|
-
"footer_no_axis_distortion": "坐标轴无失真",
|
|
390
|
-
"footer_colorblind_safe": "色盲安全",
|
|
391
|
-
"footer": "EduEvidence 证据报告 · {integrity} · 单文件离线可打开 · 数据源:result.json",
|
|
392
|
-
"matrix_search_label": "筛选 / 搜索证据",
|
|
393
|
-
"matrix_dir_filter": "按效应方向筛选",
|
|
394
|
-
"matrix_outcome_filter": "按结果类型筛选",
|
|
395
|
-
"svg_balance_title": "各结果类型证据效应分布",
|
|
396
|
-
"svg_balance_desc": "各结果类型的正向 / 负向 / 零效应证据条数(基于 effect_direction,不等同于 Claim 是否被支持)。",
|
|
397
|
-
"svg_figure1_title": "各结果类型效应方向分布(出版级学术图)",
|
|
398
|
-
"svg_figure1_desc": "各结果类型的正向 / 负向 / 零效应证据条数;计数轴整数刻度,不随主题变化。来源:EduEvidence result.json。",
|
|
399
|
-
"svg_benchmark_title": "基准对比:各基线引用支持精度",
|
|
400
|
-
"svg_benchmark_desc": "各基线的引用支持精度(Citation support precision)对比;无基准数据时不绘制。",
|
|
401
|
-
"svg_workflow_title": "EvidenceFlow 协议流程",
|
|
402
|
-
"svg_workflow_desc": "从问题框架、检索、抓取验证、证据抽取、反方质疑、方法审计、裁决到适用性与干预评价的完整流程。",
|
|
403
|
-
"svg_tribunal_title": "证据裁决信息图",
|
|
404
|
-
"svg_tribunal_desc": "可以主张与不可主张的证据 ID 与建议决策徽章;完整主张文本见下方裁决卡片。",
|
|
405
|
-
"svg_intervention_title": "教学干预时间线",
|
|
406
|
-
"svg_intervention_desc": "各试点阶段的短名称与活动数量;完整 AI 使用规则见阶段说明块。",
|
|
407
|
-
"svg_evaluation_title": "评价设计流程",
|
|
408
|
-
"svg_evaluation_desc": "基线、后测、保持测试与迁移测试的评价流程;完整指标与分析计划见评估章节。",
|
|
409
|
-
"raw_tag_title": "原始标识",
|
|
410
|
-
"summary_tag_support": "支持",
|
|
411
|
-
"summary_tag_contradict": "反驳",
|
|
412
|
-
"summary_confidence_prefix": "(置信度:",
|
|
413
|
-
"summary_confidence_suffix": ")",
|
|
414
|
-
"method_target": "审查目标",
|
|
415
|
-
"applicability_not_suitable": "不适用于",
|
|
416
|
-
"applicability_conditions": "适用条件",
|
|
417
|
-
"trace_claim_sep": ":",
|
|
418
|
-
"colon": ":",
|
|
419
|
-
"lang_switcher_aria": "语言切换 / Language switch",
|
|
420
|
-
"theme_switcher_aria": "主题 / Theme",
|
|
421
|
-
"v2_project_title": "项目与研究历史",
|
|
422
|
-
"v2_project_id": "项目 ID",
|
|
423
|
-
"v2_graph_revision": "证据图版本",
|
|
424
|
-
"v2_decision_snapshot": "决策快照",
|
|
425
|
-
"v2_timeline": "项目时间线",
|
|
426
|
-
"v2_gaps_title": "知识缺口",
|
|
427
|
-
"v2_gap_type": "缺口类型",
|
|
428
|
-
"v2_gap_priority": "优先级",
|
|
429
|
-
"v2_gap_reasoning": "依据",
|
|
430
|
-
"v2_design_title": "研究设计",
|
|
431
|
-
"v2_design_type": "设计类型",
|
|
432
|
-
"v2_design_question": "研究问题",
|
|
433
|
-
"v2_provenance_title": "数据集与分析溯源",
|
|
434
|
-
"v2_diff_title": "决策变更",
|
|
435
|
-
"v2_diff_action": "决策动作",
|
|
436
|
-
"v2_diff_confidence": "置信度",
|
|
437
|
-
"v2_diff_claims": "变更的主张",
|
|
438
|
-
"v2_diff_gaps": "已解决/新增缺口",
|
|
439
|
-
"v2_revision": "版本",
|
|
440
|
-
"v2_decision": "决策",
|
|
441
|
-
"v2_no_v2_data": "(无 V2 项目数据)",
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
UI_EN = {
|
|
445
|
-
"theme_label": "Theme",
|
|
446
|
-
"lang_label": "Language",
|
|
447
|
-
"zh": "中文",
|
|
448
|
-
"en": "EN",
|
|
449
|
-
"visual_brief": "Visual Brief",
|
|
450
|
-
"full_report": "Full Report",
|
|
451
|
-
"contents": "Contents",
|
|
452
|
-
"collapse_contents": "Collapse contents",
|
|
453
|
-
"expand_contents": "Expand contents",
|
|
454
|
-
"expand_evidence": "View full evidence",
|
|
455
|
-
"expand_methodology": "View audit rationale",
|
|
456
|
-
"expand_source": "View source & provenance",
|
|
457
|
-
"expand_details": "Expand full explanation",
|
|
458
|
-
"what_this_means": "What this means",
|
|
459
|
-
"original_title": "Original title",
|
|
460
|
-
"original_text": "Original text",
|
|
461
|
-
"full_report_intro": "Conclusions first: every traceable piece of evidence and method note lives here, with visuals only at points where they add meaning. Every number traces back to result.json.",
|
|
462
|
-
"section_titles": {
|
|
463
|
-
"01": "01 Executive Decision", "02": "02 Outcome Evidence Overview",
|
|
464
|
-
"03": "03 Evidence Matrix", "04": "04 Evidence Tribunal",
|
|
465
|
-
"05": "05 Methodology Audit", "06": "06 Conflict Analysis",
|
|
466
|
-
"07": "07 Claim-Evidence Trace", "08": "08 Applicability",
|
|
467
|
-
"09": "09 Teaching Intervention", "10": "10 Evaluation Plan",
|
|
468
|
-
"11": "11 Benchmark", "12": "12 Sources & Provenance",
|
|
469
|
-
},
|
|
470
|
-
"section_leads": {
|
|
471
|
-
"01": "The verdict first: what we decide, at what confidence, on which evidence.",
|
|
472
|
-
"02": "At a glance: which learning outcomes have supporting evidence, which are contradicted.",
|
|
473
|
-
"03": "Where each piece of evidence comes from, what it measures, its direction and quality — filterable and searchable.",
|
|
474
|
-
"04": "What the evidence lets us claim, what it does not, and what is still missing.",
|
|
475
|
-
"05": "How reliable are these studies, and which methodological concerns discount the conclusions.",
|
|
476
|
-
"06": "Why studies disagree — and where exactly they diverge.",
|
|
477
|
-
"07": "Every step from conclusion to evidence to source stays traceable.",
|
|
478
|
-
"08": "Who the conclusion applies to, for which course and outcomes, under what conditions.",
|
|
479
|
-
"09": "How AI usage rules phase in during a pilot, and when we must stop.",
|
|
480
|
-
"10": "How we verify real effects: metrics, comparison, success threshold.",
|
|
481
|
-
"11": "How EduEvidence itself performs: citation precision and cost.",
|
|
482
|
-
"12": "Who wrote each cited study, where it came from, how it was fetched.",
|
|
483
|
-
},
|
|
484
|
-
"decision_kpi": ["Decision", "Confidence", "Best-supported outcome", "Most uncertain outcome", "Main risk", "Sources"],
|
|
485
|
-
"summary_title": "Bottom line",
|
|
486
|
-
"summary_question": "Question",
|
|
487
|
-
"summary_evidence": "Evidence",
|
|
488
|
-
"summary_action": "Action",
|
|
489
|
-
"outcome_table": ["Outcome", "Positive effect", "Negative effect", "Null effect", "Evidence"],
|
|
490
|
-
"figure1_caption": "Fig. 1. Positive / negative / null effect counts per outcome (based on effect_direction, not claim support).",
|
|
491
|
-
"matrix_filter": "Filter / Search",
|
|
492
|
-
"matrix_search_ph": "Search evidence…",
|
|
493
|
-
"matrix_all_dir": "All effects",
|
|
494
|
-
"matrix_all_outcome": "All outcomes",
|
|
495
|
-
"matrix_heads": ["ID", "Outcome", "Effect", "Quality", "Claim", "Source"],
|
|
496
|
-
"matrix_details": "View full evidence",
|
|
497
|
-
"matrix_detail_labels": ["Study title", "Design", "Population", "Intervention", "Directness"],
|
|
498
|
-
"matrix_source_missing": "No verifiable source",
|
|
499
|
-
"hero_action": "Recommended decision",
|
|
500
|
-
"hero_confidence": "Confidence",
|
|
501
|
-
"hero_supported": "Strongest supported conclusion",
|
|
502
|
-
"hero_uncertain": "Key uncertainty / contradiction",
|
|
503
|
-
"hero_risk": "Main risk",
|
|
504
|
-
"hero_next": "Next action",
|
|
505
|
-
"hero_provenance": "Evidence / sources",
|
|
506
|
-
"outcome_separation_title": "Outcome Separation · Task performance ≠ learning",
|
|
507
|
-
"effect_positive": "Positive effect",
|
|
508
|
-
"effect_negative": "Negative effect",
|
|
509
|
-
"effect_null": "Null effect",
|
|
510
|
-
"outcome_group_task": "Task / proximal performance",
|
|
511
|
-
"outcome_group_learning": "Learning / retention / transfer",
|
|
512
|
-
"outcome_group_risk": "Risk / dependency",
|
|
513
|
-
"outcome_group_other": "Other outcomes",
|
|
514
|
-
"outcome_sep_note": "Outcomes are adjudicated separately so faster training performance is not silently treated as evidence of learning.",
|
|
515
|
-
"tribunal_decision": "Decision",
|
|
516
|
-
"tribunal_confidence": "Confidence",
|
|
517
|
-
"tribunal_can": "Can claim",
|
|
518
|
-
"tribunal_uncertain": "Cannot yet claim",
|
|
519
|
-
"tribunal_cannot": "Contradicted claims",
|
|
520
|
-
"tribunal_missing": "Missing evidence",
|
|
521
|
-
"tribunal_flow": "EvidenceFlow Protocol",
|
|
522
|
-
"tribunal_figure": "Tribunal infographic",
|
|
523
|
-
"method_audit_heads": ["Item", "Status", "Note"],
|
|
524
|
-
"method_guard": "Task vs learning guard",
|
|
525
|
-
"conflict_verdict": "Tribunal note",
|
|
526
|
-
"trace_decision": "Decision",
|
|
527
|
-
"trace_claim_prefix": "Claim",
|
|
528
|
-
"trace_no_source": "No source",
|
|
529
|
-
"applicability": ["Suitable for", "Course", "Outcomes", "Conditions", "Target population", "Target context"],
|
|
530
|
-
"intervention_learners": "Target learners",
|
|
531
|
-
"intervention_duration": "Pilot duration",
|
|
532
|
-
"intervention_policy": "AI usage policy",
|
|
533
|
-
"intervention_rule": "AI rule",
|
|
534
|
-
"intervention_activities": "Activities",
|
|
535
|
-
"intervention_check": "Outcome check",
|
|
536
|
-
"intervention_stop": "Stop conditions",
|
|
537
|
-
"intervention_timeline": "Intervention timeline infographic",
|
|
538
|
-
"evaluation_question": "Research question",
|
|
539
|
-
"evaluation_measures": ["Baseline", "Post test", "Retention", "Transfer"],
|
|
540
|
-
"evaluation_metrics": ["Process metrics", "Learning metrics", "Risk metrics"],
|
|
541
|
-
"evaluation_threshold": "Success threshold",
|
|
542
|
-
"evaluation_plan": "Analysis plan",
|
|
543
|
-
"evaluation_figure": "Evaluation design infographic",
|
|
544
|
-
"benchmark_note": "result.json carries no benchmark.baselines, so this visual is omitted; see the standalone benchmark report.",
|
|
545
|
-
"sources_title": "Source list",
|
|
546
|
-
"sources_heads": ["ID", "Title", "Year", "Authority", "Verifiable location"],
|
|
547
|
-
"provenance_title": "Fetch provenance",
|
|
548
|
-
"provenance_heads": ["Source", "Fetch method", "Status", "Fallback", "Time"],
|
|
549
|
-
"provenance_search": "Search provider",
|
|
550
|
-
"provenance_time": "Fetched at",
|
|
551
|
-
"provenance_empty": "No per-source fetch records (sources provided directly by the research pipeline).",
|
|
552
|
-
"no_data": "No data.",
|
|
553
|
-
"header_evidence": "Evidence: ",
|
|
554
|
-
"header_sources": "Sources: ",
|
|
555
|
-
"header_mode": "Mode: ",
|
|
556
|
-
"header_generated": "Generated: ",
|
|
557
|
-
"header_evidence_suffix": "",
|
|
558
|
-
"header_sources_suffix": "",
|
|
559
|
-
"footer_schema": "Schema",
|
|
560
|
-
"footer_claims": "Claim Binding",
|
|
561
|
-
"footer_numbers": "Numeric Consistency",
|
|
562
|
-
"footer_bilingual": "Bilingual Structure",
|
|
563
|
-
"footer_language": "Human Language",
|
|
564
|
-
"footer_no_false_precision": "False Precision",
|
|
565
|
-
"footer_lieflat_bound": "Lieflat Data Bound",
|
|
566
|
-
"footer_no_axis_distortion": "Axis Distortion",
|
|
567
|
-
"footer_colorblind_safe": "Colorblind Safe",
|
|
568
|
-
"footer": "EduEvidence Evidence Report · {integrity} · single-file offline · source: result.json",
|
|
569
|
-
"matrix_search_label": "Filter / search evidence",
|
|
570
|
-
"matrix_dir_filter": "Filter by effect direction",
|
|
571
|
-
"matrix_outcome_filter": "Filter by outcome type",
|
|
572
|
-
"svg_balance_title": "Outcome evidence effect balance",
|
|
573
|
-
"svg_balance_desc": "Positive / negative / null effect counts per outcome (based on effect_direction, not claim support).",
|
|
574
|
-
"svg_figure1_title": "Effect direction by outcome type (publication figure)",
|
|
575
|
-
"svg_figure1_desc": "Counts of positive / negative / null effects per outcome type with an integer count axis, theme-independent. Source: EduEvidence result.json.",
|
|
576
|
-
"svg_benchmark_title": "Benchmark: citation support precision by baseline",
|
|
577
|
-
"svg_benchmark_desc": "Citation support precision per baseline; not drawn when no baseline data exists.",
|
|
578
|
-
"svg_workflow_title": "EvidenceFlow Protocol",
|
|
579
|
-
"svg_workflow_desc": "Research flow from framing, retrieval, fetch/verify, extraction, challenge, method audit and adjudication to applicability and intervention evaluation.",
|
|
580
|
-
"svg_tribunal_title": "Evidence Tribunal infographic",
|
|
581
|
-
"svg_tribunal_desc": "Evidence IDs for claims that can and cannot be claimed, plus the recommended action badge; full claim text is in the tribunal cards below.",
|
|
582
|
-
"svg_intervention_title": "Teaching intervention timeline",
|
|
583
|
-
"svg_intervention_desc": "Short phase names and activity counts; full AI usage rules are in the phase blocks.",
|
|
584
|
-
"svg_evaluation_title": "Evaluation design flow",
|
|
585
|
-
"svg_evaluation_desc": "Evaluation flow across baseline, post test, retention and transfer; full metrics and analysis plan are in the evaluation section.",
|
|
586
|
-
"raw_tag_title": "raw id",
|
|
587
|
-
"summary_tag_support": "Support",
|
|
588
|
-
"summary_tag_contradict": "Contradict",
|
|
589
|
-
"summary_confidence_prefix": " (confidence: ",
|
|
590
|
-
"summary_confidence_suffix": ")",
|
|
591
|
-
"method_target": "Audit target",
|
|
592
|
-
"applicability_not_suitable": "Not suitable for",
|
|
593
|
-
"applicability_conditions": "Conditions",
|
|
594
|
-
"trace_claim_sep": ": ",
|
|
595
|
-
"colon": ": ",
|
|
596
|
-
"lang_switcher_aria": "语言切换 / Language switch",
|
|
597
|
-
"theme_switcher_aria": "主题 / Theme",
|
|
598
|
-
"v2_project_title": "Project & Research History",
|
|
599
|
-
"v2_project_id": "Project ID",
|
|
600
|
-
"v2_graph_revision": "Graph revision",
|
|
601
|
-
"v2_decision_snapshot": "Decision snapshot",
|
|
602
|
-
"v2_timeline": "Project timeline",
|
|
603
|
-
"v2_gaps_title": "Knowledge gaps",
|
|
604
|
-
"v2_gap_type": "Gap type",
|
|
605
|
-
"v2_gap_priority": "Priority",
|
|
606
|
-
"v2_gap_reasoning": "Reasoning",
|
|
607
|
-
"v2_design_title": "Study design",
|
|
608
|
-
"v2_design_type": "Design type",
|
|
609
|
-
"v2_design_question": "Research question",
|
|
610
|
-
"v2_provenance_title": "Dataset & analysis provenance",
|
|
611
|
-
"v2_diff_title": "Decision diff",
|
|
612
|
-
"v2_diff_action": "Decision action",
|
|
613
|
-
"v2_diff_confidence": "Confidence",
|
|
614
|
-
"v2_diff_claims": "Changed claims",
|
|
615
|
-
"v2_diff_gaps": "Resolved/new gaps",
|
|
616
|
-
"v2_revision": "Revision",
|
|
617
|
-
"v2_decision": "Decision",
|
|
618
|
-
"v2_no_v2_data": "(no V2 project data)",
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
|
|
622
93
|
class ReportInvalid(Exception):
|
|
623
94
|
"""Scientific Integrity Gate failure (§27/§60): report must not be published."""
|
|
624
95
|
|
|
@@ -630,7 +101,7 @@ def esc(text: Any) -> str:
|
|
|
630
101
|
def _svg_a11y(svg: str, title: str, desc: str) -> str:
|
|
631
102
|
"""6.5: 为嵌入的 SVG 注入双语 <title>/<desc>,并把 aria-label 换成 UI 字典文案。
|
|
632
103
|
|
|
633
|
-
title/desc 由调用方按当前语言从
|
|
104
|
+
title/desc 由调用方按当前语言从 build_ui() 取;aria-label 已存在时覆盖,
|
|
634
105
|
不存在时补上,保证英文模式下不残留中文 aria-label。
|
|
635
106
|
"""
|
|
636
107
|
if not svg:
|
|
@@ -1345,7 +816,7 @@ _ZH_RESIDUE_RE = [
|
|
|
1345
816
|
]
|
|
1346
817
|
|
|
1347
818
|
#: Raw storage identifiers inside a sentence. A single-token value may be an
|
|
1348
|
-
#: enum rendered through
|
|
819
|
+
#: enum rendered through frame_enum_table(), but an underscore inside a multi-word
|
|
1349
820
|
#: string or inside a zh sentence is a copy defect the reader would see.
|
|
1350
821
|
_RAW_IDENTIFIER_RE = re.compile(r"[A-Za-z][A-Za-z0-9]*_[A-Za-z0-9_]+")
|
|
1351
822
|
|
|
@@ -1361,7 +832,8 @@ def _scan_raw_identifiers(problems: list[str], path: str, text: str) -> None:
|
|
|
1361
832
|
return
|
|
1362
833
|
for match in _RAW_IDENTIFIER_RE.finditer(cleaned):
|
|
1363
834
|
token = match.group(0)
|
|
1364
|
-
|
|
835
|
+
enum_tokens = set(frame_enum_table("zh")) | set(frame_enum_table("en"))
|
|
836
|
+
if token in enum_tokens:
|
|
1365
837
|
continue
|
|
1366
838
|
problems.append(path + ": raw identifier " + repr(token) + " leaked into prose")
|
|
1367
839
|
return
|
|
@@ -1464,7 +936,7 @@ def check_language_parallel(result_en: dict, result_zh: dict) -> list[str]:
|
|
|
1464
936
|
z_text = z_item.get("claim") if isinstance(z_item, dict) else None
|
|
1465
937
|
if z_text:
|
|
1466
938
|
_scan_narrative(problems, key + "[" + str(i) + "].claim", e_text, z_text, strict=False)
|
|
1467
|
-
# Frame prose: field values may be enums (rendered via
|
|
939
|
+
# Frame prose: field values may be enums (rendered via frame_enum_table), but a
|
|
1468
940
|
# storage identifier must never reach the reader inside a sentence. Both
|
|
1469
941
|
# language versions are checked because the leak is an authoring defect.
|
|
1470
942
|
# Weeks and pilot duration are structural tokens (W1, W2-W13, one_semester):
|
|
@@ -1684,7 +1156,7 @@ def diverging_bar_svg(option: dict, width: int = 720, height: int = 300,
|
|
|
1684
1156
|
"""真 diverging 静态图(P0-10):support 从中心向右、contradict 从中心向左,
|
|
1685
1157
|
neutral 走独立的细条道(第二网格),三系列互不覆盖。计数轴整数刻度(P0-11)。
|
|
1686
1158
|
6.5: aria-label / title / desc 从 UI 字典按语言取。"""
|
|
1687
|
-
ui = ui or
|
|
1159
|
+
ui = ui or build_ui("zh")
|
|
1688
1160
|
cats = option.get("yAxis", [{}])[0].get("data", []) if isinstance(option.get("yAxis"), list) \
|
|
1689
1161
|
else option.get("yAxis", {}).get("data", [])
|
|
1690
1162
|
series = option.get("series", [])
|
|
@@ -1772,7 +1244,7 @@ def diverging_bar_svg(option: dict, width: int = 720, height: int = 300,
|
|
|
1772
1244
|
|
|
1773
1245
|
def grouped_bar_svg(option: dict, width: int = 720, height: int = 260,
|
|
1774
1246
|
note: str = "", lang: str = "zh", ui: dict | None = None) -> str:
|
|
1775
|
-
ui = ui or
|
|
1247
|
+
ui = ui or build_ui("zh")
|
|
1776
1248
|
cats = option.get("xAxis", {}).get("data", [])
|
|
1777
1249
|
series = option.get("series", [])
|
|
1778
1250
|
parts = [f'<rect x="0" y="0" width="{width}" height="{height}" fill="#FFFFFF"/>']
|
|
@@ -1884,10 +1356,7 @@ def _outcome_support_score(evidence: list[dict], outcome: dict) -> float:
|
|
|
1884
1356
|
def first_screen(result: dict, lang: str, ui: dict) -> str:
|
|
1885
1357
|
"""Decision-first hero: meaning before raw counts."""
|
|
1886
1358
|
decision = result.get("decision", {})
|
|
1887
|
-
outcomes = result.get("outcomes", [])
|
|
1888
1359
|
evidence = result.get("evidence", [])
|
|
1889
|
-
ranked = sorted(outcomes, key=lambda o: _outcome_support_score(evidence, o), reverse=True)
|
|
1890
|
-
best_type = next((o.get("outcome_type") for o in ranked if o.get("positive_count", 0) > 0), None)
|
|
1891
1360
|
action = decision.get("recommended_action", "insufficient_evidence")
|
|
1892
1361
|
cls = {"adopt": "adopt", "pilot": "pilot", "reject": "reject"}.get(action, "")
|
|
1893
1362
|
|
|
@@ -1924,23 +1393,17 @@ def first_screen(result: dict, lang: str, ui: dict) -> str:
|
|
|
1924
1393
|
</div>"""
|
|
1925
1394
|
|
|
1926
1395
|
|
|
1927
|
-
OUTCOME_GROUPS = {
|
|
1928
|
-
"task": {"completion_time", "accuracy", "assignment_score", "task_performance", "code_quality"},
|
|
1929
|
-
"learning": {"knowledge_gain", "learning_gain", "concept_understanding", "retention", "transfer",
|
|
1930
|
-
"independent_problem_solving", "programming_skill", "writing_skill"},
|
|
1931
|
-
"risk": {"ai_dependency", "over_reliance", "reduced_effort", "reduced_transfer",
|
|
1932
|
-
"academic_integrity_risk", "false_confidence", "cognitive_load"},
|
|
1933
|
-
}
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
1396
|
def render_outcome_separation(result: dict, lang: str, ui: dict) -> str:
|
|
1937
1397
|
outcomes = effect_outcomes(result)
|
|
1938
1398
|
if len(outcomes) < 2:
|
|
1939
1399
|
return ""
|
|
1940
1400
|
buckets: dict[str, list[dict]] = {"task": [], "learning": [], "risk": [], "other": []}
|
|
1401
|
+
group_map = outcome_group_map()
|
|
1941
1402
|
for outcome in outcomes:
|
|
1942
1403
|
kind = outcome.get("outcome_type") or ""
|
|
1943
|
-
group = next((name for name, values in
|
|
1404
|
+
group = next((name for name, values in group_map.items() if kind in values), "other")
|
|
1405
|
+
if group not in buckets:
|
|
1406
|
+
group = "other"
|
|
1944
1407
|
buckets[group].append(outcome)
|
|
1945
1408
|
group_labels = {
|
|
1946
1409
|
"task": ui["outcome_group_task"], "learning": ui["outcome_group_learning"],
|
|
@@ -2086,31 +1549,7 @@ def _quality_dims_text(lang: str, dims: Any) -> str:
|
|
|
2086
1549
|
|
|
2087
1550
|
def render_evidence_detail(ev: dict, source: dict, lang: str, ui: dict) -> str:
|
|
2088
1551
|
"""Render complete traceable evidence detail without inventing missing fields."""
|
|
2089
|
-
labels = (
|
|
2090
|
-
"study_id": "研究 ID", "sample_id": "样本 ID", "title": "研究标题",
|
|
2091
|
-
"year": "年份", "study_type": "研究设计", "education_level": "教育阶段",
|
|
2092
|
-
"population": "研究人群", "sample_size": "样本量", "intervention": "干预",
|
|
2093
|
-
"comparison": "对照 / 比较条件", "outcome_measure": "结果测量", "claim": "完整主张",
|
|
2094
|
-
"effect": "效应 / 结果", "effect_direction": "效应方向", "relation_to_claim": "与主张关系",
|
|
2095
|
-
"duration": "干预时长", "method": "方法", "strengths": "优势",
|
|
2096
|
-
"limitations": "局限", "confounders": "混杂因素", "quality_dimensions": "质量维度",
|
|
2097
|
-
"quality_score": "质量分", "evidence_level": "证据等级", "directness": "直接性",
|
|
2098
|
-
"applicability": "适用性", "confidence": "置信度", "status": "证据状态",
|
|
2099
|
-
"source_location": "来源位置", "source_title": "来源标题", "source_url": "可验证链接",
|
|
2100
|
-
"claim_id": "Claim ID",
|
|
2101
|
-
} if lang == "zh" else {
|
|
2102
|
-
"study_id": "Study ID", "sample_id": "Sample ID", "title": "Study title",
|
|
2103
|
-
"year": "Year", "study_type": "Study design", "education_level": "Education level",
|
|
2104
|
-
"population": "Population", "sample_size": "Sample size", "intervention": "Intervention",
|
|
2105
|
-
"comparison": "Comparison", "outcome_measure": "Outcome measure", "claim": "Full claim",
|
|
2106
|
-
"effect": "Effect / result", "effect_direction": "Effect direction", "relation_to_claim": "Relation to claim",
|
|
2107
|
-
"duration": "Duration", "method": "Method", "strengths": "Strengths",
|
|
2108
|
-
"limitations": "Limitations", "confounders": "Confounders", "quality_dimensions": "Quality dimensions",
|
|
2109
|
-
"quality_score": "Quality score", "evidence_level": "Evidence level", "directness": "Directness",
|
|
2110
|
-
"applicability": "Applicability", "confidence": "Confidence", "status": "Evidence status",
|
|
2111
|
-
"source_location": "Source location", "source_title": "Source title", "source_url": "Verifiable link",
|
|
2112
|
-
"claim_id": "Claim ID",
|
|
2113
|
-
})
|
|
1552
|
+
labels = evidence_detail_label_table(lang)
|
|
2114
1553
|
|
|
2115
1554
|
values: list[tuple[str, Any]] = []
|
|
2116
1555
|
source_title = source.get("title") or ev.get("title")
|
|
@@ -2368,9 +1807,10 @@ def render_tribunal(result: dict, workflow_svg: str, tribunal_svg: str, lang: st
|
|
|
2368
1807
|
|
|
2369
1808
|
def methodology_item_label(lang: str, item: Any) -> str:
|
|
2370
1809
|
key = str(item or "")
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
1810
|
+
table = methodology_label_table(lang)
|
|
1811
|
+
if key in table:
|
|
1812
|
+
return table[key]
|
|
1813
|
+
return key.replace("_", " ").strip().title() if lang != "zh" else key.replace("_", " ")
|
|
2374
1814
|
|
|
2375
1815
|
|
|
2376
1816
|
def render_methodology(result: dict, lang: str, ui: dict) -> str:
|
|
@@ -2478,7 +1918,8 @@ def render_intervention(result: dict, svg: str, lang: str, ui: dict) -> str:
|
|
|
2478
1918
|
if not intervention:
|
|
2479
1919
|
return f"<p>{esc(ui['no_data'])}</p>"
|
|
2480
1920
|
population = intervention.get("target_population") or intervention.get("target_learners")
|
|
2481
|
-
|
|
1921
|
+
population_key = "intervention_population" if intervention.get("target_population") else "intervention_learners"
|
|
1922
|
+
population_label = ui.get(population_key) or ui.get("intervention_learners") or ("目标人群" if lang == "zh" else "Target population")
|
|
2482
1923
|
lines = [f"<p><strong>{esc(population_label)}{esc(ui['colon'])}</strong>{esc(population)} · "
|
|
2483
1924
|
f"<strong>{esc(ui['intervention_duration'])}{esc(ui['colon'])}</strong>{esc(intervention.get('pilot_duration'))}</p>"]
|
|
2484
1925
|
if intervention.get("ai_usage_policy"):
|
|
@@ -2645,16 +2086,16 @@ def resolve_full_report_plan(result: dict) -> list[dict[str, Any]]:
|
|
|
2645
2086
|
raw = result.get("report_outline") or result.get("report_structure") or {}
|
|
2646
2087
|
chapters = raw.get("chapters") if isinstance(raw, dict) else raw if isinstance(raw, list) else None
|
|
2647
2088
|
if not isinstance(chapters, list) or not 5 <= len(chapters) <= 7:
|
|
2648
|
-
return
|
|
2089
|
+
return default_full_report_plan()
|
|
2649
2090
|
|
|
2650
2091
|
normalized: list[dict[str, Any]] = []
|
|
2651
2092
|
seen_modules: list[str] = []
|
|
2652
2093
|
for index, chapter in enumerate(chapters, 1):
|
|
2653
2094
|
if not isinstance(chapter, dict):
|
|
2654
|
-
return
|
|
2095
|
+
return default_full_report_plan()
|
|
2655
2096
|
modules = [m for m in (chapter.get("modules") or []) if m in FULL_REPORT_MODULES]
|
|
2656
2097
|
if not modules or any(m in seen_modules for m in modules):
|
|
2657
|
-
return [dict(item) for item in
|
|
2098
|
+
return [dict(item) for item in default_full_report_plan()]
|
|
2658
2099
|
seen_modules.extend(modules)
|
|
2659
2100
|
key = re.sub(r"[^a-z0-9-]+", "-", str(chapter.get("key") or f"chapter-{index}").lower()).strip("-")
|
|
2660
2101
|
normalized.append({
|
|
@@ -2666,9 +2107,9 @@ def resolve_full_report_plan(result: dict) -> list[dict[str, Any]]:
|
|
|
2666
2107
|
"modules": tuple(modules),
|
|
2667
2108
|
})
|
|
2668
2109
|
if set(seen_modules) != set(FULL_REPORT_MODULES):
|
|
2669
|
-
return [dict(item) for item in
|
|
2110
|
+
return [dict(item) for item in default_full_report_plan()]
|
|
2670
2111
|
if "decision" not in normalized[0]["modules"] or "sources" not in normalized[-1]["modules"]:
|
|
2671
|
-
return [dict(item) for item in
|
|
2112
|
+
return [dict(item) for item in default_full_report_plan()]
|
|
2672
2113
|
return normalized
|
|
2673
2114
|
|
|
2674
2115
|
|
|
@@ -2699,7 +2140,7 @@ def frame_enum_label(lang: str, value: Any) -> str:
|
|
|
2699
2140
|
text = str(value or "")
|
|
2700
2141
|
if not text:
|
|
2701
2142
|
return ""
|
|
2702
|
-
table =
|
|
2143
|
+
table = frame_enum_table(lang)
|
|
2703
2144
|
if text in table:
|
|
2704
2145
|
return table[text]
|
|
2705
2146
|
return _humanize_identifier(text, lang)
|
|
@@ -2738,7 +2179,7 @@ def _frame_value_label(lang: str, value: Any) -> str:
|
|
|
2738
2179
|
if value in (None, "", [], {}):
|
|
2739
2180
|
return ""
|
|
2740
2181
|
text_value = str(value)
|
|
2741
|
-
table =
|
|
2182
|
+
table = frame_enum_table(lang)
|
|
2742
2183
|
if text_value in table:
|
|
2743
2184
|
return table[text_value]
|
|
2744
2185
|
if " " in text_value.strip() or _HAS_CJK.search(text_value):
|
|
@@ -2752,26 +2193,10 @@ def render_research_scope(result: dict, lang: str, ui: dict) -> str:
|
|
|
2752
2193
|
course = frame.get("course", {}) or {}
|
|
2753
2194
|
intervention = frame.get("intervention", {}) or {}
|
|
2754
2195
|
scope = frame.get("scope", {}) or {}
|
|
2755
|
-
labels = (
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
"question": "Research question", "learner": "Target learners", "course": "Course context", "intervention": "AI intervention",
|
|
2760
|
-
"comparison": "Comparison", "outcomes": "Outcome constructs", "scope": "Research scope", "success": "Decision success condition",
|
|
2761
|
-
})
|
|
2762
|
-
learner_text = labeled_pairs(lang, learner,
|
|
2763
|
-
{"education_level":"教育阶段", "major":"专业", "prior_knowledge":"先验知识", "special_characteristics":"学习者特征"},
|
|
2764
|
-
{"education_level":"Education level", "major":"Major", "prior_knowledge":"Prior knowledge", "special_characteristics":"Learner characteristics"})
|
|
2765
|
-
course_text = labeled_pairs(lang, course,
|
|
2766
|
-
{"subject":"课程", "course_type":"课程类型", "duration":"课程周期"},
|
|
2767
|
-
{"subject":"Subject", "course_type":"Course type", "duration":"Duration"})
|
|
2768
|
-
intervention_text = labeled_pairs(lang, intervention,
|
|
2769
|
-
{"teaching_method": "干预方式", "ai_tool": "AI 工具", "allowed_usage": "允许使用",
|
|
2770
|
-
"frequency": "使用频率", "duration": "干预周期", "policy_name": "政策名称",
|
|
2771
|
-
"policy_type": "政策类型", "jurisdiction": "适用辖区", "mechanism": "作用机制"},
|
|
2772
|
-
{"teaching_method": "Method", "ai_tool": "AI tool", "allowed_usage": "Allowed usage",
|
|
2773
|
-
"frequency": "Frequency", "duration": "Duration", "policy_name": "Policy",
|
|
2774
|
-
"policy_type": "Policy type", "jurisdiction": "Jurisdiction", "mechanism": "Mechanism"})
|
|
2196
|
+
labels = scope_field_labels(lang)
|
|
2197
|
+
learner_text = labeled_pairs(lang, learner, scope_subfield_labels(lang, "learner"), scope_subfield_labels(lang, "learner"))
|
|
2198
|
+
course_text = labeled_pairs(lang, course, scope_subfield_labels(lang, "course"), scope_subfield_labels(lang, "course"))
|
|
2199
|
+
intervention_text = labeled_pairs(lang, intervention, scope_subfield_labels(lang, "intervention"), scope_subfield_labels(lang, "intervention"))
|
|
2775
2200
|
outcome_map = frame.get("outcomes", {}) or {}
|
|
2776
2201
|
outcome_parts = []
|
|
2777
2202
|
for group, values in outcome_map.items():
|
|
@@ -2779,8 +2204,8 @@ def render_research_scope(result: dict, lang: str, ui: dict) -> str:
|
|
|
2779
2204
|
rendered = "、".join(label(lang, "outcome", v) for v in values) if lang == "zh" else ", ".join(label(lang, "outcome", v) for v in values)
|
|
2780
2205
|
outcome_parts.append(f"{frame_enum_label(lang, group)}:{rendered}" if lang == "zh" else f"{frame_enum_label(lang, group)}: {rendered}")
|
|
2781
2206
|
scope_parts = []
|
|
2782
|
-
scope_labels_zh =
|
|
2783
|
-
scope_labels_en =
|
|
2207
|
+
scope_labels_zh = scope_subfield_labels("zh", "scope")
|
|
2208
|
+
scope_labels_en = scope_subfield_labels("en", "scope")
|
|
2784
2209
|
for key, value in scope.items():
|
|
2785
2210
|
if value in (None, "", [], {}):
|
|
2786
2211
|
continue
|
|
@@ -2890,20 +2315,13 @@ def render_full_report(result: dict, lang: str, ui: dict, charts: dict, infograp
|
|
|
2890
2315
|
+ render_v2_history(result, lang, ui)),
|
|
2891
2316
|
}
|
|
2892
2317
|
|
|
2893
|
-
|
|
2894
|
-
"decision": ("先明确最终裁决与研究边界,再解释为什么。" if lang == "zh" else "State the final adjudication and research boundary before explaining why."),
|
|
2895
|
-
"evidence": ("把任务表现、真实学习、保持与风险放在同一证据地图中,但不混为一谈。" if lang == "zh" else "Place task performance, actual learning, retention and risk on one evidence map without conflating them."),
|
|
2896
|
-
"quality": ("检查证据为什么可信、哪里冲突,以及哪些结论必须降级。" if lang == "zh" else "Examine why evidence is credible, where it conflicts, and which conclusions require downgrading."),
|
|
2897
|
-
"action": ("把可外推范围、护栏和教学动作连接到具体证据。" if lang == "zh" else "Connect applicability, guardrails and teaching actions to specific evidence."),
|
|
2898
|
-
"evaluation": ("用独立学习结果验证试点,并预先写清停止条件。" if lang == "zh" else "Validate the pilot with independent learning outcomes and pre-specified stop conditions."),
|
|
2899
|
-
"sources": ("保留原始来源、URL、证据 ID 和获取信息,确保可回查。" if lang == "zh" else "Preserve original sources, URLs, evidence IDs and retrieval metadata for auditability."),
|
|
2900
|
-
}
|
|
2318
|
+
leads_by_key = default_leads(lang)
|
|
2901
2319
|
|
|
2902
2320
|
plan = resolve_full_report_plan(result)
|
|
2903
2321
|
rendered = []
|
|
2904
2322
|
for index, chapter in enumerate(plan, 1):
|
|
2905
2323
|
content = "".join(module_content.get(module, "") for module in chapter.get("modules", ()))
|
|
2906
|
-
lead = chapter.get("lead_zh" if lang == "zh" else "lead_en") or
|
|
2324
|
+
lead = chapter.get("lead_zh" if lang == "zh" else "lead_en") or leads_by_key.get(chapter.get("key"), "")
|
|
2907
2325
|
rendered.append(render_full_chapter(
|
|
2908
2326
|
chapter_dom_id(lang, str(chapter.get("key") or f"chapter-{index}"), index),
|
|
2909
2327
|
full_chapter_title(chapter, lang, index), content, str(lead or "")))
|
|
@@ -3082,12 +2500,6 @@ def _enhancer_js(charts_zh: dict, charts_en: dict, result_en: dict) -> str:
|
|
|
3082
2500
|
trace_en = next((c for c in charts_en.get("charts", [])
|
|
3083
2501
|
if c.get("chart_id") == "claim-evidence-trace"), None)
|
|
3084
2502
|
benchmark_en = charts_en.get("benchmark") or {}
|
|
3085
|
-
matrix_rows = [
|
|
3086
|
-
{"id": e.get("evidence_id"), "title": e.get("title", ""),
|
|
3087
|
-
"direction": e.get("direction", "neutral"),
|
|
3088
|
-
"outcome": e.get("outcome_type", ""), "quality": e.get("quality_score", 0)}
|
|
3089
|
-
for e in result_en.get("evidence", [])
|
|
3090
|
-
]
|
|
3091
2503
|
return f"""
|
|
3092
2504
|
(function () {{
|
|
3093
2505
|
'use strict';
|
|
@@ -3326,24 +2738,7 @@ def render_body(result: dict, lang: str, ui: dict, charts: dict, infographics: d
|
|
|
3326
2738
|
outcome_chart = next((c for c in charts.get("charts", [])
|
|
3327
2739
|
if c.get("chart_id") == "outcome-evidence-overview"), None)
|
|
3328
2740
|
|
|
3329
|
-
|
|
3330
|
-
brief_titles = {
|
|
3331
|
-
"decision": ("先看结论", "该不该做、置信度多高、最关键的证据边界在哪。"),
|
|
3332
|
-
"lieflat": ("Lieflat 实证手作画廊", "AI 按数据形状从 Lieflat 目录选型编排;每张图的数字都可溯源到 result.json。"),
|
|
3333
|
-
"outcomes": ("任务表现 ≠ 学习效果", "只展示真正有解释力的结果分离;正向、负向与零效应按 effect_direction 编码。"),
|
|
3334
|
-
"tribunal": ("证据裁决", "支持、不确定、被反驳与缺失证据分开放置,不把长段落平铺在同一层。"),
|
|
3335
|
-
"action": ("从证据到行动", "适用性、护栏、停止条件与评价连成一条可执行路径。"),
|
|
3336
|
-
"sources": ("关键来源", "摘要页只列最关键的来源;完整溯源在完整报告中展开。"),
|
|
3337
|
-
}
|
|
3338
|
-
else:
|
|
3339
|
-
brief_titles = {
|
|
3340
|
-
"decision": ("Decision first", "What to do, how confident we are, and the most important evidence boundary."),
|
|
3341
|
-
"lieflat": ("Lieflat Editorial Gallery", "Charts selected and composed by AI from the Lieflat catalog; every number traces back to result.json."),
|
|
3342
|
-
"outcomes": ("Task performance ≠ learning", "Only informative outcome separation; positive, negative and null effects use effect_direction."),
|
|
3343
|
-
"tribunal": ("Evidence tribunal", "Supported, uncertain, contradicted and missing evidence stay separated instead of flattened into long prose."),
|
|
3344
|
-
"action": ("Evidence to action", "Applicability, guardrails, stop conditions and evaluation form one executable path."),
|
|
3345
|
-
"sources": ("Key sources", "Only the key sources in the brief; full traceability expands in the full report."),
|
|
3346
|
-
}
|
|
2741
|
+
brief_titles = brief_block_titles(lang)
|
|
3347
2742
|
|
|
3348
2743
|
lieflat_layout = viz.get("lieflat_layout") or {"entries": []}
|
|
3349
2744
|
lieflat_meta = (viz.get("lieflat_meta") or {}).get(lang, {})
|
|
@@ -3369,7 +2764,7 @@ def render_body(result: dict, lang: str, ui: dict, charts: dict, infographics: d
|
|
|
3369
2764
|
nav_items = [("decision", brief_titles["decision"][0]), ("outcomes", brief_titles["outcomes"][0]),
|
|
3370
2765
|
("tribunal", brief_titles["tribunal"][0]), ("action", brief_titles["action"][0]),
|
|
3371
2766
|
("sources", brief_titles["sources"][0])]
|
|
3372
|
-
brief_nav = '<nav class="brief-navigation" aria-label="' + ("
|
|
2767
|
+
brief_nav = '<nav class="brief-navigation" aria-label="' + esc(ui["brief_nav_aria"]) + '">' + ''.join(
|
|
3373
2768
|
f'<a href="#brief-{key}-{lang}"><span>{i:02d}</span>{esc(title)}</a>'
|
|
3374
2769
|
for i, (key, title) in enumerate(nav_items, 1)) + '</nav>'
|
|
3375
2770
|
full_report = render_full_report(result, lang, ui, charts, infographics, figures, viz)
|
|
@@ -3414,9 +2809,11 @@ def render_html(result_en: dict, result_zh: dict, charts_zh: dict, charts_en: di
|
|
|
3414
2809
|
figures_en: dict, theme: str, viz: dict,
|
|
3415
2810
|
result_sha256: str = "", integrity: dict | None = None) -> str:
|
|
3416
2811
|
# Theme is fixed at generation time; language remains switchable in the HTML.
|
|
3417
|
-
|
|
2812
|
+
ui_zh = build_ui("zh")
|
|
2813
|
+
ui_en = build_ui("en")
|
|
2814
|
+
body_zh = render_body(result_zh, "zh", ui_zh, charts_zh, infographics_zh, figures_zh, viz, theme,
|
|
3418
2815
|
integrity=integrity)
|
|
3419
|
-
body_en = render_body(result_en, "en",
|
|
2816
|
+
body_en = render_body(result_en, "en", ui_en, charts_en, infographics_en, figures_en, viz, theme,
|
|
3420
2817
|
integrity=integrity)
|
|
3421
2818
|
hash_meta = (f'<meta name="eduevidence-result-sha256" content="{esc(result_sha256)}">\n'
|
|
3422
2819
|
if result_sha256 else "")
|
|
@@ -3440,10 +2837,10 @@ def render_html(result_en: dict, result_zh: dict, charts_zh: dict, charts_en: di
|
|
|
3440
2837
|
<div class="controls reader-toolbar">
|
|
3441
2838
|
<a class="reader-home" href="#" aria-label="Back to report start">EduEvidence<span class="generated-theme">{esc(THEME_DISPLAY[theme])}</span></a>
|
|
3442
2839
|
<div class="reader-view-controls" role="group" aria-label="Report view">
|
|
3443
|
-
<button type="button" class="report-view-btn active" data-report-view="brief" data-copy="brief" aria-pressed="true"
|
|
3444
|
-
<button type="button" class="report-view-btn" data-report-view="full" data-copy="full" aria-pressed="false"
|
|
2840
|
+
<button type="button" class="report-view-btn active" data-report-view="brief" data-copy="brief" aria-pressed="true">{esc(ui_zh['toolbar_brief'])}</button>
|
|
2841
|
+
<button type="button" class="report-view-btn" data-report-view="full" data-copy="full" aria-pressed="false">{esc(ui_zh['toolbar_full'])}</button>
|
|
3445
2842
|
</div>
|
|
3446
|
-
<div class="reader-tools">{_lang_switcher(
|
|
2843
|
+
<div class="reader-tools">{_lang_switcher(ui_zh, ui_en)}<button class="reader-print" type="button" data-copy="print">{esc(ui_zh['toolbar_print'])}</button></div>
|
|
3447
2844
|
</div>
|
|
3448
2845
|
{body_zh}
|
|
3449
2846
|
{body_en}
|
|
@@ -3540,6 +2937,13 @@ def main() -> int:
|
|
|
3540
2937
|
return 2
|
|
3541
2938
|
result_zh = json.loads(zh_path.read_text(encoding="utf-8"))
|
|
3542
2939
|
|
|
2940
|
+
# 0. Domain copy pack(result.meta.domain / frame.extensions.domain)
|
|
2941
|
+
try:
|
|
2942
|
+
activate_copy_pack(result_en)
|
|
2943
|
+
except AssertionError as exc:
|
|
2944
|
+
print(f"REPORT_INVALID — domain copy pack rejected:\n{exc}")
|
|
2945
|
+
return 2
|
|
2946
|
+
|
|
3543
2947
|
# 1. Contract validation(两份数据分别校验)
|
|
3544
2948
|
for label, data in (("result.json", result_en), ("result.zh.json", result_zh)):
|
|
3545
2949
|
problems = validate_contract(data)
|
|
@@ -3569,8 +2973,8 @@ def main() -> int:
|
|
|
3569
2973
|
# 3. Adapters(两份数据分别生成 spec / 信息图 / 学术图;数字同构)
|
|
3570
2974
|
charts_zh = build_chart_specs(result_zh, lang="zh")
|
|
3571
2975
|
charts_en = build_chart_specs(result_en, lang="en")
|
|
3572
|
-
infographics_zh = build_infographics(result_zh, lang="zh")
|
|
3573
|
-
infographics_en = build_infographics(result_en, lang="en")
|
|
2976
|
+
infographics_zh = build_infographics(result_zh, lang="zh", ui=build_ui("zh"))
|
|
2977
|
+
infographics_en = build_infographics(result_en, lang="en", ui=build_ui("en"))
|
|
3574
2978
|
figure_data = build_figure_data(result_en)
|
|
3575
2979
|
figures_zh = render_figures(figure_data, theme=args.theme, lang="zh")
|
|
3576
2980
|
figures_en = render_figures(figure_data, theme=args.theme, lang="en")
|
|
@@ -3647,5 +3051,14 @@ def main() -> int:
|
|
|
3647
3051
|
return 0
|
|
3648
3052
|
|
|
3649
3053
|
|
|
3054
|
+
def __getattr__(name: str):
|
|
3055
|
+
"""Back-compat UI dicts for tests: br.UI_ZH / br.UI_EN."""
|
|
3056
|
+
if name == "UI_ZH":
|
|
3057
|
+
return build_ui("zh")
|
|
3058
|
+
if name == "UI_EN":
|
|
3059
|
+
return build_ui("en")
|
|
3060
|
+
raise AttributeError(name)
|
|
3061
|
+
|
|
3062
|
+
|
|
3650
3063
|
if __name__ == "__main__":
|
|
3651
3064
|
sys.exit(main())
|