chore: curate monthly assessment evidence and scripts
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python3
|
||||
"""按三份GPS日里程文件处理2026年7月测试车辆考核源表。"""
|
||||
from copy import copy
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
import re
|
||||
import shutil
|
||||
import openpyxl
|
||||
from openpyxl.styles import PatternFill
|
||||
|
||||
BASE = Path('/Users/kkfluous/Downloads')
|
||||
OUT = BASE / '7月核算结果'
|
||||
SRC = BASE / '租赁任务考核_2026年7月 (1).xlsx'
|
||||
TEST = Path('/Users/kkfluous/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/wxid_1704407055112_3af8/temp/RWTemp/2026-08/4340099467fbf08b4c6583c49adba4ee/7月测试车辆明细.xlsx')
|
||||
GPS_DAILY = BASE / '智能管车_车辆里程日报-140106360.xlsx'
|
||||
GPS_DETAIL = [
|
||||
BASE / '里程统计[天][2026-07-01至2026-07-31].xlsx',
|
||||
BASE / '里程统计[天][2026-07-01至2026-07-31] (1).xlsx',
|
||||
]
|
||||
EXCLUDED_TEST_PLATES = {'粤AG37316'}
|
||||
ZERO_OVERRIDE = set()
|
||||
DEPARTMENT_OVERRIDE = {'粤AGP6675': '业务六部', '粤AGP9757': '业务六部'}
|
||||
|
||||
def norm(v):
|
||||
return re.sub(r'\s+', '', str(v or '')).upper()
|
||||
|
||||
def number(v):
|
||||
try:
|
||||
return float(v or 0)
|
||||
except (TypeError, ValueError):
|
||||
return 0.0
|
||||
|
||||
def read_test_plates():
|
||||
wb = openpyxl.load_workbook(TEST, data_only=True)
|
||||
ws = wb.active
|
||||
plates = {norm(r[0]) for r in ws.iter_rows(min_row=2, values_only=True) if norm(r[0])}
|
||||
wb.close()
|
||||
return plates - EXCLUDED_TEST_PLATES
|
||||
|
||||
def read_daily_matrix():
|
||||
wb = openpyxl.load_workbook(GPS_DAILY, data_only=True)
|
||||
ws = wb.active
|
||||
headers = [c.value for c in ws[1]]
|
||||
day_cols = {}
|
||||
for i, v in enumerate(headers):
|
||||
m = re.fullmatch(r'07月(\d{2})日', str(v or ''))
|
||||
if m:
|
||||
day_cols[int(m.group(1))] = i
|
||||
data = {}
|
||||
for row in ws.iter_rows(min_row=2, values_only=True):
|
||||
plate = norm(row[0])
|
||||
if plate:
|
||||
data[plate] = {d: number(row[i]) for d, i in day_cols.items()}
|
||||
wb.close()
|
||||
return data
|
||||
|
||||
def read_detail_files():
|
||||
data = defaultdict(lambda: defaultdict(float))
|
||||
for path in GPS_DETAIL:
|
||||
wb = openpyxl.load_workbook(path, data_only=True)
|
||||
ws = wb.active
|
||||
headers = [c.value for c in ws[3]]
|
||||
pcol = next(i for i, v in enumerate(headers) if v and '车牌' in str(v))
|
||||
dcol = next(i for i, v in enumerate(headers) if str(v or '').strip() == '日期')
|
||||
mcol = next(i for i, v in enumerate(headers) if v and '里程' in str(v) and 'km' in str(v).lower())
|
||||
for row in ws.iter_rows(min_row=4, values_only=True):
|
||||
plate = norm(row[pcol])
|
||||
if not plate:
|
||||
continue
|
||||
try:
|
||||
dt = row[dcol] if isinstance(row[dcol], datetime) else datetime.strptime(str(row[dcol])[:10], '%Y-%m-%d')
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
# 同一车辆同一天在两份明细中如重复,取较大值,避免重复累计。
|
||||
data[plate][dt.day] = max(data[plate][dt.day], number(row[mcol]))
|
||||
wb.close()
|
||||
return {p: dict(days) for p, days in data.items()}
|
||||
|
||||
def copy_cell_style(src, dst):
|
||||
dst._style = copy(src._style)
|
||||
if src.has_style:
|
||||
dst.number_format = src.number_format
|
||||
dst.protection = copy(src.protection)
|
||||
dst.alignment = copy(src.alignment)
|
||||
|
||||
def main():
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(SRC, OUT / '租赁任务考核_2026年7月_原版备份.xlsx')
|
||||
tests = read_test_plates()
|
||||
matrix = read_daily_matrix()
|
||||
details = read_detail_files()
|
||||
|
||||
wb = openpyxl.load_workbook(SRC)
|
||||
ws = wb['业务考核视图']
|
||||
# 原表O列是实际里程。插入后统一为 O原TBOX、P实际、Q测试、R完成率、S是否达标。
|
||||
ws.insert_cols(15, 1)
|
||||
ws.insert_cols(17, 1)
|
||||
ws['O1'] = '原TBOX里程(km)'
|
||||
ws['Q1'] = '测试里程(km)'
|
||||
copy_cell_style(ws['P1'], ws['O1'])
|
||||
copy_cell_style(ws['P1'], ws['Q1'])
|
||||
yellow = PatternFill(fill_type='solid', fgColor='FFFF00')
|
||||
records = []
|
||||
missing = []
|
||||
for ri in range(2, ws.max_row + 1):
|
||||
plate = norm(ws.cell(ri, 1).value)
|
||||
if plate in DEPARTMENT_OVERRIDE:
|
||||
ws.cell(ri, 4).value = DEPARTMENT_OVERRIDE[plate]
|
||||
original = number(ws.cell(ri, 16).value)
|
||||
ws.cell(ri, 15).value = round(original, 2)
|
||||
copy_cell_style(ws.cell(ri, 16), ws.cell(ri, 15))
|
||||
copy_cell_style(ws.cell(ri, 16), ws.cell(ri, 17))
|
||||
if plate not in tests:
|
||||
ws.cell(ri, 17).value = 0
|
||||
continue
|
||||
start = ws.cell(ri, 11).value
|
||||
end = ws.cell(ri, 12).value
|
||||
start_day = start.day if isinstance(start, datetime) else 1
|
||||
end_day = end.day if isinstance(end, datetime) else 31
|
||||
if plate in ZERO_OVERRIDE:
|
||||
actual = 0.0
|
||||
source = 'GPS缺失,业务确认暂按0'
|
||||
elif plate in matrix:
|
||||
actual = sum(matrix[plate].get(d, 0) for d in range(start_day, end_day + 1))
|
||||
source = GPS_DAILY.name
|
||||
elif plate in details:
|
||||
actual = sum(details[plate].get(d, 0) for d in range(start_day, end_day + 1))
|
||||
source = '里程统计日明细'
|
||||
else:
|
||||
missing.append(plate)
|
||||
continue
|
||||
target = number(ws.cell(ri, 14).value)
|
||||
test_mileage = max(0.0, original - actual)
|
||||
rate = actual / target * 100 if target > 0 else 0
|
||||
ws.cell(ri, 16).value = round(actual, 2)
|
||||
ws.cell(ri, 17).value = round(test_mileage, 2)
|
||||
ws.cell(ri, 18).value = round(rate, 2)
|
||||
ws.cell(ri, 19).value = '达标' if target > 0 and actual >= target else '未达标'
|
||||
for cell in ws[ri]:
|
||||
cell.fill = copy(yellow)
|
||||
records.append((plate, ri, original, actual, test_mileage, source))
|
||||
if missing:
|
||||
raise RuntimeError('仍缺GPS车牌: ' + ','.join(sorted(set(missing))))
|
||||
|
||||
# 对齐5/6月的宽度、筛选和冻结窗格;保留原表其余样式。
|
||||
ref = openpyxl.load_workbook(BASE / '租赁任务考核_2026年6月.xlsx', read_only=False)
|
||||
rws = ref['业务考核视图']
|
||||
for i in range(1, 24):
|
||||
letter = openpyxl.utils.get_column_letter(i)
|
||||
if rws.column_dimensions[letter].width:
|
||||
ws.column_dimensions[letter].width = rws.column_dimensions[letter].width
|
||||
ws.freeze_panes = rws.freeze_panes
|
||||
ws.auto_filter.ref = f'A1:W{ws.max_row}'
|
||||
ref.close()
|
||||
|
||||
out = OUT / '租赁任务考核_2026年7月.xlsx'
|
||||
wb.save(out)
|
||||
wb.close()
|
||||
print(f'output={out}')
|
||||
print(f'test_list={len(tests)} intervention_records={len(records)} intervention_plates={len(set(r[0] for r in records))}')
|
||||
print(f'zero_override={sorted(ZERO_OVERRIDE)}')
|
||||
print(f'department_override={DEPARTMENT_OVERRIDE}')
|
||||
print(f'total_original={sum(r[2] for r in records):.2f} total_gps={sum(r[3] for r in records):.2f} total_test={sum(r[4] for r in records):.2f}')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user