From 8bcd59732179e920733dee79fc917aa513f6a37c Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期四, 17 九月 2026 16:42:07 +0800
Subject: [PATCH] chore(git): 根目录临时文件归档至 _tmp/ 并加固 .gitignore(防真实数据误提交),工作日结补记

---
 docs/投资/工具/格式探测.py |  397 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 397 insertions(+), 0 deletions(-)

diff --git "a/docs/\346\212\225\350\265\204/\345\267\245\345\205\267/\346\240\274\345\274\217\346\216\242\346\265\213.py" "b/docs/\346\212\225\350\265\204/\345\267\245\345\205\267/\346\240\274\345\274\217\346\216\242\346\265\213.py"
new file mode 100644
index 0000000..a6ed4dd
--- /dev/null
+++ "b/docs/\346\212\225\350\265\204/\345\267\245\345\205\267/\346\240\274\345\274\217\346\216\242\346\265\213.py"
@@ -0,0 +1,397 @@
+锘�# -*- coding: utf-8 -*-
+"""鏍煎紡鎺㈡祴.py 鈥斺�� 鎶曡祫鏈堟姤婧愭枃浠舵牸寮忔帰娴嬶紙鍙锛屼笉淇敼浠讳綍婧愭枃浠讹級
+
+鐢ㄦ硶锛�
+    python docs\鎶曡祫\宸ュ叿\鏍煎紡鎺㈡祴.py --month 2026-07
+    python docs\鎶曡祫\宸ュ叿\鏍煎紡鎺㈡祴.py --month 2026-07 --body 鐗╂祦锛堢墿娴佸洯鍖猴級
+杈撳嚭锛�
+    <out>\鏍煎紡鎺㈡祴_<YYYY-MM>.md     浜鸿鎶ュ憡
+    <out>\鏍煎紡鎺㈡祴_<YYYY-MM>.tsv   鏈哄櫒鍙鏄庣粏锛堜緵绋嬪簭鍥炲綊姣斿锛�
+
+鍘熷垯锛�
+    1) 鍒椾綅涓嶅啓姝伙細鎸夎〃澶村叧閿瓧鍦ㄢ�滃墠 14 琛� 脳 鍓� 34 鍒椻�濋噷鍖归厤瀹氫綅锛�
+    2) 宸ヤ綔琛ㄥ厛鎸夆�滆〃澶村尯鏄惁甯︽湰鏈熷勾鏈堟爣璁扳�濅紭鍏堬紝鍐嶆寜鍛戒腑瀛楁鏁版墦鍒嗭紝
+       閬垮厤鍙栧埌绌烘ā鏉胯〃 / 鍘嗗彶鏈堣〃锛堝娼滄睙銆佹灄鍖恒�佽闃崇殑澶氭湀宸ヤ綔绨匡級锛�
+    3) 椤圭洰琛屽垽瀹氾細鍚嶇О鍒楅潪绌轰笖涓嶆槸灏忚/鍚堣/鍒嗙被琛岋紝涓旀湁鏁板�奸敋鐐癸紙鑷紑濮嬪缓璁剧疮璁$瓑锛夛紝
+       鍏煎鈥滄棤搴忓彿鍒椻�濓紙闅忓窞锛変笌鈥滅己搴忓彿鈥濓紙鏋楀尯 7 鏈堬級涓ょ鎯呭喌锛�
+    4) 甯傚窞鍒嗘锛氬厛鎵惧嚭銆屽競宸炶銆嶏紙棣� 5 鍒楅噷鎭板ソ鍙湁 1 涓崟鍏冩牸鏄競宸炲悕锛屽悓琛屽叾浣欏垪鍏ㄧ┖锛夛紝
+       鍐嶅彧鍙栫洰鏍囧競宸炴鍐呯殑椤圭洰琛屻�傛箹鍖楃渷鍗佸牥浜ら�氱墿娴佲�﹁〃鐨� 88 琛岄噷锛孯11-R63 鏄殣钘忕殑
+       姝︽眽鍘嗗彶鍖哄潡锛岃嫢涓嶆寜甯傚窞鍒嗘灏变細鎶婃姹夌殑鍘嗗彶椤圭洰褰撴垚鍗佸牥鐨勫彇杩涙潵锛�
+    5) 闅愯棌琛� / 闅愯棌宸ヤ綔琛ㄤ笉浣滀负鍒ゅ畾渚濇嵁锛氶粍鐭崇墿娴� R10 鏄殣钘忚锛屼絾瀹冩槸涓�鏉$湡瀹為」鐩
+       锛堥槼鏂板幙鐒﹀北鐗╂祦浠撳偍涓績锛夛紝涓斿凡璁″叆榛勭煶甯傚窞姹囨�昏锛屽繀椤诲彇銆�
+"""
+import sys, os, io, re, argparse, datetime as _dt
+
+for _base in (os.path.dirname(os.path.abspath(__file__)), os.getcwd()):
+    for _sub in ('_pylibs', os.path.join('..', '_pylibs')):
+        _lib = os.path.abspath(os.path.join(_base, _sub))
+        if os.path.isdir(_lib) and _lib not in sys.path:
+            sys.path.insert(0, _lib)
+
+try:
+    import xlrd
+except ImportError:
+    xlrd = None
+try:
+    import openpyxl
+except ImportError:
+    openpyxl = None
+
+FIELD_PAT = [
+    ('椤圭洰鍚嶇О',     r'^(椤圭洰鍚嶇О|椤圭洰鍚嶇О鍙婄珯绾椤圭洰鍙婄珯绾椤圭洰鍚嶇О鍙婃垬绾�)$'),
+    ('寤鸿鍗曚綅',     r'(鎶曡祫寤鸿\s*鍗曚綅鍚嶇О|鎶曡祫寤鸿鍗曚綅鍚嶇О|椤圭洰涓氫富|寤鸿鍗曚綅)'),
+    ('寤鸿鎬ц川',     r'^(寤鸿鎬ц川|寤鸿 鎬ц川)$'),
+    ('寮�宸ユ椂闂�',     r'^(寮�宸ユ椂闂磡寮�宸ュ勾)$'),
+    ('绔e伐鏃堕棿',     r'^(绔e伐鏃堕棿|寤烘垚鏃堕棿|瀹屽伐骞�)$'),
+    ('鎬绘姇璧�',       r'(璁″垝鎬绘姇璧剕鎬绘姇璧�)'),
+    ('鑷紑濮嬪缓璁剧疮璁�', r'(鑷紑濮嬪缓璁緗鑷紑濮嬪缓璁捐嚦鏈勾)'),
+    ('鏈勾璁″垝鎶曡祫', r'(鏈勾璁″垝鎶曡祫|鏈勾璁″垝)'),
+    ('鑷勾鍒濈疮璁�',   r'((鑷�)?骞村垵绱瀹屾垚鎶曡祫|鑷勾鍒濈疮璁鑷勾鍒�-绱瀹屾垚鎶曡祫)'),
+    ('鏈湀瀹屾垚',     r'(鏈湀瀹屾垚鎶曡祫|鏈湀瀹屾垚|褰撴湀瀹屾垚鎶曡祫|^鏈湀$)'),
+    ('寤鸿闃舵',     r'^(椤圭洰寤鸿闃舵|寤鸿闃舵)$'),
+    ('褰㈣薄杩涘害',     r'^(褰㈣薄杩涘害|杩涘害鎯呭喌)$'),
+    ('宸ュ彲',         r'^宸ュ彲'),
+    ('鍒濊',         r'^鍒濊'),
+    ('甯傚窞',         r'^(甯傚窞|甯傚窞鍚嶇О)$'),
+    ('鏃ユ湡',         r'^(20\d\d[-./]\d{1,2})'),
+]
+KEY = ['椤圭洰鍚嶇О', '寤鸿鍗曚綅', '寤鸿鎬ц川', '寮�宸ユ椂闂�', '绔e伐鏃堕棿', '鎬绘姇璧�',
+       '鑷紑濮嬪缓璁剧疮璁�', '鏈勾璁″垝鎶曡祫', '鑷勾鍒濈疮璁�', '鏈湀瀹屾垚', '寤鸿闃舵', '褰㈣薄杩涘害']
+BAD_NAME = ('濉姤鍗曚綅', '绛剧珷', '鍗曚綅璐熻矗浜�', '缁熻璐熻矗浜�', '瀹℃牳', '璇存槑', '娉細', '鏈堟姤', '鎶ヨ〃',
+            '搴� 鍙�', '搴忓彿', '椤圭洰鍚嶇О', '椤圭洰涓氫富')
+
+CITIES = ('姝︽眽', '榛勭煶', '鍗佸牥', '瀹滄槍', '瑗勯槼', '閯傚窞', '鑽嗛棬', '瀛濇劅', '鑽嗗窞', '榛勫唸',
+          '鍜稿畞', '闅忓窞', '鎭╂柦', '浠欐', '娼滄睙', '澶╅棬', '绁炲啘鏋�', '鏋楀尯')
+
+
+def city_segments(cell, nrows, ncols, maxc=5):
+    # 甯傚窞琛� = 棣� maxc 鍒楅噷鎭板ソ鍙湁 1 涓崟鍏冩牸鏄競宸炲悕锛堝悓琛屽叾浣欏垪涓虹┖锛夈��
+    # 杩斿洖 [(琛屽彿1鍩�, 甯傚窞鍚�), ...]锛屽凡鎸夎鍙锋帓搴忋��
+    segs = []
+    for r in range(min(nrows, 800)):
+        vals = [norm(cell(r, c)) for c in range(min(ncols, maxc))]
+        hit = [i for i, v in enumerate(vals) if v in CITIES]
+        if len(hit) == 1 and sum(1 for v in vals if v) == 1:
+            segs.append((r + 1, vals[hit[0]]))
+    return segs
+
+
+def city_of_text(text):
+    for c in CITIES:
+        if c in str(text or ''):
+            return c
+    return None
+
+
+def norm(s):
+    s = str(s or '')
+    for ch in (' ', '\u3000', '\n', '\r', '\t'):
+        s = s.replace(ch, '')
+    return s
+
+
+def colname(i):
+    s = ''
+    i += 1
+    while i:
+        i, r = divmod(i - 1, 26)
+        s = chr(65 + r) + s
+    return s
+
+
+def colindex(letters):
+    n = 0
+    for ch in letters:
+        n = n * 26 + (ord(ch) - 64)
+    return n - 1
+
+
+def sheet_merges(path):
+    """杩斿洖 {sheet_name: [(rlo, rhi, clo, chi), ...]}锛�0 鍩恒�佸彸寮�锛夈��
+
+    鐢ㄤ簬璇嗗埆銆屼袱绾ц〃澶淬�嶏細缁勮〃澶达紙璺ㄥ鍒楃殑鍚堝苟鍗曞厓鏍硷級涓嶈兘鐩存帴褰撴垚鏌愬瓧娈电殑鍒椼��
+    璇讳笉鍒板悎骞朵俊鎭椂杩斿洖宸茶В鏋愬埌鐨勯儴鍒嗭紙涓嶆姏寮傚父锛夈��
+    """
+    with open(path, 'rb') as fh:
+        magic = fh.read(4)
+    out = {}
+    try:
+        if magic[:4] == b'PK\x03\x04':
+            if openpyxl is None:
+                return out
+            wb = openpyxl.load_workbook(path, data_only=True)
+            for nm in wb.sheetnames:
+                ws = wb[nm]
+                out[nm] = [(r.min_row - 1, r.max_row, r.min_col - 1, r.max_col)
+                           for r in ws.merged_cells.ranges]
+            wb.close()
+        else:
+            if xlrd is None:
+                return out
+            bk = xlrd.open_workbook(path, formatting_info=True)
+            for si in range(bk.nsheets):
+                sh = bk.sheet_by_index(si)
+                out[sh.name] = list(getattr(sh, 'merged_cells', []) or [])
+    except Exception:
+        return out
+    return out
+
+
+def merge_span(merges, row1, col0):
+    """璇ュ崟鍏冩牸锛�1 鍩鸿鍙枫��0 鍩哄垪鍙凤級鎵�鍦ㄥ悎骞跺尯鐨� (鍒�0璧�, 鍒�0姝㈠彸寮�)锛涗笉鍦ㄥ悎骞跺尯杩斿洖 None銆�"""
+    for (rlo, rhi, clo, chi) in (merges or ()):
+        if rlo <= row1 - 1 < rhi and clo <= col0 < chi:
+            return (clo, chi)
+    return None
+
+
+def open_sheets(path):
+    """杩斿洖 [(sheet_name, nrows, ncols, cell_fn), ...]銆�
+
+    娉ㄦ剰锛歚.et`锛圵PS锛夊疄涓� OLE2/BIFF8 澶嶅悎鏂囨。锛寈lrd 鍙洿鎺ヨ锛屼笉蹇呰蛋 Excel COM銆�
+    """
+    with open(path, 'rb') as fh:
+        magic = fh.read(4)
+    if magic[:4] == b'PK\x03\x04':
+        if openpyxl is None:
+            raise RuntimeError('闇�瑕� openpyxl 璇诲彇 .xlsx')
+        wb = openpyxl.load_workbook(path, data_only=True)
+        out = []
+        for nm in wb.sheetnames:
+            ws = wb[nm]
+            out.append((nm, ws.max_row, ws.max_column,
+                        (lambda r, c, _w=ws: _w.cell(row=r + 1, column=c + 1).value)))
+        wb.close()
+        return out
+    if xlrd is None:
+        raise RuntimeError('闇�瑕� xlrd 璇诲彇 .xls')
+    bk = xlrd.open_workbook(path)
+    out = []
+    for si in range(bk.nsheets):
+        sh = bk.sheet_by_index(si)
+        out.append((sh.name, sh.nrows, sh.ncols,
+                    (lambda r, c, _s=sh: _s.cell_value(r, c) if (r < _s.nrows and c < _s.ncols) else '')))
+    return out
+
+
+def scan_fields(cell, nrows, ncols, maxrow=14, maxcol=34):
+    hits = {}
+    for r in range(min(nrows, maxrow)):
+        for c in range(min(ncols, maxcol)):
+            t = norm(cell(r, c))
+            if not t or len(t) > 30:
+                continue
+            for f, p in FIELD_PAT:
+                if re.search(p, t):
+                    hits.setdefault(f, []).append((r + 1, colname(c), t[:24]))
+    return hits
+
+
+def is_num(v):
+    if isinstance(v, (int, float)) and not isinstance(v, bool):
+        return True
+    return bool(re.match(r'^-?\d+(\.\d+)?$', norm(v)))
+
+
+def _num_count(cell, nrows, col, start_row1):
+    """缁熻璇ュ垪鍦ㄨ〃澶磋浠ヤ笅鐨勬暟鍊煎崟鍏冩牸涓暟锛堢敤浜庡尯鍒嗗悓鍚嶅垪锛屽銆岃嚜寮�濮嬪缓璁剧疮璁″畬鎴愭姇璧勩�嶄笌銆屸�︽柊澧炲缓绛戦潰绉�嶏級銆�"""
+    n = 0
+    for r in range(start_row1, min(nrows, 800)):
+        v = cell(r, col)
+        if isinstance(v, (int, float)) and not isinstance(v, bool):
+            n += 1
+    return n
+
+
+def pick_columns(cell, nrows, ncols, merges=None, fields=None):
+    """鎸夎〃澶村叧閿瓧瀹氫綅鍒� 鈫� ({瀛楁: 0 鍩哄垪鍙穧, hits)銆�
+
+    涓ょ骇琛ㄥご娑堟锛氳嫢鏌愬瓧娈电殑鍛戒腑钀藉湪**璺ㄥ鍒楃殑鍚堝苟鍗曞厓鏍�**閲岋紝璇存槑瀹冩槸缁勮〃澶达紝涓嶈兘鐩存帴褰撹瀛楁鐨勫垪锛�
+    姝ゆ椂鍦ㄦ湰鍚堝苟鍖哄唴鎺掗櫎銆屽叾瀹冨瓧娈垫洿娣辩殑瀛愯〃澶淬�嶆墍鍗犵殑鍒楋紝鑻ュ彧鍓╁敮涓�涓�鍒楋紝鍒欒鍒楁墠鏄湰瀛楁鐨勫垪銆�
+    渚嬶細閯傚窞銆婁紒涓氭湀鎶ユ眹鎬汇�婻5 缁勮〃澶淬�岃嚜骞村垵绱瀹屾垚鎶曡祫锛堜竾鍏冿級銆嶅悎骞� I5:K5锛�
+        瀛愯〃澶� I=鏈勾璁″垝鎶曡祫銆丣=鍚堣銆並=鍏朵腑锛氭湰鏈堝畬鎴愭姇璧� 鈫�
+        銆岃嚜骞村垵绱銆嶈惤鍦� J锛堜笌鎶曡祫绯荤粺 G 鍒椾竴鑷达級锛岃�屼笉鏄� I锛堥偅鏄湰骞磋鍒掓姇璧勶級銆�
+    """
+    hits = scan_fields(cell, nrows, ncols)
+    fields = list(fields) if fields else [k for k, _ in FIELD_PAT]
+    best = {}
+    for f in fields:
+        if f not in hits:
+            continue
+        uniq, seen = [], set()
+        for h in sorted(hits[f], key=lambda h: (h[0], colindex(h[1]))):
+            if h[1] in seen:
+                continue
+            seen.add(h[1]); uniq.append(h)
+        # 鍚屽悕鍒楁秷姝э細浼樺厛銆岃〃澶翠笅鏂圭湡鐨勬湁鏁板瓧銆嶇殑閭d竴鍒椼��
+        # 渚嬶細姝︽眽鐗╂祦銆岃嚜寮�濮嬪缓璁剧疮璁″畬鎴愭姇璧勶紙涓囧厓锛夈�岺 涓庛�岃嚜寮�濮嬪缓璁剧疮璁℃柊澧炲缓绛戦潰绉�峆 鍚屽悕锛�
+        #     鍙湁 H 鍒楁湁鏁板瓧 鈫� 鍙� H锛堟寜鈥滃懡涓瘝鏈�鐭�濅細閿欏彇 P锛岄偅鏄缓绛戦潰绉級銆�
+        withnum = [h for h in uniq if _num_count(cell, nrows, colindex(h[1]), h[0]) > 0]
+        pool = withnum or uniq
+        best[f] = sorted(pool, key=lambda h: (h[0], colindex(h[1])))[0]
+    cols = {}
+    for f, h in best.items():
+        row1, letter, _txt = h
+        c0 = colindex(letter)
+        span = merge_span(merges, row1, c0)
+        if span and (span[1] - span[0]) > 1:
+            taken = set()
+            for f2, h2 in best.items():
+                if f2 == f or h2[0] <= row1:
+                    continue
+                c2 = colindex(h2[1])
+                if span[0] <= c2 < span[1]:
+                    taken.add(c2)
+            free = [c for c in range(span[0], span[1]) if c not in taken]
+            if len(free) == 1:
+                cols[f] = free[0]
+                continue
+        cols[f] = c0
+    return cols, hits
+
+
+def profile(cell, nrows, ncols, hits, city=None, merges=None):
+    cols, _ = pick_columns(cell, nrows, ncols, merges)
+    namecol = colname(cols['椤圭洰鍚嶇О']) if '椤圭洰鍚嶇О' in cols else None
+    data_start = data_end = None
+    project_rows = 0
+    groups = []
+    segs = city_segments(cell, nrows, ncols)
+    if city is None and len({s[1] for s in segs}) == 1:
+        city = segs[0][1]
+    lo, hi, city_row = 1, 10 ** 9, None
+    if city:
+        for i, (rr, cc) in enumerate(segs):
+            if cc == city:
+                city_row, lo = rr, rr
+                hi = segs[i + 1][0] if i + 1 < len(segs) else 10 ** 9
+                break
+    if namecol is not None:
+        nc = cols['椤圭洰鍚嶇О']
+        anchors = [cols[k] for k in ('鑷紑濮嬪缓璁剧疮璁�', '鑷勾鍒濈疮璁�', '鎬绘姇璧�') if k in cols]
+        for r in range(min(nrows, 800)):
+            if not (lo <= r + 1 < hi):
+                continue
+            a = norm(cell(r, 0))
+            name = norm(cell(r, nc))
+            is_group = bool(re.match(r'^(涓�|浜寍涓墊鍥泑浜攟鍏�)銆�', a + name)) \
+                or a in ('灏忚', '鍚堣') or name in ('灏忚', '鍚堣')
+            if is_group:
+                if name:
+                    groups.append((r + 1, name[:20]))
+                continue
+            if not name or len(name) < 4 or any(name.startswith(x) for x in BAD_NAME):
+                continue
+            if nc == 0:
+                numbered = True            # 鏃犲簭鍙峰垪锛堥殢宸烇級锛氬悕绉板垪灏辨槸 A 鍒�
+            else:
+                numbered = bool(re.match(r'^\d{1,3}(\.0)?$', a)) or a == ''
+            has_value = any(is_num(cell(r, c)) for c in anchors) if anchors else True
+            if numbered and has_value:
+                data_start = data_start or r + 1
+                data_end = r + 1
+                project_rows += 1
+    return dict(namecol=namecol, cols=cols, data_start=data_start, data_end=data_end,
+                project_rows=project_rows, groups=groups[:6],
+                city=city or '', city_row=city_row or '', segments=segs[:8],
+                header_rows=sorted({h[0] for v in hits.values() for h in v}))
+
+
+def hdr_dates(cell, nrows, ncols):
+    """琛ㄥご鍖猴紙鍓� 14 琛� 脳 鍓� 32 鍒楋級鍑虹幇鐨勫勾鏈堟爣璁帮紝濡� '202607'锛堝吋瀹规枃鏈棩鏈熶笌 Excel 鏃ユ湡搴忓垪鍙凤級銆�"""
+    out = set()
+    for r in range(min(nrows, 14)):
+        for c in range(min(ncols, 32)):
+            v = cell(r, c)
+            t = str(v or '')
+            for m in re.finditer(r'(20\d{2})\s*[-./骞碷\s*(\d{1,2})', t):
+                out.add('%s%02d' % (m.group(1), int(m.group(2))))
+            if isinstance(v, (int, float)) and not isinstance(v, bool) and 40000 <= v <= 60000:
+                d = _dt.date(1899, 12, 30) + _dt.timedelta(days=int(v))
+                out.add('%04d%02d' % (d.year, d.month))
+    return out
+
+
+def pick_sheet(sheets, ym=None):
+    best = None
+    for (nm, nr, nc, cell) in sheets:
+        hits = scan_fields(cell, nr, nc)
+        score = sum(3 if k == '椤圭洰鍚嶇О' else 2 for k in hits if k in KEY)
+        if ym and ym in hdr_dates(cell, nr, nc):
+            score += 20
+        if best is None or score > best[1]:
+            best = ((nm, nr, nc, cell, hits), score)
+    return best[0] if best else None
+
+
+def month_dir(month):
+    return '%d鏈�' % int(month.split('-')[1])
+
+
+def main():
+    ap = argparse.ArgumentParser()
+    ap.add_argument('--month', required=True, help='濡� 2026-07')
+    ap.add_argument('--root', default=os.path.join('docs', '鎶曡祫'))
+    ap.add_argument('--out', default=None)
+    ap.add_argument('--body', default=None, help='鍙壂涓�涓搧绫荤洰褰�')
+    args = ap.parse_args()
+    out = args.out or os.path.join(args.root, '杈撳嚭')
+    os.makedirs(out, exist_ok=True)
+    md = month_dir(args.month)
+    ym = args.month.replace('-', '')
+    bodies = [args.body] if args.body else ['瀹㈣繍锛堝杩愮珯锛�', '鐗╂祦锛堢墿娴佸洯鍖猴級']
+    rows, report = [], []
+    for body in bodies:
+        d = os.path.join(args.root, '杈撳叆', body, md)
+        if not os.path.isdir(d):
+            report.append('!! 鐩綍涓嶅瓨鍦細%s' % d)
+            continue
+        files = sorted(f for f in os.listdir(d)
+                       if os.path.isfile(os.path.join(d, f)) and not f.startswith('~$'))
+        report.append('## %s锛�%d 涓枃浠讹級' % (body, len(files)))
+        for f in files:
+            p = os.path.join(d, f)
+            rec = dict(鍝佺被=body.split('锛�')[0], 鏂囦欢=f, 宸ヤ綔琛�='', 琛ㄥご琛�='', 鏁版嵁璧�='', 鏁版嵁姝�='',
+                       椤圭洰琛屾暟=0, 鍚嶇О鍒�='', 鍒嗙粍琛�='', 甯傚窞='', 甯傚窞琛�='', 甯傚窞娈�='')
+            file_city = city_of_text(f)
+            try:
+                sel = pick_sheet(open_sheets(p), ym=ym)
+                if sel:
+                    nm, nr, nc, cell, hits = sel
+                    pr = profile(cell, nr, nc, hits, city=file_city,
+                                 merges=sheet_merges(p).get(nm, []))
+                    rec.update(宸ヤ綔琛�=nm, 琛ㄥご琛�=','.join(map(str, pr['header_rows'])),
+                               鏁版嵁璧�=pr['data_start'], 鏁版嵁姝�=pr['data_end'],
+                               椤圭洰琛屾暟=pr['project_rows'], 鍚嶇О鍒�=pr['namecol'] or '',
+                               鍒嗙粍琛�=';'.join('R%s:%s' % g for g in pr['groups']),
+                               甯傚窞=pr['city'] or (file_city or ''), 甯傚窞琛�=pr['city_row'],
+                               甯傚窞娈�=';'.join('R%s:%s' % s for s in pr['segments']))
+                    for k in KEY:
+                        if k in pr['cols']:
+                            rec[k + '鍒�'] = colname(pr['cols'][k])
+            except Exception as e:
+                rec['閿欒'] = '%s: %s' % (type(e).__name__, e)
+            rows.append(rec)
+            report.append('   %-46s sheet=%-12s 甯傚窞=%s@R%s 琛ㄥご=%-12s 鏁版嵁=%s~%s 椤圭洰琛�=%-3s 鍚嶇О鍒�=%s %s'
+                          % (rec['鏂囦欢'][:46], str(rec['宸ヤ綔琛�'])[:12], rec['甯傚窞'], rec['甯傚窞琛�'],
+                             rec['琛ㄥご琛�'], rec['鏁版嵁璧�'],
+                             rec['鏁版嵁姝�'], rec['椤圭洰琛屾暟'], rec['鍚嶇О鍒�'],
+                             ''.join('%s=%s ' % (k, rec.get(k + '鍒�', '-')) for k in KEY)
+                             + ('   [%s]' % rec['閿欒'] if rec.get('閿欒') else '')))
+    cols = ['鍝佺被', '鏂囦欢', '宸ヤ綔琛�', '甯傚窞', '甯傚窞琛�', '甯傚窞娈�', '琛ㄥご琛�', '鏁版嵁璧�', '鏁版嵁姝�', '椤圭洰琛屾暟',
+            '鍚嶇О鍒�'] + [k + '鍒�' for k in KEY] + ['鍒嗙粍琛�', '閿欒']
+    tsv = os.path.join(out, '鏍煎紡鎺㈡祴_%s.tsv' % args.month)
+    with io.open(tsv, 'w', encoding='utf-8-sig') as fh:
+        fh.write('\t'.join(cols) + '\n')
+        for r in rows:
+            fh.write('\t'.join(str(r.get(c, '')) for c in cols) + '\n')
+    mdpath = os.path.join(out, '鏍煎紡鎺㈡祴_%s.md' % args.month)
+    io.open(mdpath, 'w', encoding='utf-8').write(
+        '# 鎶曡祫鏈堟姤婧愭枃浠舵牸寮忔帰娴嬶紙%s锛塡n\n' % args.month + '\n'.join(report) +
+        '\n\n鏄庣粏琛細`%s`\n' % tsv)
+    print('wrote', mdpath)
+    print('wrote', tsv)
+
+
+if __name__ == '__main__':
+    main()

--
Gitblit v1.9.1