cars: default to most-common-in-NL order; dex numbers oldest first with unknown years last (--renumber)
All checks were successful
Deploy production / deploy (push) Successful in 2m41s

This commit is contained in:
2026-10-24 15:47:44 +00:00
parent 9e30e4246f
commit 45c124a365
4 changed files with 9 additions and 6 deletions

View File

@@ -4,6 +4,7 @@
// bun scripts/cars-import.ts --dry-run fetch + build only, print a summary // bun scripts/cars-import.ts --dry-run fetch + build only, print a summary
// bun scripts/cars-import.ts --cache /tmp/cars reuse raw downloads from that folder // bun scripts/cars-import.ts --cache /tmp/cars reuse raw downloads from that folder
// bun scripts/cars-import.ts --dry-run --out x.json also dump the built catalogue // bun scripts/cars-import.ts --dry-run --out x.json also dump the built catalogue
// bun scripts/cars-import.ts --renumber reassign Dex numbers from #1 (oldest first)
// //
// Sources (no API keys): // Sources (no API keys):
// - fueleconomy.gov vehicles.csv: every US EPA-tested car since 1984 (make, base model, // - fueleconomy.gov vehicles.csv: every US EPA-tested car since 1984 (make, base model,
@@ -26,6 +27,8 @@ import { join } from 'node:path';
const args = process.argv.slice(2); const args = process.argv.slice(2);
const dryRun = args.includes('--dry-run'); const dryRun = args.includes('--dry-run');
// --renumber: hand out Dex numbers from #1 again instead of keeping existing ones.
const renumber = args.includes('--renumber');
const cacheDir = args.includes('--cache') ? args[args.indexOf('--cache') + 1] : null; const cacheDir = args.includes('--cache') ? args[args.indexOf('--cache') + 1] : null;
const outFile = args.includes('--out') ? args[args.indexOf('--out') + 1] : null; const outFile = args.includes('--out') ? args[args.indexOf('--out') + 1] : null;
@@ -927,12 +930,12 @@ async function write(brands: ReturnType<typeof build>) {
const existing = await tx<{ id: number; dex: number; brand_id: number; slug: string }[]>` const existing = await tx<{ id: number; dex: number; brand_id: number; slug: string }[]>`
select id, dex, brand_id, slug from cars.models`; select id, dex, brand_id, slug from cars.models`;
const byKey = new Map(existing.map((e) => [`${e.brand_id}|${e.slug}`, e])); const byKey = new Map(existing.map((e) => [`${e.brand_id}|${e.slug}`, e]));
let nextDex = Math.max(0, ...existing.map((e) => e.dex)) + 1; let nextDex = renumber ? 1 : Math.max(0, ...existing.map((e) => e.dex)) + 1;
const all = brands const all = brands
.flatMap((b) => b.models.map((m) => ({ b, m, brandId: brandIds.get(b.slug)! }))) .flatMap((b) => b.models.map((m) => ({ b, m, brandId: brandIds.get(b.slug)! })))
.sort( .sort(
(x, y) => (x, y) =>
x.m.firstYear - y.m.firstYear || (x.m.firstYear ?? 9999) - (y.m.firstYear ?? 9999) ||
x.b.name.localeCompare(y.b.name) || x.b.name.localeCompare(y.b.name) ||
x.m.name.localeCompare(y.m.name) x.m.name.localeCompare(y.m.name)
); );
@@ -942,7 +945,7 @@ async function write(brands: ReturnType<typeof build>) {
await tx`delete from cars.variants`; await tx`delete from cars.variants`;
let variantCount = 0; let variantCount = 0;
for (const { m, brandId } of all) { for (const { m, brandId } of all) {
const prev = byKey.get(`${brandId}|${m.slug}`); const prev = renumber ? undefined : byKey.get(`${brandId}|${m.slug}`);
const dex = prev?.dex ?? nextDex++; const dex = prev?.dex ?? nextDex++;
const { variants, ...cols } = m; const { variants, ...cols } = m;
const row = { const row = {

View File

@@ -24,8 +24,8 @@ export const BODIES = [
] as const; ] as const;
export const DRIVES = ['fwd', 'rwd', 'awd', '4wd'] as const; export const DRIVES = ['fwd', 'rwd', 'awd', '4wd'] as const;
export const SORTS = { export const SORTS = {
dex: 'dex #',
popular: 'most common in NL', popular: 'most common in NL',
dex: 'dex #',
name: 'name', name: 'name',
newest: 'newest', newest: 'newest',
oldest: 'oldest', oldest: 'oldest',

View File

@@ -38,7 +38,7 @@ export function parseFilters(params: URLSearchParams): CarFilters {
market: pick(params.get('market'), ['nl', 'us'] as const), market: pick(params.get('market'), ['nl', 'us'] as const),
from: year(params.get('from')), from: year(params.get('from')),
to: year(params.get('to')), to: year(params.get('to')),
sort: sort && sort in SORTS ? (sort as CarSort) : 'dex', sort: sort && sort in SORTS ? (sort as CarSort) : 'popular',
page: Math.min(Math.max(1, Math.floor(Number(params.get('page')) || 1)), 1000) page: Math.min(Math.max(1, Math.floor(Number(params.get('page')) || 1)), 1000)
}; };
} }

View File

@@ -50,7 +50,7 @@
} }
const quick = [ const quick = [
{ label: '🔥 most common in NL', params: { sort: 'popular', market: 'nl' } }, { label: '📖 dex order', params: { sort: 'dex' } },
{ label: '⚡ electric', params: { fuel: 'electric', sort: 'popular' } }, { label: '⚡ electric', params: { fuel: 'electric', sort: 'popular' } },
{ label: '🍃 hybrids', params: { fuel: 'hybrid', sort: 'popular' } }, { label: '🍃 hybrids', params: { fuel: 'hybrid', sort: 'popular' } },
{ label: '🏎️ coupés', params: { body: 'coupe', sort: 'popular' } }, { label: '🏎️ coupés', params: { body: 'coupe', sort: 'popular' } },