feat: excel import (#527)

* refactor: simplify excel import

* chore: add external deepmerge utility

* chore: create import map utilities

* chore: increase size limits on uploads

* style: small presentation tweaks

* feat: resolve times on excel import, refs #508
This commit is contained in:
Carlos Valente
2023-11-11 14:28:43 +01:00
committed by GitHub
parent 0ce3449083
commit 45fe669f0a
61 changed files with 1905 additions and 685 deletions
+34 -5
View File
@@ -2,18 +2,33 @@ import axios, { AxiosError } from 'axios';
import { LogLevel } from 'ontime-types';
import { generateId, millisToString } from 'ontime-utils';
import { ontimeQueryClient } from '../queryClient';
import { addLog } from '../stores/logger';
import { nowInMillis } from '../utils/time';
export function logAxiosError(prepend: string, error: unknown) {
let message;
export function maybeAxiosError(error: unknown) {
if (axios.isAxiosError(error)) {
const statusText = (error as AxiosError).response?.statusText ?? '';
const data = (error as AxiosError).response?.data ?? '';
message = `${prepend} ${statusText}: ${data}`;
let data = (error as AxiosError).response?.data ?? '';
if (typeof data === 'object') {
// TODO: use error instead, when migrated
if ('message' in data) {
data = JSON.stringify(data.message);
} else {
data = JSON.stringify(data);
}
}
return `${statusText}: ${data}`;
} else {
message = `${prepend}: ${error}`;
if (typeof error !== 'string') {
return JSON.stringify(error);
}
return error;
}
}
export function logAxiosError(prepend: string, error: unknown) {
const message = `${prepend}: ${maybeAxiosError(error)}`;
addLog({
id: generateId(),
@@ -23,3 +38,17 @@ export function logAxiosError(prepend: string, error: unknown) {
text: message,
});
}
/**
* Utility function invalidates react-query caches
*/
export async function invalidateAllCaches() {
await ontimeQueryClient.invalidateQueries(['project']);
await ontimeQueryClient.invalidateQueries(['aliases']);
await ontimeQueryClient.invalidateQueries(['userFields']);
await ontimeQueryClient.invalidateQueries(['rundown']);
await ontimeQueryClient.invalidateQueries(['appinfo']);
await ontimeQueryClient.invalidateQueries(['oscSettings']);
await ontimeQueryClient.invalidateQueries(['appSettings']);
await ontimeQueryClient.invalidateQueries(['viewSettings']);
}
+67 -7
View File
@@ -1,5 +1,16 @@
import axios from 'axios';
import { Alias, OSCSettings, OscSubscription, ProjectData, Settings, UserFields, ViewSettings } from 'ontime-types';
import axios, { AxiosResponse } from 'axios';
import {
Alias,
DatabaseModel,
OntimeRundown,
OSCSettings,
OscSubscription,
ProjectData,
Settings,
UserFields,
ViewSettings,
} from 'ontime-types';
import { ExcelImportMap } from 'ontime-utils';
import { apiRepoLatest } from '../../externals';
import { InfoType } from '../models/Info';
@@ -123,17 +134,25 @@ export const downloadRundown = () => {
return fileDownload(ontimeURL, { name: 'rundown', type: 'json' }, { type: 'application/json;charset=utf-8;' });
};
// TODO: should this be extracted to shared code?
export type ProjectFileImportOptions = {
onlyRundown: boolean;
};
/**
* @description HTTP request to upload events db
* @return {Promise}
*/
type UploadDataOptions = {
onlyRundown?: boolean;
};
export const uploadData = async (file: File, setProgress: (value: number) => void, options?: UploadDataOptions) => {
export const uploadProjectFile = async (
file: File,
setProgress: (value: number) => void,
options?: Partial<ProjectFileImportOptions>,
) => {
const formData = new FormData();
formData.append('userFile', file);
const onlyRundown = options?.onlyRundown || 'false';
const onlyRundown = Boolean(options?.onlyRundown);
await axios
.post(`${ontimeURL}/db?onlyRundown=${onlyRundown}`, formData, {
headers: {
@@ -147,6 +166,47 @@ export const uploadData = async (file: File, setProgress: (value: number) => voi
.then((response) => response.data.id);
};
/**
* @description Make patch changes to the objects in the db
* @return {Promise}
*/
export async function patchData(patchDb: Partial<DatabaseModel>) {
const response = await axios.patch(`${ontimeURL}/db`, patchDb);
return response;
}
type PostPreviewExcelResponse = {
rundown: OntimeRundown;
project: ProjectData;
userFields: UserFields;
};
/**
* @description Make patch changes to the objects in the db
* @return {Promise} - returns parsed rundown and userfields
*/
export async function postPreviewExcel(file: File, setProgress: (value: number) => void, options?: ExcelImportMap) {
const formData = new FormData();
formData.append('userFile', file);
formData.append('options', JSON.stringify(options));
const response: AxiosResponse<PostPreviewExcelResponse> = await axios.post(
`${ontimeURL}/preview-spreadsheet`,
formData,
{
headers: {
'Content-Type': 'multipart/form-data',
},
onUploadProgress: (progressEvent) => {
const complete = progressEvent?.total ? Math.round((progressEvent.loaded * 100) / progressEvent.total) : 0;
setProgress(complete);
},
},
);
return response;
}
export type HasUpdate = {
url: string;
version: string;