| | |
| | | 2) å·¥ä½è¡¨å
æâè¡¨å¤´åºæ¯å¦å¸¦æ¬æå¹´ææ è®°âä¼å
ï¼åæå½ä¸åæ®µæ°æåï¼ |
| | | é¿å
åå°ç©ºæ¨¡æ¿è¡¨ / åå²æè¡¨ï¼å¦æ½æ±ãæåºãè¥é³ç夿工ä½ç°¿ï¼ï¼ |
| | | 3) 项ç®è¡å¤å®ï¼åç§°åé空ä¸ä¸æ¯å°è®¡/å计/åç±»è¡ï¼ä¸ææ°å¼éç¹ï¼èªå¼å§å»ºè®¾ç´¯è®¡çï¼ï¼ |
| | | å
¼å®¹âæ åºå·åâï¼éå·ï¼ä¸â缺åºå·âï¼æåº 7 æï¼ä¸¤ç§æ
åµã |
| | | å
¼å®¹âæ åºå·åâï¼éå·ï¼ä¸â缺åºå·âï¼æåº 7 æï¼ä¸¤ç§æ
åµï¼ |
| | | 4) å¸å·å段ï¼å
æ¾åºãå¸å·è¡ãï¼é¦ 5 åéæ°å¥½åªæ 1 个åå
æ ¼æ¯å¸å·åï¼åè¡å
¶ä½åå
¨ç©ºï¼ï¼ |
| | | ååªåç®æ å¸å·æ®µå
ç项ç®è¡ãæ¹åçåå °äº¤éç©æµâ¦è¡¨ç 88 è¡éï¼R11-R63 æ¯éèç |
| | | æ¦æ±åå²åºåï¼è¥ä¸æå¸å·åæ®µå°±ä¼ææ¦æ±çåå²é¡¹ç®å½æåå °çåè¿æ¥ï¼ |
| | | 5) éèè¡ / éèå·¥ä½è¡¨ä¸ä½ä¸ºå¤å®ä¾æ®ï¼é»ç³ç©æµ R10 æ¯éèè¡ï¼ä½å®æ¯ä¸æ¡çå®é¡¹ç®è¡ |
| | | ï¼é³æ°å¿ç¦å±±ç©æµä»å¨ä¸å¿ï¼ï¼ä¸å·²è®¡å
¥é»ç³å¸å·æ±æ»è¡ï¼å¿
é¡»åã |
| | | """ |
| | | import sys, os, io, re, argparse, datetime as _dt |
| | | |
| | |
| | | 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 '') |
| | |
| | | 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 请æ¹ç¨ Excel COMã""" |
| | | """è¿å [(sheet_name, nrows, ncols, cell_fn), ...]ã |
| | | |
| | | 注æï¼`.et`ï¼WPSï¼å®ä¸º OLE2/BIFF8 å¤åææ¡£ï¼xlrd å¯ç´æ¥è¯»ï¼ä¸å¿
èµ° Excel COMã |
| | | """ |
| | | with open(path, 'rb') as fh: |
| | | magic = fh.read(4) |
| | | if magic[:4] == b'PK\x03\x04': |
| | |
| | | return bool(re.match(r'^-?\d+(\.\d+)?$', norm(v))) |
| | | |
| | | |
| | | def profile(cell, nrows, ncols, hits): |
| | | namecol = None |
| | | if '项ç®åç§°' in hits: |
| | | namecol = hits['项ç®åç§°'][0][1] |
| | | 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)ã |
| | | |
| | | ä¸¤çº§è¡¨å¤´æ¶æ§ï¼è¥æå段çå½ä¸è½å¨**è·¨å¤åçåå¹¶åå
æ ¼**éï¼è¯´æå®æ¯ç»è¡¨å¤´ï¼ä¸è½ç´æ¥å½è¯¥å段çåï¼ |
| | | æ¤æ¶å¨æ¬åå¹¶åºå
æé¤ãå
¶å®åæ®µæ´æ·±çå表头ãæå çåï¼è¥åªå©å¯ä¸ä¸åï¼åè¯¥åææ¯æ¬å段çåã |
| | | ä¾ï¼éå·ãä¼ä¸ææ¥æ±æ»ãR5 ç»è¡¨å¤´ãèªå¹´åç´¯è®¡å®ææèµï¼ä¸å
ï¼ãåå¹¶ I5:K5ï¼ |
| | | å表头 I=æ¬å¹´è®¡åæèµãJ=å计ãK=å
¶ä¸ï¼æ¬æå®ææèµ â |
| | | ãèªå¹´å累计ãè½å¨ 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) |
| | | # åååæ¶æ§ï¼ä¼å
ãè¡¨å¤´ä¸æ¹ççææ°åãçé£ä¸åã |
| | | # ä¾ï¼æ¦æ±ç©æµãèªå¼å§å»ºè®¾ç´¯è®¡å®ææèµï¼ä¸å
ï¼ãH ä¸ãèªå¼å§å»ºè®¾ç´¯è®¡æ°å¢å»ºçé¢ç§¯ãP ååï¼ |
| | | # åªæ 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 = colindex(namecol) |
| | | anchors = [colindex(hits[k][0][1]) for k in ('èªå¼å§å»ºè®¾ç´¯è®¡', 'èªå¹´å累计', 'æ»æèµ') if k in hits] |
| | | 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)) \ |
| | |
| | | data_start = data_start or r + 1 |
| | | data_end = r + 1 |
| | | project_rows += 1 |
| | | return dict(namecol=namecol, data_start=data_start, data_end=data_end, |
| | | 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})) |
| | | |
| | | |
| | |
| | | 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, |
| | | 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'])) |
| | | åç»è¡=';'.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] |
| | | 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 表头=%-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') |