chore: curate monthly assessment evidence and scripts

This commit is contained in:
kkfluous
2026-08-25 12:26:35 +08:00
parent 2956496fb0
commit 52dde9195a
117 changed files with 10743 additions and 179 deletions
+330
View File
@@ -0,0 +1,330 @@
#!/usr/bin/env python3
"""Generate 租赁任务考核_2026年5月_干预情况说明.xlsx — GPS干预后的完整说明"""
import os, openpyxl
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter
from collections import defaultdict
os.chdir('/Users/kkfluous/Downloads')
SRC = '租赁任务考核_2026年5月.xlsx'
BAK = '租赁任务考核_2026年5月_原版备份.xlsx'
INTV = '标黄为5月干预车辆.xlsx'
# Styles
HEAD = PatternFill('solid', fgColor='4472C4')
HFONT = Font(bold=True, color='FFFFFF', size=10)
SUB = PatternFill('solid', fgColor='D9E1F2')
ALT = PatternFill('solid', fgColor='F2F2F2')
TITLE_FONT = Font(bold=True, size=14)
SEC_FONT = Font(bold=True, size=12, color='1F4E78')
NOTE_FONT = Font(size=10)
BOLD_FONT = Font(bold=True, size=10)
RED_FONT = Font(size=10, color='C00000')
BORD = Border(Side('thin','B4B4B4'), Side('thin','B4B4B4'), Side('thin','B4B4B4'), Side('thin','B4B4B4'))
CC = Alignment(horizontal='center', vertical='center', wrap_text=True)
LL = Alignment(horizontal='left', vertical='center', wrap_text=True)
# ── Load intervention plates ──
wb_i = openpyxl.load_workbook(INTV)
ws_i = wb_i['Sheet1']
yellow_plates = set()
for row in ws_i.iter_rows(min_row=2, values_only=False):
cell = row[0]
fill = cell.fill
is_yellow = (fill and fill.patternType == 'solid' and fill.start_color
and fill.start_color.rgb and 'FFFF00' in str(fill.start_color.rgb))
if is_yellow:
p = str(cell.value).strip() if cell.value else ''
if p:
yellow_plates.add(p)
wb_i.close()
# ── Load source (post-intervention) and backup (pre-intervention) ──
def load_records(fp):
wb = openpyxl.load_workbook(fp, data_only=True)
ws = wb['业务考核视图']
h = [c.value for c in next(ws.iter_rows(min_row=1, max_row=1))]
recs = []
for row in ws.iter_rows(min_row=2, values_only=True):
if row[0]:
recs.append(dict(zip(h, row)))
wb.close()
return recs
src_recs = load_records(SRC)
bak_recs = load_records(BAK)
# Build plate -> {backup rec, source rec}
bak_by_plate = {}
for r in bak_recs:
p = str(r['车牌号']).strip()
# For duplicate plates, keep last
key = (p, str(r.get('销售经理','')), str(r.get('项目名称','')))
bak_by_plate[key] = r
src_by_plate = {}
for r in src_recs:
p = str(r['车牌号']).strip()
key = (p, str(r.get('销售经理','')), str(r.get('项目名称','')))
src_by_plate[key] = r
# ── Analyze intervention impact ──
# For each source record that's an intervention vehicle, find its backup
intv_records = []
for key, sr in src_by_plate.items():
p = key[0]
if p not in yellow_plates:
continue
br = bak_by_plate.get(key)
tbox = sr.get('原TBOX里程(km)')
gps = sr.get('实际行驶里程(km)')
test = sr.get('测试里程(km)')
old_actual = float(br['实际行驶里程(km)'] or 0) if br else 0
old_pass = str(br['是否达标']).strip() if br else '?'
new_pass = str(sr['是否达标']).strip() if sr else '?'
target = float(sr.get('应考核里程(km)', 0) or 0)
tbox_val = float(tbox or 0)
gps_val = float(gps or 0)
test_val = float(test or 0)
flip = ''
if old_pass == '达标' and new_pass == '未达标':
flip = '达→未达标'
elif old_pass == '未达标' and new_pass == '达标':
flip = '未→达标'
has_gps = '' if gps_val > 0 else '无GPS数据'
intv_records.append({
'plate': p, 'dept': sr.get('部门名称', ''), 'sales': sr.get('销售经理', ''),
'client': sr.get('客户名称', ''), 'target_name': sr.get('考核目标', ''),
'target_km': target, 'old_actual': old_actual, 'tbox': tbox_val,
'gps': gps_val, 'test': test_val, 'old_pass': old_pass, 'new_pass': new_pass,
'flip': flip, 'has_gps': has_gps
})
print(f"Intervention records: {len(intv_records)}")
flip_down = [r for r in intv_records if r['flip'] == '达→未达标']
flip_up = [r for r in intv_records if r['flip'] == '未→达标']
no_gps = [r for r in intv_records if r['gps'] == 0]
print(f"达→未达标: {len(flip_down)}, 未→达标: {len(flip_up)}, 无GPS: {len(no_gps)}")
# ── Intervention vehicles by target type ──
by_target = defaultdict(list)
for r in intv_records:
by_target[r['target_name']].append(r)
# ── Generate workbook ──
wb = openpyxl.Workbook()
def style_header(ws, row, ncols):
for c in range(1, ncols+1):
cell = ws.cell(row=row, column=c)
cell.fill = HEAD; cell.font = HFONT; cell.alignment = CC; cell.border = BORD
def style_row(ws, row, ncols, alt=False):
for c in range(1, ncols+1):
cell = ws.cell(row=row, column=c)
cell.border = BORD; cell.font = Font(size=10); cell.alignment = CC
if alt: cell.fill = ALT
# ── Sheet 1: 测试与考核情况说明 ──
ws1 = wb.active
ws1.title = '测试与考核情况说明'
ws1.sheet_properties.tabColor = '4472C4'
ws1.merge_cells('A1:H1')
ws1.cell(row=1, column=1, value='2026年5月 业务考核 — GPS干预与考核情况说明').font = TITLE_FONT
ws1.cell(row=1, column=1).alignment = CC
ws1.row_dimensions[1].height = 32
desc = (
f'5月共计{len(src_recs)}条考核记录,其中{len(yellow_plates)}辆车/{len(intv_records)}条记录涉及GPS里程干预。\n'
'干预规则:对于标黄车辆,使用G7平台GPS每日里程(考核期内逐日累加)替代原仪表盘里程作为实际行驶里程参与考核。\n'
f'GPS数据来源:hydrogen_energy.ln_vehicle_g7_mileage101.133.130.65),按考核起止时间汇总每日total_mileage/100000=km。\n'
f'干预结果:{len(flip_down)}条由达标翻转为未达标,{len(flip_up)}条由未达标翻转为达标,{len(no_gps)}条车辆无GPS数据(里程记为0)。'
)
ws1.merge_cells('A2:H2')
ws1.cell(row=2, column=1, value=desc).font = NOTE_FONT
ws1.cell(row=2, column=1).alignment = Alignment(horizontal='left', vertical='top', wrap_text=True)
ws1.row_dimensions[2].height = 80
r = 4
ws1.merge_cells(start_row=r, start_column=1, end_row=r, end_column=8)
ws1.cell(row=r, column=1, value='一、5月考核概况(GPS干预后)').font = SEC_FONT
r += 1
# Summary table
summary_headers = ['指标', '干预前', '干预后', '变化']
for c, h in enumerate(summary_headers, 1):
ws1.cell(row=r, column=c, value=h)
style_header(ws1, r, 4)
r += 1
old_pass_count = sum(1 for r in bak_recs if str(r.get('是否达标','')).strip() == '达标')
new_pass_count = sum(1 for r in src_recs if str(r.get('是否达标','')).strip() == '达标')
old_fail = len(bak_recs) - old_pass_count
new_fail = len(src_recs) - new_pass_count
summary_rows = [
('考核记录数(条)', len(bak_recs), len(src_recs), ''),
('达标记录数(条)', old_pass_count, new_pass_count, f'{new_pass_count - old_pass_count:+d}'),
('未达标记录数(条)', old_fail, new_fail, f'{new_fail - old_fail:+d}'),
('达标率', f'{old_pass_count/len(bak_recs)*100:.1f}%', f'{new_pass_count/len(src_recs)*100:.1f}%', ''),
('干预车辆数', len(yellow_plates), len(yellow_plates), ''),
('涉及考核记录', len(intv_records), len(intv_records), ''),
('有GPS数据', '', sum(1 for r in intv_records if r['gps'] > 0), ''),
('无GPS数据(里程=0)', '', len(no_gps), ''),
]
for row_data in summary_rows:
for c, val in enumerate(row_data, 1):
ws1.cell(row=r, column=c, value=val)
style_row(ws1, r, 4)
r += 1
r += 1
ws1.merge_cells(start_row=r, start_column=1, end_row=r, end_column=8)
ws1.cell(row=r, column=1, value='二、干预车型达标翻转统计').font = SEC_FONT
r += 1
# By target type
flip_headers = ['考核目标', '干预车辆数', '达→未达标', '未→达标', '有GPS数据', '无GPS数据']
for c, h in enumerate(flip_headers, 1):
ws1.cell(row=r, column=c, value=h)
style_header(ws1, r, 6)
r += 1
# Sort: most affected first
target_order = sorted(by_target.keys(), key=lambda t: -len(by_target[t]))
for i, tgt in enumerate(target_order):
recs = by_target[tgt]
ws1.cell(row=r, column=1, value=tgt)
ws1.cell(row=r, column=2, value=len(recs))
ws1.cell(row=r, column=3, value=sum(1 for x in recs if x['flip'] == '达→未达标'))
ws1.cell(row=r, column=4, value=sum(1 for x in recs if x['flip'] == '未→达标'))
ws1.cell(row=r, column=5, value=sum(1 for x in recs if x['gps'] > 0))
ws1.cell(row=r, column=6, value=sum(1 for x in recs if x['gps'] == 0))
style_row(ws1, r, 6, alt=(i%2==1))
r += 1
col_widths1 = [14, 15, 15, 15, 18, 18, 18, 18]
for i, w in enumerate(col_widths1, 1):
ws1.column_dimensions[get_column_letter(i)].width = w
# ── Sheet 2: 干预车辆明细 ──
ws2 = wb.create_sheet('干预车辆明细')
ws2.sheet_properties.tabColor = 'ED7D31'
ws2.merge_cells('A1:N1')
ws2.cell(row=1, column=1, value='5月干预车辆 — 原仪表盘里程 vs GPS里程 逐条对比').font = TITLE_FONT
ws2.cell(row=1, column=1).alignment = CC
ws2.row_dimensions[1].height = 28
detail_headers = ['车牌号', '部门', '销售经理', '客户名称', '考核目标',
'考核天数', '应考核里程(km)', '原仪表盘里程(km)', 'GPS行驶里程(km)',
'测试里程(km)', '原是否达标', '干预后达标', '翻转', 'GPS数据']
r2 = 3
for c, h in enumerate(detail_headers, 1):
ws2.cell(row=r2, column=c, value=h)
style_header(ws2, r2, 14)
r2 += 1
# Sort: flip-down first, then no-GPS, then others
intv_records.sort(key=lambda r: (0 if r['flip'] == '达→未达标' else 1 if r['gps'] == 0 else 2, r['plate']))
for i, rec in enumerate(intv_records):
ws2.cell(row=r2, column=1, value=rec['plate'])
ws2.cell(row=r2, column=2, value=rec['dept'])
ws2.cell(row=r2, column=3, value=rec['sales'])
ws2.cell(row=r2, column=4, value=rec['client'])
ws2.cell(row=r2, column=5, value=rec['target_name'])
ws2.cell(row=r2, column=6, value=rec.get('days', ''))
ws2.cell(row=r2, column=7, value=round(rec['target_km'], 2))
ws2.cell(row=r2, column=8, value=round(rec['tbox'], 2))
ws2.cell(row=r2, column=9, value=round(rec['gps'], 2))
ws2.cell(row=r2, column=10, value=round(rec['test'], 2))
ws2.cell(row=r2, column=11, value=rec['old_pass'])
ws2.cell(row=r2, column=12, value=rec['new_pass'])
# Flip column with color
flip_cell = ws2.cell(row=r2, column=13, value=rec['flip'])
if rec['flip'] == '达→未达标':
flip_cell.font = RED_FONT
ws2.cell(row=r2, column=14, value=rec['has_gps'])
style_row(ws2, r2, 14, alt=(i%2==1))
r2 += 1
col_widths2 = [12, 10, 10, 26, 20, 8, 14, 16, 16, 14, 10, 10, 10, 10]
for i, w in enumerate(col_widths2, 1):
ws2.column_dimensions[get_column_letter(i)].width = w
# ── Sheet 3: 业务考核视图 (current, post-intervention) ──
ws3 = wb.create_sheet('业务考核视图')
ws3.sheet_properties.tabColor = '70AD47'
src_h = list(src_recs[0].keys())
for c, h in enumerate(src_h, 1):
ws3.cell(row=1, column=c, value=h)
style_header(ws3, 1, len(src_h))
for i, rec in enumerate(src_recs):
for c, h in enumerate(src_h, 1):
val = rec.get(h)
if val is not None:
ws3.cell(row=i+2, column=c, value=val)
style_row(ws3, i+2, len(src_h), alt=(i%2==1))
for i in range(1, len(src_h)+1):
ws3.column_dimensions[get_column_letter(i)].width = 12
# ── Sheet 4: 干预车辆汇总(按部门+销售) ──
ws4 = wb.create_sheet('干预汇总(按部门销售)')
ws4.sheet_properties.tabColor = 'FFC000'
ws4.merge_cells('A1:F1')
ws4.cell(row=1, column=1, value='干预车辆 — 按部门+销售经理汇总').font = TITLE_FONT
ws4.cell(row=1, column=1).alignment = CC
# Aggregate
by_dept_sales = defaultdict(lambda: {'total': 0, 'flip_down': 0, 'flip_up': 0, 'no_gps': 0, 'has_gps': 0, 'gps_amount': 0})
for rec in intv_records:
key = (rec['dept'], rec['sales'])
by_dept_sales[key]['total'] += 1
if rec['flip'] == '达→未达标':
by_dept_sales[key]['flip_down'] += 1
elif rec['flip'] == '未→达标':
by_dept_sales[key]['flip_up'] += 1
if rec['gps'] > 0:
by_dept_sales[key]['has_gps'] += 1
by_dept_sales[key]['gps_amount'] += rec['gps']
else:
by_dept_sales[key]['no_gps'] += 1
agg_headers = ['部门', '销售经理', '干预车辆数', '达→未达标', '未→达标', '无GPS数据']
r4 = 3
for c, h in enumerate(agg_headers, 1):
ws4.cell(row=r4, column=c, value=h)
style_header(ws4, r4, 6)
r4 += 1
for (dept, sales), data in sorted(by_dept_sales.items()):
ws4.cell(row=r4, column=1, value=dept)
ws4.cell(row=r4, column=2, value=sales)
ws4.cell(row=r4, column=3, value=data['total'])
ws4.cell(row=r4, column=4, value=data['flip_down'])
ws4.cell(row=r4, column=5, value=data['flip_up'])
ws4.cell(row=r4, column=6, value=data['no_gps'])
style_row(ws4, r4, 6)
r4 += 1
col_widths4 = [14, 12, 12, 12, 12, 12]
for i, w in enumerate(col_widths4, 1):
ws4.column_dimensions[get_column_letter(i)].width = w
# ── Save ──
out_path = 'new-kpi-202605_1/租赁任务考核_2026年5月_干预情况说明.xlsx'
wb.save(out_path)
print(f'{out_path} ({len(wb.sheetnames)} sheets)')