fix(stats): elect trusted mileage source
This commit is contained in:
@@ -29,6 +29,9 @@ type MetricSample struct {
|
||||
Protocol envelope.Protocol
|
||||
StatDate string
|
||||
TotalMileageKM float64
|
||||
SourceKey string
|
||||
Phone string
|
||||
SourceEndpoint string
|
||||
}
|
||||
|
||||
func NewWriter(exec Execer, loc *time.Location) *Writer {
|
||||
@@ -45,6 +48,11 @@ func (w *Writer) EnsureSchema(ctx context.Context) error {
|
||||
if _, err := w.exec.ExecContext(ctx, DailyMileageTableSQL); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, statement := range DailyMileageAlterSQL {
|
||||
if _, err := w.exec.ExecContext(ctx, statement); err != nil && !isDuplicateColumnError(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -62,7 +70,10 @@ func (w *Writer) Append(ctx context.Context, env envelope.FrameEnvelope) error {
|
||||
sample.StatDate,
|
||||
string(sample.Protocol),
|
||||
sample.TotalMileageKM,
|
||||
sample.TotalMileageKM); err != nil {
|
||||
sample.TotalMileageKM,
|
||||
sample.SourceKey,
|
||||
sample.Phone,
|
||||
sample.SourceEndpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
w.markMileageWritten(sample)
|
||||
@@ -122,9 +133,35 @@ func SamplesFromEnvelope(env envelope.FrameEnvelope, loc *time.Location) ([]Metr
|
||||
Protocol: env.Protocol,
|
||||
StatDate: statDate,
|
||||
TotalMileageKM: totalMileage,
|
||||
SourceKey: sourceKey(env),
|
||||
Phone: strings.TrimSpace(env.Phone),
|
||||
SourceEndpoint: strings.TrimSpace(env.SourceEndpoint),
|
||||
}}, nil
|
||||
}
|
||||
|
||||
func sourceKey(env envelope.FrameEnvelope) string {
|
||||
parts := []string{
|
||||
strings.TrimSpace(env.Phone),
|
||||
strings.TrimSpace(env.DeviceID),
|
||||
strings.TrimSpace(env.SourceEndpoint),
|
||||
}
|
||||
var kept []string
|
||||
for _, part := range parts {
|
||||
if part != "" {
|
||||
kept = append(kept, part)
|
||||
}
|
||||
}
|
||||
return strings.Join(kept, "@")
|
||||
}
|
||||
|
||||
func isDuplicateColumnError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
text := strings.ToLower(err.Error())
|
||||
return strings.Contains(text, "duplicate column") || strings.Contains(text, "1060")
|
||||
}
|
||||
|
||||
type mileageFieldMapping struct {
|
||||
key string
|
||||
scale float64
|
||||
@@ -163,32 +200,86 @@ func mileageMappingsByProtocol(protocol envelope.Protocol) []mileageFieldMapping
|
||||
const upsertDailyMileageSQL = `
|
||||
INSERT INTO vehicle_daily_mileage
|
||||
(vin, stat_date, protocol, daily_mileage_km,
|
||||
first_total_mileage_km, latest_total_mileage_km, sample_count)
|
||||
VALUES (?, ?, ?, 0, ?, ?, 1)
|
||||
first_total_mileage_km, latest_total_mileage_km,
|
||||
trusted_source_key, trusted_phone, trusted_source_endpoint, sample_count)
|
||||
VALUES (?, ?, ?, 0, ?, ?, ?, ?, ?, 1)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
first_total_mileage_km = CASE
|
||||
WHEN trusted_source_key IS NOT NULL
|
||||
AND trusted_source_key <> ''
|
||||
AND trusted_source_key <> VALUES(trusted_source_key)
|
||||
AND ABS(COALESCE(latest_total_mileage_km, VALUES(latest_total_mileage_km)) - VALUES(latest_total_mileage_km)) > 50
|
||||
THEN first_total_mileage_km
|
||||
WHEN first_total_mileage_km IS NULL OR first_total_mileage_km <= 0
|
||||
THEN VALUES(first_total_mileage_km)
|
||||
ELSE LEAST(first_total_mileage_km, VALUES(first_total_mileage_km))
|
||||
END,
|
||||
latest_total_mileage_km = CASE
|
||||
WHEN trusted_source_key IS NOT NULL
|
||||
AND trusted_source_key <> ''
|
||||
AND trusted_source_key <> VALUES(trusted_source_key)
|
||||
AND ABS(COALESCE(latest_total_mileage_km, VALUES(latest_total_mileage_km)) - VALUES(latest_total_mileage_km)) > 50
|
||||
THEN latest_total_mileage_km
|
||||
WHEN latest_total_mileage_km IS NULL OR latest_total_mileage_km <= 0
|
||||
THEN VALUES(latest_total_mileage_km)
|
||||
ELSE GREATEST(latest_total_mileage_km, VALUES(latest_total_mileage_km))
|
||||
END,
|
||||
daily_mileage_km = GREATEST(
|
||||
CASE
|
||||
WHEN trusted_source_key IS NOT NULL
|
||||
AND trusted_source_key <> ''
|
||||
AND trusted_source_key <> VALUES(trusted_source_key)
|
||||
AND ABS(COALESCE(latest_total_mileage_km, VALUES(latest_total_mileage_km)) - VALUES(latest_total_mileage_km)) > 50
|
||||
THEN latest_total_mileage_km
|
||||
WHEN latest_total_mileage_km IS NULL OR latest_total_mileage_km <= 0
|
||||
THEN VALUES(latest_total_mileage_km)
|
||||
ELSE latest_total_mileage_km
|
||||
END,
|
||||
VALUES(latest_total_mileage_km)
|
||||
CASE
|
||||
WHEN trusted_source_key IS NOT NULL
|
||||
AND trusted_source_key <> ''
|
||||
AND trusted_source_key <> VALUES(trusted_source_key)
|
||||
AND ABS(COALESCE(latest_total_mileage_km, VALUES(latest_total_mileage_km)) - VALUES(latest_total_mileage_km)) > 50
|
||||
THEN latest_total_mileage_km
|
||||
ELSE VALUES(latest_total_mileage_km)
|
||||
END
|
||||
) - CASE
|
||||
WHEN trusted_source_key IS NOT NULL
|
||||
AND trusted_source_key <> ''
|
||||
AND trusted_source_key <> VALUES(trusted_source_key)
|
||||
AND ABS(COALESCE(latest_total_mileage_km, VALUES(latest_total_mileage_km)) - VALUES(latest_total_mileage_km)) > 50
|
||||
THEN first_total_mileage_km
|
||||
WHEN first_total_mileage_km IS NULL OR first_total_mileage_km <= 0
|
||||
THEN VALUES(first_total_mileage_km)
|
||||
ELSE LEAST(first_total_mileage_km, VALUES(first_total_mileage_km))
|
||||
END,
|
||||
sample_count = sample_count + 1,
|
||||
trusted_source_key = CASE
|
||||
WHEN trusted_source_key IS NULL OR trusted_source_key = ''
|
||||
THEN VALUES(trusted_source_key)
|
||||
WHEN trusted_source_key = VALUES(trusted_source_key)
|
||||
THEN trusted_source_key
|
||||
WHEN ABS(COALESCE(latest_total_mileage_km, VALUES(latest_total_mileage_km)) - VALUES(latest_total_mileage_km)) <= 50
|
||||
THEN VALUES(trusted_source_key)
|
||||
ELSE trusted_source_key
|
||||
END,
|
||||
trusted_phone = CASE
|
||||
WHEN trusted_source_key IS NULL OR trusted_source_key = '' OR trusted_source_key = VALUES(trusted_source_key)
|
||||
THEN VALUES(trusted_phone)
|
||||
ELSE trusted_phone
|
||||
END,
|
||||
trusted_source_endpoint = CASE
|
||||
WHEN trusted_source_key IS NULL OR trusted_source_key = '' OR trusted_source_key = VALUES(trusted_source_key)
|
||||
THEN VALUES(trusted_source_endpoint)
|
||||
ELSE trusted_source_endpoint
|
||||
END,
|
||||
sample_count = CASE
|
||||
WHEN trusted_source_key IS NOT NULL
|
||||
AND trusted_source_key <> ''
|
||||
AND trusted_source_key <> VALUES(trusted_source_key)
|
||||
AND ABS(COALESCE(latest_total_mileage_km, VALUES(latest_total_mileage_km)) - VALUES(latest_total_mileage_km)) > 50
|
||||
THEN sample_count
|
||||
ELSE sample_count + 1
|
||||
END,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user