diff --git a/TODO.md b/TODO.md index 942f657..2e6a77a 100644 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,9 @@ # TODO -get mcrates.au up +push offers +redux that bitch + + +seller + eg min 6 items + shipping is 101aud diff --git a/api/index.ts b/api/index.ts index c87793e..b4d6d96 100644 --- a/api/index.ts +++ b/api/index.ts @@ -28,7 +28,8 @@ async function start() { events, } = createServices(log); - events.emit(Events.StartupSync); + // events.emit(Events.StartupSync); + events.emit(Events.ListingsUpdated); await api.listen(); } diff --git a/api/prisma/schema.prisma b/api/prisma/schema.prisma index f3d5bad..36d6411 100644 --- a/api/prisma/schema.prisma +++ b/api/prisma/schema.prisma @@ -81,6 +81,7 @@ model Offer { want Want @relation(fields: [wantId], references: [id], onDelete: Cascade, onUpdate: Cascade) // only one offer per want for a user - @@unique([userId, wantId]) + // @@unique([userId, wantId]) + @@unique([listingId]) @@map("offers") } diff --git a/api/src/api.ts b/api/src/api.ts index 4f0a679..f8f650c 100644 --- a/api/src/api.ts +++ b/api/src/api.ts @@ -37,46 +37,6 @@ export default function createApi(log: Log, pg: Pg, discogs, offers, events) { const router = koaRouter(); - interface ListingData extends Listing { - data: any, - want: { user: User }, - }; - - async function getBundles() { - const bundles = await pg.bundleCandidates(); - - return bundles.map((b: ListingData[]) => { - const total = b.reduce((acc, l) => acc + l.data.price.value, 0); - const items = b.map(l => ({ - id: l.data.id, - description: l.data.release.description, - originalPrice: l.data.original_price.value, - originalCurrency: l.data.original_price.curr_abbr, - price: l.data.price.value, - currency: l.data.price.currency, - condition: l.data.condition, - uri: l.data.uri, - user: l.want.user, - })); - - const shipping = b[0]?.data.seller.shipping; - - const bundle = { - username: b[0]?.data.seller.username, - seller: b[0]?.data.seller.id, - shipsFrom: b[0]?.data.ships_from, - total, - currency: b[0]?.data.price.currency, - shipping: '...', - items, - mentionsAustralia: shipping.toLowerCase().includes('australia'), - mentionsWorldwide: shipping.toLowerCase().includes('world'), - } - - return bundle; - }); - } - function listen() { return new Promise((resolve, reject) => { app.on('error', err => { @@ -105,7 +65,7 @@ export default function createApi(log: Log, pg: Pg, discogs, offers, events) { }); router.get('/acp/bundles', async function bundlesGet(ctx, next) { - const bundles = await getBundles(); + const bundles = await offers.bundles(); ctx.response.body = bundles; }) diff --git a/api/src/events.ts b/api/src/events.ts index 160c634..0568550 100644 --- a/api/src/events.ts +++ b/api/src/events.ts @@ -11,9 +11,10 @@ import routerAuth from './routers/router.auth'; export enum Events { WantsAdded = 'WantsAdded', StartupSync = 'StartupSync', + ListingsUpdated = 'ListingsUpdated', } -export default function createEvents(log: Log, pg: Pg, discogs) { +export default function createEvents(log: Log, pg: Pg, discogs, offers) { const events = new EventEmitter({ captureRejections: true }); events.on('error', function eventError(e) { @@ -50,5 +51,31 @@ export default function createEvents(log: Log, pg: Pg, discogs) { }); + events.on(Events.ListingsUpdated, async function listingsUpdated() { + const bundles = await offers.bundles(); + log.info({ event: Events.ListingsUpdated, bundles: bundles.length }); + + // const bundle = { + // username: b[0]?.data.seller.username, + // seller: b[0]?.data.seller.id, + // shipsFrom: b[0]?.data.ships_from, + // total, + // currency: b[0]?.data.price.currency, + // shipping: '...', + // items, + // mentionsAustralia: shipping.toLowerCase().includes('australia'), + // mentionsWorldwide: shipping.toLowerCase().includes('world'), + // } + + for (const bundle of bundles) { + log.info({ seller: bundle.seller, items: bundle.items.length }, 'generating offers...'); + const listingIds = bundle.items.map(i => i.id); + const updatedOffers = await offers.generate(listingIds, 100, 30) + log.info({ offers: updatedOffers.length }, 'offers set'); + } + + log.info('offers up to date'); + }); + return events; } \ No newline at end of file diff --git a/api/src/offers.ts b/api/src/offers.ts index d4a01e0..a27fcac 100644 --- a/api/src/offers.ts +++ b/api/src/offers.ts @@ -19,9 +19,11 @@ export default function createOffers(log: Log, pg: Pg) { log.info('generateOffers', { listingIds, bulkShipping, baseShipping }); const listings = await pg.listingByIds(listingIds); - log.info({ listings }, 'creating offers for listings') + // log.info({ listings }, 'creating offers for listings') - const shipping = bulkShipping / listings.length; + const shipping = 10 + Math.random(); // yolt + + // const shipping = bulkShipping / listings.length; // how to deal with users who don't want all items in a bundle... const offers = listings.map(l => ({ @@ -60,8 +62,50 @@ export default function createOffers(log: Log, pg: Pg) { return pg.offerSetStatus(offer.id, Status.Accepted); } + async function bundles() { + const bundles = await pg.bundleCandidates(); + + interface ListingData extends Listing { + data: any, + want: { user: User }, + }; + + return bundles.map((b: ListingData[]) => { + const total = b.reduce((acc, l) => acc + l.data.price.value, 0); + const items = b.map(l => ({ + id: l.data.id, + description: l.data.release.description, + originalPrice: l.data.original_price.value, + originalCurrency: l.data.original_price.curr_abbr, + price: l.data.price.value, + currency: l.data.price.currency, + condition: l.data.condition, + uri: l.data.uri, + user: l.want.user, + })); + + const shipping = b[0]?.data.seller.shipping; + + const bundle = { + username: b[0]?.data.seller.username, + seller: b[0]?.data.seller.id, + shipsFrom: b[0]?.data.ships_from, + total, + currency: b[0]?.data.price.currency, + shipping: '...', + items, + mentionsAustralia: shipping.toLowerCase().includes('australia'), + mentionsWorldwide: shipping.toLowerCase().includes('world'), + } + + return bundle; + }); + } + + return { generate, accept, + bundles, }; } diff --git a/api/src/pg.ts b/api/src/pg.ts index 7c80358..c1faa27 100644 --- a/api/src/pg.ts +++ b/api/src/pg.ts @@ -2,6 +2,7 @@ import bcrypt from 'bcrypt'; import type Log from 'bunyan'; import type { PrismaClient, Want, User, Listing, Offer, Prisma } from '@prisma/client'; import * as Bluebird from 'bluebird'; +import omit from 'lodash/omit'; import { Status } from './offers'; @@ -54,7 +55,7 @@ export default function createPg(log: Log, prisma: PrismaClient): Pg { want: true, }, orderBy: { - status: 'desc', + total: 'asc', }, }); } @@ -184,6 +185,8 @@ export default function createPg(log: Log, prisma: PrismaClient): Pg { seller, count(distinct("wantId")), array_agg(id) as listings from listings + where + seller not in (3709203) group by seller order by @@ -208,13 +211,10 @@ export default function createPg(log: Log, prisma: PrismaClient): Pg { return await prisma.$transaction( offers.map(offer => prisma.offer.upsert({ where: { - userId_wantId: { - userId: offer.userId, - wantId: offer.wantId, - }, + listingId: offer.listingId, }, create: offer, - update: offer + update: omit(offer, ['status']), })) ); } diff --git a/api/src/services.ts b/api/src/services.ts index 899459c..0012178 100644 --- a/api/src/services.ts +++ b/api/src/services.ts @@ -11,8 +11,8 @@ export default function creates(log) { const prisma = new PrismaClient(); const pg = createPg(log, prisma); const discogs = createDiscogs(log, pg); - const events = createEvents(log, pg, discogs); const offers = createOffers(log, pg); + const events = createEvents(log, pg, discogs, offers); const api = createApi(log, pg, discogs, offers, events); return { diff --git a/client/src/components/offer.tsx b/client/src/components/offer.tsx index d44d39d..c1fc736 100644 --- a/client/src/components/offer.tsx +++ b/client/src/components/offer.tsx @@ -26,7 +26,8 @@ export default function Offer(offer) {