mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
65013331a3
Summary: - When importing into a Ref column, use lookupOne() formula for correct previews. - When selecting columns to import into a Ref column, now a Numeric column like 'Order' will produce two options: "Order" and "Order (as row ID)". - Fixes exports to correct the formatting of visible columns. This addresses multiple bugs: 1. Formatting wasn't used, e.g. a Ref showing a custom-formatted date was still presented as YYYY-MM-DD in CSVs. 2. Ref showing a Numeric column was formatted as if a row ID (e.g. `Table1[1.5]`), which is very wrong. - If importing into a table that doesn't have a primary view, don't switch page after import. Refactorings: - Generalize GenImporterView to be usable in more cases; removed near-duplicated logic from node side - Some other refactoring in importing code. - Fix field/column option selection in ValueParser - Add NUM() helper to turn integer-valued floats into ints, useful for "as row ID" lookups. Test Plan: Added test cases for imports into reference columns, updated Exports test fixtures. Reviewers: georgegevoian Reviewed By: georgegevoian Differential Revision: https://phab.getgrist.com/D3875
97 lines
3.2 KiB
TypeScript
97 lines
3.2 KiB
TypeScript
import {ApiError} from 'app/common/ApiError';
|
|
import {ActiveDoc} from 'app/server/lib/ActiveDoc';
|
|
import {DownloadOptions, ExportData, exportSection, exportTable, Filter} from 'app/server/lib/Export';
|
|
import log from 'app/server/lib/log';
|
|
import * as bluebird from 'bluebird';
|
|
import contentDisposition from 'content-disposition';
|
|
import csv from 'csv';
|
|
import * as express from 'express';
|
|
|
|
// promisify csv
|
|
bluebird.promisifyAll(csv);
|
|
|
|
/**
|
|
* Converts `activeDoc` to a CSV and sends the converted data through `res`.
|
|
*/
|
|
export async function downloadCSV(activeDoc: ActiveDoc, req: express.Request,
|
|
res: express.Response, options: DownloadOptions) {
|
|
log.info('Generating .csv file...');
|
|
const {filename, tableId, viewSectionId, filters, sortOrder} = options;
|
|
const data = viewSectionId ?
|
|
await makeCSVFromViewSection(activeDoc, viewSectionId, sortOrder, filters, req) :
|
|
await makeCSVFromTable(activeDoc, tableId, req);
|
|
res.set('Content-Type', 'text/csv');
|
|
res.setHeader('Content-Disposition', contentDisposition(filename + '.csv'));
|
|
res.send(data);
|
|
}
|
|
|
|
/**
|
|
* Returns a csv stream of a view section that can be transformed or parsed.
|
|
*
|
|
* See https://github.com/wdavidw/node-csv for API details.
|
|
*
|
|
* @param {Object} activeDoc - the activeDoc that the table being converted belongs to.
|
|
* @param {Integer} viewSectionId - id of the viewsection to export.
|
|
* @param {Integer[]} activeSortOrder (optional) - overriding sort order.
|
|
* @param {Filter[]} filters (optional) - filters defined from ui.
|
|
* @return {Promise<string>} Promise for the resulting CSV.
|
|
*/
|
|
export async function makeCSVFromViewSection(
|
|
activeDoc: ActiveDoc,
|
|
viewSectionId: number,
|
|
sortOrder: number[],
|
|
filters: Filter[],
|
|
req: express.Request) {
|
|
|
|
const data = await exportSection(activeDoc, viewSectionId, sortOrder, filters, req);
|
|
const file = convertToCsv(data);
|
|
return file;
|
|
}
|
|
|
|
/**
|
|
* Returns a csv stream of a table that can be transformed or parsed.
|
|
*
|
|
* @param {Object} activeDoc - the activeDoc that the table being converted belongs to.
|
|
* @param {Integer} tableId - id of the table to export.
|
|
* @return {Promise<string>} Promise for the resulting CSV.
|
|
*/
|
|
export async function makeCSVFromTable(
|
|
activeDoc: ActiveDoc,
|
|
tableId: string,
|
|
req: express.Request) {
|
|
|
|
if (!activeDoc.docData) {
|
|
throw new Error('No docData in active document');
|
|
}
|
|
|
|
// Look up the table to make a CSV from.
|
|
const tables = activeDoc.docData.getMetaTable('_grist_Tables');
|
|
const tableRef = tables.findRow('tableId', tableId);
|
|
|
|
if (tableRef === 0) {
|
|
throw new ApiError(`Table ${tableId} not found.`, 404);
|
|
}
|
|
|
|
const data = await exportTable(activeDoc, tableRef, req);
|
|
const file = convertToCsv(data);
|
|
return file;
|
|
}
|
|
|
|
function convertToCsv({
|
|
rowIds,
|
|
access,
|
|
columns: viewColumns,
|
|
docSettings
|
|
}: ExportData) {
|
|
|
|
// create formatters for columns
|
|
const formatters = viewColumns.map(col => col.formatter);
|
|
// Arrange the data into a row-indexed matrix, starting with column headers.
|
|
const csvMatrix = [viewColumns.map(col => col.label)];
|
|
// populate all the rows with values as strings
|
|
rowIds.forEach(row => {
|
|
csvMatrix.push(access.map((getter, c) => formatters[c].formatAny(getter(row))));
|
|
});
|
|
return csv.stringifyAsync(csvMatrix);
|
|
}
|