docs/Ͷ×Ê/¹¤¾ß/¸ñʽ̽²â.py
@@ -13,7 +13,12 @@
    2) å·¥ä½œè¡¨å…ˆæŒ‰â€œè¡¨å¤´åŒºæ˜¯å¦å¸¦æœ¬æœŸå¹´æœˆæ ‡è®°â€ä¼˜å…ˆï¼Œå†æŒ‰å‘½ä¸­å­—段数打分,
       é¿å…å–到空模板表 / åŽ†å²æœˆè¡¨ï¼ˆå¦‚æ½œæ±Ÿã€æž—åŒºã€è¥„é˜³çš„å¤šæœˆå·¥ä½œç°¿ï¼‰ï¼›
    3) é¡¹ç›®è¡Œåˆ¤å®šï¼šåç§°åˆ—非空且不是小计/合计/分类行,且有数值锚点(自开始建设累计等),
       å…¼å®¹â€œæ— åºå·åˆ—”(随州)与“缺序号”(林区 7 æœˆï¼‰ä¸¤ç§æƒ…况。
       å…¼å®¹â€œæ— åºå·åˆ—”(随州)与“缺序号”(林区 7 æœˆï¼‰ä¸¤ç§æƒ…况;
    4) å¸‚州分段:先找出「市州行」(首 5 åˆ—里恰好只有 1 ä¸ªå•元格是市州名,同行其余列全空),
       å†åªå–目标市州段内的项目行。湖北省十堰交通物流…表的 88 è¡Œé‡Œï¼ŒR11-R63 æ˜¯éšè—çš„
       æ­¦æ±‰åŽ†å²åŒºå—ï¼Œè‹¥ä¸æŒ‰å¸‚å·žåˆ†æ®µå°±ä¼šæŠŠæ­¦æ±‰çš„åŽ†å²é¡¹ç›®å½“æˆåå °çš„å–è¿›æ¥ï¼›
    5) éšè—è¡Œ / éšè—å·¥ä½œè¡¨ä¸ä½œä¸ºåˆ¤å®šä¾æ®ï¼šé»„石物流 R10 æ˜¯éšè—è¡Œï¼Œä½†å®ƒæ˜¯ä¸€æ¡çœŸå®žé¡¹ç›®è¡Œ
       ï¼ˆé˜³æ–°åŽ¿ç„¦å±±ç‰©æµä»“å‚¨ä¸­å¿ƒï¼‰ï¼Œä¸”å·²è®¡å…¥é»„çŸ³å¸‚å·žæ±‡æ€»è¡Œï¼Œå¿…é¡»å–ã€‚
"""
import sys, os, io, re, argparse, datetime as _dt
@@ -54,6 +59,28 @@
       '自开始建设累计', '本年计划投资', '自年初累计', '本月完成', '建设阶段', '形象进度']
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):
@@ -124,17 +151,29 @@
    return bool(re.match(r'^-?\d+(\.\d+)?$', norm(v)))
def profile(cell, nrows, ncols, hits):
def profile(cell, nrows, ncols, hits, city=None):
    namecol = None
    if '项目名称' in hits:
        namecol = hits['项目名称'][0][1]
    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 = colindex(namecol)
        anchors = [colindex(hits[k][0][1]) for k in ('自开始建设累计', '自年初累计', '总投资') if k in hits]
        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)) \
@@ -156,6 +195,7 @@
                project_rows += 1
    return dict(namecol=namecol, 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}))
@@ -214,28 +254,33 @@
        for f in files:
            p = os.path.join(d, f)
            rec = dict(品类=body.split('(')[0], æ–‡ä»¶=f, å·¥ä½œè¡¨='', è¡¨å¤´è¡Œ='', æ•°æ®èµ·='', æ•°æ®æ­¢='',
                       é¡¹ç›®è¡Œæ•°=0, åç§°åˆ—='', åˆ†ç»„行='')
                       é¡¹ç›®è¡Œæ•°=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)
                    pr = profile(cell, nr, nc, hits, city=file_city)
                    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']))
                               åˆ†ç»„行=';'.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 hits:
                            rec[k + '列'] = hits[k][0][1]
            except Exception as e:
                rec['错误'] = '%s: %s' % (type(e).__name__, e)
            rows.append(rec)
            report.append('   %-46s sheet=%-12s è¡¨å¤´=%-12s æ•°æ®=%s~%s é¡¹ç›®è¡Œ=%-3s åç§°åˆ—=%s %s'
                          % (rec['文件'][:46], str(rec['工作表'])[:12], rec['表头行'], 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] + ['分组行', '错误']
    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')