push to sheet

This commit is contained in:
arc-alex
2023-11-19 17:04:14 +01:00
parent a7c9ce876c
commit 543e365f82
5 changed files with 162 additions and 41 deletions
+11
View File
@@ -265,3 +265,14 @@ export const postPreviewSheet = async (sheetId: string, worksheet: string, optio
});
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,
});
return response.data.data;
};
@@ -17,6 +17,7 @@ import {
postPreviewSheet,
uploadSheetClientFile,
patchData,
postPushSheet,
} from '../../../common/api/ontimeApi';
import { OntimeRundown, ProjectData, UserFields } from 'ontime-types';
@@ -88,6 +89,12 @@ export default function SheetsModal(props: SheetsModalProps) {
});
};
const handlePushData = () => {
postPushSheet(sheetid.current?.value ?? '', worksheet.current?.value ?? '').then((data) => {
console.log(data);
});
};
const handleFinalise = async () => {
// this step is currently only used for excel files, after preview
if (rundown && userFields && project) {
@@ -181,6 +188,9 @@ export default function SheetsModal(props: SheetsModalProps) {
<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>
</>
)}
@@ -405,6 +405,19 @@ export async function previewSheet(req, res) {
}
}
/**
* downloads and parses an sheet
* @returns parsed result
*/
export async function pushSheet(req, res) {
try {
const data = await Sheet.push(req.body.sheetid, req.body.worksheet);
res.status(200).send('ok');
} catch (error) {
res.status(500).send({ message: error.toString() });
}
}
/**
* uploads Client secrets file
* @returns parsed result
+5 -1
View File
@@ -23,6 +23,7 @@ import {
sheetAuthState,
sheetClientFile,
previewSheet,
pushSheet,
} from '../controllers/ontimeController.js';
import {
@@ -103,4 +104,7 @@ router.get('/sheet-authstatus', sheetAuthState);
router.get('/sheet-authurl', sheetAuthUrl);
// create route between controller and '/ontime/preview-sheet' endpoint
router.post('/sheet-preview', validateSheetPreview, previewSheet);
router.post('/sheet-preview', validateSheetPreview, previewSheet);
// create route between controller and '/ontime/preview-sheet' endpoint
router.post('/sheet-push', pushSheet);
+123 -40
View File
@@ -6,10 +6,11 @@ import { URL } from 'url';
import { logger } from '../classes/Logger.js';
import { getAppDataPath } from '../setup.js';
import { DatabaseModel, LogOrigin, OntimeRundownEntry, isOntimeEvent } from 'ontime-types';
import { ExcelImportOptions, isExcelImportMap, defaultExcelImportMap } from 'ontime-utils';
import { ExcelImportOptions, isExcelImportMap, defaultExcelImportMap, millisToString } from 'ontime-utils';
import { parseExcel } from './parser.js';
import { parseProject, parseRundown, parseUserFields } from './parserFunctions.js';
import { ensureDirectory } from './fileManagement.js';
import { DataProvider } from '../classes/data-provider/DataProvider.js';
type ResponseOK = {
data: Partial<DatabaseModel>;
@@ -47,6 +48,71 @@ class sheet {
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,
valueRenderOption: 'FORMATTED_VALUE',
majorDimension: 'ROWS',
range: worksheet + '!A:Z', //FIXME: this is an abitrary range
});
if (rq.status === 200) {
const { projectMetadata, rundownMetadata } = parseExcel(rq.data.values, options);
const rundown = DataProvider.getRundown();
const titleRow = Object.values(rundownMetadata)[0]['row'];
const updateRundown = Array<sheets_v4.Schema$Request>();
// we can't delete the last unflozzen row so we create an empty one
updateRundown.push({
insertDimension: {
inheritFromBefore: false,
range: { dimension: 'ROWS', startIndex: titleRow + 1, endIndex: titleRow + 2, sheetId: 0 },
},
});
//and delete the rest
updateRundown.push({
deleteDimension: { range: { dimension: 'ROWS', startIndex: titleRow + 2, sheetId: 0 } },
});
// insert the lenght of the rundown
updateRundown.push({
insertDimension: {
inheritFromBefore: false,
range: {
dimension: 'ROWS',
startIndex: titleRow + 1,
endIndex: titleRow + rundown.length,
sheetId: 0,
},
},
});
//TODO: this is a mess
const titleCol = Object.values(rundownMetadata)
// .filter(([key, value]) => value !== undefined)
.reduce((accumulator: number, val) => Math.min(accumulator, val['col']), Number.MAX_VALUE);
if (titleCol == Number.MAX_VALUE) {
throw new Error(`could not finde starting title column`);
}
//update the corespunding row with event data
rundown.forEach((entry, index) =>
updateRundown.push(this.cellRequenstFromEvent(entry, index, rundownMetadata, titleCol as number)),
);
const writeResponds = await sheets({ version: 'v4', auth: sheet.client }).spreadsheets.batchUpdate({
spreadsheetId: sheetId,
requestBody: {
includeSpreadsheetInResponse: false,
responseRanges: [worksheet + '!A:Z'], //FIXME:
requests: updateRundown,
},
});
if (writeResponds.status == 200) {
logger.info(LogOrigin.Server, `Sheet write: ${writeResponds.statusText}`);
} else {
throw new Error(`Sheet write faild: ${writeResponds.statusText}`);
}
} else {
throw new Error(`Sheet read faild: ${rq.statusText}`);
}
}
/**
@@ -149,51 +215,68 @@ class sheet {
}
/**
* @param index - the index of the event in Sheet dimentions
* @param index - the index of the event in rundown dimentions
*/
private cellRequenstFromEvent(event: OntimeRundownEntry, index: number): sheets_v4.Schema$Request['updateCells'] {
const titelCol = 1
// index += titelRow;
private cellRequenstFromEvent(
event: OntimeRundownEntry,
index: number,
metadata,
titleCol: number,
): sheets_v4.Schema$Request {
let r: sheets_v4.Schema$CellData[] = [];
Object.entries(this.metadata['rundown'])
const tmp = Object.entries(metadata)
.filter(([key, value]) => value !== undefined)
.sort(([aKey, a], [bKey, b]) => a['col'] - b['col'])
.forEach(([key, value]) => {
if (isOntimeEvent(event)) {
if (key === 'colour') {
r.push({
userEnteredValue: { stringValue: event.colour },
userEnteredFormat: { backgroundColor: hexToRgb(event?.colour ?? null) },
});
} else if (typeof event[key] === 'number') {
r.push({
userEnteredValue: { stringValue: millisToString(event[key], true) },
});
} else if (typeof event[key] === 'string') {
r.push({
userEnteredValue: { stringValue: event[key] },
});
} else if (typeof event[key] === 'boolean') {
r.push({
userEnteredValue: { stringValue: event[key] ? 'x' : '' },
});
} else {
r.push({});
}
.sort(([aKey, a], [bKey, b]) => a['col'] - b['col']);
tmp.forEach(([key, value], index, arr) => {
if (index != 0) {
if (arr[index - 1][1]['col'] + 1 < value['col']) {
console.log('missing', key, value['col'], arr[index - 1][1]['col']);
arr.splice(index, 0, ['blank', { col: arr[index - 1][1]['col'] + 1 }]);
}
});
}
});
console.log(tmp);
tmp.forEach(([key, value]) => {
if (isOntimeEvent(event)) {
if (key === 'blank') {
r.push({});
} else if (key === 'colour') {
r.push({
userEnteredValue: { stringValue: event.colour },
});
} else if (typeof event[key] === 'number') {
r.push({
userEnteredValue: { stringValue: millisToString(event[key], true) },
});
} else if (typeof event[key] === 'string') {
r.push({
userEnteredValue: { stringValue: event[key] },
});
} else if (typeof event[key] === 'boolean') {
r.push({
userEnteredValue: { stringValue: event[key] ? 'x' : '' },
});
} else {
r.push({});
}
}
});
return {
start: {
sheetId: 0, //FIXME: get from smalest medata col
rowIndex: index,
columnIndex: titelCol,
},
fields: 'userEnteredValue,userEnteredFormat.backgroundColor',
rows: [
{
values: r,
updateCells: {
start: {
sheetId: 0, //FIXME:
rowIndex: index + Object.values(metadata)[0]['row'] + 1,
columnIndex: titleCol,
},
],
fields: 'userEnteredValue',
rows: [
{
values: r,
},
],
},
};
}