refactor: gather group metadata

This commit is contained in:
Carlos Valente
2025-03-28 19:16:38 +01:00
committed by Carlos Valente
parent 730cb95c04
commit 876d111c61
48 changed files with 1044 additions and 751 deletions
+11 -11
View File
@@ -31,9 +31,9 @@ describe('test parseDatabaseModel() with demo project (valid)', () => {
const filteredDemoProject = structuredClone(demoDb);
const { data } = parseDatabaseModel(filteredDemoProject);
it('has 16 events', () => {
expect(data.rundowns.demo.order.length).toBe(16);
expect(Object.keys(data.rundowns.demo.entries).length).toBe(16);
it('has 17 events with 12 top level events', () => {
expect(data.rundowns.default.order.length).toBe(12);
expect(Object.keys(data.rundowns.default.entries).length).toBe(17);
});
it('is the same as the demo project since all data is valid', () => {
@@ -663,7 +663,7 @@ describe('parseExcel()', () => {
});
});
it('parses link start and checks that is applicable', () => {
it('parses link start', () => {
const testData = [
['Time Start', 'Time End', 'ID', 'Link Start', 'Timer type'],
['4:30:00', '9:45:00', 'A', '', 'count-down'],
@@ -688,22 +688,22 @@ describe('parseExcel()', () => {
expect(result.rundown.entries).toMatchObject({
A: {
linkStart: null,
linkStart: false,
},
B: {
linkStart: 'true', // <--- this will be populated by the cache generation
linkStart: true,
},
C: {
linkStart: 'true', // <--- this will be populated by the cache generation
linkStart: true,
},
D: {
linkStart: null,
linkStart: false,
},
BLOCK: {
type: SupportedEvent.Block,
},
E: {
linkStart: 'true', // <--- this will be populated by the cache generation
linkStart: true,
},
});
});
@@ -775,7 +775,7 @@ describe('parseExcel()', () => {
expect(parsedData.rundown.entries['MEET3']).toMatchObject({
duration: 90 * MILLIS_PER_MINUTE,
linkStart: null,
linkStart: false,
timeWarning: 11 * MILLIS_PER_MINUTE,
timeDanger: 5 * MILLIS_PER_MINUTE,
});
@@ -783,7 +783,7 @@ describe('parseExcel()', () => {
expect(parsedData.rundown.entries['MEET4']).toMatchObject({
duration: 30 * MILLIS_PER_MINUTE,
timeWarning: 11 * MILLIS_PER_MINUTE,
linkStart: 'true', // if we get a boolean, we should just use that
linkStart: true,
});
});
});
@@ -12,6 +12,7 @@ import {
parseViewSettings,
sanitiseCustomFields,
} from '../parserFunctions.js';
import { makeOntimeBlock, makeOntimeEvent } from '../../services/rundown-service/__mocks__/rundown.mocks.js';
describe('parseRundowns()', () => {
it('returns a default project rundown if nothing is given', () => {
@@ -166,6 +167,43 @@ describe('parseRundown()', () => {
expect(parsedRundown.order.length).toEqual(2);
expect(Object.keys(parsedRundown.entries).length).toEqual(2);
});
it('handles empty events', () => {
const rundown = {
id: 'test',
title: '',
order: ['1', '2', '3', '4'],
entries: {
'1': { id: '1', type: SupportedEvent.Event } as OntimeEvent,
'2': { id: '2', type: SupportedEvent.Event } as OntimeEvent,
'not-mentioned': {} as OntimeEvent,
},
revision: 1,
} as Rundown;
const parsedRundown = parseRundown(rundown, {});
expect(parsedRundown.order.length).toEqual(2);
expect(Object.keys(parsedRundown.entries).length).toEqual(2);
});
it('parses events nested in blocks', () => {
const rundown = {
id: 'test',
title: '',
order: ['block'],
entries: {
block: makeOntimeBlock({ id: 'block', events: ['1', '2'] }),
'1': makeOntimeEvent({ id: '1' }),
'2': makeOntimeEvent({ id: '2' }),
},
revision: 1,
} as Rundown;
const parsedRundown = parseRundown(rundown, {});
expect(parsedRundown.order.length).toEqual(1);
expect(parsedRundown.entries.block).toMatchObject({ events: ['1', '2'] });
expect(Object.keys(parsedRundown.entries).length).toEqual(3);
});
});
describe('parseProject()', () => {
@@ -381,123 +419,3 @@ describe('sanitiseCustomFields()', () => {
expect(sanitationResult).toStrictEqual(expectedCustomFields);
});
});
describe('parseRundown() linking', () => {
it('returns linked events', () => {
const rundown: Rundown = {
id: '',
title: '',
revision: 1,
order: ['1', '2'],
entries: {
'1': {
id: '1',
type: SupportedEvent.Event,
skip: false,
} as OntimeEvent,
'2': {
id: '2',
type: SupportedEvent.Event,
linkStart: 'true',
skip: false,
} as OntimeEvent,
},
};
const result = parseRundown(rundown, {});
expect(result).toMatchObject({
order: ['1', '2'],
entries: {
'2': {
linkStart: '1',
},
},
});
});
it('returns unlinked if no previous', () => {
const rundown: Rundown = {
id: '',
title: '',
revision: 1,
order: ['1', '2'],
entries: {
'2': {
id: '2',
type: SupportedEvent.Event,
linkStart: 'true',
skip: false,
} as OntimeEvent,
},
};
const result = parseRundown(rundown, {});
expect(result).toMatchObject({
order: ['2'],
entries: {
'2': {
linkStart: null,
},
},
});
});
it('returns linked events past blocks and delays', () => {
const rundown: Rundown = {
id: '',
title: '',
revision: 1,
order: ['1', 'delay1', '2', 'block1', '3'],
entries: {
'1': {
id: '1',
type: SupportedEvent.Event,
skip: false,
} as OntimeEvent,
delay1: {
id: 'delay1',
type: SupportedEvent.Delay,
duration: 0,
},
'2': {
id: '2',
type: SupportedEvent.Event,
linkStart: 'true',
skip: false,
} as OntimeEvent,
block1: {
id: 'block1',
type: SupportedEvent.Block,
title: '',
} as OntimeBlock,
'3': {
id: '3',
type: SupportedEvent.Event,
linkStart: 'true',
skip: false,
} as OntimeEvent,
},
};
const result = parseRundown(rundown, {});
expect(result).toMatchObject({
order: rundown.order,
entries: {
'1': {
id: '1',
cue: '1',
},
'2': {
id: '2',
cue: '2',
linkStart: '1',
},
'3': {
id: '3',
cue: '3',
linkStart: '2',
},
},
});
});
});
+2 -3
View File
@@ -6,7 +6,6 @@ import {
type ImportMap,
isKnownTimerType,
validateEndAction,
validateLinkStart,
validateTimerType,
validateTimes,
} from 'ontime-utils';
@@ -246,7 +245,7 @@ export const parseExcel = (
} else if (j === timeStartIndex) {
entry.timeStart = parseExcelDate(column);
} else if (j === linkStartIndex) {
entry.linkStart = parseBooleanString(column) ? 'true' : null;
entry.linkStart = parseBooleanString(column);
} else if (j === timeEndIndex) {
entry.timeEnd = parseExcelDate(column);
} else if (j === durationIndex) {
@@ -412,7 +411,7 @@ export function createPatch(originalEvent: OntimeEvent, patchEvent: Partial<Onti
timeEnd,
duration,
timeStrategy,
linkStart: validateLinkStart(patchEvent.linkStart, originalEvent.linkStart),
linkStart: typeof patchEvent.linkStart === 'boolean' ? patchEvent.linkStart : originalEvent.linkStart,
endAction: validateEndAction(patchEvent.endAction, originalEvent.endAction),
timerType: validateTimerType(patchEvent.timerType, originalEvent.timerType),
countToEnd: typeof patchEvent.countToEnd === 'boolean' ? patchEvent.countToEnd : originalEvent.countToEnd,
+30 -9
View File
@@ -75,11 +75,11 @@ export function parseRundown(
};
let eventIndex = 0;
let previousId: string | null = null;
for (let i = 0; i < rundown.order.length; i++) {
const entryId = rundown.order[i];
const event = rundown.entries[entryId];
if (event === undefined) {
emitError?.('Could not find referenced event, skipping');
continue;
@@ -94,13 +94,7 @@ export function parseRundown(
let newEvent: OntimeEvent | OntimeDelay | OntimeBlock | null;
if (isOntimeEvent(event)) {
const maybeEvent = { ...event };
if (event.linkStart) {
maybeEvent.linkStart = previousId;
}
newEvent = createEvent(maybeEvent, eventIndex);
newEvent = createEvent(event, eventIndex);
// skip if event is invalid
if (newEvent == null) {
emitError?.('Skipping event without payload');
@@ -115,11 +109,38 @@ export function parseRundown(
}
}
previousId = id;
eventIndex += 1;
} else if (isOntimeDelay(event)) {
newEvent = { ...delayDef, duration: event.duration, id };
} else if (isOntimeBlock(event)) {
for (let i = 0; i < event.events.length; i++) {
const nestedEventId = event.events[i];
const nestedEvent = rundown.entries[nestedEventId];
if (isOntimeEvent(nestedEvent)) {
const newNestedEvent = createEvent(nestedEvent, eventIndex);
// skip if event is invalid
if (newNestedEvent == null) {
emitError?.('Skipping event without payload');
continue;
}
// for every field in custom, check that a key exists in customfields
for (const field in newNestedEvent.custom) {
if (!Object.hasOwn(parsedCustomFields, field)) {
emitError?.(`Custom field ${field} not found`);
delete newNestedEvent.custom[field];
}
}
eventIndex += 1;
if (newNestedEvent) {
parsedRundown.entries[nestedEventId] = newNestedEvent;
}
}
}
newEvent = {
...blockDef,
title: event.title,