clean up sheet flow

This commit is contained in:
arc-alex
2023-11-24 17:47:21 +01:00
parent 111df94d6d
commit e8bcbb4291
6 changed files with 63 additions and 104 deletions
+4 -12
View File
@@ -251,21 +251,13 @@ export const getSheetsAuthUrl = async () => {
return res.data; return res.data;
}; };
export const postPreviewSheet = async (sheetId: string, worksheet: string, options?: ExcelImportMap) => { export const postPreviewSheet = async () => {
const response = await axios.post(`${ontimeURL}/sheet-preview`, { const response = await axios.post(`${ontimeURL}/sheet-preview`);
sheetid: sheetId,
worksheet: worksheet,
options: options,
});
return response.data.data; return response.data.data;
}; };
export const postPushSheet = async (sheetId: string, worksheet: string, options?: ExcelImportMap) => { export const postPushSheet = async () => {
const response = await axios.post(`${ontimeURL}/sheet-push`, { const response = await axios.post(`${ontimeURL}/sheet-push`);
sheetid: sheetId,
worksheet: worksheet,
options: options,
});
return response.data.data; return response.data.data;
}; };
@@ -13,7 +13,7 @@ import {
import { IoCheckmarkCircleOutline } from '@react-icons/all-files/io5/IoCheckmarkCircleOutline'; import { IoCheckmarkCircleOutline } from '@react-icons/all-files/io5/IoCheckmarkCircleOutline';
import { IoCloseCircleOutline } from '@react-icons/all-files/io5/IoCloseCircleOutline'; import { IoCloseCircleOutline } from '@react-icons/all-files/io5/IoCloseCircleOutline';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import { GoogleSheetState,OntimeRundown, ProjectData, UserFields } from 'ontime-types'; import { GoogleSheetState, OntimeRundown, ProjectData, UserFields } from 'ontime-types';
import { PROJECT_DATA, RUNDOWN, USERFIELDS } from '../../../common/api/apiConstants'; import { PROJECT_DATA, RUNDOWN, USERFIELDS } from '../../../common/api/apiConstants';
import { maybeAxiosError } from '../../../common/api/apiUtils'; import { maybeAxiosError } from '../../../common/api/apiUtils';
@@ -56,19 +56,19 @@ export default function SheetsModal(props: SheetsModalProps) {
setRundown(null); setRundown(null);
setProject(null); setProject(null);
setUserFields(null); setUserFields(null);
// setSheetState({ auth: false, id: false, worksheet: false });
onClose(); onClose();
}; };
const handleClick = () => { const handleClick = () => {
fileInputRef.current?.click(); fileInputRef.current?.click();
}; };
const handleFile = (event: ChangeEvent<HTMLInputElement>) => { const handleFile = async (event: ChangeEvent<HTMLInputElement>) => {
const selectedFile = event?.target?.files?.[0]; const selectedFile = event?.target?.files?.[0];
if (!selectedFile) { if (!selectedFile) {
return; return;
} else { } else {
uploadSheetClientFile(selectedFile); await uploadSheetClientFile(selectedFile);
_onChange();
} }
}; };
@@ -112,7 +112,7 @@ export default function SheetsModal(props: SheetsModalProps) {
}; };
const handlePullData = () => { const handlePullData = () => {
postPreviewSheet(sheetid.current?.value ?? '', worksheet.current?.value ?? '').then((data) => { postPreviewSheet().then((data) => {
setProject(data.project); setProject(data.project);
setRundown(data.rundown); setRundown(data.rundown);
setUserFields(data.userFields); setUserFields(data.userFields);
@@ -120,7 +120,7 @@ export default function SheetsModal(props: SheetsModalProps) {
}; };
const handlePushData = () => { const handlePushData = () => {
postPushSheet(sheetid.current?.value ?? '', worksheet.current?.value ?? '').then((data) => { postPushSheet().then((data) => {
console.log(data); console.log(data);
}); });
}; };
@@ -218,18 +218,20 @@ export default function SheetsModal(props: SheetsModalProps) {
/> />
{sheetState.worksheet ? <IoCheckmarkCircleOutline /> : <IoCloseCircleOutline />} {sheetState.worksheet ? <IoCheckmarkCircleOutline /> : <IoCloseCircleOutline />}
</div> </div>
<div>
<Button variant='ontime-filled' padding='0 2em' onClick={handlePullData}>
Pull data
</Button>
<Button variant='ontime-filled' padding='0 2em' onClick={handlePushData}>
Push data
</Button>
</div>
</> </>
)} )}
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
{!rundown && (
<div>
<Button variant='ontime-subtle-on-light' padding='0 2em' onClick={handlePullData}>
Pull data
</Button>
<Button variant='ontime-subtle-on-light' padding='0 2em' onClick={handlePushData}>
Push data
</Button>
</div>
)}
<Button variant='ontime-ghost-on-light'>Reset</Button> <Button variant='ontime-ghost-on-light'>Reset</Button>
{!rundown && ( {!rundown && (
<Button variant='ontime-filled' padding='0 2em' onClick={handelSave}> <Button variant='ontime-filled' padding='0 2em' onClick={handelSave}>
@@ -423,7 +423,7 @@ export const postNew: RequestHandler = async (req, res) => {
*/ */
export async function previewSheet(req, res) { export async function previewSheet(req, res) {
try { try {
const data = await Sheet.pull(req.body.sheetid, req.body.worksheet); const data = await Sheet.pull();
res.status(200).send(data); res.status(200).send(data);
} catch (error) { } catch (error) {
res.status(500).send({ message: error.toString() }); res.status(500).send({ message: error.toString() });
@@ -436,7 +436,7 @@ export async function previewSheet(req, res) {
*/ */
export async function pushSheet(req, res) { export async function pushSheet(req, res) {
try { try {
await Sheet.push(req.body.sheetid, req.body.worksheet); await Sheet.push();
res.status(200).send('ok'); res.status(200).send('ok');
} catch (error) { } catch (error) {
res.status(500).send({ message: error.toString() }); res.status(500).send({ message: error.toString() });
@@ -465,14 +465,6 @@ export async function uploadGoogleSheetClientFile(req, res) {
}); });
} }
/**
* @returns sheet auth state
*/
export async function sheetAuthState(req, res) {
const send = (await Sheet.authorized()) ? 'true' : 'false';
res.status(200).send(send);
}
/** /**
* @returns link to sheet auth url * @returns link to sheet auth url
*/ */
-4
View File
@@ -20,7 +20,6 @@ import {
postViewSettings, postViewSettings,
previewExcel, previewExcel,
sheetAuthUrl, sheetAuthUrl,
sheetAuthState,
uploadGoogleSheetClientFile, uploadGoogleSheetClientFile,
previewSheet, previewSheet,
pushSheet, pushSheet,
@@ -101,9 +100,6 @@ router.post('/new', projectSanitiser, postNew);
// create route between controller and '/ontime/sheet-client' endpoint // create route between controller and '/ontime/sheet-client' endpoint
router.post('/sheet-clientsecrect', uploadFile, uploadGoogleSheetClientFile); router.post('/sheet-clientsecrect', uploadFile, uploadGoogleSheetClientFile);
// create route between controller and '/ontime/sheet-authstatus' endpoint
router.get('/sheet-authstatus', sheetAuthState);
// create route between controller and '/ontime/sheet-authstatus' endpoint // create route between controller and '/ontime/sheet-authstatus' endpoint
router.get('/sheet-authurl', sheetAuthUrl); router.get('/sheet-authurl', sheetAuthUrl);
+1 -1
View File
@@ -17,7 +17,6 @@ import {
UserFields, UserFields,
EndAction, EndAction,
TimerType, TimerType,
GoogleSheet,
} from 'ontime-types'; } from 'ontime-types';
import fs from 'fs'; import fs from 'fs';
@@ -370,6 +369,7 @@ export const parseJson = async (jsonData): Promise<DatabaseModel | null> => {
returnData.osc = parseOsc(jsonData) ?? dbModel.osc; returnData.osc = parseOsc(jsonData) ?? dbModel.osc;
// Import HTTP settings if any // Import HTTP settings if any
// returnData.http = parseHttp(jsonData, enforce); // returnData.http = parseHttp(jsonData, enforce);
// Import GoogleSheet settings if any
returnData.googleSheet = parseGoogleSheet(jsonData, true); returnData.googleSheet = parseGoogleSheet(jsonData, true);
return returnData as DatabaseModel; return returnData as DatabaseModel;
+40 -63
View File
@@ -6,7 +6,7 @@ import { URL } from 'url';
import { logger } from '../classes/Logger.js'; import { logger } from '../classes/Logger.js';
import { getAppDataPath } from '../setup.js'; import { getAppDataPath } from '../setup.js';
import { DatabaseModel, LogOrigin, OntimeRundownEntry, isOntimeEvent } from 'ontime-types'; import { DatabaseModel, LogOrigin, OntimeRundownEntry, isOntimeEvent } from 'ontime-types';
import { isExcelImportMap, defaultExcelImportMap, millisToString } from 'ontime-utils'; import { millisToString } from 'ontime-utils';
import { parseExcel } from './parser.js'; import { parseExcel } from './parser.js';
import { parseProject, parseRundown, parseUserFields } from './parserFunctions.js'; import { parseProject, parseRundown, parseUserFields } from './parserFunctions.js';
import { ensureDirectory } from './fileManagement.js'; import { ensureDirectory } from './fileManagement.js';
@@ -24,6 +24,9 @@ class sheet {
private readonly client_secret = this.sheetsFolder + '/client_secret.json'; private readonly client_secret = this.sheetsFolder + '/client_secret.json';
private readonly token = this.sheetsFolder + '/token.json'; private readonly token = this.sheetsFolder + '/token.json';
private static authUrl: null | string = null; private static authUrl: null | string = null;
private worksheetId: number = 0;
private sheetId: string = '';
private range: string = '';
public async getSheetState(): Promise<GoogleSheetState> { public async getSheetState(): Promise<GoogleSheetState> {
const ret: GoogleSheetState = { const ret: GoogleSheetState = {
@@ -31,35 +34,31 @@ class sheet {
id: false, id: false,
worksheet: false, worksheet: false,
}; };
ret.auth = await this.authorized(); this.sheetId = '';
this.worksheetId = 0;
if (!sheet.client) {
return ret;
}
await this.loadToken();
ret.auth = await this.refreshToken();
if (ret.auth) { if (ret.auth) {
const settings = DataProvider.getGoogleSheet(); const settings = DataProvider.getGoogleSheet();
const x = await this.exist(settings.id, settings.worksheet); const x = await this.exist(settings.id, settings.worksheet);
if (x === true) { if (x === true) {
ret.id = true; ret.id = true;
this.sheetId = settings.id;
} else if (x !== false) { } else if (x !== false) {
ret.id = true; ret.id = true;
ret.worksheet = true; ret.worksheet = true;
this.sheetId = settings.id;
this.worksheetId = x.worksheetId;
this.range = x.range;
} }
} }
logger.info(LogOrigin.Server, `Sheet State: ${JSON.stringify(ret)}`); logger.info(LogOrigin.Server, `Sheet State: ${JSON.stringify(ret)}`);
return ret; return ret;
} }
/**
* checks the authorized state
* @returns {Promise<boolean>}
*/
public async authorized(): Promise<boolean> {
if (await this.loadToken()) {
if (await this.refreshToken()) {
return true;
}
} else {
return false;
}
}
/** /**
* test existance of sheet and workssheet and get is index * test existance of sheet and workssheet and get is index
* @param {string} sheetId - https://docs.google.com/spreadsheets/d/[[spreadsheetId]]/edit#gid=0 * @param {string} sheetId - https://docs.google.com/spreadsheets/d/[[spreadsheetId]]/edit#gid=0
@@ -67,7 +66,7 @@ class sheet {
* @returns {Promise<false | {worksheetId: number, range: string}>} - false if not found | true if sheetId existes | id of worksheet and rage of worksheet * @returns {Promise<false | {worksheetId: number, range: string}>} - false if not found | true if sheetId existes | id of worksheet and rage of worksheet
* @throws * @throws
*/ */
public async exist( private async exist(
sheetId: string, sheetId: string,
worksheet: string, worksheet: string,
): Promise<false | true | { worksheetId: number; range: string }> { ): Promise<false | true | { worksheetId: number; range: string }> {
@@ -90,32 +89,21 @@ class sheet {
return false; return false;
} }
public async push(sheetId: string, worksheet: string, options = defaultExcelImportMap) { public async push() {
if (!sheet.client) { const { auth, id, worksheet } = await this.getSheetState();
if (!(await this.authorized())) { if (!auth && !id && !worksheet) {
throw new Error(`Sheet not authorized`); throw new Error(`Sheet not authorized or incorrect ID or worksheet`);
}
} }
const sheetInfo = await this.exist(sheetId, worksheet);
if (!sheetInfo) {
throw new Error(`Sheet not dose not exits`);
} else if (sheetInfo === true) {
throw new Error(`Worksheet not dose not exits`);
}
if (!isExcelImportMap(options)) {
throw new Error('Got incorrect options to excel import', JSON.parse(options));
}
const rq = await sheets({ version: 'v4', auth: sheet.client }).spreadsheets.values.get({ const rq = await sheets({ version: 'v4', auth: sheet.client }).spreadsheets.values.get({
spreadsheetId: sheetId, spreadsheetId: this.sheetId,
valueRenderOption: 'FORMATTED_VALUE', valueRenderOption: 'FORMATTED_VALUE',
majorDimension: 'ROWS', majorDimension: 'ROWS',
range: sheetInfo.range, range: this.range,
}); });
if (rq.status === 200) { if (rq.status === 200) {
//TODO: projectMetadata //TODO: projectMetadata
const { rundownMetadata } = parseExcel(rq.data.values, options); const { rundownMetadata } = parseExcel(rq.data.values);
const rundown = DataProvider.getRundown(); const rundown = DataProvider.getRundown();
const titleRow = Object.values(rundownMetadata)[0]['row']; const titleRow = Object.values(rundownMetadata)[0]['row'];
@@ -129,13 +117,13 @@ class sheet {
dimension: 'ROWS', dimension: 'ROWS',
startIndex: titleRow + 1, startIndex: titleRow + 1,
endIndex: titleRow + 2, endIndex: titleRow + 2,
sheetId: sheetInfo.worksheetId, sheetId: this.worksheetId,
}, },
}, },
}); });
//and delete the rest //and delete the rest
updateRundown.push({ updateRundown.push({
deleteDimension: { range: { dimension: 'ROWS', startIndex: titleRow + 2, sheetId: sheetInfo.worksheetId } }, deleteDimension: { range: { dimension: 'ROWS', startIndex: titleRow + 2, sheetId: this.worksheetId } },
}); });
// insert the lenght of the rundown // insert the lenght of the rundown
updateRundown.push({ updateRundown.push({
@@ -145,7 +133,7 @@ class sheet {
dimension: 'ROWS', dimension: 'ROWS',
startIndex: titleRow + 1, startIndex: titleRow + 1,
endIndex: titleRow + rundown.length, endIndex: titleRow + rundown.length,
sheetId: sheetInfo.worksheetId, sheetId: this.worksheetId,
}, },
}, },
}); });
@@ -160,14 +148,14 @@ class sheet {
//update the corespunding row with event data //update the corespunding row with event data
rundown.forEach((entry, index) => rundown.forEach((entry, index) =>
updateRundown.push( updateRundown.push(
this.cellRequenstFromEvent(entry, index, sheetInfo.worksheetId, rundownMetadata, titleCol as number), this.cellRequenstFromEvent(entry, index, this.worksheetId, rundownMetadata, titleCol as number),
), ),
); );
const writeResponds = await sheets({ version: 'v4', auth: sheet.client }).spreadsheets.batchUpdate({ const writeResponds = await sheets({ version: 'v4', auth: sheet.client }).spreadsheets.batchUpdate({
spreadsheetId: sheetId, spreadsheetId: this.sheetId,
requestBody: { requestBody: {
includeSpreadsheetInResponse: false, includeSpreadsheetInResponse: false,
responseRanges: [sheetInfo.range], responseRanges: [this.range],
requests: updateRundown, requests: updateRundown,
}, },
}); });
@@ -185,42 +173,31 @@ class sheet {
/** /**
* `parse` a given sheet * `parse` a given sheet
* @param {string} sheetId - https://docs.google.com/spreadsheets/d/[[spreadsheetId]]/edit#gid=0 * @param {string} sheetId - https://docs.google.com/spreadsheets/d/[[spreadsheetId]]/edit#gid=0
* @param {string} worksheet - the name of the worksheet containg ontime data * @param {string} worksheetName - the name of the worksheet containg ontime data
* @param {ExcelImportOptions} options * @param {ExcelImportOptions} options
* @returns {Promise<Partial<ResponseOK>>} * @returns {Promise<Partial<ResponseOK>>}
* @throws * @throws
*/ */
public async pull(sheetId: string, worksheet: string, options = defaultExcelImportMap) { public async pull() {
if (!sheet.client) { const { auth, id, worksheet } = await this.getSheetState();
if (!(await this.authorized())) { if (!auth && !id && !worksheet) {
throw new Error(`Sheet not authorized`); throw new Error(`Sheet not authorized or incorrect ID or worksheet`);
}
} }
const res: Partial<ResponseOK> = {}; const res: Partial<ResponseOK> = {};
if (!isExcelImportMap(options)) {
throw new Error('Got incorrect options to excel import', JSON.parse(options));
}
const sheetInfo = await this.exist(sheetId, worksheet);
if (!sheetInfo) {
throw new Error(`Sheet not dose not exits`);
} else if (sheetInfo === true) {
throw new Error(`Worksheet not dose not exits`);
}
const rq = await sheets({ version: 'v4', auth: sheet.client }).spreadsheets.values.get({ const rq = await sheets({ version: 'v4', auth: sheet.client }).spreadsheets.values.get({
spreadsheetId: sheetId, spreadsheetId: this.sheetId,
valueRenderOption: 'FORMATTED_VALUE', valueRenderOption: 'FORMATTED_VALUE',
majorDimension: 'ROWS', majorDimension: 'ROWS',
range: sheetInfo.range, range: this.range,
}); });
if (rq.status === 200) { if (rq.status === 200) {
res.data = {}; res.data = {};
const dataFromSheet = parseExcel(rq.data.values, options); const dataFromSheet = parseExcel(rq.data.values);
res.data.rundown = parseRundown(dataFromSheet); res.data.rundown = parseRundown(dataFromSheet);
if (res.data.rundown.length < 1) { if (res.data.rundown.length < 1) {
throw new Error(`Could not find data to import in the worksheet ${options.worksheet}`); throw new Error(`Could not find data to import in the worksheet`);
} }
res.data.project = parseProject(dataFromSheet); res.data.project = parseProject(dataFromSheet);
res.data.userFields = parseUserFields(dataFromSheet); res.data.userFields = parseUserFields(dataFromSheet);
@@ -242,7 +219,7 @@ class sheet {
sheet.client = null; sheet.client = null;
sheet.authUrl = null; sheet.authUrl = null;
await writeFile(this.client_secret, JSON.stringify(secrets), 'utf-8'); await writeFile(this.client_secret, JSON.stringify(secrets), 'utf-8');
return await this.getSheetState(); return;
} }
/** /**