From 4c6922f76baf7db04286b51ca1a9030d06f79165 Mon Sep 17 00:00:00 2001 From: Thad Miller Date: Sat, 21 Mar 2026 16:40:04 -0400 Subject: [PATCH] [feat] testing tcgcollector upload --- src/pages/api/upload.ts | 95 ++++++++++++++++++++++++++++++++++++++++ src/pages/myprices.astro | 26 +++++++++++ 2 files changed, 121 insertions(+) create mode 100644 src/pages/api/upload.ts create mode 100644 src/pages/myprices.astro diff --git a/src/pages/api/upload.ts b/src/pages/api/upload.ts new file mode 100644 index 0000000..9e284d9 --- /dev/null +++ b/src/pages/api/upload.ts @@ -0,0 +1,95 @@ +// src/pages/api/upload.ts +import type { APIRoute } from 'astro'; +import { parse, stringify, transform } from 'csv'; +import { Readable } from 'stream'; +import { client } from '../../db/typesense'; +import chalk from 'chalk'; +import { db, ClosePool } from '../../db/index'; + +// Define the transformation logic +const transformer = transform({ parallel: 1 }, async function(this: any, row: any, callback: any) { +try { + // Specific query bsaed on tcgcollector CSV + const query = String(Object.values(row)[1]); + const setname = String(Object.values(row)[4]).replace(/Wizards of the coast promos/ig,'WoTC Promo'); + const cardNumber = String(Object.values(row)[7]); + console.log(`${query} ${cardNumber} : ${setname}`); + + // Use Typesense to find the card because we can easily use the combined fields + let cards = await client.collections('cards').documents().search({ q: query, query_by: 'productName', filter_by: `setName:\`${setname}\` && number:${cardNumber}` }); + if (cards.hits?.length === 0) { + // Try without card number + cards = await client.collections('cards').documents().search({ q: query, query_by: 'productName', filter_by: `setName:\`${setname}\`` }); + } + if (cards.hits?.length === 0) { + // Try without set name + cards = await client.collections('cards').documents().search({ q: query, query_by: 'productName', filter_by: `number:${cardNumber}` }); + } + if (cards.hits?.length === 0) { + // I give up, just output the values from the csv + console.log(chalk.red(' - not found')); + const newRow = { ...row }; + newRow.Variant = ''; + newRow.marketPrice = ''; + this.push(newRow); + } + else { + for (const card of cards.hits?.map((hit: any) => hit.document) ?? []) { + console.log(chalk.blue(` - ${card.cardId} : ${card.productName} : ${card.number}`), chalk.yellow(`${card.setName}`), chalk.green(`${card.variant}`)); + const variant = await db.query.cards.findFirst({ + with: { prices: true, tcgdata: true }, + where: { cardId: card.cardId } + }); + const newRow = { ...row }; + newRow.Variant = variant?.variant; + newRow.marketPrice = variant?.prices.find(p => p.condition === 'Near Mint')?.marketPrice; + this.push(newRow); + } + } + + callback(); +} catch (error) { + callback(error); +} +}); + + +export const POST: APIRoute = async ({ request }) => { + try { + const formData = await request.formData(); + const file = formData.get('file') as File; + const inputStream = Readable.from(file.stream()); + + if (!file) { + return new Response('No file uploaded', { status: 400 }); + } + + // Pipe the streams: Read -> Parse -> Transform -> Stringify -> Write + const outputStream = inputStream + .on('error', (error) => console.error('Input stream error:', error)) + .pipe(parse({ columns: true, trim: true })) + .on('error', (error) => console.error('Parse error:', error)) + .pipe(transformer) + .on('error', (error) => console.error('Transform error:', error)) + .pipe(stringify({ header: true })) + .on('error', (error) => console.error('Stringify error:', error)); + + outputStream.on('finish', () => { + ClosePool(); + }).on('error', (error) => { + ClosePool(); + }); + + + return new Response(outputStream as any, { + headers: { + 'Content-Type': 'text/csv', + 'Content-Disposition': 'attachment; filename=transformed.csv', + }, + }); + + } catch (error) { + console.error('Error processing CSV stream:', error); + return new Response('Internal Server Error', { status: 500 }); + } +}; diff --git a/src/pages/myprices.astro b/src/pages/myprices.astro new file mode 100644 index 0000000..cf80082 --- /dev/null +++ b/src/pages/myprices.astro @@ -0,0 +1,26 @@ +--- +import Layout from '../layouts/Main.astro'; +import NavItems from '../components/NavItems.astro'; +import NavBar from '../components/NavBar.astro'; +import Footer from '../components/Footer.astro'; + +--- + + + + +
+
+

Rigid's App Thing

+

(working title)

+
+
+ +
+ + +
+
+
+