From a43d4f32e484cd8dd98b432caf73527261486633 Mon Sep 17 00:00:00 2001 From: ntr Date: Fri, 2 Sep 2022 00:06:51 +1000 Subject: [PATCH] simplify and automate offer creation --- TODO.md | 8 +++++- api/index.ts | 3 ++- api/prisma/schema.prisma | 3 ++- api/src/api.ts | 42 +---------------------------- api/src/events.ts | 29 +++++++++++++++++++- api/src/offers.ts | 48 +++++++++++++++++++++++++++++++-- api/src/pg.ts | 12 ++++----- api/src/services.ts | 2 +- client/src/components/offer.tsx | 3 ++- docker-compose.yaml | 14 ++-------- 10 files changed, 97 insertions(+), 67 deletions(-) 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) {

{details.artists_sort} - {details.title}

{details.year}
{details.formats[0].descriptions.join(' ')}
-
${offer.total}
+
mcrates price: ${offer.total}
+
shipped by itself: ${offer.comparison}
diff --git a/docker-compose.yaml b/docker-compose.yaml index f9f07d8..4387a6b 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -12,14 +12,6 @@ services: volumes: - "./db:/var/lib/postgresql/data" - # pg-test: - # image: postgres:latest - # environment: - # POSTGRES_USER: 'mcrates' - # POSTGRES_DB: 'test'# POSTGRES_PASSWORD: 'digdeep' - # ports: - # - '55432:5432' - api: image: node:16 working_dir: /opt/mcrates/api @@ -28,8 +20,6 @@ services: entrypoint: npm start environment: DATABASE_URL: 'postgresql://mcrates:digdeep@pg:5432/mcrates?schema=public' - ports: - - '41337:41337' volumes: - "./api:/opt/mcrates/api" @@ -37,13 +27,13 @@ services: image: node:16 working_dir: /opt/mcrates/client entrypoint: npm start - ports: - - '8080:8080' volumes: - "./client:/opt/mcrates/client" nginx: image: nginx:latest + depends_on: + - 'api' volumes: - './nginx/localhost.nginx.conf:/etc/nginx/conf.d/default.conf' ports: