from ast import parse, walk, Call, Constant, keyword from collections import defaultdict from datetime import datetime, date from pathlib import Path import re import shutil import openpyxl import pymysql OUTPUT = Path('/Users/kkfluous/Downloads/7月核算结果/7月考核车辆多源里程汇总.xlsx') BACKUP = Path('/Users/kkfluous/Downloads/7月核算结果/7月考核车辆多源里程汇总_GPS全量回补前.xlsx') def norm(value): return re.sub(r'\s+', '', str(value or '')).upper() def db_config(): tree = parse(Path('/Users/kkfluous/Downloads/replace_gps_jun.py').read_text()) for node in walk(tree): if not isinstance(node, Call) or getattr(node.func, 'attr', None) != 'connect': continue values = {} for item in node.keywords: if isinstance(item, keyword) and isinstance(item.value, Constant): values[item.arg] = item.value.value if {'host', 'user', 'password', 'database'} <= values.keys(): return values raise RuntimeError('未找到既有GPS数据库配置') wb = openpyxl.load_workbook(OUTPUT) ws = wb.active header_row = next(r for r in range(1, 8) if ws.cell(r, 12).value == 'GPS里程(km)') data_start = header_row + 1 plates = sorted({norm(ws.cell(r, 2).value) for r in range(data_start, ws.max_row + 1) if ws.cell(r, 2).value}) cfg = db_config() cfg.update(charset='utf8mb4', connect_timeout=8, read_timeout=30, write_timeout=30) conn = pymysql.connect(**cfg) try: with conn.cursor() as cur: placeholders = ','.join(['%s'] * len(plates)) cur.execute( f'''SELECT plate_number, dates, total_mileage FROM ln_vehicle_g7_mileage WHERE dates >= %s AND dates <= %s AND plate_number IN ({placeholders})''', [date(2026, 7, 1), date(2026, 7, 31), *plates], ) db_rows = cur.fetchall() finally: conn.close() daily = defaultdict(dict) for plate, day, raw_mileage in db_rows: if isinstance(day, datetime): day = day.date() daily[norm(plate)][day] = float(raw_mileage or 0) / 100000.0 if not BACKUP.exists(): shutil.copy2(OUTPUT, BACKUP) filled = [] still_missing = [] for row in range(data_start, ws.max_row + 1): cell = ws.cell(row, 12) if cell.value != '无来源': continue plate = norm(ws.cell(row, 2).value) start = ws.cell(row, 4).value end = ws.cell(row, 5).value if isinstance(start, datetime): start = start.date() if isinstance(end, datetime): end = end.date() matched = [value for day, value in daily.get(plate, {}).items() if start <= day <= end] if matched: cell.value = round(sum(matched), 2) cell.number_format = '#,##0.0' filled.append((plate, start, end, cell.value)) else: still_missing.append((plate, start, end)) ws['A1'] = ( '1. 纯氢里程(km)、纯电里程(km)来源于广州平台统计,TBOX里程来源于车机上报的仪表盘里程统计,' 'GPS里程优先使用两份7月GPS日明细,缺失车辆由GPS数据库每日里程按考核区间回补;测试车辆以7月最终考核源表已确认GPS为准\n' '2. 广州平台总里程(km)=纯氢里程(km)+纯电里程(km)\n' '3. 已确认广州平台暂未接入86辆普货车数据;所有GPS来源均无对应记录时标记为“无来源”' ) wb.save(OUTPUT) print({ 'database_rows': len(db_rows), 'database_plates': len(daily), 'backfilled_records': len(filled), 'remaining_missing_records': len(still_missing), 'remaining_missing_plates': len({x[0] for x in still_missing}), })