Files
mileage-bonus/fix_july_intervention_style.py
T

148 lines
7.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""复刻6月干预说明配色,并将达标分类改为一行一人。"""
from copy import copy
from collections import defaultdict
from pathlib import Path
import openpyxl
BASE = Path('/Users/kkfluous/Downloads')
JUL = BASE / '7月核算结果/租赁任务考核_2026年7月_干预情况说明.xlsx'
JUN = BASE / '6月核算结果/租赁任务考核_2026年6月_干预情况说明.xlsx'
SRC = BASE / '7月核算结果/租赁任务考核_2026年7月.xlsx'
BAK = BASE / '7月核算结果/租赁任务考核_2026年7月_原版备份.xlsx'
TEST = Path('/Users/kkfluous/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/wxid_1704407055112_3af8/temp/RWTemp/2026-08/4340099467fbf08b4c6583c49adba4ee/7月测试车辆明细.xlsx')
def recs(path):
wb = openpyxl.load_workbook(path, data_only=True)
ws = wb['业务考核视图']
h = [c.value for c in ws[1]]
out = [dict(zip(h, r)) for r in ws.iter_rows(min_row=2, values_only=True) if r[0]]
wb.close()
return out
def clone_style(src, dst):
dst.font = copy(src.font)
dst.fill = copy(src.fill)
dst.border = copy(src.border)
dst.alignment = copy(src.alignment)
dst.protection = copy(src.protection)
dst.number_format = src.number_format
def style_row(ws, row, cols, ref_ws, ref_row, ref_cols=None):
ref_cols = ref_cols or cols
for c, rc in zip(cols, ref_cols):
clone_style(ref_ws.cell(ref_row, rc), ws.cell(row, c))
new = recs(SRC)
old = recs(BAK)
old_map = {}
for r in old:
old_map[r['车牌号']] = r
twb = openpyxl.load_workbook(TEST, data_only=True)
test_plates = {str(r[0]).strip() for r in twb.active.iter_rows(min_row=2, values_only=True) if r[0]}
test_plates.discard('粤AG37316')
twb.close()
intervention = [r for r in new if r['车牌号'] in test_plates]
flip = []
for r in intervention:
old_r = old_map.get(r['车牌号'])
if old_r and old_r.get('是否达标') == '达标' and r.get('是否达标') != '达标':
flip.append(r)
groups = defaultdict(lambda: defaultdict(int))
for r in flip:
groups[r.get('考核目标') or '未知'][(r.get('部门名称') or '', r.get('销售经理') or '')] += 1
wb = openpyxl.load_workbook(JUL)
ref = openpyxl.load_workbook(JUN)
ws = wb['测试与考核情况说明']
rws = ref['测试与考核情况说明']
# 清空原说明页后,按6月视觉规范重建。
for merged in list(ws.merged_cells.ranges):
ws.unmerge_cells(str(merged))
ws.delete_rows(1, ws.max_row)
for col in range(1, 8):
letter = openpyxl.utils.get_column_letter(col)
ws.column_dimensions[letter].width = rws.column_dimensions[letter].width
total_plates = len({r['车牌号'] for r in new})
interv_plates = len({r['车牌号'] for r in intervention})
total_tbox = sum(float(r.get('原TBOX里程(km)') or 0) for r in new)
total_actual = sum(float(r.get('实际行驶里程(km)') or 0) for r in new)
total_test = sum(float(r.get('测试里程(km)') or 0) for r in new)
interv_tbox = sum(float(r.get('原TBOX里程(km)') or 0) for r in intervention)
interv_actual = sum(float(r.get('实际行驶里程(km)') or 0) for r in intervention)
interv_test = sum(float(r.get('测试里程(km)') or 0) for r in intervention)
# 标题及说明:深蓝标题、浅黄说明,与6月一致。
ws.merge_cells('A1:G1'); ws['A1'] = '2026年7月 业务考核 — 测试与考核情况说明'; clone_style(rws['A1'], ws['A1'])
ws.row_dimensions[1].height = rws.row_dimensions[1].height
ws.merge_cells('A2:G3'); ws['A2'] = ('7月业务考核车辆中,测试车辆统一以指定GPS日里程按考核期汇总替换TBOX里程,差额作为测试里程剥离且最低取0。\n'
'粤AG37316因GPS缺失,经业务确认暂按0核算。'); clone_style(rws['A2'], ws['A2'])
ws.row_dimensions[2].height = rws.row_dimensions[2].height
ws.merge_cells('A5:G5'); ws['A5'] = '一、7月业务考核与干预情况'; clone_style(rws['A5'], ws['A5'])
headers = ['项目', '7月业务考核车辆', '涉及测试的车辆', '占比']
for c, v in enumerate(headers, 1): ws.cell(6,c,v); clone_style(rws.cell(6,c), ws.cell(6,c))
stats = [
('车辆数(辆)', total_plates, interv_plates),
('产生考核记录数(条)', len(new), len(intervention)),
('仪表盘里程(km)', round(total_tbox), round(interv_tbox)),
('业务考核里程 GPS(km)', round(total_actual), round(interv_actual)),
('测试里程(km)', round(total_test), round(interv_test)),
]
for rr, (label, allv, partv) in enumerate(stats, 7):
for c, v in enumerate([label, allv, partv, partv/allv if allv else 0], 1):
ws.cell(rr,c,v); clone_style(rws.cell(7 + ((rr-7)%2),c), ws.cell(rr,c))
# “测试里程”比例改为测试车辆剔除率,避免原100%被理解成车辆全部里程均为测试。
ws['D6'] = '占比/剔除率'
ws['D11'] = interv_test / interv_tbox if interv_tbox else 0
negative_floor_adjustment = sum(max(0, float(r.get('实际行驶里程(km)') or 0) - float(r.get('原TBOX里程(km)') or 0)) for r in intervention)
ws.merge_cells('A12:G12')
ws['A12'] = (f'勾稽:测试里程 {round(interv_tbox):,} {round(interv_actual):,} '
f'{round(negative_floor_adjustment):,}(负差归0调整)= {round(interv_test):,} km')
clone_style(rws['A2'], ws['A12'])
ws.row_dimensions[12].height = 22
ws.merge_cells('A13:G13'); ws['A13']='二、干预车辆考核达标情况说明'; clone_style(rws['A13'],ws['A13'])
ws.merge_cells('A14:G14'); ws['A14']=f'共 {len(intervention)} 条考核记录涉及里程干预;替换为GPS里程后,共 {len(flip)} 条考核车辆记录由“达标”转判为“未达标”。按车型分类如下:'; clone_style(rws['A14'],ws['A14'])
target_order = ['羚牛136辆4.5T冷链车','羚牛100辆18T','交投40辆4.5T普货','交投190辆4.5T冷链车','恒运50辆4.5T普货','恒运50辆4.5T 普货']
rn = 16
for target in target_order:
items = sorted(groups.get(target, {}).items())
if not items: continue
total = sum(v for _, v in items)
ws.merge_cells(start_row=rn,start_column=1,end_row=rn,end_column=3)
ws.cell(rn,1,f'◎{target}{total} 辆)'); clone_style(rws['A15'],ws.cell(rn,1)); rn += 1
for c,v in enumerate(['部门','销售经理','数量'],1): ws.cell(rn,c,v); clone_style(rws.cell(16,c),ws.cell(rn,c))
rn += 1
for idx, ((dept,sales),cnt) in enumerate(items):
for c,v in enumerate([dept,sales,cnt],1): ws.cell(rn,c,v); clone_style(rws.cell(17 + idx%2,c),ws.cell(rn,c))
rn += 1
rn += 1
# 数据来源说明沿用6月蓝色区域。
ws.merge_cells(start_row=rn,start_column=1,end_row=rn,end_column=7); ws.cell(rn,1,'三、里程三列数据来源说明'); clone_style(rws['A13'],ws.cell(rn,1)); rn += 1
for c,v in enumerate(['列','字段名','数据来源','说明'],1): ws.cell(rn,c,v); clone_style(rws.cell(6,c),ws.cell(rn,c))
rn += 1
descs = [
('O','原TBOX里程(km)','车辆 T-Box / 仪表盘读数','业务系统原始TBOX里程,包含运营、测试及调试里程。'),
('P','实际行驶里程(km)','指定GPS日里程文件','按考核日期逐日汇总GPS里程;粤AG37316经确认暂按0核算。'),
('Q','测试里程(km)','O P(最低取0','原TBOX里程与GPS里程的非负差额,不计入考核。'),
]
for idx,row in enumerate(descs):
for c,v in enumerate(row,1): ws.cell(rn,c,v); clone_style(rws.cell(28 + idx,c),ws.cell(rn,c))
ws.row_dimensions[rn].height = rws.row_dimensions[28 + idx].height
rn += 1
ws.sheet_view.showGridLines = rws.sheet_view.showGridLines
ws.freeze_panes = rws.freeze_panes
wb.save(JUL)
wb.close(); ref.close()
print(f'updated={JUL}')
print(f'rows={rn-1} flip={len(flip)} groups={sum(len(v) for v in groups.values())}')