33 lines
2.9 KiB
Python
33 lines
2.9 KiB
Python
import sys,pathlib,json,decimal,datetime
|
|
sys.path.insert(0,'/tmp/codex-mileage-diag-pymysql.zip');sys.path.insert(0,'/tmp')
|
|
from db_access import connect
|
|
root=pathlib.Path('/opt/lingniu-go-native/backups/mileage-all-baseline-20260918');root.mkdir(parents=True,exist_ok=True)
|
|
c=connect();D=decimal.Decimal;changes=[]
|
|
with c.cursor() as q:
|
|
q.execute('START TRANSACTION WITH CONSISTENT SNAPSHOT, READ ONLY')
|
|
q.execute("""SELECT * FROM vehicle_daily_mileage_source WHERE stat_date BETWEEN '2026-09-15' AND '2026-09-18'
|
|
AND quality_status='OK' AND latest_total_mileage_km>0 AND first_event_time IS NOT NULL
|
|
AND source_ip NOT IN ('legacy-mysql.lingniu-prod','manual-lingniu-prod-day-mileage')
|
|
AND COALESCE(quality_reason,'')<>'gps_coordinate_accumulation'
|
|
AND latest_event_time>=TIMESTAMP(stat_date) AND latest_event_time<TIMESTAMP(stat_date)+INTERVAL 1 DAY
|
|
ORDER BY stat_date,vin,protocol,source_key""")
|
|
rows=q.fetchall()
|
|
for r in rows:
|
|
selector='source_key=%s';order='stat_date DESC,latest_event_time DESC';extra=''
|
|
if r['protocol']=='GB32960':
|
|
selector='(source_key=%s OR is_selected=1)';order='stat_date DESC,is_selected DESC,latest_event_time DESC,source_key ASC'
|
|
extra=" AND quality_status='OK' AND COALESCE(quality_reason,'')<>'gps_coordinate_accumulation' AND source_ip NOT IN ('legacy-mysql.lingniu-prod','manual-lingniu-prod-day-mileage')"
|
|
q.execute("SELECT latest_total_mileage_km,latest_event_time,source_key FROM vehicle_daily_mileage_source WHERE vin=%s AND stat_date<%s AND protocol=%s AND "+selector+" AND latest_total_mileage_km>0 AND latest_event_time>=TIMESTAMP(stat_date) AND latest_event_time<TIMESTAMP(stat_date)+INTERVAL 1 DAY "+extra+" ORDER BY "+order+" LIMIT 1",(r['vin'],r['stat_date'],r['protocol'],r['source_key']))
|
|
p=q.fetchone()
|
|
if not p:continue
|
|
if p['latest_event_time']<=r['first_event_time'] and r['first_event_time'].date()<r['stat_date']:continue
|
|
delta=r['latest_total_mileage_km']-p['latest_total_mileage_km'];status='OK';reason='historical_source_baseline'
|
|
if D(-1)<=delta<0:delta=D(0);reason='negative_jitter_clamped'
|
|
elif delta<0:status='INVALID_DELTA';reason='TOTAL_MILEAGE_ROLLBACK'
|
|
elif delta>D(2500*max(1,(r['latest_event_time'].date()-p['latest_event_time'].date()).days)):status='INVALID_DELTA';reason='outside_daily_range'
|
|
changes.append(dict(before=r,baseline=dict(total=p['latest_total_mileage_km'],time=p['latest_event_time']),daily=delta,status=status,reason=reason))
|
|
c.rollback();c.close()
|
|
(root/'plan.json').write_text(json.dumps(changes,default=str))
|
|
from collections import Counter
|
|
print(json.dumps({'scanned':len(rows),'changes':len(changes),'byProtocol':dict(Counter(x['before']['protocol'] for x in changes)),'invalid':sum(x['status']!='OK' for x in changes),'examples':[dict(vin=x['before']['vin'],date=x['before']['stat_date'],protocol=x['before']['protocol'],old=x['before']['daily_mileage_km'],new=x['daily']) for x in changes if x['before']['vin']=='LMRKH9AC8R1004109']},default=str))
|