mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-07 00:13:53 +00:00
push to sheet
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
@@ -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,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user