mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-13 03:13:47 +00:00
Merge remote-tracking branch 'origin/refactor-project-data' into excel-import-preview
This commit is contained in:
@@ -13,7 +13,7 @@ import { LogOrigin, OSCSettings } from 'ontime-types';
|
||||
|
||||
// Import Routes
|
||||
import { router as rundownRouter } from './routes/rundownRouter.js';
|
||||
import { router as eventDataRouter } from './routes/eventDataRouter.js';
|
||||
import { router as projectRouter } from './routes/projectRouter.js';
|
||||
import { router as ontimeRouter } from './routes/ontimeRouter.js';
|
||||
import { router as playbackRouter } from './routes/playbackRouter.js';
|
||||
|
||||
@@ -55,7 +55,7 @@ app.use(express.json({ limit: '1mb' }));
|
||||
|
||||
// Implement route endpoints
|
||||
app.use('/events', rundownRouter);
|
||||
app.use('/eventdata', eventDataRouter);
|
||||
app.use('/project', projectRouter);
|
||||
app.use('/ontime', ontimeRouter);
|
||||
app.use('/playback', playbackRouter);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Class Event Provider is a mediator for handling the local db
|
||||
* and adds logic specific to ontime data
|
||||
*/
|
||||
import { EventData, OntimeRundown, ViewSettings } from 'ontime-types';
|
||||
import { ProjectData, OntimeRundown, ViewSettings } from 'ontime-types';
|
||||
|
||||
import { data, db } from '../../modules/loadDb.js';
|
||||
import { safeMerge } from './DataProvider.utils.js';
|
||||
@@ -12,14 +12,14 @@ export class DataProvider {
|
||||
return data;
|
||||
}
|
||||
|
||||
static async setEventData(newData: Partial<EventData>) {
|
||||
data.eventData = { ...data.eventData, ...newData };
|
||||
static async setProjectData(newData: Partial<ProjectData>) {
|
||||
data.project = { ...data.project, ...newData };
|
||||
await this.persist();
|
||||
return data.eventData;
|
||||
return data.project;
|
||||
}
|
||||
|
||||
static getEventData() {
|
||||
return data.eventData;
|
||||
static getProjectData() {
|
||||
return data.project;
|
||||
}
|
||||
|
||||
static async setRundown(newData: OntimeRundown) {
|
||||
@@ -97,7 +97,7 @@ export class DataProvider {
|
||||
|
||||
static async mergeIntoData(newData) {
|
||||
const mergedData = safeMerge(data, newData);
|
||||
data.eventData = mergedData.eventData;
|
||||
data.project = mergedData.project;
|
||||
data.settings = mergedData.settings;
|
||||
data.viewSettings = mergedData.viewSettings;
|
||||
data.osc = mergedData.osc;
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
* @param {object} newData
|
||||
*/
|
||||
export function safeMerge(existing, newData) {
|
||||
const { rundown, eventData, settings, viewSettings, osc, http, aliases, userFields } = newData || {};
|
||||
const { rundown, project, settings, viewSettings, osc, http, aliases, userFields } = newData || {};
|
||||
return {
|
||||
...existing,
|
||||
rundown: rundown ?? existing.rundown,
|
||||
eventData: { ...existing.eventData, ...eventData },
|
||||
project: { ...existing.project, ...project },
|
||||
settings: { ...existing.settings, ...settings },
|
||||
viewSettings: { ...existing.viewSettings, ...viewSettings },
|
||||
aliases: aliases ?? existing.aliases,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { safeMerge } from '../DataProvider.utils.js';
|
||||
describe('safeMerge', () => {
|
||||
const existing = {
|
||||
rundown: [],
|
||||
eventData: {
|
||||
project: {
|
||||
title: 'existing title',
|
||||
publicUrl: 'existing public URL',
|
||||
backstageUrl: 'existing backstageUrl',
|
||||
@@ -62,15 +62,15 @@ describe('safeMerge', () => {
|
||||
expect(mergedData.rundown).toEqual(newData.rundown);
|
||||
});
|
||||
|
||||
it('merges the event key', () => {
|
||||
it('merges the project key', () => {
|
||||
const newData = {
|
||||
eventData: {
|
||||
project: {
|
||||
title: 'new title',
|
||||
publicInfo: 'new public info',
|
||||
},
|
||||
};
|
||||
const mergedData = safeMerge(existing, newData);
|
||||
expect(mergedData.eventData).toEqual({
|
||||
expect(mergedData.project).toEqual({
|
||||
title: 'new title',
|
||||
publicUrl: 'existing public URL',
|
||||
publicInfo: 'new public info',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Alias, EventData, LogOrigin } from 'ontime-types';
|
||||
import { Alias, LogOrigin, ProjectData } from 'ontime-types';
|
||||
|
||||
import { RequestHandler } from 'express';
|
||||
import fs from 'fs';
|
||||
@@ -31,7 +31,7 @@ export const poll = async (req, res) => {
|
||||
// Create controller for GET request to '/ontime/db'
|
||||
// Returns -
|
||||
export const dbDownload = async (req, res) => {
|
||||
const { title } = DataProvider.getEventData();
|
||||
const { title } = DataProvider.getProjectData();
|
||||
const fileTitle = title || 'ontime data';
|
||||
|
||||
res.download(resolveDbPath, `${fileTitle}.json`, (err) => {
|
||||
@@ -368,7 +368,7 @@ export const dbUpload = async (req, res) => {
|
||||
// Create controller for POST request to '/ontime/new'
|
||||
export const postNew: RequestHandler = async (req, res) => {
|
||||
try {
|
||||
const newEventData: EventData = {
|
||||
const newProjectData: ProjectData = {
|
||||
title: req.body?.title ?? '',
|
||||
description: req.body?.description ?? '',
|
||||
publicUrl: req.body?.publicUrl ?? '',
|
||||
@@ -376,7 +376,7 @@ export const postNew: RequestHandler = async (req, res) => {
|
||||
backstageUrl: req.body?.backstageUrl ?? '',
|
||||
backstageInfo: req.body?.backstageInfo ?? '',
|
||||
};
|
||||
const newData = await DataProvider.setEventData(newEventData);
|
||||
const newData = await DataProvider.setProjectData(newProjectData);
|
||||
await deleteAllEvents();
|
||||
res.status(201).send(newData);
|
||||
} catch (error) {
|
||||
|
||||
+8
-8
@@ -1,24 +1,24 @@
|
||||
import { RequestHandler } from 'express';
|
||||
|
||||
import { EventData } from 'ontime-types';
|
||||
import { ProjectData } from 'ontime-types';
|
||||
|
||||
import { removeUndefined } from '../utils/parserUtils.js';
|
||||
import { failEmptyObjects } from '../utils/routerUtils.js';
|
||||
import { DataProvider } from '../classes/data-provider/DataProvider.js';
|
||||
|
||||
// Create controller for GET request to 'event'
|
||||
export const getEventData: RequestHandler = async (req, res) => {
|
||||
res.json(DataProvider.getEventData());
|
||||
// Create controller for GET request to 'project'
|
||||
export const getProject: RequestHandler = async (req, res) => {
|
||||
res.json(DataProvider.getProjectData());
|
||||
};
|
||||
|
||||
// Create controller for POST request to 'event'
|
||||
export const postEventData: RequestHandler = async (req, res) => {
|
||||
// Create controller for POST request to 'project'
|
||||
export const postProject: RequestHandler = async (req, res) => {
|
||||
if (failEmptyObjects(req.body, res)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const newEvent: Partial<EventData> = removeUndefined({
|
||||
const newEvent: Partial<ProjectData> = removeUndefined({
|
||||
title: req.body?.title,
|
||||
description: req.body?.description,
|
||||
publicUrl: req.body?.publicUrl,
|
||||
@@ -27,7 +27,7 @@ export const postEventData: RequestHandler = async (req, res) => {
|
||||
backstageInfo: req.body?.backstageInfo,
|
||||
endMessage: req.body?.endMessage,
|
||||
});
|
||||
const newData = await DataProvider.setEventData(newEvent);
|
||||
const newData = await DataProvider.setProjectData(newEvent);
|
||||
res.status(200).send(newData);
|
||||
} catch (error) {
|
||||
res.status(400).send(error);
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { body, validationResult } from 'express-validator';
|
||||
|
||||
export const eventDataSanitizer = [
|
||||
export const projectSanitiser = [
|
||||
body('title').optional().isString().trim(),
|
||||
body('description').optional().isString().trim(),
|
||||
body('publicUrl').optional().isString().trim(),
|
||||
@@ -2,7 +2,7 @@ import { DatabaseModel } from 'ontime-types';
|
||||
|
||||
export const dbModel: DatabaseModel = {
|
||||
rundown: [],
|
||||
eventData: {
|
||||
project: {
|
||||
title: '',
|
||||
description: '',
|
||||
publicUrl: '',
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import express from 'express';
|
||||
import { getEventData, postEventData } from '../controllers/eventDataController.js';
|
||||
import { eventDataSanitizer } from '../controllers/eventDataController.validate.js';
|
||||
|
||||
export const router = express.Router();
|
||||
|
||||
// create route between controller and 'GET /event' endpoint
|
||||
router.get('/', getEventData);
|
||||
|
||||
// create route between controller and 'POST /event' endpoint
|
||||
router.post('/', eventDataSanitizer, postEventData);
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
validateUserFields,
|
||||
viewValidator,
|
||||
} from '../controllers/ontimeController.validate.js';
|
||||
import { eventDataSanitizer } from '../controllers/eventDataController.validate.js';
|
||||
import { projectSanitiser } from '../controllers/projectController.validate.js';
|
||||
|
||||
export const router = express.Router();
|
||||
|
||||
@@ -81,4 +81,4 @@ router.post('/osc', validateOSC, postOSC);
|
||||
router.post('/osc-subscriptions', validateOscSubscription, postOscSubscriptions);
|
||||
|
||||
// create route between controller and '/ontime/new' endpoint
|
||||
router.post('/new', eventDataSanitizer, postNew);
|
||||
router.post('/new', projectSanitiser, postNew);
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import express from 'express';
|
||||
import { getProject, postProject } from '../controllers/projectController.js';
|
||||
import { projectSanitiser } from '../controllers/projectController.validate.js';
|
||||
|
||||
export const router = express.Router();
|
||||
|
||||
// create route between controller and 'GET /project' endpoint
|
||||
router.get('/', getProject);
|
||||
|
||||
// create route between controller and 'POST /project' endpoint
|
||||
router.post('/', projectSanitiser, postProject);
|
||||
@@ -1,6 +1,6 @@
|
||||
import { vi } from 'vitest';
|
||||
|
||||
import { EndAction, OntimeEvent, TimerType } from 'ontime-types';
|
||||
import { EndAction, TimerType } from 'ontime-types';
|
||||
|
||||
import { dbModel } from '../../models/dataModel.js';
|
||||
import { parseExcel, parseJson, validateEvent } from '../parser.js';
|
||||
@@ -192,7 +192,7 @@ describe('test json parser with valid def', () => {
|
||||
user9: '',
|
||||
},
|
||||
],
|
||||
eventData: {
|
||||
project: {
|
||||
title: 'This is a test definition',
|
||||
url: 'www.carlosvalente.com',
|
||||
publicInfo: 'WiFi: demoproject \nPassword: ontimeproject',
|
||||
@@ -246,7 +246,7 @@ describe('test json parser with valid def', () => {
|
||||
});
|
||||
|
||||
it('loaded event settings', () => {
|
||||
const eventTitle = parseResponse?.eventData?.title;
|
||||
const eventTitle = parseResponse?.project?.title;
|
||||
expect(eventTitle).toBe('This is a test definition');
|
||||
});
|
||||
|
||||
@@ -402,10 +402,10 @@ describe('test corrupt data', () => {
|
||||
expect(parsedDef.rundown.length).toBe(0);
|
||||
});
|
||||
|
||||
it('handles missing event data', async () => {
|
||||
const emptyEventData = {
|
||||
it('handles missing project data', async () => {
|
||||
const emptyProjectData = {
|
||||
rundown: [{}, {}, {}, {}, {}, {}, {}, {}],
|
||||
eventData: {},
|
||||
project: {},
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 2,
|
||||
@@ -415,8 +415,8 @@ describe('test corrupt data', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const parsedDef = await parseJson(emptyEventData);
|
||||
expect(parsedDef.eventData).toStrictEqual(dbModel.eventData);
|
||||
const parsedDef = await parseJson(emptyProjectData);
|
||||
expect(parsedDef.project).toStrictEqual(dbModel.project);
|
||||
});
|
||||
|
||||
it('handles missing settings', async () => {
|
||||
@@ -629,7 +629,7 @@ describe('test parseExcel function', () => {
|
||||
[],
|
||||
];
|
||||
|
||||
const expectedParsedEvent = {
|
||||
const expectedParsedProjectData = {
|
||||
title: 'Test Event',
|
||||
publicUrl: 'www.public.com',
|
||||
backstageUrl: 'www.backstage.com',
|
||||
@@ -682,7 +682,7 @@ describe('test parseExcel function', () => {
|
||||
];
|
||||
|
||||
const parsedData = await parseExcel(testdata);
|
||||
expect(parsedData.eventData).toStrictEqual(expectedParsedEvent);
|
||||
expect(parsedData.project).toStrictEqual(expectedParsedProjectData);
|
||||
expect(parsedData.rundown).toBeDefined();
|
||||
expect(parsedData.rundown[0]).toMatchObject(expectedParsedRundown[0]);
|
||||
expect(parsedData.rundown[1]).toMatchObject(expectedParsedRundown[1]);
|
||||
|
||||
@@ -7,7 +7,6 @@ import { generateId, calculateDuration } from 'ontime-utils';
|
||||
import {
|
||||
DatabaseModel,
|
||||
EndAction,
|
||||
EventData,
|
||||
OntimeEvent,
|
||||
OntimeRundown,
|
||||
SupportedEvent,
|
||||
@@ -19,7 +18,7 @@ import { dbModel } from '../models/dataModel.js';
|
||||
import { deleteFile, makeString } from './parserUtils.js';
|
||||
import {
|
||||
parseAliases,
|
||||
parseEventData,
|
||||
parseProject,
|
||||
parseOsc,
|
||||
parseRundown,
|
||||
parseSettings,
|
||||
@@ -37,8 +36,9 @@ export const JSON_MIME = 'application/json';
|
||||
* @returns {object} - parsed object
|
||||
*/
|
||||
export const parseExcel = async (excelData) => {
|
||||
const eventData: Partial<EventData> = {
|
||||
const projectData: Partial<ProjectData> = {
|
||||
title: '',
|
||||
description: '',
|
||||
publicUrl: '',
|
||||
backstageUrl: '',
|
||||
};
|
||||
@@ -71,6 +71,7 @@ export const parseExcel = async (excelData) => {
|
||||
.filter((e) => e.length > 0)
|
||||
.forEach((row) => {
|
||||
let eventTitleNext = false;
|
||||
let projectTitleNext = false;
|
||||
let publicUrlNext = false;
|
||||
let publicInfoNext = false;
|
||||
let backstageUrlNext = false;
|
||||
@@ -81,19 +82,22 @@ export const parseExcel = async (excelData) => {
|
||||
row.forEach((column, j) => {
|
||||
// check flags
|
||||
if (eventTitleNext) {
|
||||
eventData.title = column;
|
||||
projectData.title = column;
|
||||
eventTitleNext = false;
|
||||
} else if (projectTitleNext) {
|
||||
projectData.description = column;
|
||||
projectTitleNext = false;
|
||||
} else if (publicUrlNext) {
|
||||
eventData.publicUrl = column;
|
||||
projectData.publicUrl = column;
|
||||
publicUrlNext = false;
|
||||
} else if (publicInfoNext) {
|
||||
eventData.publicInfo = column;
|
||||
projectData.publicInfo = column;
|
||||
publicInfoNext = false;
|
||||
} else if (backstageUrlNext) {
|
||||
eventData.backstageUrl = column;
|
||||
projectData.backstageUrl = column;
|
||||
backstageUrlNext = false;
|
||||
} else if (backstageInfoNext) {
|
||||
eventData.backstageInfo = column;
|
||||
projectData.backstageInfo = column;
|
||||
backstageInfoNext = false;
|
||||
} else if (j === timeStartIndex) {
|
||||
event.timeStart = parseExcelDate(column);
|
||||
@@ -154,9 +158,12 @@ export const parseExcel = async (excelData) => {
|
||||
// look for keywords
|
||||
// need to make sure it is a string first
|
||||
switch (col) {
|
||||
case 'event name':
|
||||
case 'project name':
|
||||
eventTitleNext = true;
|
||||
break;
|
||||
case 'project description':
|
||||
projectTitleNext = true;
|
||||
break;
|
||||
case 'public url':
|
||||
publicUrlNext = true;
|
||||
break;
|
||||
@@ -273,7 +280,7 @@ export const parseExcel = async (excelData) => {
|
||||
});
|
||||
return {
|
||||
rundown,
|
||||
eventData,
|
||||
project: projectData,
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 2,
|
||||
@@ -299,7 +306,7 @@ export const parseJson = async (jsonData, enforce = false): Promise<DatabaseMode
|
||||
// parse Events
|
||||
returnData.rundown = parseRundown(jsonData);
|
||||
// parse Event
|
||||
returnData.eventData = parseEventData(jsonData, enforce);
|
||||
returnData.project = parseProject(jsonData, enforce);
|
||||
// Settings handled partially
|
||||
returnData.settings = parseSettings(jsonData, enforce);
|
||||
// View settings handled partially
|
||||
@@ -396,7 +403,7 @@ export const fileHandler = async (file): Promise<ResponseOK | ResponseError> =>
|
||||
const dataFromExcel = await parseExcel(excelData.data);
|
||||
res.data = {};
|
||||
res.data.rundown = parseRundown(dataFromExcel);
|
||||
res.data.eventData = parseEventData(dataFromExcel, true);
|
||||
res.data.project = parseProject(dataFromExcel, true);
|
||||
res.data.userFields = parseUserFields(dataFromExcel);
|
||||
res.message = 'success';
|
||||
} else {
|
||||
|
||||
@@ -2,11 +2,11 @@ import { generateId } from 'ontime-utils';
|
||||
import {
|
||||
Alias,
|
||||
EndAction,
|
||||
EventData,
|
||||
OntimeRundown,
|
||||
OSCSettings,
|
||||
OscSubscription,
|
||||
OscSubscriptionOptions,
|
||||
ProjectData,
|
||||
Settings,
|
||||
TimerLifeCycle,
|
||||
TimerType,
|
||||
@@ -91,26 +91,26 @@ export const parseRundown = (data): OntimeRundown => {
|
||||
* @param {boolean} enforce - whether to create a definition if one is missing
|
||||
* @returns {object} - event object data
|
||||
*/
|
||||
export const parseEventData = (data, enforce): EventData => {
|
||||
let newEventData: Partial<EventData> = {};
|
||||
if ('eventData' in data) {
|
||||
console.log('Found event data, importing...');
|
||||
const e = data.eventData;
|
||||
export const parseProject = (data, enforce): ProjectData => {
|
||||
let newProjectData: Partial<ProjectData> = {};
|
||||
if ('project' in data) {
|
||||
console.log('Found project data, importing...');
|
||||
const project = data.project;
|
||||
// filter known properties and write to db
|
||||
newEventData = {
|
||||
...dbModel.eventData,
|
||||
title: e.title || dbModel.eventData.title,
|
||||
description: e.description || dbModel.eventData.description,
|
||||
publicUrl: e.publicUrl || dbModel.eventData.publicUrl,
|
||||
publicInfo: e.publicInfo || dbModel.eventData.publicInfo,
|
||||
backstageUrl: e.backstageUrl || dbModel.eventData.backstageUrl,
|
||||
backstageInfo: e.backstageInfo || dbModel.eventData.backstageInfo,
|
||||
newProjectData = {
|
||||
...dbModel.project,
|
||||
title: project.title || dbModel.project.title,
|
||||
description: project.description || dbModel.project.description,
|
||||
publicUrl: project.publicUrl || dbModel.project.publicUrl,
|
||||
publicInfo: project.publicInfo || dbModel.project.publicInfo,
|
||||
backstageUrl: project.backstageUrl || dbModel.project.backstageUrl,
|
||||
backstageInfo: project.backstageInfo || dbModel.project.backstageInfo,
|
||||
};
|
||||
} else if (enforce) {
|
||||
newEventData = { ...dbModel.eventData };
|
||||
console.log('Created event object in db');
|
||||
newProjectData = { ...dbModel.project };
|
||||
console.log('Created project object in db');
|
||||
}
|
||||
return newEventData as EventData;
|
||||
return newProjectData as ProjectData;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user