评测页的分析方法、模式叫法改为引用 lib/termsAnalysis 那一份(原来 termsEval 另抄了一份 OP_LABELS / MODE_LABELS):EVAL_TEXT.analysis.op / mode 直接用 ANALYSIS_OP_LABEL、ANALYSIS_MODE_LABEL,字一样、界面不变。tests/test_frontend_eval_unions.py 改成读 termsAnalysis.ts 的两张表和契约逐项对,并守住评测页引用的是这一份

This commit is contained in:
ThearchyHelios
2026-10-21 20:03:54 +08:00
parent 28c538a72f
commit d211d2fac8
2 changed files with 23 additions and 11 deletions
+17 -3
View File
@@ -12,6 +12,8 @@ from pathlib import Path
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
TYPES_EVAL = ROOT / "frontend" / "src" / "typesEval.ts" TYPES_EVAL = ROOT / "frontend" / "src" / "typesEval.ts"
TERMS_EVAL = ROOT / "frontend" / "src" / "lib" / "termsEval.ts" TERMS_EVAL = ROOT / "frontend" / "src" / "lib" / "termsEval.ts"
#: 分析方法、模式的叫法只有这一份(termsEval 引用它,不另抄)
TERMS_ANALYSIS = ROOT / "frontend" / "src" / "lib" / "termsAnalysis.ts"
_COMMENT = re.compile(r"/\*.*?\*/|//[^\n]*", re.S) _COMMENT = re.compile(r"/\*.*?\*/|//[^\n]*", re.S)
@@ -111,16 +113,28 @@ def test_signal_bands_follow_the_server():
assert set(_labels("signals.band")) == {c["band"] for c in [*SIGNAL_CRITERIA["e1"], *SIGNAL_CRITERIA["e3"]]} assert set(_labels("signals.band")) == {c["band"] for c in [*SIGNAL_CRITERIA["e1"], *SIGNAL_CRITERIA["e3"]]}
def _analysis_table(name: str) -> list[tuple[str, str]]:
"""lib/termsAnalysis.ts 里一张叫法表(export const NAME … = { … })的 (键, 叫法),按写的顺序。"""
text = _source(TERMS_ANALYSIS)
found = re.search(rf"export const {name}\b[^=]*= \{{(.*?)\n\}}", text, re.S)
assert found is not None, f"termsAnalysis.ts 里没有 export const {name}"
return re.findall(r"(\w+): '([^']*)'", found.group(1))
def test_analysis_expectations_follow_the_server(): def test_analysis_expectations_follow_the_server():
"""分析期望:判定类和服务端 app/eval/judgements 一致;分析方法、模式的叫法和契约(app/analysis/contract 的 OP_LABELS、 """分析期望:判定类和服务端 app/eval/judgements 一致;分析方法、模式的叫法和契约(app/analysis/contract 的 OP_LABELS、
MODE_LABELS)一字不差——界面上的「变化拆解(可加)」和服务端拼的、证据面板写的是同一种说法。""" MODE_LABELS)一字不差——界面上的「变化拆解(可加)」和服务端拼的、证据面板写的是同一种说法。叫法只有 lib/termsAnalysis
一份:评测页(EVAL_TEXT.analysis.op / mode)引用它,不另抄一份。"""
from app.analysis.contract import MODE_LABELS, MODES, OP_LABELS, OPS from app.analysis.contract import MODE_LABELS, MODES, OP_LABELS, OPS
from app.eval.judgements import JUDGEMENTS from app.eval.judgements import JUDGEMENTS
assert _union("EvalJudgement") == list(JUDGEMENTS) assert _union("EvalJudgement") == list(JUDGEMENTS)
assert _labels("analysis.judgement") == list(JUDGEMENTS) assert _labels("analysis.judgement") == list(JUDGEMENTS)
assert _union("EvalAnalysisOp") == list(OPS) assert _union("EvalAnalysisOp") == list(OPS)
assert _pairs("analysis.op") == list(OP_LABELS.items()) assert _analysis_table("ANALYSIS_OP_LABEL") == list(OP_LABELS.items())
assert _union("EvalAnalysisMode") == list(MODE_LABELS) assert _union("EvalAnalysisMode") == list(MODE_LABELS)
assert _pairs("analysis.mode") == list(MODE_LABELS.items()) assert _analysis_table("ANALYSIS_MODE_LABEL") == list(MODE_LABELS.items())
assert {m for modes in MODES.values() for m in modes} == set(MODE_LABELS) assert {m for modes in MODES.values() for m in modes} == set(MODE_LABELS)
terms = _source(TERMS_EVAL)
assert re.search(r"\n\s*op: ANALYSIS_OP_LABEL\b", terms) and re.search(r"\n\s*mode: ANALYSIS_MODE_LABEL\b", terms), \
"评测页的分析方法、模式叫法要引用 lib/termsAnalysis 的那一份"
+6 -8
View File
@@ -10,6 +10,7 @@ import type {
EvalTrigger, EvalUpgradePolicy, EvalVerdict, EvalTrigger, EvalUpgradePolicy, EvalVerdict,
} from '../typesEval' } from '../typesEval'
import { formatCost, formatNumber } from './format' import { formatCost, formatNumber } from './format'
import { ANALYSIS_MODE_LABEL, ANALYSIS_OP_LABEL } from './termsAnalysis'
/** 比率写成百分数:0.756 → 「75.6%」;没有分母(null)写「—」 */ /** 比率写成百分数:0.756 → 「75.6%」;没有分母(null)写「—」 */
export function pct(rate: number | null | undefined, digits = 1): string { export function pct(rate: number | null | undefined, digits = 1): string {
@@ -310,16 +311,13 @@ export const EVAL_TEXT = {
refused: (reason: string) => `算不了:${reason}(这一项不能评分)`, refused: (reason: string) => `算不了:${reason}(这一项不能评分)`,
}, },
/** /**
* 分析期望(贡献、判定)的叫法。分析方法、模式的叫法和服务端契约(app/analysis/contract 的 OP_LABELS、MODE_LABELS)一字 * 分析期望(贡献、判定)的叫法。分析方法、模式的叫法只有 lib/termsAnalysis 一份(照服务端契约 app/analysis/contract 的
* 不差(tests/test_frontend_eval_unions.py 逐个对照);判定类是评测自己的(app/eval/judgements.JUDGEMENTS) * OP_LABELS、MODE_LABELS 一字不差,tests/test_frontend_eval_unions.py 与 check-studio 逐个对照),这里引用它,不另抄;
* 判定类是评测自己的(app/eval/judgements.JUDGEMENTS)
*/ */
analysis: { analysis: {
op: { op: ANALYSIS_OP_LABEL as Record<EvalAnalysisOp, string>,
decompose: '变化拆解', baseline: '波动基线', trend: '趋势判断', differ: '差异检验', comparable: '可比性检查', mode: ANALYSIS_MODE_LABEL as Record<EvalAnalysisMode, string>,
} as Record<EvalAnalysisOp, string>,
mode: {
additive: '可加', ratio: '比率', factor: '乘积', rate: '比率', mean: '均值', rows: '逐行明细',
} as Record<EvalAnalysisMode, string>,
judgement: { judgement: {
source: '主要来源', dispersed: '分散,没有主要来源', flat: '基本持平', mix: '主要来自结构变化', rate: '主要来自各段自身的变化', source: '主要来源', dispersed: '分散,没有主要来源', flat: '基本持平', mix: '主要来自结构变化', rate: '主要来自各段自身的变化',
mixed: '几方面都有影响', normal: '常规波动', anomaly: '超出常规波动', rising: '上升趋势', falling: '下降趋势', mixed: '几方面都有影响', normal: '常规波动', anomaly: '超出常规波动', rising: '上升趋势', falling: '下降趋势',