diff --git a/apps/backend/controllers/flowsheet.controller.ts b/apps/backend/controllers/flowsheet.controller.ts index c8074c5..8b18185 100644 --- a/apps/backend/controllers/flowsheet.controller.ts +++ b/apps/backend/controllers/flowsheet.controller.ts @@ -1,6 +1,6 @@ import { Request, RequestHandler } from 'express'; import { Mutex } from 'async-mutex'; -import { NewFSEntry, FSEntry, Show, ShowDJ, library } from '@wxyc/database'; +import { FSEntry, Show, ShowDJ, library } from '@wxyc/database'; import * as flowsheet_service from '../services/flowsheet.service.js'; import { fetchAndCacheMetadata } from '../services/metadata/index.js'; @@ -159,7 +159,7 @@ export const addEntry: RequestHandler = async (req: Request { } }; +/** Get the next play_order value (global max + 1, or 1 if no entries exist) */ +export const getNextPlayOrder = async (): Promise => { + const result = await db.select({ maxOrder: max(flowsheet.play_order) }).from(flowsheet); + return (result[0]?.maxOrder ?? 0) + 1; +}; + // SQL query fields (flat structure from database) const FSEntryFieldsRaw = { id: flowsheet.id, @@ -174,7 +180,7 @@ export const getEntriesByShow = async (...show_ids: number[]): Promise => { +export const addTrack = async (entry: Omit & { play_order?: number }): Promise => { /* TODO: logic for updating album playcount */ @@ -202,7 +208,15 @@ export const addTrack = async (entry: NewFSEntry): Promise => { // } // } - const response = await db.insert(flowsheet).values(entry).returning(); + // Compute play_order if not already set + if (entry.show_id != null && !entry.play_order) { + entry = { ...entry, play_order: await getNextPlayOrder() }; + } + + const response = await db + .insert(flowsheet) + .values(entry as NewFSEntry) + .returning(); updateLastModified(); return response[0]; }; @@ -291,8 +305,10 @@ export const startShow = async (dj_id: string, show_name?: string, specialty_id? }) .returning(); + const startPlayOrder = await getNextPlayOrder(); await db.insert(flowsheet).values({ show_id: new_show[0].id, + play_order: startPlayOrder, entry_type: 'show_start', message: `Start of Show: DJ ${dj_info.djName || dj_info.name} joined the set at ${new Date().toLocaleString( 'en-US', @@ -352,10 +368,12 @@ const createJoinNotification = async (id: string, show_id: number): Promise => { const dj_information = (await db.select().from(user).where(eq(user.id, primary_dj_id)).limit(1))[0]; const dj_name = dj_information?.djName || dj_information?.name || 'A DJ'; + const endPlayOrder = await getNextPlayOrder(); await db.insert(flowsheet).values({ show_id: currentShow.id, + play_order: endPlayOrder, entry_type: 'show_end', message: `End of Show: ${dj_name} left the set at ${new Date().toLocaleString('en-US', { timeZone: 'America/New_York', @@ -431,10 +451,12 @@ const createLeaveNotification = async (dj_id: string, show_id: number): Promise< const message = `${dj_name} left the set!`; + const leavePlayOrder = await getNextPlayOrder(); const notification = await db .insert(flowsheet) .values({ show_id: show_id, + play_order: leavePlayOrder, entry_type: 'dj_leave', message: message, }) diff --git a/shared/database/src/schema.ts b/shared/database/src/schema.ts index 7a9bfc5..3a11a10 100644 --- a/shared/database/src/schema.ts +++ b/shared/database/src/schema.ts @@ -304,7 +304,7 @@ export const flowsheet = wxyc_schema.table('flowsheet', { album_title: varchar('album_title', { length: 128 }), artist_name: varchar('artist_name', { length: 128 }), record_label: varchar('record_label', { length: 128 }), - play_order: serial('play_order').notNull(), + play_order: integer('play_order').notNull(), request_flag: boolean('request_flag').default(false).notNull(), message: varchar('message', { length: 250 }), add_time: timestamp('add_time').defaultNow().notNull(), diff --git a/tests/unit/database/schema.flowsheet.test.ts b/tests/unit/database/schema.flowsheet.test.ts new file mode 100644 index 0000000..caf9e5c --- /dev/null +++ b/tests/unit/database/schema.flowsheet.test.ts @@ -0,0 +1,13 @@ +import { readFileSync } from 'fs'; +import { resolve } from 'path'; + +describe('flowsheet schema', () => { + const schemaSource = readFileSync(resolve(__dirname, '../../../shared/database/src/schema.ts'), 'utf-8'); + + it('play_order should not use serial (it is manually managed)', () => { + const playOrderLine = schemaSource.split('\n').find((line) => line.includes('play_order')); + + expect(playOrderLine).toBeDefined(); + expect(playOrderLine).not.toMatch(/serial\s*\(\s*['"]play_order['"]\s*\)/); + }); +});