Files
mileage-bonus/gps_replace_full.py

259 lines
9.4 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日里程 + 东方北斗月里程)替换125辆测试车辆的实际行驶里程。
测试里程 = TBOX - GPS(负数为0)。
"""
import openpyxl, os, shutil, xlrd
from datetime import datetime, date, timedelta
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'
# ========== 读取125辆测试车辆清单 ==========
TEST_FILE = '/Users/kkfluous/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/wxid_1704407055112_3af8/temp/RWTemp/2026-07/65b41c10aa83cabb7a365b2b8437c1c7/6月测试车辆.xlsx'
wb_test = openpyxl.load_workbook(TEST_FILE, data_only=True)
ws_test = wb_test.active
TEST_PLATES = set()
for row in ws_test.iter_rows(min_row=2, values_only=True):
for ci in range(len(row)):
p = str(row[ci]).strip() if row[ci] else ''
if p and p != 'None' and len(p) >= 7:
TEST_PLATES.add(p)
wb_test.close()
print(f"测试车辆清单: {len(TEST_PLATES)}辆")
# ========== Step 1: 加载G7 GPS日里程 ==========
print("\n加载G7 GPS日里程...")
g7_wb = openpyxl.load_workbook(f'{GPS_DIR}/G7/202606_G7.xlsx')
g7_ws = g7_wb.active
gps_daily = {} # plate -> [day1, day2, ..., day30]
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 or plate not in TEST_PLATES:
continue
daily = [float(row[c]) if row[c] else 0 for c in range(3, 33)]
gps_daily[plate] = daily
g7_wb.close()
print(f"G7覆盖: {len(gps_daily)}辆测试车")
# ========== Step 2: 加载东方北斗GPS月里程 ==========
print("加载东方北斗GPS月里程...")
df_wb = openpyxl.load_workbook(f'{GPS_DIR}/东方北斗/202606_东方北斗.xlsx')
df_ws = df_wb.active
gps_monthly = {} # plate -> monthly_total_km
for row in df_ws.iter_rows(min_row=4, values_only=True):
plate = str(row[5]).strip() if row[5] else ''
if not plate or plate not in TEST_PLATES:
continue
monthly = float(row[10]) if row[10] else 0
gps_monthly[plate] = monthly
df_wb.close()
print(f"东方北斗覆盖: {len(gps_monthly)}辆测试车")
# ========== Step 3: 读取源文件,执行GPS替换 ==========
print("\n执行GPS替换...")
# 备份
BAK = '租赁任务考核_2026年6月_备份_0715.xlsx'
if os.path.exists(DST):
shutil.copy2(DST, BAK)
shutil.copy2(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"源文件: {ws.max_row}行, {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))}辆)")
# 添加列
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')
replaced_g7 = 0
replaced_df = 0
no_gps = 0
total_test_km = 0
total_gps_km = 0
total_tbox_km = 0
for r in test_recs:
plate = r['车牌号']
rn = r['row_num']
# 获取考核区间
start_date = r.get('考核开始日期')
end_date = r.get('考核结束日期')
gps_total = None
source = None
# 优先用G7日数据
if plate in gps_daily:
daily = gps_daily[plate]
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)
gps_total = 0
for day_offset in range(30):
day = date(2026, 6, 1) + timedelta(days=day_offset)
if sd <= day <= ed:
gps_total += daily[day_offset]
gps_total = round(gps_total, 1)
source = 'G7'
replaced_g7 += 1
# 其次用东方北斗月里程
elif plate in gps_monthly:
# 东方北斗只有月度合计,考核区间一般都是整月
gps_total = gps_monthly[plate]
source = '东方北斗'
replaced_df += 1
if gps_total is None:
no_gps += 1
continue
# 原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)
total_test_km += test_km
total_gps_km += gps_total
total_tbox_km += tbox_val
print(f"\n替换统计:")
print(f" G7日数据: {replaced_g7}条")
print(f" 东方北斗月数据: {replaced_df}条")
print(f" 无GPS数据: {no_gps}条")
print(f" TBOX总里程: {total_tbox_km:.1f}")
print(f" GPS总里程: {total_gps_km:.1f}")
print(f" 测试总里程: {total_test_km:.1f}")
# ========== Step 4: TBOX回填(非测试车辆) ==========
print("\nTBOX回填(非测试车辆)...")
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 5: 列序排版 ==========
print("\n列序排版...")
target_order = [
'车牌号', '考核年份', '考核月份', '部门名称', '销售经理', '客户名称',
'合同编号', '项目名称', '考核目标ID', '考核目标', '考核开始日期', '考核结束日期',
'考核天数', '应考核里程(km)', '原TBOX里程(km)', '实际行驶里程(km)', '测试里程(km)',
'完成率(%)', '是否达标', '考核状态', '交车日期', '预计还车日期', '实际还车日期'
]
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
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)
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)
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
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'
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"\n{'='*60}")
print(f"GPS替换完成!")
print(f"测试车辆: {len(TEST_PLATES)}辆 -> 源数据{len(test_recs)}条记录")
print(f"GPS来源: G7({replaced_g7}条) + 东方北斗({replaced_df}条)")
print(f"测试总里程: {total_test_km:.1f} km")
print(f"已保存: {DST}")
print(f"{'='*60}")