Files
mileage-bonus/gps_replace_local.py

265 lines
9.2 KiB
Python
Raw Permalink 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
"""
从本地GPS数据(G7)整合6月GPS里程,替换测试车辆实际行驶里程
测试里程 = TBOX总里程 - GPS总里程(负数为0)
重新核算6月里程考核,输出到 ./20260713_1500/
"""
import openpyxl, os, shutil
from datetime import datetime, date
from openpyxl.styles import PatternFill, Font, Alignment, Border, Side
from openpyxl.utils import get_column_letter
os.chdir('/Users/kkfluous/Downloads')
SRC = '租赁任务考核_2026年6月 (1).xlsx'
DST = '租赁任务考核_2026年6月.xlsx'
GPS_DIR = 'gps里程汇总2026年1-6月 3'
# 36辆测试车辆
TEST_PLATES = {
'粤AGE5480','粤AGF4535','粤AGG3490','粤AGG4135','粤AGG4191',
'粤AGP2033','粤AGP3080','粤AGP3663','粤AGP3676','粤AGP5139',
'粤AGP5156','粤AGP5163','粤AGP5168','粤AGP5636','粤AGP5767',
'粤AGP5768','粤AGP5796','粤AGP9733','粤AGP9786','粤AGR0298',
'粤AGR1586','粤AGR3288','粤AGR5056','粤AGR5068','粤AGR5088',
'粤AGR6869','粤AGR6877','粤AGR6879','粤AGR6881','粤AGR8506',
'粤AGR8551','粤AGR8558','粤AGR8799','粤AGR9866','粤AGR9893',
'粤AGR9899',
}
# ========== Step 1: 加载G7 GPS日里程数据 ==========
print("=" * 60)
print("Step 1: 加载G7 GPS日里程数据")
print("=" * 60)
g7_wb = openpyxl.load_workbook(f'{GPS_DIR}/G7/202606_G7.xlsx')
g7_ws = g7_wb.active
# G7格式: 车牌号(0), 机构(1), 行驶里程(2), 06月01日(3)...06月30日(32), 运行时长(33), 数据完整度说明(34)
gps_daily = {} # plate -> [day1_km, day2_km, ..., day30_km]
gps_monthly = {} # plate -> monthly_total_km
no_data_plates = set()
for row in g7_ws.iter_rows(min_row=2, values_only=True):
plate = str(row[0]).strip() if row[0] else ''
if not plate:
continue
monthly_km = float(row[2]) if row[2] else 0
daily = []
for c in range(3, 33): # 06月01日 to 06月30日
daily.append(float(row[c]) if row[c] else 0)
gps_daily[plate] = daily
gps_monthly[plate] = monthly_km
g7_wb.close()
print(f"G7加载完成: {len(gps_daily)}辆车")
# ========== Step 2: 读取源文件,执行GPS替换 ==========
print("\n" + "=" * 60)
print("Step 2: GPS里程替换")
print("=" * 60)
# 备份现有文件
BAK = '租赁任务考核_2026年6月_备份_0715.xlsx'
if os.path.exists(DST):
shutil.copy2(DST, BAK)
print(f'已备份: {BAK}')
# 从原始新文件开始
shutil.copy2(SRC, DST)
print(f'源文件: {SRC}{DST}')
wb = openpyxl.load_workbook(DST)
ws = wb['业务考核视图']
h = [c.value for c in next(ws.iter_rows(min_row=1, max_row=1))]
print(f'列数: {len(h)}')
# 读取所有记录
recs = []
for row in ws.iter_rows(min_row=2, values_only=True):
r = dict(zip(h, row))
r['row_num'] = len(recs) + 2
recs.append(r)
# 找测试车辆记录
test_recs = [r for r in recs if r['车牌号'] in TEST_PLATES]
print(f'测试车辆记录: {len(test_recs)}条 ({len(set(r["车牌号"] for r in test_recs))}辆)')
# 添加 原TBOX里程(km) 和 测试里程(km) 列
if '原TBOX里程(km)' not in h:
tbox_new_col = ws.max_column + 1
test_new_col = ws.max_column + 2
ws.cell(row=1, column=tbox_new_col, value='原TBOX里程(km)')
ws.cell(row=1, column=test_new_col, value='测试里程(km)')
h_new = [c.value for c in next(ws.iter_rows(min_row=1, max_row=1))]
else:
h_new = h
actual_idx = h_new.index('实际行驶里程(km)') + 1
tbox_idx = h_new.index('原TBOX里程(km)') + 1
test_idx = h_new.index('测试里程(km)') + 1
rate_idx = h_new.index('完成率(%)') + 1
qualified_idx = h_new.index('是否达标') + 1
yellow_fill = PatternFill(start_color='FFFF00', end_color='FFFF00', fill_type='solid')
DAYSM = {1: 31, 2: 28, 3: 31, 4: 30, 5: 31, 6: 30}
replaced = 0
no_gps = 0
no_gps_plates = set()
for r in test_recs:
plate = r['车牌号']
rn = r['row_num']
# 获取考核起止日期
start_date = r.get('考核开始日期')
end_date = r.get('考核结束日期')
if plate not in gps_daily:
no_gps += 1
no_gps_plates.add(plate)
continue
daily = gps_daily[plate]
# 计算考核区间内的GPS总里程
if start_date and end_date:
try:
sd = start_date.date() if hasattr(start_date, 'date') else datetime.strptime(str(start_date)[:10], '%Y-%m-%d').date()
ed = end_date.date() if hasattr(end_date, 'date') else datetime.strptime(str(end_date)[:10], '%Y-%m-%d').date()
except:
sd = date(2026, 6, 1)
ed = date(2026, 6, 30)
else:
sd = date(2026, 6, 1)
ed = date(2026, 6, 30)
# 汇总考核区间内的GPS日里程(G7日数据索引: 0=6/1, 29=6/30
gps_total = 0
for day_offset in range(30):
day = date(2026, 6, 1) + date.resolution * day_offset # timedelta
from datetime import timedelta
day = date(2026, 6, 1) + timedelta(days=day_offset)
if sd <= day <= ed:
gps_total += daily[day_offset]
gps_total = round(gps_total, 1)
# 原TBOX里程 = 当前实际行驶里程
tbox_val = float(ws.cell(row=rn, column=actual_idx).value or 0)
# 测试里程 = TBOX - GPS(负数为0
test_km = max(0, round(tbox_val - gps_total, 1))
# 写入
ws.cell(row=rn, column=tbox_idx, value=tbox_val)
ws.cell(row=rn, column=test_idx, value=test_km)
ws.cell(row=rn, column=actual_idx, value=gps_total)
# 标记黄色
for c in range(1, ws.max_column + 1):
ws.cell(row=rn, column=c).fill = yellow_fill
# 重算完成率
target = float(r.get('应考核里程(km)') or 0)
pct = round(gps_total / target * 100, 2) if target > 0 else 0
qualified = '达标' if gps_total >= target and target > 0 else '未达标'
ws.cell(row=rn, column=rate_idx, value=pct)
ws.cell(row=rn, column=qualified_idx, value=qualified)
replaced += 1
print(f" {plate}: TBOX={tbox_val}, GPS={gps_total}, 测试={test_km}, 达标={qualified}")
print(f'\nGPS替换: 成功{replaced}条, 无GPS数据{no_gps}条')
if no_gps_plates:
print(f'无GPS数据车牌: {sorted(no_gps_plates)}')
# ========== Step 3: TBOX回填(非测试车辆) ==========
print("\n" + "=" * 60)
print("Step 3: TBOX回填(非测试车辆)")
print("=" * 60)
filled = 0
for row_idx in range(2, ws.max_row + 1):
tbox_val = ws.cell(row=row_idx, column=tbox_idx).value
actual_val = ws.cell(row=row_idx, column=actual_idx).value
test_val = ws.cell(row=row_idx, column=test_idx).value
if (tbox_val is None or float(tbox_val) == 0):
if test_val is None or float(test_val) == 0:
ws.cell(row=row_idx, column=tbox_idx, value=float(actual_val) if actual_val else 0)
filled += 1
print(f'TBOX回填: {filled}条')
# ========== Step 4: 列序排版 ==========
print("\n" + "=" * 60)
print("Step 4: 列序排版")
print("=" * 60)
target_order = [
'车牌号', '考核年份', '考核月份', '部门名称', '销售经理', '客户名称',
'合同编号', '项目名称', '考核目标ID', '考核目标', '考核开始日期', '考核结束日期',
'考核天数', '应考核里程(km)', '原TBOX里程(km)', '实际行驶里程(km)', '测试里程(km)',
'完成率(%)', '是否达标', '考核状态', '交车日期', '预计还车日期', '实际还车日期'
]
# Read all data with styles
h_cur = [c.value for c in next(ws.iter_rows(min_row=1, max_row=1))]
all_rows = []
for row_idx in range(1, ws.max_row + 1):
row_data = {}
for c in range(1, ws.max_column + 1):
row_data[h_cur[c-1]] = ws.cell(row=row_idx, column=c).value
all_rows.append(row_data)
# Clear sheet
for r in range(1, ws.max_row + 1):
for c in range(1, ws.max_column + 1):
ws.cell(row=r, column=c, value=None)
# Apply styles
thin_border = Border(left=Side(style='thin'), right=Side(style='thin'),
top=Side(style='thin'), bottom=Side(style='thin'))
header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')
header_font = Font(name='宋体', size=10, bold=True, color='FFFFFF')
data_font = Font(name='宋体', size=10)
# Write headers
for i, col_name in enumerate(target_order):
c = i + 1
cell = ws.cell(row=1, column=c, value=col_name)
cell.font = header_font
cell.fill = header_fill
cell.alignment = Alignment(horizontal='center', vertical='center')
cell.border = thin_border
# Write data
for row_idx, row_data in enumerate(all_rows[1:], start=2):
for i, col_name in enumerate(target_order):
c = i + 1
val = row_data.get(col_name)
cell = ws.cell(row=row_idx, column=c, value=val)
cell.font = data_font
cell.alignment = Alignment(vertical='center')
cell.border = thin_border
if col_name in ['考核开始日期', '考核结束日期', '交车日期', '预计还车日期', '实际还车日期']:
cell.number_format = 'yyyy-mm-dd h:mm:ss'
# Restore yellow fill for test vehicles
if row_data.get('测试里程(km)') and float(row_data.get('测试里程(km)', 0) or 0) != 0:
cell.fill = yellow_fill
for i in range(len(target_order)):
ws.column_dimensions[get_column_letter(i+1)].width = 15
wb.save(DST)
wb.close()
print(f'列序排版完成 ({len(target_order)}列)')
print(f'已保存: {DST}')
print("\n" + "=" * 60)
print("GPS替换完成!接下来运行 main.py...")
print("=" * 60)