mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-07 08:23:55 +00:00
clean up sheet flow
This commit is contained in:
@@ -251,21 +251,13 @@ export const getSheetsAuthUrl = async () => {
|
||||
return res.data;
|
||||
};
|
||||
|
||||
export const postPreviewSheet = async (sheetId: string, worksheet: string, options?: ExcelImportMap) => {
|
||||
const response = await axios.post(`${ontimeURL}/sheet-preview`, {
|
||||
sheetid: sheetId,
|
||||
worksheet: worksheet,
|
||||
options: options,
|
||||
});
|
||||
export const postPreviewSheet = async () => {
|
||||
const response = await axios.post(`${ontimeURL}/sheet-preview`);
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
export const postPushSheet = async (sheetId: string, worksheet: string, options?: ExcelImportMap) => {
|
||||
const response = await axios.post(`${ontimeURL}/sheet-push`, {
|
||||
sheetid: sheetId,
|
||||
worksheet: worksheet,
|
||||
options: options,
|
||||
});
|
||||
export const postPushSheet = async () => {
|
||||
const response = await axios.post(`${ontimeURL}/sheet-push`);
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
import { IoCheckmarkCircleOutline } from '@react-icons/all-files/io5/IoCheckmarkCircleOutline';
|
||||
import { IoCloseCircleOutline } from '@react-icons/all-files/io5/IoCloseCircleOutline';
|
||||
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 { maybeAxiosError } from '../../../common/api/apiUtils';
|
||||
@@ -56,19 +56,19 @@ export default function SheetsModal(props: SheetsModalProps) {
|
||||
setRundown(null);
|
||||
setProject(null);
|
||||
setUserFields(null);
|
||||
// setSheetState({ auth: false, id: false, worksheet: false });
|
||||
onClose();
|
||||
};
|
||||
const handleClick = () => {
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
|
||||
const handleFile = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const handleFile = async (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const selectedFile = event?.target?.files?.[0];
|
||||
if (!selectedFile) {
|
||||
return;
|
||||
} else {
|
||||
uploadSheetClientFile(selectedFile);
|
||||
await uploadSheetClientFile(selectedFile);
|
||||
_onChange();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -112,7 +112,7 @@ export default function SheetsModal(props: SheetsModalProps) {
|
||||
};
|
||||
|
||||
const handlePullData = () => {
|
||||
postPreviewSheet(sheetid.current?.value ?? '', worksheet.current?.value ?? '').then((data) => {
|
||||
postPreviewSheet().then((data) => {
|
||||
setProject(data.project);
|
||||
setRundown(data.rundown);
|
||||
setUserFields(data.userFields);
|
||||
@@ -120,7 +120,7 @@ export default function SheetsModal(props: SheetsModalProps) {
|
||||
};
|
||||
|
||||
const handlePushData = () => {
|
||||
postPushSheet(sheetid.current?.value ?? '', worksheet.current?.value ?? '').then((data) => {
|
||||
postPushSheet().then((data) => {
|
||||
console.log(data);
|
||||
});
|
||||
};
|
||||
@@ -218,18 +218,20 @@ export default function SheetsModal(props: SheetsModalProps) {
|
||||
/>
|
||||
{sheetState.worksheet ? <IoCheckmarkCircleOutline /> : <IoCloseCircleOutline />}
|
||||
</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>
|
||||
<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>
|
||||
{!rundown && (
|
||||
<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) {
|
||||
try {
|
||||
const data = await Sheet.pull(req.body.sheetid, req.body.worksheet);
|
||||
const data = await Sheet.pull();
|
||||
res.status(200).send(data);
|
||||
} catch (error) {
|
||||
res.status(500).send({ message: error.toString() });
|
||||
@@ -436,7 +436,7 @@ export async function previewSheet(req, res) {
|
||||
*/
|
||||
export async function pushSheet(req, res) {
|
||||
try {
|
||||
await Sheet.push(req.body.sheetid, req.body.worksheet);
|
||||
await Sheet.push();
|
||||
res.status(200).send('ok');
|
||||
} catch (error) {
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
postViewSettings,
|
||||
previewExcel,
|
||||
sheetAuthUrl,
|
||||
sheetAuthState,
|
||||
uploadGoogleSheetClientFile,
|
||||
previewSheet,
|
||||
pushSheet,
|
||||
@@ -101,9 +100,6 @@ router.post('/new', projectSanitiser, postNew);
|
||||
// create route between controller and '/ontime/sheet-client' endpoint
|
||||
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
|
||||
router.get('/sheet-authurl', sheetAuthUrl);
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
UserFields,
|
||||
EndAction,
|
||||
TimerType,
|
||||
GoogleSheet,
|
||||
} from 'ontime-types';
|
||||
|
||||
import fs from 'fs';
|
||||
@@ -370,6 +369,7 @@ export const parseJson = async (jsonData): Promise<DatabaseModel | null> => {
|
||||
returnData.osc = parseOsc(jsonData) ?? dbModel.osc;
|
||||
// Import HTTP settings if any
|
||||
// returnData.http = parseHttp(jsonData, enforce);
|
||||
// Import GoogleSheet settings if any
|
||||
returnData.googleSheet = parseGoogleSheet(jsonData, true);
|
||||
|
||||
return returnData as DatabaseModel;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { URL } from 'url';
|
||||
import { logger } from '../classes/Logger.js';
|
||||
import { getAppDataPath } from '../setup.js';
|
||||
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 { parseProject, parseRundown, parseUserFields } from './parserFunctions.js';
|
||||
import { ensureDirectory } from './fileManagement.js';
|
||||
@@ -24,6 +24,9 @@ class sheet {
|
||||
private readonly client_secret = this.sheetsFolder + '/client_secret.json';
|
||||
private readonly token = this.sheetsFolder + '/token.json';
|
||||
private static authUrl: null | string = null;
|
||||
private worksheetId: number = 0;
|
||||
private sheetId: string = '';
|
||||
private range: string = '';
|
||||
|
||||
public async getSheetState(): Promise<GoogleSheetState> {
|
||||
const ret: GoogleSheetState = {
|
||||
@@ -31,35 +34,31 @@ class sheet {
|
||||
id: 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) {
|
||||
const settings = DataProvider.getGoogleSheet();
|
||||
const x = await this.exist(settings.id, settings.worksheet);
|
||||
if (x === true) {
|
||||
ret.id = true;
|
||||
this.sheetId = settings.id;
|
||||
} else if (x !== false) {
|
||||
ret.id = 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)}`);
|
||||
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
|
||||
* @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
|
||||
* @throws
|
||||
*/
|
||||
public async exist(
|
||||
private async exist(
|
||||
sheetId: string,
|
||||
worksheet: string,
|
||||
): Promise<false | true | { worksheetId: number; range: string }> {
|
||||
@@ -90,32 +89,21 @@ class sheet {
|
||||
return false;
|
||||
}
|
||||
|
||||
public async push(sheetId: string, worksheet: string, options = defaultExcelImportMap) {
|
||||
if (!sheet.client) {
|
||||
if (!(await this.authorized())) {
|
||||
throw new Error(`Sheet not authorized`);
|
||||
}
|
||||
public async push() {
|
||||
const { auth, id, worksheet } = await this.getSheetState();
|
||||
if (!auth && !id && !worksheet) {
|
||||
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({
|
||||
spreadsheetId: sheetId,
|
||||
spreadsheetId: this.sheetId,
|
||||
valueRenderOption: 'FORMATTED_VALUE',
|
||||
majorDimension: 'ROWS',
|
||||
range: sheetInfo.range,
|
||||
range: this.range,
|
||||
});
|
||||
if (rq.status === 200) {
|
||||
//TODO: projectMetadata
|
||||
const { rundownMetadata } = parseExcel(rq.data.values, options);
|
||||
const { rundownMetadata } = parseExcel(rq.data.values);
|
||||
const rundown = DataProvider.getRundown();
|
||||
const titleRow = Object.values(rundownMetadata)[0]['row'];
|
||||
|
||||
@@ -129,13 +117,13 @@ class sheet {
|
||||
dimension: 'ROWS',
|
||||
startIndex: titleRow + 1,
|
||||
endIndex: titleRow + 2,
|
||||
sheetId: sheetInfo.worksheetId,
|
||||
sheetId: this.worksheetId,
|
||||
},
|
||||
},
|
||||
});
|
||||
//and delete the rest
|
||||
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
|
||||
updateRundown.push({
|
||||
@@ -145,7 +133,7 @@ class sheet {
|
||||
dimension: 'ROWS',
|
||||
startIndex: titleRow + 1,
|
||||
endIndex: titleRow + rundown.length,
|
||||
sheetId: sheetInfo.worksheetId,
|
||||
sheetId: this.worksheetId,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -160,14 +148,14 @@ class sheet {
|
||||
//update the corespunding row with event data
|
||||
rundown.forEach((entry, index) =>
|
||||
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({
|
||||
spreadsheetId: sheetId,
|
||||
spreadsheetId: this.sheetId,
|
||||
requestBody: {
|
||||
includeSpreadsheetInResponse: false,
|
||||
responseRanges: [sheetInfo.range],
|
||||
responseRanges: [this.range],
|
||||
requests: updateRundown,
|
||||
},
|
||||
});
|
||||
@@ -185,42 +173,31 @@ class sheet {
|
||||
/**
|
||||
* `parse` a given sheet
|
||||
* @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
|
||||
* @returns {Promise<Partial<ResponseOK>>}
|
||||
* @throws
|
||||
*/
|
||||
public async pull(sheetId: string, worksheet: string, options = defaultExcelImportMap) {
|
||||
if (!sheet.client) {
|
||||
if (!(await this.authorized())) {
|
||||
throw new Error(`Sheet not authorized`);
|
||||
}
|
||||
public async pull() {
|
||||
const { auth, id, worksheet } = await this.getSheetState();
|
||||
if (!auth && !id && !worksheet) {
|
||||
throw new Error(`Sheet not authorized or incorrect ID or worksheet`);
|
||||
}
|
||||
|
||||
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({
|
||||
spreadsheetId: sheetId,
|
||||
spreadsheetId: this.sheetId,
|
||||
valueRenderOption: 'FORMATTED_VALUE',
|
||||
majorDimension: 'ROWS',
|
||||
range: sheetInfo.range,
|
||||
range: this.range,
|
||||
});
|
||||
if (rq.status === 200) {
|
||||
res.data = {};
|
||||
const dataFromSheet = parseExcel(rq.data.values, options);
|
||||
const dataFromSheet = parseExcel(rq.data.values);
|
||||
res.data.rundown = parseRundown(dataFromSheet);
|
||||
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.userFields = parseUserFields(dataFromSheet);
|
||||
@@ -242,7 +219,7 @@ class sheet {
|
||||
sheet.client = null;
|
||||
sheet.authUrl = null;
|
||||
await writeFile(this.client_secret, JSON.stringify(secrets), 'utf-8');
|
||||
return await this.getSheetState();
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user