From 11c2c7a343306ed2d552404dc164b4fa12a7847c Mon Sep 17 00:00:00 2001 From: Aromia Date: Sat, 31 Jan 2026 17:32:04 -0500 Subject: [PATCH 1/4] feat: add ratingsAvg field for endpoint locations --- README.md | 10 +++++++++- src/db/dbQueryUtils.ts | 22 ++++++++++++++++++---- src/db/getLocations.ts | 2 ++ src/endpoints/schemas.ts | 1 + 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2b40bbf..561b693 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,15 @@ Now install the API's dependencies by 'cd'-ing into the root of the repository a pnpm install ``` -Start your local database with `pnpm db:start`, `pnpm db:push` (if this is your first time) and then start the server with `pnpm dev` and it should work, assuming you have the correct env variables. (To see the contents of the database, I recommend using DBeaver. You can also run `pnpm db:studio` to start up drizzle studio) +Then start your local database with (assuming you have the correct env variables) + +```bash +pnpm db:start +pnpm db:push # if this is your first time running the db +pnpm dev +``` + +To see the contents of the database, I recommend using DBeaver. You can also run `pnpm db:studio` to start up drizzle studio ## Database schema changes (important!) diff --git a/src/db/dbQueryUtils.ts b/src/db/dbQueryUtils.ts index 0b62c2e..e476e53 100644 --- a/src/db/dbQueryUtils.ts +++ b/src/db/dbQueryUtils.ts @@ -1,9 +1,11 @@ +import { avg, sql } from "drizzle-orm"; import { externalIdToInternalIdTable, emailTable, locationDataTable, overwritesTable, specialsTable, + starReviewTable, timeOverwritesTable, timesTable, } from "./schema"; @@ -83,10 +85,10 @@ export class QueryUtils { return locationData.reduce< Record< string, - | typeof locationDataTable.$inferSelect & { - times: IDateTimeRange[]; - conceptId: string | null; - } + typeof locationDataTable.$inferSelect & { + times: IDateTimeRange[]; + conceptId: string | null; + } > >((acc, { location_data, location_times, external_id_to_internal_id }) => { if (!acc[location_data.id]) { @@ -162,6 +164,18 @@ export class QueryUtils { return idToTimeOverrides; } + async getRatingsAvgs() { + const ratingsAvgs = await this.db + .select({ + starRating: sql`cast(${avg(starReviewTable.starRating)} as decimal(2,1))`, + locationId: starReviewTable.locationId, + }) + .from(starReviewTable) + .groupBy(starReviewTable.locationId); + + return Object.fromEntries(ratingsAvgs.map((e) => [e.locationId, e.starRating])); + } + async getEmails(): Promise<{ name: string; email: string }[]> { const result = await this.db .select({ diff --git a/src/db/getLocations.ts b/src/db/getLocations.ts index e8d8865..7702d6b 100644 --- a/src/db/getLocations.ts +++ b/src/db/getLocations.ts @@ -20,6 +20,7 @@ export async function getAllLocationsFromDB(db: DBType, today: DateTime) { const specials = await DB.getSpecials(today.toSQLDate()); const generalOverrides = await DB.getGeneralOverrides(); const timeOverrides = await DB.getTimeOverrides(timeSearchCutoff.toSQLDate()); + const ratingsAvgs = await DB.getRatingsAvgs(); // apply overrides, merge all time intervals, and add specials const finalLocationData = Object.entries(locationIdToData).map( @@ -36,6 +37,7 @@ export async function getAllLocationsFromDB(db: DBType, today: DateTime) { // time.end // ).toLocaleString()}` // ), + ratingsAvg: ratingsAvgs[id] ?? null, todaysSoups: specials[id]?.soups ?? [], todaysSpecials: specials[id]?.specials ?? [], }; diff --git a/src/endpoints/schemas.ts b/src/endpoints/schemas.ts index 6ccf14b..e053e4e 100644 --- a/src/endpoints/schemas.ts +++ b/src/endpoints/schemas.ts @@ -24,6 +24,7 @@ export const LocationSchema = t.Object({ name: t.Nullable(t.String({ examples: ["Schatz"] })), location: t.String(), + ratingsAvg: t.Nullable(t.Number()), shortDescription: t.Nullable(t.String()), description: t.String(), url: t.String(), From 879287e6a72bb1c6e6462b1db7c54ecb6fc9ac4d Mon Sep 17 00:00:00 2001 From: Aromia Date: Sat, 31 Jan 2026 17:50:22 -0500 Subject: [PATCH 2/4] chore: fix typo --- src/endpoints/reviews.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/endpoints/reviews.ts b/src/endpoints/reviews.ts index 7198609..c5b1120 100644 --- a/src/endpoints/reviews.ts +++ b/src/endpoints/reviews.ts @@ -40,7 +40,7 @@ reviewEndpoints buckets: t.Array(t.Number(), { example: [0, 1, 0, 4, 12, 4], description: - "Count of ratings of star rating [{.5},{1,1.5},{2,2.5},{3,3.5},{4,4.5},{5}", + "Count of ratings of star rating [{.5},{1,1.5},{2,2.5},{3,3.5},{4,4.5},{5}]", }), }), tagData: t.Array( From d68120cd93296ec43609f26e8a26b2472e28c6ea Mon Sep 17 00:00:00 2001 From: Aromia Date: Sat, 31 Jan 2026 20:53:50 -0500 Subject: [PATCH 3/4] chore: fix test --- tests/database.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/database.test.ts b/tests/database.test.ts index f66d00c..8763ef8 100644 --- a/tests/database.test.ts +++ b/tests/database.test.ts @@ -30,6 +30,7 @@ const locationIn: ILocation = { const locationOut = { id: "DYNAMICALLY GENERATED, replace with real id", name: "dbTest", + ratingsAvg: null, shortDescription: "hi", description: "description", url: "https://hi.com", From 6a38c2c48d92b259e857213e53cec3442810f00d Mon Sep 17 00:00:00 2001 From: Aromia Date: Sat, 31 Jan 2026 21:25:39 -0500 Subject: [PATCH 4/4] chore: explicite type cast --- src/db/dbQueryUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/db/dbQueryUtils.ts b/src/db/dbQueryUtils.ts index e476e53..40e6995 100644 --- a/src/db/dbQueryUtils.ts +++ b/src/db/dbQueryUtils.ts @@ -167,7 +167,8 @@ export class QueryUtils { async getRatingsAvgs() { const ratingsAvgs = await this.db .select({ - starRating: sql`cast(${avg(starReviewTable.starRating)} as decimal(2,1))`, + // only declares it to be a number, we explicitly cast it to a num + starRating: sql`cast(${avg(starReviewTable.starRating)} as decimal(2,1))`.mapWith(Number), locationId: starReviewTable.locationId, }) .from(starReviewTable)