From afed4dfc3b1b79ba1beec26e68a1ba6ac1192c15 Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期四, 17 九月 2026 14:39:59 +0800
Subject: [PATCH] fix(投资): 修复两级表头导致的取列错位 + 新增《弹窗提示》机制(14 条规则)
---
docs/投资/工具/匹配与校验.py | 226 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 210 insertions(+), 16 deletions(-)
diff --git "a/docs/\346\212\225\350\265\204/\345\267\245\345\205\267/\345\214\271\351\205\215\344\270\216\346\240\241\351\252\214.py" "b/docs/\346\212\225\350\265\204/\345\267\245\345\205\267/\345\214\271\351\205\215\344\270\216\346\240\241\351\252\214.py"
index ab4734b..b57f5e6 100644
--- "a/docs/\346\212\225\350\265\204/\345\267\245\345\205\267/\345\214\271\351\205\215\344\270\216\346\240\241\351\252\214.py"
+++ "b/docs/\346\212\225\350\265\204/\345\267\245\345\205\267/\345\214\271\351\205\215\344\270\216\346\240\241\351\252\214.py"
@@ -40,6 +40,8 @@
}
WANT = ['鑷紑濮嬪缓璁剧疮璁�', '鏈勾璁″垝鎶曡祫', '鑷勾鍒濈疮璁�', '鏈湀瀹屾垚', '寤鸿闃舵', '褰㈣薄杩涘害']
NUMF = ['鎬绘姇璧�', '鑷紑濮嬪缓璁剧疮璁�', '鏈勾璁″垝鎶曡祫', '鑷勾鍒濈疮璁�', '鏈湀瀹屾垚']
+CITY17 = ('姝︽眽', '榛勭煶', '鍗佸牥', '瀹滄槍', '瑗勯槼', '閯傚窞', '鑽嗛棬', '瀛濇劅', '鑽嗗窞', '榛勫唸',
+ '鍜稿畞', '闅忓窞', '鎭╂柦', '浠欐', '娼滄睙', '澶╅棬', '绁炲啘鏋�')
GROUP_PAT = re.compile(r'^(涓�|浜寍涓墊鍥泑浜攟鍏瓅涓億鍏�)銆�')
SKIP_PAT = re.compile(r'^(灏廫s*璁鍚圽s*璁鎬籠s*璁�)$')
@@ -64,6 +66,13 @@
s = s[:-2]
s = s.rstrip('.銆�-')
return s
+
+
+def normname2(s):
+ """鎶曡祫绯荤粺姣斿涓撶敤锛氬湪 normname 鍩虹涓婂啀鍘绘帀闂撮殧鍙风瓑绾楗板瓧绗︺��"""
+ for ch in ('路', '锛�', '鈥�', '鈻�', '鈼�', '.', '-'):
+ s = str(s or '').replace(ch, '')
+ return normname(s)
def to_num(v):
@@ -144,6 +153,55 @@
return out, period
+def normname3(s):
+ """鍐嶅幓鎺夋嫭鍙峰強鍏跺唴瀹癸紙鐢ㄤ簬璇嗗埆銆屽悕绉板浜嗘嫭娉ㄣ�嶇殑鍚屼竴椤圭洰锛夈��"""
+ t = normname(s)
+ t = re.sub(r'\([^)]*\)', '', t)
+ for ch in ('路', '锛�', '鈥�', '鈼�', '-', '.', '路'):
+ t = t.replace(ch, '')
+ return t
+
+
+def best_name_hint(key, tkeys, cutoff=0.80):
+ """鍦ㄧ洰鏍囪〃椤圭洰鍚嶉噷鎵句笌 key 鏈�鍍忕殑涓�涓紙瀹芥澗/鍘绘嫭娉ㄤ袱绉嶅彛寰勫彇鏈�浼橈級銆�
+
+ 杩斿洖 (鐩爣琛ㄩ」鐩悕, 鐩镐技搴�)锛涙壘涓嶅埌杩斿洖 (None, 0)銆�
+ """
+ best = (None, 0.0)
+ for normf in (lambda s: normname(s, loose=True), normname3):
+ base = normf(key)
+ table = [normf(x) for x in tkeys]
+ hit = difflib.get_close_matches(base, table, n=1, cutoff=cutoff)
+ if hit:
+ ratio = difflib.SequenceMatcher(None, base, hit[0]).ratio()
+ if ratio > best[1]:
+ best = (tkeys[table.index(hit[0])], ratio)
+ return best
+
+
+def sys_lookup(key, table):
+ """鍦ㄦ姇璧勭郴缁熷瓧鍏搁噷鎵鹃」鐩細鍏堢簿纭紙鍚幓闂撮殧鍙凤級锛屽啀瀹芥澗+鐩镐技搴︼紙>=0.90锛夈��
+
+ 杩斿洖 (璁板綍, 璇存槑)锛涙壘涓嶅埌杩斿洖 (None, '')銆�
+ """
+ if not table:
+ return None, ''
+ s = table.get(key)
+ if s:
+ return s, ''
+ for kk, vv in table.items():
+ if normname2(kk) == normname2(key):
+ return vv, '鍚嶇О鍐欐硶涓嶅悓锛堟姇璧勭郴缁燂細%s锛�' % vv['椤圭洰鍚嶇О']
+ idx = {}
+ for kk in table:
+ idx.setdefault(normname2(kk), []).append(kk)
+ cands = difflib.get_close_matches(normname2(key), list(idx.keys()), n=1, cutoff=0.90)
+ if cands:
+ kk = idx[cands[0]][0]
+ return table[kk], '鍚嶇О鍐欐硶涓嶅悓锛堟姇璧勭郴缁燂細%s锛�' % table[kk]['椤圭洰鍚嶇О']
+ return None, ''
+
+
def find_system_files(indir, ym):
"""鍦ㄨ緭鍏ョ洰褰曢噷鎵俱�屾湰鏈�/涓婃湀銆嶆姇璧勭郴缁熸煡璇㈢粨鏋溿�傛枃浠跺悕鎴栥�庢椂鏈熴�忓垪閲岀殑骞存湀閮借銆�"""
found = {}
@@ -195,6 +253,23 @@
rep.append('> 鍙锛氭湰姝ラ涓嶆敼浠讳綍鏂囦欢锛涜緭鍑虹殑銆婂彉鏇存竻鍗曘�嬩緵銆屽啓鍏ヤ晶銆嶆墽琛屻��')
rep.append('')
changes = []
+ alerts = []
+
+ def add_alert(level, kind, cat, city, name, src, detail, action):
+ alerts.append(dict(绾у埆=level, 绫诲瀷=kind, 鍝佺被=cat, 甯傚窞=city, 椤圭洰鍚嶇О=name,
+ 鏉ユ簮=src, 璇︽儏=detail, 寤鸿鍔ㄤ綔=action))
+
+ # 婧愭枃浠惰В鏋愭牎楠岋紙鐢� 甯傚窞鏈堟姤瑙f瀽.py 浜у嚭锛�
+ checks = []
+ p_ck = os.path.join(out, '瑙f瀽鏍¢獙_%s.tsv' % a.month)
+ if os.path.exists(p_ck):
+ _lines = io.open(p_ck, encoding='utf-8-sig').read().splitlines()
+ if _lines:
+ _ckc = _lines[0].split('\t')
+ for _ln in _lines[1:]:
+ _v = _ln.split('\t')
+ _v += [''] * (len(_ckc) - len(_v))
+ checks.append(dict(zip(_ckc, _v)))
# 鎶曡祫绯荤粺鏂囦欢锛堟湰鏈� / 涓婃湀锛�
indir = os.path.join(a.root, '杈撳叆')
@@ -214,11 +289,41 @@
rep.append('| 涓婃湀锛堟牎楠屽熀鏁帮級 | %s | %d |' % (os.path.basename(pre_path) if pre_path else '**鏈壘鍒帮紙鐢ㄦ埛鍙e緞 a锛氶渶姣忔湀鏀句竴浠斤級**', len(cur_pre)))
rep.append('')
+ # 鈥斺�� 寮圭獥瑙勫垯锛氭姇璧勭郴缁熸枃浠剁己澶� / 婧愭枃浠惰В鏋愬紓甯� / 婧愭枃浠跺唴閮ㄥ涓嶄笂 鈥斺��
+ if not cur_path:
+ add_alert('闃绘柇', '缂烘湰鏈熸姇璧勭郴缁熻〃', '', '', '', os.path.basename(indir),
+ '鏈湪銆岃緭鍏ャ�嶇洰褰曟壘鍒� %s 鐨勬姇璧勭郴缁熸煡璇㈢粨鏋�' % a.month,
+ '鎶婃湰鏈熸姇璧勭郴缁熷鍑虹殑銆婃ā鏉縚鏌ヨ缁撴灉銆嬫斁杩� docs\\鎶曡祫\\杈撳叆\\ 鍚庨噸璺�')
+ if not pre_path:
+ add_alert('璀﹀憡', '缂轰笂鏈堟姇璧勭郴缁熻〃', '', '', '', os.path.basename(indir),
+ '鏈壘鍒� %s 鐨勬姇璧勭郴缁熸煡璇㈢粨鏋� 鈫� 鏈堝害鏍¢獙锛堟湰鏈堣嚜骞村垵绱 = 涓婃湀鑷勾鍒濈疮璁� + 鏈湀瀹屾垚锛夋棤娉曟墽琛�' % prev_ym(a.month),
+ '姣忔湀鎶婁笂鏈堛�佹湰鏈堜袱浠芥姇璧勭郴缁熸煡璇㈢粨鏋滈兘鏀捐繘 docs\\鎶曡祫\\杈撳叆\\')
+ if not checks:
+ add_alert('闃绘柇', '缂烘簮鏂囦欢瑙f瀽鏍¢獙', '', '', '', os.path.basename(p_ck),
+ '鏈壘鍒般�婅В鏋愭牎楠屻�嬫竻鍗曪紝鏃犳硶鍒ゆ柇婧愭枃浠舵槸鍚﹁嚜娲姐�佹槸鍚﹁В鏋愬け璐�',
+ '鍏堣窇 甯傚窞鏈堟姤瑙f瀽.py --month %s' % a.month)
+ for ck in checks:
+ st = ck.get('鐘舵��', 'OK')
+ if st and st != 'OK':
+ add_alert('闃绘柇' if st.startswith('瑙f瀽澶辫触') else '璀﹀憡', '婧愭枃浠惰В鏋愬紓甯�',
+ ck.get('鍝佺被', ''), ck.get('甯傚窞', ''), '', ck.get('鏂囦欢', ''),
+ st, '浜哄伐鎵撳紑婧愭枃浠剁‘璁よ〃澶�/鏁版嵁锛涘繀瑕佹椂鐧昏鐗逛緥鍚庡啀瀵煎叆')
+ elif ck.get('涓�鑷存��', '') == '鈿狅笍 涓嶄竴鑷�':
+ add_alert('璀﹀憡', '婧愭枃浠跺唴閮ㄥ涓嶄笂', ck.get('鍝佺被', ''), ck.get('甯傚窞', ''), '', ck.get('鏂囦欢', ''),
+ '椤圭洰琛屾眰鍜� %s/%s/%s 鈮� %s 琛屽悎璁� %s/%s/%s' % (
+ ck.get('姹傚拰_鑷紑濮嬪缓璁剧疮璁�', ''), ck.get('姹傚拰_鑷勾鍒濈疮璁�', ''), ck.get('姹傚拰_鏈湀瀹屾垚', ''),
+ ck.get('鍚堣琛�', ''), ck.get('鍚堣_鑷紑濮嬪缓璁剧疮璁�', ''),
+ ck.get('鍚堣_鑷勾鍒濈疮璁�', ''), ck.get('鍚堣_鏈湀瀹屾垚', '')),
+ '璁╁競宸炴牳瀵规簮鏂囦欢锛堝悎璁¤鎴栭」鐩鏈夐敊锛夛紝纭鍚庡啀瀵煎叆')
+
for cat, spec in TARGETS.items():
rep.append('## %s 鈥斺�� 鐩爣鏄庣粏琛�' % cat)
rep.append('')
if not os.path.exists(spec['path']):
- rep.append('鐩爣琛ㄤ笉瀛樺湪锛�%s' % spec['path']); rep.append(''); continue
+ rep.append('鐩爣琛ㄤ笉瀛樺湪锛�%s' % spec['path']); rep.append('')
+ add_alert('闃绘柇', '缂虹洰鏍囨槑缁嗚〃', cat, '', '', spec['path'],
+ '鐩爣鏄庣粏琛ㄤ笉瀛樺湪锛屾湰瀛e害璇ュ搧绫绘棤娉曞啓鍏�', '鎶婃ā鏉胯〃鏀惧埌 docs\\鎶曡祫\\杈撳嚭\\')
+ continue
tgt = read_target(spec)
prow = [r for r in parsed if r['鍝佺被'] == cat]
tmap = {}
@@ -235,12 +340,19 @@
loose_tgt = {}
for k in tmap:
loose_tgt.setdefault(normname(k, loose=True), []).append(k)
+ plain_tgt = {}
+ for k in tmap:
+ plain_tgt.setdefault(normname3(k), []).append(k)
tkeys = list(tmap.keys())
new_in_src, suspicious = [], []
for k in raw_new:
nl = normname(k, loose=True)
if nl in loose_tgt:
suspicious.append((k, loose_tgt[nl][0], '瀹芥澗绛変环锛堝幓鎷彿/鍘烩�滈」鐩�濅簩瀛楀悗鐩稿悓锛�'))
+ continue
+ n3 = normname3(k)
+ if n3 and n3 in plain_tgt:
+ suspicious.append((k, plain_tgt[n3][0], '鍘绘帀鎷敞涓庨棿闅斿彿鍚庣浉鍚�'))
continue
close = difflib.get_close_matches(nl, [normname(x, loose=True) for x in tkeys], n=1, cutoff=0.90)
if close:
@@ -254,6 +366,12 @@
rep.append('- **鐤戜技鍚屼竴椤圭洰銆侀渶浜哄伐纭** **%d**锛堟寜鐢ㄦ埛鍙e緞锛�**涓嶈嚜鍔ㄦ柊澧炪�佷笉鍔ㄥ悕绉帮紝寮圭獥鎻愮ず**锛�' % len(suspicious))
rep.append('- 鐩爣琛ㄦ湁浣嗘湰鏈熸簮鏂囦欢娌℃湁锛堜繚鎸佸師鍊硷級**%d**' % len(only_tgt))
rep.append('')
+ for k1, hit1, why1 in suspicious:
+ _p1 = pmap[k1][0]
+ add_alert('璀﹀憡', '鐤戜技鍚屼竴椤圭洰锛堝悕绉板啓娉曚笉鍚岋級', cat, _p1.get('甯傚窞', ''), _p1['椤圭洰鍚嶇О'],
+ os.path.basename(_p1['鏂囦欢']),
+ '涓庣洰鏍囪〃宸叉湁椤圭洰銆�%s銆�%s' % (tmap[hit1][0]['椤圭洰鍚嶇О'], why1),
+ '浜哄伐纭鏄惁鍚屼竴椤圭洰锛氭槸 鈫� 涓嶆敼鍚嶇О銆佹寜鎶曡祫绯荤粺鍚嶇О鏇存柊锛涘惁 鈫� 鏁磋鏂板')
if suspicious:
rep.append('### 鈿狅笍 鐤戜技鍚屼竴椤圭洰浣嗗悕绉板啓娉曚笉鍚� 鈫� 闇�浜哄伐纭锛堢▼搴忎笉鑷姩鍚堝苟銆佷篃涓嶉噸澶嶆柊澧烇級')
rep.append('')
@@ -267,16 +385,17 @@
rep.append('')
rep.append('| 椤圭洰鍚嶇О | 鏉ユ簮鏂囦欢 | 鑷紑濮嬪缓璁剧疮璁� | 鑷勾鍒濈疮璁� | 鏈湀瀹屾垚 | 鍚嶇О鐩镐技搴︽彁绀� |')
rep.append('| --- | --- | --- | --- | --- | --- |')
- loose_keys = [normname(x, loose=True) for x in tkeys]
for k in new_in_src:
p0 = pmap[k][0]
- nl = normname(k, loose=True)
- close = difflib.get_close_matches(nl, loose_keys, n=1, cutoff=0.80)
- hint = ''
- if close:
- hit = tkeys[loose_keys.index(close[0])]
- hint = '鈿狅笍 涓庛��%s銆嶇浉浼� %.2f锛岃纭鏄惁鍚屼竴椤圭洰' % (
- hit, difflib.SequenceMatcher(None, nl, close[0]).ratio())
+ _hit, _r = best_name_hint(k, tkeys)
+ hint = ('鈿狅笍 涓庛��%s銆嶇浉浼� %.2f锛岃纭鏄惁鍚屼竴椤圭洰' % (_hit, _r)) if _hit else ''
+ _sim = hint
+ add_alert('鎻愮ず', '灏嗘柊澧為」鐩�', cat, p0.get('甯傚窞', ''), p0['椤圭洰鍚嶇О'],
+ os.path.basename(p0['鏂囦欢']),
+ '鐩爣琛ㄦ病鏈夎繖涓」鐩紱鑷紑濮嬪缓璁剧疮璁�/鑷勾鍒濈疮璁�/鏈湀瀹屾垚 = %s/%s/%s%s' % (
+ p0.get('鑷紑濮嬪缓璁剧疮璁�', ''), p0.get('鑷勾鍒濈疮璁�', ''), p0.get('鏈湀瀹屾垚', ''),
+ ('锛�' + hint) if hint else ''),
+ '纭鏄湡瀹炴柊椤圭洰鍚庡啓鍏ワ紙鏁磋鎻掑叆锛屽鍒剁浉閭昏鏍煎紡锛�')
rep.append('| %s | %s | %s | %s | %s | %s |' % (
p0['椤圭洰鍚嶇О'], os.path.basename(p0['鏂囦欢'])[:30], p0.get('鑷紑濮嬪缓璁剧疮璁�', ''),
p0.get('鑷勾鍒濈疮璁�', ''), p0.get('鏈湀瀹屾垚', ''), hint))
@@ -295,23 +414,51 @@
'椤圭洰鍚嶇О': p['椤圭洰鍚嶇О'], '鍐欏叆鏉ユ簮': os.path.basename(p['鏂囦欢']),
**{c: p.get(c, '') for c in WANT}})
+ # 鐩爣琛ㄦ湁銆佹湰鏈熸簮鏂囦欢娌℃湁 鈫� 淇濇寔鍘熷�硷紙鎻愮ず锛屼笉闃绘柇锛�
+ if only_tgt:
+ _names = [tmap[k][0]['椤圭洰鍚嶇О'] for k in only_tgt]
+ add_alert('鎻愮ず', '鏈湡婧愭枃浠堕噷娌℃湁鐨勯」鐩�', cat, '', '', '鈥�',
+ '鐩爣琛ㄩ噷鏈� %d 涓」鐩湰鏈熷競宸炴湀鎶ユ病鎶ワ細%s%s' % (
+ len(_names), '銆�'.join(_names[:8]), ' 绛�' if len(_names) > 8 else ''),
+ '鎸夊彛寰勪繚鎸佷笂鏈堝師鍊硷紱鑻ュ競宸炵‘瀹炲凡鍋滃缓锛岃浜哄伐纭鏄惁淇濈暀')
+
# 涓夋柟鏁板�兼瘮瀵癸細婧愭枃浠� vs 鎶曡祫绯荤粺锛堟湰鏈燂級
if cur_sys:
- diff, missing = [], []
+ diff, missing, shifted = [], [], []
for k in matched:
p, t = pmap[k][0], tmap[k][0]
- s = cur_sys.get(k)
+ s, note = sys_lookup(k, cur_sys)
if not s:
missing.append(t['椤圭洰鍚嶇О'])
continue
- for c in ('鑷紑濮嬪缓璁剧疮璁�', '鑷勾鍒濈疮璁�', '鏈湀瀹屾垚'):
+ if note:
+ add_alert('鎻愮ず', '鎶曡祫绯荤粺鍚嶇О鍐欐硶涓嶅悓', cat, p.get('甯傚窞', ''), t['椤圭洰鍚嶇О'],
+ os.path.basename(p['鏂囦欢']), note + '锛涘凡鎸夋姇璧勭郴缁熻繖鏉¤褰曞仛鏁板�煎鐓э紙椤圭洰鍚嶇О涓嶅姩锛�',
+ '鐭ユ倝鍗冲彲')
+ for c in ('鎬绘姇璧�', '鑷紑濮嬪缓璁剧疮璁�', '鏈勾璁″垝鎶曡祫', '鑷勾鍒濈疮璁�', '鏈湀瀹屾垚'):
pv, sv = to_num(p.get(c)), s.get(c)
if pv is None or sv is None:
continue
- if abs(pv - sv) > 0.05:
- diff.append((t['椤圭洰鍚嶇О'], c, pretty(pv), pretty(sv), pretty(sv - pv)))
+ if abs(pv - sv) <= 0.05:
+ continue
+ diff.append((t['椤圭洰鍚嶇О'], c, pretty(pv), pretty(sv), pretty(sv - pv)))
+ if abs(pv - sv) > 1.0:
+ add_alert('璀﹀憡', '婧愭枃浠朵笌鎶曡祫绯荤粺涓嶄竴鑷�', cat, p.get('甯傚窞', ''), t['椤圭洰鍚嶇О'],
+ os.path.basename(p['鏂囦欢']),
+ '%s锛氬競宸炴湀鎶� %s 锛� 鎶曡祫绯荤粺 %s锛堝樊 %s锛�' % (c, pretty(pv), pretty(sv), pretty(sv - pv)),
+ '浜哄伐纭浠ュ摢涓负鍑嗭紱鑻ユ槸鍙栧垪閿欎綅鍒欎慨瑙f瀽瑙勫垯鍚庨噸璺�')
+ for c2 in ('鎬绘姇璧�', '鑷紑濮嬪缓璁剧疮璁�', '鏈勾璁″垝鎶曡祫', '鑷勾鍒濈疮璁�', '鏈湀瀹屾垚'):
+ if c2 == c:
+ continue
+ sv2 = s.get(c2)
+ if sv2 is not None and abs(pv - sv2) <= 0.05:
+ shifted.append((t['椤圭洰鍚嶇О'], c, c2))
+ add_alert('璀﹀憡', '鐤戜技鍙栧垪閿欎綅', cat, p.get('甯傚窞', ''), t['椤圭洰鍚嶇О'],
+ os.path.basename(p['鏂囦欢']),
+ '甯傚窞鏈堟姤銆�%s銆�= %s锛屾伆濂界瓑浜庢姇璧勭郴缁熴��%s銆�' % (c, pretty(pv), c2),
+ '妫�鏌ユ簮鏂囦欢琛ㄥご锛堢粍琛ㄥご鍚堝苟鍗曞厓鏍兼渶瀹规槗閿欎綅锛夛紝淇彇鍒楄鍒欏悗閲嶈窇瑙f瀽')
rep.append('')
- rep.append('### 婧愭枃浠� vs 鎶曡祫绯荤粺锛堟湰鏈燂級锛�**鏁板�间笉涓�鑷� %d 澶�**锛堥渶寮圭獥鎻愮ず锛�' % len(diff))
+ rep.append('### 婧愭枃浠� vs 鎶曡祫绯荤粺锛堟湰鏈燂級锛�**鏁板�间笉涓�鑷� %d 澶�**锛堥渶寮圭獥鎻愮ず锛夛紝鍏朵腑鐤戜技鍙栧垪閿欎綅 **%d** 澶�' % (len(diff), len(shifted)))
rep.append('')
if diff:
rep.append('| 椤圭洰鍚嶇О | 瀛楁 | 甯傚窞鏈堟姤 | 鎶曡祫绯荤粺 | 宸 |')
@@ -326,6 +473,9 @@
for nm1 in missing[:60]:
rep.append('- %s' % nm1)
rep.append('')
+ for nm1 in missing:
+ add_alert('鎻愮ず', '鎶曡祫绯荤粺鏌ユ棤姝ら」鐩�', cat, '', nm1, '鈥�',
+ '鎶曡祫绯荤粺鏈湡鏌ヨ缁撴灉閲屾病鏈夎繖涓」鐩悕绉�', '鐭ユ倝鍗冲彲锛涘闇�浠ユ姇璧勭郴缁熶负鍑嗭紝璇蜂汉宸ユ牳瀵瑰悕绉�')
# 鏈堝害鏍¢獙锛氭湰鏈堣嚜骞村垵绱 = 涓婃湀鑷勾鍒濈疮璁� + 鏈湀瀹屾垚
if cur_pre:
@@ -334,7 +484,7 @@
p, t = pmap[k][0], tmap[k][0]
nowj = to_num(p.get('鑷勾鍒濈疮璁�'))
nowk = to_num(p.get('鏈湀瀹屾垚'))
- pre = cur_pre.get(k)
+ pre, _note = sys_lookup(k, cur_pre)
if nowj is None or nowk is None or not pre:
continue
prej = pre.get('鑷勾鍒濈疮璁�')
@@ -342,6 +492,11 @@
continue
if abs(nowj - (prej + nowk)) > 0.05:
bad.append((t['椤圭洰鍚嶇О'], pretty(prej), pretty(nowk), pretty(nowj), pretty(prej + nowk)))
+ add_alert('璀﹀憡', '鏈堝害绛夊紡涓嶇', cat, p.get('甯傚窞', ''), t['椤圭洰鍚嶇О'],
+ '%s + %s = %s锛岃�屾湰鏈堣嚜骞村垵绱鏄� %s锛堝樊 %s锛�' % (
+ pretty(prej), pretty(nowk), pretty(prej + nowk), pretty(nowj),
+ pretty(nowj - prej - nowk)),
+ '鍐欏叆鏃舵寜绛夊紡绾犳涓� %s锛屽苟璁╁競宸炵‘璁�' % pretty(prej + nowk))
rep.append('### 鏈堝害鏍¢獙銆屾湰鏈堣嚜骞村垵绱 = 涓婃湀鑷勾鍒濈疮璁� + 鏈湀瀹屾垚銆嶏細涓嶇 **%d** 椤�' % len(bad))
rep.append('')
if bad:
@@ -354,6 +509,43 @@
rep.append('### 鏈堝害鏍¢獙锛�**璺宠繃**锛堟湭鎵惧埌涓婃湀鎶曡祫绯荤粺鏁版嵁琛紝鎸夌敤鎴峰彛寰� (a) 闇�姣忔湀鏀句竴浠斤級')
rep.append('')
+ # 鏈湡娌℃湁浠讳綍椤圭洰琛岀殑甯傚窞 鈫� 鎻愮ず锛堝彲鑳芥紡鎶ワ級
+ for cat in TARGETS:
+ _cities = {r['甯傚窞'] for r in parsed if r['鍝佺被'] == cat and r['甯傚窞']}
+ _miss = [c for c in CITY17 if c not in _cities]
+ if _miss:
+ add_alert('鎻愮ず', '鏈湡鏃犺甯傚窞鏁版嵁', cat, '', '', '鈥�',
+ '鍏� %d 涓競宸炴湰鏈熸病鏈変换浣曢」鐩锛�%s' % (len(_miss), '銆�'.join(_miss)),
+ '纭鏄�岃甯傚窞纭疄娌℃湁椤圭洰銆嶈繕鏄�屾紡鎶ャ�嶏紱婕忔姤鍒欒甯傚窞琛ユ姤')
+
+ # ---- 姹囨�汇�婂脊绐楁彁绀恒�嬶紙鏈潵鎼繘 Java 鍚庣鏃舵寜姝ら�愭潯寮圭獥锛� ----
+ _lv = {'闃绘柇': 0, '璀﹀憡': 1, '鎻愮ず': 2}
+ alerts.sort(key=lambda x: (_lv.get(x['绾у埆'], 9), x['绫诲瀷'], x['鍝佺被'], x['甯傚窞'], x['椤圭洰鍚嶇О']))
+ _n = {k: sum(1 for x in alerts if x['绾у埆'] == k) for k in ('闃绘柇', '璀﹀憡', '鎻愮ず')}
+ a_cols = ['搴忓彿', '绾у埆', '绫诲瀷', '鍝佺被', '甯傚窞', '椤圭洰鍚嶇О', '鏉ユ簮', '璇︽儏', '寤鸿鍔ㄤ綔']
+ p_al = os.path.join(out, '寮圭獥鎻愮ず_%s.tsv' % a.month)
+ with io.open(p_al, 'w', encoding='utf-8-sig') as fh:
+ fh.write('\t'.join(a_cols) + '\n')
+ for _i, _x in enumerate(alerts, 1):
+ fh.write('\t'.join(str(_i) if _k == '搴忓彿' else str(_x.get(_k, '')).replace('\t', ' ')
+ for _k in a_cols) + '\n')
+ al_md = ['## 鈿狅笍 寮圭獥鎻愮ず鎬昏锛堝叡 %d 椤癸細闃绘柇 %d 锛� 璀﹀憡 %d 锛� 鎻愮ず %d锛�' % (
+ len(alerts), _n['闃绘柇'], _n['璀﹀憡'], _n['鎻愮ず']), '']
+ al_md.append('> 绾у埆锛�**闃绘柇**=涓嶈兘鍐欒〃锛屽厛澶勭悊锛�**璀﹀憡**=鏁版嵁鏈夌枒鐐癸紝浜哄伐纭鍚庡啀鍐欙紱**鎻愮ず**=浠呭憡鐭ャ��')
+ al_md.append('> 鏈哄櫒鍙娓呭崟锛歚%s`锛堟湭鏉� Java/缃戦〉鎸夋閫愭潯寮圭獥锛夈�傝鍒欒 `docs\\鎶曡祫\\寮圭獥鎻愮ず瑙勫垯_2026-09-17.md`銆�'
+ % os.path.basename(p_al))
+ al_md.append('')
+ if alerts:
+ al_md.append('| 搴忓彿 | 绾у埆 | 绫诲瀷 | 鍝佺被 | 甯傚窞 | 椤圭洰鍚嶇О | 璇︽儏 | 寤鸿鍔ㄤ綔 |')
+ al_md.append('| --- | --- | --- | --- | --- | --- | --- | --- |')
+ for _i, _x in enumerate(alerts, 1):
+ al_md.append('| %d | %s | %s | %s | %s | %s | %s | %s |' % (
+ _i, _x['绾у埆'], _x['绫诲瀷'], _x['鍝佺被'], _x['甯傚窞'], _x['椤圭洰鍚嶇О'], _x['璇︽儏'], _x['寤鸿鍔ㄤ綔']))
+ al_md.append('')
+ else:
+ al_md.append('锛堟棤锛氭墍鏈夋鏌ラ」鍧囬�氳繃锛�'); al_md.append('')
+ rep = rep[:4] + al_md + rep[4:]
+
cols = ['鍝佺被', '鍔ㄤ綔', '鐩爣琛屽彿', '椤圭洰鍚嶇О', '鍐欏叆鏉ユ簮'] + WANT
p_tsv = os.path.join(out, '鍙樻洿娓呭崟_%s.tsv' % a.month)
with io.open(p_tsv, 'w', encoding='utf-8-sig') as fh:
@@ -363,8 +555,10 @@
p_md = os.path.join(out, '鍖归厤鏍¢獙鎶ュ憡_%s.md' % a.month)
io.open(p_md, 'w', encoding='utf-8').write('\n'.join(rep) + '\n')
print('鍙樻洿鏉℃暟 = %d' % len(changes))
+ print('寮圭獥鎻愮ず = %d锛堥樆鏂� %d / 璀﹀憡 %d / 鎻愮ず %d锛�' % (len(alerts), _n['闃绘柇'], _n['璀﹀憡'], _n['鎻愮ず']))
print('wrote', p_md)
print('wrote', p_tsv)
+ print('wrote', p_al)
if __name__ == '__main__':
--
Gitblit v1.9.1