Merge pull request 'fix(pcat): robust year extraction across key spelling + date format' (#138) from dev into main

Reviewed-on: #138
This commit was merged in pull request #138.
This commit is contained in:
2026-06-12 13:11:22 +00:00
2 changed files with 39 additions and 19 deletions

View File

@@ -654,19 +654,33 @@ describe("VehiclesService", () => {
expect(year).toBe(2021);
});
it("falls back to production_date when no numeric Year param exists", () => {
const extractYear = (parameters: unknown[]) => {
const { service } = createService();
const year = (
return (
service as unknown as {
extractYearFromPcatCar: (c: { parameters: unknown[] }) => number | null;
}
).extractYearFromPcatCar({
parameters: [
).extractYearFromPcatCar({ parameters });
};
it("falls back to year-first production date (space-keyed)", () => {
expect(
extractYear([
{ key: "year", name: "Year", value: "All" },
{ key: "production_date", name: "Production date", value: "2020/10/05" },
],
});
expect(year).toBe(2020);
{ key: "production date", name: "Production date", value: "2020/10/05" },
]),
).toBe(2020);
});
it("reads the year from a DAY-first production date (DD/MM/YYYY)", () => {
// parseInt("08/03/2016") would have returned 8 (the day) — regex saves us.
expect(
extractYear([{ key: "production date", name: "Production date", value: "08/03/2016" }]),
).toBe(2016);
});
it("ignores the year=All axis with no concrete year/date", () => {
expect(extractYear([{ key: "year", name: "Year", value: "All" }])).toBeNull();
});
});

View File

@@ -1186,25 +1186,31 @@ export class VehiclesService {
private extractYearFromPcatCar(car: PcatCar): number | null {
if (!car.parameters) return null;
// pcat returns a faceting AXIS param `year` (often value "All") BEFORE the
// concrete per-car `Year` param, so a naive find(includes("year")) grabs the
// axis → parseInt("All") = NaN → null. Scan every year-ish param and take the
// first that parses to a plausible year; only then fall back to a
// production_date like "2020/10/05".
const plausible = (raw: string | undefined): number | null => {
const num = Number.parseInt(raw ?? "", 10);
return num > 1900 && num < 2100 ? num : null;
// pcat is messy about year. Three traps:
// 1. A faceting AXIS param key="year" value="All" precedes the concrete
// per-car key="Year" value="2021" — a naive find(includes("year")) grabs
// the axis and parseInt("All")=NaN.
// 2. The build date lives under key "production date" (space) on most cars,
// "production_date" (underscore) on others.
// 3. Those dates come in BOTH "2006/02/27" (year-first) and "08/03/2016"
// (day-first), so parseInt would read the day. Pull a 4-digit 19xx/20xx
// token by regex instead — robust to either ordering.
const yearIn = (raw: string | undefined): number | null => {
const m = (raw ?? "").match(/\b(19|20)\d{2}\b/);
return m ? Number.parseInt(m[0], 10) : null;
};
// Prefer an explicit year param (skips the "All" axis, which has no digits).
for (const p of car.parameters) {
const key = p.key.toLowerCase();
if (key.includes("year") || key.includes("model_year")) {
const year = plausible(p.value);
const year = yearIn(p.value);
if (year) return year;
}
}
// Fall back to the specific car's build date (either key spelling / format).
for (const p of car.parameters) {
if (p.key.toLowerCase().includes("production_date")) {
const year = plausible(p.value);
if (p.key.toLowerCase().includes("production") && p.key.toLowerCase().includes("date")) {
const year = yearIn(p.value);
if (year) return year;
}
}