refactor: remove project CSV export option

This commit is contained in:
Carlos Valente
2025-10-05 07:31:43 +02:00
committed by Alex Christoffer Rasmussen
parent ccf1ddf2fd
commit 846eaf569d
6 changed files with 3 additions and 234 deletions
-24
View File
@@ -1,9 +1,6 @@
import axios, { AxiosResponse } from 'axios';
import { DatabaseModel, MessageResponse, ProjectData, ProjectFileListResponse, QuickStartData } from 'ontime-types';
import { makeTable } from '../../views/cuesheet/cuesheet.utils';
import { aggregateRundowns, makeCSVFromArrayOfArrays } from '../utils/csv';
import { apiEntryUrl } from './constants';
import { createBlob, downloadBlob } from './utils';
@@ -33,27 +30,6 @@ export async function downloadProject(fileName: string) {
}
}
/**
* Request download of the current rundown as a CSV file
* @param fileName
*/
export async function downloadCSV(fileName: string = 'rundown') {
try {
const { data, name } = await fileDownload(fileName);
const { project, rundowns, customFields } = data;
const flatRundowns = aggregateRundowns(rundowns);
const sheetData = makeTable(project, flatRundowns, customFields);
const fileContent = makeCSVFromArrayOfArrays(sheetData);
const blob = createBlob(fileContent, 'text/csv;charset=utf-8;');
downloadBlob(blob, `${name}.csv`);
} catch (error) {
console.error(error);
}
}
/**
* HTTP request to upload project file
*/
@@ -1,6 +1,4 @@
import { OntimeEntry, ProjectRundowns, Rundown } from 'ontime-types';
import { aggregateRundowns, makeCSVFromArrayOfArrays } from '../csv';
import { makeCSVFromArrayOfArrays } from '../csv';
describe('makeCSVFromArrayOfArrays()', () => {
it('joins an array of arrays with commas and newlines', () => {
@@ -13,34 +11,3 @@ after newline,after comma
`);
});
});
describe('aggregateRundowns()', () => {
it('flattens an object of rundowns into a single array', () => {
const rundowns = {
first: {
id: '',
title: '',
revision: 0,
order: ['1', '2'],
flatOrder: ['1', '2'],
entries: {
'1': { id: '1' } as OntimeEntry,
'2': { id: '2' } as OntimeEntry,
},
},
second: {
id: '',
title: '',
revision: 0,
order: ['3', '4'],
flatOrder: ['3', '4'],
entries: {
'3': { id: '3' } as OntimeEntry,
'4': { id: '4' } as OntimeEntry,
},
} as Rundown,
} as ProjectRundowns;
expect(aggregateRundowns(rundowns)).toStrictEqual([{ id: '1' }, { id: '2' }, { id: '3' }, { id: '4' }]);
});
});
-23
View File
@@ -1,5 +1,4 @@
import { stringify } from 'csv-stringify/browser/esm/sync';
import { OntimeEntry, ProjectRundowns } from 'ontime-types';
/**
* Converts an array of arrays to a CSV file
@@ -7,25 +6,3 @@ import { OntimeEntry, ProjectRundowns } from 'ontime-types';
export function makeCSVFromArrayOfArrays(arrayOfArrays: string[][]): string {
return stringify(arrayOfArrays);
}
/**
* Receives an object of rundowns, and flattens them into a single, linear rundown
* Used for CSV export
*/
export function aggregateRundowns(rundowns: ProjectRundowns): OntimeEntry[] {
const rundownKeys = Object.keys(rundowns);
if (rundownKeys.length === 0) return [];
const flatRundown: OntimeEntry[] = [];
for (const key of rundownKeys) {
const { order, entries } = rundowns[key];
for (let i = 0; i < order.length; i++) {
const entryId = order[i];
const entry = entries[entryId];
flatRundown.push(entry);
}
}
return flatRundown;
}
@@ -10,7 +10,6 @@ import {
import {
deleteProject,
downloadCSV,
downloadProject,
duplicateProject,
loadProject,
@@ -187,10 +186,6 @@ function ActionMenu(props: ActionMenuProps) {
await downloadProject(filename);
};
const handleExportCSV = async () => {
await downloadCSV(filename);
};
return (
<DropdownMenu
render={<IconButton variant='ghosted-white' />}
@@ -213,7 +208,6 @@ function ActionMenu(props: ActionMenuProps) {
{ type: 'item', icon: IoPencilOutline, label: 'Rename', onClick: handleRename },
{ type: 'item', icon: IoCopyOutline, label: 'Duplicate', onClick: handleDuplicate },
{ type: 'item', icon: IoDocumentOutline, label: 'Download', onClick: handleDownload },
{ type: 'item', icon: IoDocumentOutline, label: 'Export CSV Rundown', onClick: handleExportCSV },
{ type: 'divider' },
{ type: 'item', icon: IoTrash, label: 'Delete', onClick: () => onDelete(filename), disabled: current },
]}
@@ -1,6 +1,4 @@
import { ProjectData } from 'ontime-types';
import { makeTable, parseField } from '../cuesheet.utils';
import { parseField } from '../cuesheet.utils';
describe('parseField()', () => {
it('returns a string from given millis on timeStart, TimeEnd and duration', () => {
@@ -32,69 +30,3 @@ describe('parseField()', () => {
});
});
});
describe('makeTable()', () => {
it('returns array of arrays with given fields', () => {
const headerData = {
title: 'test title',
description: 'test description',
logo: 'test logo',
};
const tableData = [
{
title: 'test title 1',
timeStart: 0,
timeEnd: 0,
skip: true,
lighting: { value: 'test lighting' },
sound: { value: 'test sound' },
},
];
const customFields = {
lighting: { label: 'test' },
};
// @ts-expect-error -- testing user data with missing fields
const table = makeTable(headerData as ProjectData, tableData, customFields);
expect(table).not.toContain('test logo');
expect(table).toMatchInlineSnapshot(`
[
[
"Ontime · Rundown export",
],
[
"Project title: test title",
],
[
"Project description: test description",
],
[
"Time Start",
"Time End",
"Duration",
"ID",
"Colour",
"Cue",
"Title",
"Note",
"Skip?",
"lighting",
"Type",
],
[
"00:00:00",
"00:00:00",
"",
"",
"",
"",
"test title 1",
"",
"x",
"",
"",
],
]
`);
});
});
@@ -1,12 +1,4 @@
import {
CustomFields,
isOntimeDelay,
isOntimeEvent,
MaybeNumber,
OntimeEntry,
OntimeEntryCommonKeys,
ProjectData,
} from 'ontime-types';
import { CustomFields, MaybeNumber, OntimeEntryCommonKeys } from 'ontime-types';
import { millisToString } from 'ontime-utils';
type CsvHeaderKey = OntimeEntryCommonKeys | keyof CustomFields;
@@ -29,72 +21,3 @@ export const parseField = (field: CsvHeaderKey, data: unknown): string => {
return String(data ?? '');
};
/**
* @description Creates an array of arrays usable by xlsx for export
*/
export const makeTable = (headerData: ProjectData, rundown: OntimeEntry[], customFields: CustomFields): string[][] => {
// create metadata header row
const data = [['Ontime · Rundown export']];
if (headerData.title) data.push([`Project title: ${headerData.title}`]);
if (headerData.description) data.push([`Project description: ${headerData.description}`]);
const customFieldKeys = Object.keys(customFields).map((key) => `custom-${key}`);
const customFieldLabels = Object.keys(customFields);
// we chose not to expose internals of the application
const fieldOrder: CsvHeaderKey[] = [
'timeStart',
'timeEnd',
'duration',
'id',
'colour',
'cue',
'title',
'note',
'skip',
...customFieldKeys,
'type',
];
const fieldTitles = [
'Time Start',
'Time End',
'Duration',
'ID',
'Colour',
'Cue',
'Title',
'Note',
'Skip?',
...customFieldLabels,
'Type',
];
// add header row to data
data.push(fieldTitles);
rundown.forEach((entry) => {
if (isOntimeDelay(entry)) return;
const row: string[] = [];
fieldOrder.forEach((field) => {
if (isOntimeEvent(entry)) {
// for custom fields, we need to extract the value from the custom object
if (field.startsWith('custom-')) {
const fieldLabel = field.split('custom-')[1];
const value = entry.custom[fieldLabel];
row.push(parseField(fieldLabel, value));
} else {
// @ts-expect-error -- it is ok, we will just not have the data for other fields
row.push(parseField(field, entry[field]));
}
return;
}
// @ts-expect-error -- it is ok, we will just not have the data for other fields
row.push(parseField(field, entry[field]));
});
data.push(row);
});
return data;
};