Make station price age informational only
The relay's price_updated is the maximum price_last_updated across the grades it maps, so it cannot vouch for the selected grade: stop using it to score, gate or warn about Best Value. - FuelPriceFreshness is now station-level and display-only: one age per station, fresh/outdated/unknown/invalid, "May be outdated" past 30 days, with future-tolerance and non-finite guards. - Best Value restores the closest-station-selling-the-selected-fuel baseline and its savings ranking; eligibility gating, eligible-first sorting and the unavailable banner are gone. - Price age appears in the More info sheet only - no listing-row warnings, no extra banners. - Copy reverted from "closest eligible station" to "closest station"; stale price-confidence strings removed, "May be outdated" added. - Dormant per-grade timestamp plumbing removed, and regression tests added for every fuel type, missing/invalid/future stamps and the bundled offline dump.
This commit is contained in:
+4
-23
@@ -273,20 +273,13 @@ struct FuelStation: Identifiable, Codable, Equatable {
|
||||
let lng: Double
|
||||
/// Price per fuel type in pence per litre. Missing type = station doesn't sell it.
|
||||
let prices: [FuelType: Double]
|
||||
/// Updated timestamp (seconds since 1970) — nil for sample data.
|
||||
/// Station-level price timestamp (seconds since 1970): the relay's
|
||||
/// `price_updated`, i.e. the newest price submission across the grades it
|
||||
/// maps — never a per-grade time. nil when the source omits it (sample data).
|
||||
let priceUpdated: TimeInterval?
|
||||
/// Per-grade timestamps when the source provides them. A station-wide
|
||||
/// `price_updated` must not be copied into this map: it does not prove that
|
||||
/// every grade changed at that time.
|
||||
let priceUpdatedByFuel: [FuelType: TimeInterval]?
|
||||
/// Original per-grade timestamp text, retained so malformed source values
|
||||
/// cannot silently fall back to a station-wide timestamp.
|
||||
let priceUpdatedByFuelRaw: [FuelType: String]?
|
||||
|
||||
init(id: String, name: String, brand: String, address: String, postcode: String,
|
||||
lat: Double, lng: Double, prices: [FuelType: Double],
|
||||
priceUpdated: TimeInterval?, priceUpdatedByFuel: [FuelType: TimeInterval]? = nil,
|
||||
priceUpdatedByFuelRaw: [FuelType: String]? = nil) {
|
||||
priceUpdated: TimeInterval?) {
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.brand = brand
|
||||
@@ -296,18 +289,12 @@ struct FuelStation: Identifiable, Codable, Equatable {
|
||||
self.lng = lng
|
||||
self.prices = prices
|
||||
self.priceUpdated = priceUpdated
|
||||
self.priceUpdatedByFuel = priceUpdatedByFuel
|
||||
self.priceUpdatedByFuelRaw = priceUpdatedByFuelRaw
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case id, name, brand, address, postcode, lat, lng, prices
|
||||
case priceUpdated = "priceUpdated"
|
||||
case priceUpdatedSnake = "price_updated"
|
||||
case priceUpdatedByFuel = "priceUpdatedByFuel"
|
||||
case priceUpdatedByFuelSnake = "price_updated_by_fuel"
|
||||
case priceUpdatedByFuelRaw = "priceUpdatedByFuelRaw"
|
||||
case priceUpdatedByFuelRawSnake = "price_updated_by_fuel_raw"
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
@@ -322,10 +309,6 @@ struct FuelStation: Identifiable, Codable, Equatable {
|
||||
prices = try values.decode([FuelType: Double].self, forKey: .prices)
|
||||
priceUpdated = try values.decodeIfPresent(TimeInterval.self, forKey: .priceUpdated)
|
||||
?? values.decodeIfPresent(TimeInterval.self, forKey: .priceUpdatedSnake)
|
||||
priceUpdatedByFuel = try values.decodeIfPresent([FuelType: TimeInterval].self, forKey: .priceUpdatedByFuel)
|
||||
?? values.decodeIfPresent([FuelType: TimeInterval].self, forKey: .priceUpdatedByFuelSnake)
|
||||
priceUpdatedByFuelRaw = try values.decodeIfPresent([FuelType: String].self, forKey: .priceUpdatedByFuelRaw)
|
||||
?? values.decodeIfPresent([FuelType: String].self, forKey: .priceUpdatedByFuelRawSnake)
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
@@ -339,8 +322,6 @@ struct FuelStation: Identifiable, Codable, Equatable {
|
||||
try values.encode(lng, forKey: .lng)
|
||||
try values.encode(prices, forKey: .prices)
|
||||
try values.encodeIfPresent(priceUpdated, forKey: .priceUpdated)
|
||||
try values.encodeIfPresent(priceUpdatedByFuel, forKey: .priceUpdatedByFuel)
|
||||
try values.encodeIfPresent(priceUpdatedByFuelRaw, forKey: .priceUpdatedByFuelRaw)
|
||||
}
|
||||
|
||||
/// Cheap haversine distance to a location, in km.
|
||||
|
||||
Reference in New Issue
Block a user