From 7b34c8f3b130be588fef821cbe32470757e748b1 Mon Sep 17 00:00:00 2001 From: arc-alex Date: Sun, 26 Nov 2023 16:40:35 +0100 Subject: [PATCH] push project data --- .../modals/sheets-modal/SheetsModal.tsx | 2 +- .../utils/__tests__/googleSheetUtils.test.ts | 107 +++++++++++++++++- apps/server/src/utils/googleSheetUtils.ts | 64 ++++++++++- apps/server/src/utils/parser.ts | 4 +- apps/server/src/utils/sheetsAuth.ts | 18 +-- 5 files changed, 179 insertions(+), 16 deletions(-) diff --git a/apps/client/src/features/modals/sheets-modal/SheetsModal.tsx b/apps/client/src/features/modals/sheets-modal/SheetsModal.tsx index 533b92fcd..211016e18 100644 --- a/apps/client/src/features/modals/sheets-modal/SheetsModal.tsx +++ b/apps/client/src/features/modals/sheets-modal/SheetsModal.tsx @@ -1,4 +1,4 @@ -import { ChangeEvent, useRef, useState, useEffect } from 'react'; +import { ChangeEvent, useEffect,useRef, useState } from 'react'; import { Button, Input, diff --git a/apps/server/src/utils/__tests__/googleSheetUtils.test.ts b/apps/server/src/utils/__tests__/googleSheetUtils.test.ts index 84f4866a8..d47e8d159 100644 --- a/apps/server/src/utils/__tests__/googleSheetUtils.test.ts +++ b/apps/server/src/utils/__tests__/googleSheetUtils.test.ts @@ -1,6 +1,6 @@ import { millisToString } from 'ontime-utils'; -import { getA1Notation, cellRequenstFromEvent } from '../googleSheetUtils.js'; -import { EndAction, OntimeRundownEntry, SupportedEvent, TimerType } from 'ontime-types'; +import { getA1Notation, cellRequenstFromEvent, cellRequenstFromProjectData } from '../googleSheetUtils.js'; +import { EndAction, OntimeRundownEntry, ProjectData, SupportedEvent, TimerType } from 'ontime-types'; describe('getA1Notation()', () => { test('A1', () => { @@ -328,3 +328,106 @@ describe('cellRequenstFromEvent()', () => { expect(result2.updateCells.fields).toStrictEqual('userEnteredValue'); }); }); + +describe('cellRequenstFromProjectData()', () => { + test('string to string', () => { + const projectData: ProjectData = { + title: 'Title', + description: 'Description', + publicUrl: 'Public Url', + backstageUrl: 'Backstage Url', + publicInfo: 'Public Info', + backstageInfo: 'Backstage Info', + }; + const metadata = { + title: { row: 0, col: 1 }, + description: { row: 1, col: 1 }, + publicUrl: { row: 2, col: 1 }, + backstageUrl: { row: 3, col: 1 }, + publicInfo: { row: 4, col: 1 }, + backstageInfo: { row: 5, col: 1 }, + }; + const result = cellRequenstFromProjectData(projectData, 1234, metadata); + expect(result.updateCells.rows[0].values[0].userEnteredValue.stringValue).toStrictEqual(projectData.title); + expect(result.updateCells.rows[1].values[0].userEnteredValue.stringValue).toStrictEqual(projectData.description); + expect(result.updateCells.rows[2].values[0].userEnteredValue.stringValue).toStrictEqual(projectData.publicUrl); + expect(result.updateCells.rows[3].values[0].userEnteredValue.stringValue).toStrictEqual(projectData.backstageUrl); + expect(result.updateCells.rows[4].values[0].userEnteredValue.stringValue).toStrictEqual(projectData.publicInfo); + expect(result.updateCells.rows[5].values[0].userEnteredValue.stringValue).toStrictEqual(projectData.backstageInfo); + }); + + test('metadata offset from zero', () => { + const projectData: ProjectData = { + title: 'Title', + description: 'Description', + publicUrl: 'Public Url', + backstageUrl: 'Backstage Url', + publicInfo: 'Public Info', + backstageInfo: 'Backstage Info', + }; + const metadata = { + title: { row: 5, col: 10 }, + description: { row: 6, col: 10 }, + publicUrl: { row: 7, col: 10 }, + backstageUrl: { row: 9, col: 10 }, + publicInfo: { row: 10, col: 10 }, + backstageInfo: { row: 11, col: 10 }, + }; + const result = cellRequenstFromProjectData(projectData, 1234, metadata); + expect(result.updateCells.rows[0].values[0].userEnteredValue.stringValue).toStrictEqual(projectData.title); + expect(result.updateCells.rows[1].values[0].userEnteredValue.stringValue).toStrictEqual(projectData.description); + expect(result.updateCells.rows[2].values[0].userEnteredValue.stringValue).toStrictEqual(projectData.publicUrl); + expect(result.updateCells.rows[4].values[0].userEnteredValue.stringValue).toStrictEqual(projectData.backstageUrl); + expect(result.updateCells.rows[5].values[0].userEnteredValue.stringValue).toStrictEqual(projectData.publicInfo); + expect(result.updateCells.rows[6].values[0].userEnteredValue.stringValue).toStrictEqual(projectData.backstageInfo); + }); + + test('spacing in metadata', () => { + const projectData: ProjectData = { + title: 'Title', + description: 'Description', + publicUrl: 'Public Url', + backstageUrl: 'Backstage Url', + publicInfo: 'Public Info', + backstageInfo: 'Backstage Info', + }; + const metadata = { + title: { row: 0, col: 1 }, + description: { row: 1, col: 1 }, + publicUrl: { row: 2, col: 1 }, + backstageUrl: { row: 9, col: 1 }, + publicInfo: { row: 15, col: 1 }, + backstageInfo: { row: 50, col: 1 }, + }; + const result = cellRequenstFromProjectData(projectData, 1234, metadata); + expect(result.updateCells.rows[0].values[0].userEnteredValue.stringValue).toStrictEqual(projectData.title); + expect(result.updateCells.rows[1].values[0].userEnteredValue.stringValue).toStrictEqual(projectData.description); + expect(result.updateCells.rows[2].values[0].userEnteredValue.stringValue).toStrictEqual(projectData.publicUrl); + expect(result.updateCells.rows[9].values[0].userEnteredValue.stringValue).toStrictEqual(projectData.backstageUrl); + expect(result.updateCells.rows[15].values[0].userEnteredValue.stringValue).toStrictEqual(projectData.publicInfo); + expect(result.updateCells.rows[50].values[0].userEnteredValue.stringValue).toStrictEqual(projectData.backstageInfo); + }); + + test('sheet setup', () => { + const projectData: ProjectData = { + title: 'Title', + description: 'Description', + publicUrl: 'Public Url', + backstageUrl: 'Backstage Url', + publicInfo: 'Public Info', + backstageInfo: 'Backstage Info', + }; + const metadata = { + title: { row: 0, col: 10 }, + description: { row: 1, col: 10 }, + publicUrl: { row: 2, col: 10 }, + backstageUrl: { row: 3, col: 10 }, + publicInfo: { row: 4, col: 10 }, + backstageInfo: { row: 5, col: 10 }, + }; + const result = cellRequenstFromProjectData(projectData, 1234, metadata); + expect(result.updateCells.start.rowIndex).toStrictEqual(0); + expect(result.updateCells.start.columnIndex).toStrictEqual(11); + expect(result.updateCells.fields).toStrictEqual('userEnteredValue'); + }); +}); diff --git a/apps/server/src/utils/googleSheetUtils.ts b/apps/server/src/utils/googleSheetUtils.ts index 159159fcd..07aae2022 100644 --- a/apps/server/src/utils/googleSheetUtils.ts +++ b/apps/server/src/utils/googleSheetUtils.ts @@ -1,6 +1,6 @@ import { sheets_v4 } from '@googleapis/sheets'; import { millisToString } from 'ontime-utils'; -import { OntimeRundownEntry, isOntimeEvent } from 'ontime-types'; +import { OntimeRundownEntry, ProjectData, isOntimeEvent } from 'ontime-types'; /** * @@ -34,7 +34,6 @@ export function getA1Notation(row: number, column: number): string { * @param {number} index - index of the event * @param {number} worksheetId * @param {any} metadata - object with all the cell positions of the title of each attribute - * @param {number} titleCol - smallest col index * @returns {sheets_v4.Schema} - list of update requests */ export function cellRequenstFromEvent( @@ -103,3 +102,64 @@ export function cellRequenstFromEvent( }, }; } + +/** + * @description - creates updateCells request from ontime event + * @param {ProjectData} projectData + * @param {number} worksheetId + * @param {any} metadata - object with all the cell positions of the title of each attribute + * @returns {sheets_v4.Schema} - list of update requests + */ +export function cellRequenstFromProjectData( + projectData: ProjectData, + worksheetId: number, + metadata, +): sheets_v4.Schema$Request { + const r: sheets_v4.Schema$RowData[] = []; + const tmp = Object.entries(metadata) + .filter(([_, value]) => value !== undefined) + .sort(([_a, a], [_b, b]) => a['col'] - b['col']) as [string, { col: number; row: number }][]; + + const minRow = Object.values(metadata).reduce( + (accumulator: number, val) => Math.min(accumulator, val['row']), + Number.MAX_VALUE, + ) as number; + const minCol = tmp[0][1].col + 1; + + for (const [index, e] of tmp.entries()) { + if (index != 0) { + const prevRow = tmp[index - 1][1].row; + const thisRow = e[1].row; + const diff = thisRow - prevRow; + if (diff > 1) { + const fillArr = new Array<(typeof tmp)[0]>(1).fill(['blank', { row: prevRow + 1, col: e[1].col }]); + tmp.splice(index, 0, ...fillArr); + } + } + } + tmp.forEach(([key, _]) => { + if (key == 'blank') { + r.push({}); + } else { + r.push({ + values: [ + { + userEnteredValue: { stringValue: projectData[key] }, + }, + ], + }); + } + }); + + return { + updateCells: { + start: { + sheetId: worksheetId, + rowIndex: minRow, + columnIndex: minCol, + }, + fields: 'userEnteredValue', + rows: r, + }, + }; +} diff --git a/apps/server/src/utils/parser.ts b/apps/server/src/utils/parser.ts index e4151eda2..581b965cd 100644 --- a/apps/server/src/utils/parser.ts +++ b/apps/server/src/utils/parser.ts @@ -121,11 +121,11 @@ export const parseExcel = (excelData: unknown[][], options?: Partial { projectTitleNext = true; - projectMetadata['projectName'] = { row: row, col: col }; + projectMetadata['title'] = { row: row, col: col }; }, [importMap.projectDescription]: (row: number, col: number) => { projectDescriptionNext = true; - projectMetadata['projectDescription'] = { row: row, col: col }; + projectMetadata['description'] = { row: row, col: col }; }, [importMap.publicUrl]: (row: number, col: number) => { publicUrlNext = true; diff --git a/apps/server/src/utils/sheetsAuth.ts b/apps/server/src/utils/sheetsAuth.ts index 39ce70ae7..406e93b54 100644 --- a/apps/server/src/utils/sheetsAuth.ts +++ b/apps/server/src/utils/sheetsAuth.ts @@ -11,7 +11,7 @@ import { parseProject, parseRundown, parseUserFields } from './parserFunctions.j import { ensureDirectory } from './fileManagement.js'; import { DataProvider } from '../classes/data-provider/DataProvider.js'; import { GoogleSheetState } from 'ontime-types'; -import { cellRequenstFromEvent, getA1Notation } from './googleSheetUtils.js'; +import { cellRequenstFromEvent, cellRequenstFromProjectData, getA1Notation } from './googleSheetUtils.js'; type ResponseOK = { data: Partial; @@ -97,9 +97,9 @@ class sheet { range: this.range, }); if (rq.status === 200) { - //TODO: projectMetadata - const { rundownMetadata } = parseExcel(rq.data.values); + const { rundownMetadata, projectMetadata } = parseExcel(rq.data.values); const rundown = DataProvider.getRundown(); + const projectData = DataProvider.getProjectData(); const titleRow = Object.values(rundownMetadata)[0]['row']; const updateRundown = Array(); @@ -133,12 +133,14 @@ class sheet { }, }); - //update the corespunding row with event data + //update the corresponding row with event data rundown.forEach((entry, index) => - updateRundown.push( - cellRequenstFromEvent(entry, index, this.worksheetId, rundownMetadata), - ), + updateRundown.push(cellRequenstFromEvent(entry, index, this.worksheetId, rundownMetadata)), ); + + //update project data + updateRundown.push(cellRequenstFromProjectData(projectData, this.worksheetId, projectMetadata)); + const writeResponds = await sheets({ version: 'v4', auth: sheet.client }).spreadsheets.batchUpdate({ spreadsheetId: this.sheetId, requestBody: { @@ -227,8 +229,6 @@ class sheet { return false; } - - private authServerTimeout; /** * create local Auth Server - returns url to serve on success