← back to Govarbitrage
src/importers/csv.ts
72 lines
import { parse } from "csv-parse/sync";
import type { RawListing } from "./ingest";
// Column aliases → RawListing fields, so operators can paste varied CSVs.
const FIELD_ALIASES: Record<string, keyof RawListing> = {
source: "source",
auction: "sourceAuctionId",
"auction #": "sourceAuctionId",
auction_id: "sourceAuctionId",
sourceauctionid: "sourceAuctionId",
url: "sourceUrl",
link: "sourceUrl",
title: "title",
name: "title",
description: "description",
desc: "description",
category: "category",
manufacturer: "manufacturer",
mfr: "manufacturer",
brand: "manufacturer",
model: "model",
condition: "condition",
quantity: "quantity",
qty: "quantity",
weight: "weightLbs",
weightlbs: "weightLbs",
dimensions: "dimensions",
city: "locationCity",
state: "locationState",
zip: "locationZip",
bid: "currentBid",
currentbid: "currentBid",
"current bid": "currentBid",
bidcount: "bidCount",
bids: "bidCount",
closing: "closingAt",
closingat: "closingAt",
"closing date": "closingAt",
images: "imageUrls",
imageurls: "imageUrls",
terms: "auctionTerms",
};
const NUMERIC: (keyof RawListing)[] = ["quantity", "weightLbs", "currentBid", "bidCount"];
/** Parse a CSV string into normalized RawListing rows (header-driven, alias-aware). */
export function parseListingsCsv(text: string): RawListing[] {
const records = parse(text, {
columns: (header: string[]) => header.map((h) => h.trim().toLowerCase()),
skip_empty_lines: true,
trim: true,
relax_column_count: true,
}) as Record<string, string>[];
return records.map((rec) => {
const raw: Record<string, unknown> = {};
for (const [key, value] of Object.entries(rec)) {
const field = FIELD_ALIASES[key];
if (!field || value === "" || value == null) continue;
if (NUMERIC.includes(field)) {
const num = Number(String(value).replace(/[$,]/g, ""));
if (!Number.isNaN(num)) raw[field] = num;
} else if (field === "imageUrls") {
raw[field] = String(value).split(/[|;]/).map((s) => s.trim()).filter(Boolean);
} else {
raw[field] = value;
}
}
return raw as unknown as RawListing;
});
}