From de315584e7319696077f71fa1824701cb4be23ec Mon Sep 17 00:00:00 2001 From: Todd Kazakov Date: Tue, 12 Dec 2023 14:11:21 +0200 Subject: [PATCH 1/2] Use exceljs fork --- index.js | 137 ++++++++++++++++++++++++++------------------------- package.json | 2 +- yarn.lock | 32 ++++++------ 3 files changed, 86 insertions(+), 85 deletions(-) diff --git a/index.js b/index.js index e365a04..172541d 100644 --- a/index.js +++ b/index.js @@ -9,7 +9,7 @@ import program from 'commander'; import JSONStream from 'JSONStream'; import es from 'event-stream'; import flatten from 'flat'; -import Excel from 'exceljs'; +import Excel from '@zlooun/exceljs'; import * as CSV from 'csv-string'; import { fileURLToPath } from 'url'; import process from 'process'; @@ -273,7 +273,7 @@ export default class TidepoolDataTools { return jsonStream; } - static xlsxStreamWriter(outStream, streamConfig = { bgUnits: 'mmol/L' }) { + static async xlsxStreamWriter(outStream, streamConfig = { bgUnits: 'mmol/L' }) { const options = { stream: outStream, useStyles: true, @@ -285,74 +285,75 @@ export default class TidepoolDataTools { // We create this up front, so that if the user experiences an error, this is the // first sheet they see when they open the XLSX. const errorSheet = wb.addWorksheet('EXPORT ERROR'); - (async () => { - await errorSheet.addRow([EXPORT_ERROR]).commit(); - })(); - - const xlsxStream = es.through( - (data) => { - if (data.type) { - const sheetName = this.typeDisplayName(data.type); - if (_.isUndefined(sheetName)) { - console.warn(`Warning: configuration ignores data type: '${data.type}'`); - return; - } - let sheet = wb.getWorksheet(sheetName); - if (_.isUndefined(sheet)) { - sheet = wb.addWorksheet(sheetName, { - views: [{ - state: 'frozen', - xSplit: 0, - ySplit: 1, - topLeftCell: 'A2', - activeCell: 'A2', - }], - }); - sheet.columns = Object.keys(config[data.type].fields).map((field) => ({ - header: this.fieldHeader(data.type, field), - key: field, - hidden: this.fieldHidden(data.type, field), - width: this.fieldWidth(data.type, field), - style: { numFmt: this.cellFormat(data.type, field, streamConfig) }, - })); - sheet.getRow(1).font = { - bold: true, - }; - } - // Convert timestamps to Excel Dates - if (data.time) { - _.assign(data, { - time: moment(data.time).toDate(), - }); - } - if (data.deviceTime) { - _.assign(data, { - deviceTime: moment.utc(data.deviceTime).toDate(), - }); - } - if (data.computerTime) { - _.assign(data, { - computerTime: moment.utc(data.computerTime).toDate(), - }); - } - sheet.addRow(data).commit(); - } else { - console.warn(`No data type specified: '${JSON.stringify(data)}Invalid'`); + await errorSheet.addRow([EXPORT_ERROR]).commit(); + + const addRow = async function addRow(data) { + if (data.type) { + const sheetName = TidepoolDataTools.typeDisplayName(data.type); + if (_.isUndefined(sheetName)) { + console.warn(`Warning: configuration ignores data type: '${data.type}'`); + return; } - }, - async function end() { - // Worksheet 1 will always exist. - // It's the ERROR sheet that we create at the beginning of this function. - if (wb.getWorksheet(2) === undefined) { - const emptySheet = wb.addWorksheet('NO DATA'); - await emptySheet.addRow(['Data is not available within the specified date range.']).commit(); + let sheet = wb.getWorksheet(sheetName); + if (_.isUndefined(sheet)) { + sheet = wb.addWorksheet(sheetName, { + views: [{ + state: 'frozen', + xSplit: 0, + ySplit: 1, + topLeftCell: 'A2', + activeCell: 'A2', + }], + }); + sheet.columns = Object.keys(config[data.type].fields).map((field) => ({ + header: TidepoolDataTools.fieldHeader(data.type, field), + key: field, + hidden: TidepoolDataTools.fieldHidden(data.type, field), + width: TidepoolDataTools.fieldWidth(data.type, field), + style: { numFmt: TidepoolDataTools.cellFormat(data.type, field, streamConfig) }, + })); + sheet.getRow(1).font = { + bold: true, + }; } - // Hide the ERROR sheet on success - errorSheet.state = 'veryHidden'; - await wb.commit(); - this.emit('end'); - }, - ); + // Convert timestamps to Excel Dates + if (data.time) { + _.assign(data, { + time: moment(data.time).toDate(), + }); + } + if (data.deviceTime) { + _.assign(data, { + deviceTime: moment.utc(data.deviceTime).toDate(), + }); + } + if (data.computerTime) { + _.assign(data, { + computerTime: moment.utc(data.computerTime).toDate(), + }); + } + await sheet.addRow(data).commit(); + } else { + console.warn(`No data type specified: '${JSON.stringify(data)}Invalid'`); + } + }; + + const xlsxStream = es.map((data, callback) => addRow(data).then(callback, callback)) + .pipe(es.through( + () => {}, + async function end() { + // Worksheet 1 will always exist. + // It's the ERROR sheet that we create at the beginning of this function. + if (wb.getWorksheet(2) === undefined) { + const emptySheet = wb.addWorksheet('NO DATA'); + await emptySheet.addRow(['Data is not available within the specified date range.']).commit(); + } + // Hide the ERROR sheet on success + errorSheet.state = 'veryHidden'; + await wb.commit(); + this.emit('end'); + }, + )); xlsxStream.cancel = async () => { xlsxStream.destroy(); diff --git a/package.json b/package.json index bcb6084..9b7141a 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "commander": "4.1.1", "csv-string": "3.1.7", "event-stream": "3.3.4", - "exceljs": "4.3.0", + "@zlooun/exceljs": "1.0.3", "flat": "5.0.0", "lodash": "4.17.15", "mkdirp": "1.0.3", diff --git a/yarn.lock b/yarn.lock index cd581e0..c679fce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -391,6 +391,21 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.54.tgz#fc304bd66419030141fa997dc5a9e0e374029ae8" integrity sha512-uq7O52wvo2Lggsx1x21tKZgqkJpvwCseBBPtX/nKQfpVlEsLOb11zZ1CRsWUKvJF0+lzuA9jwvA7Pr2Wt7i3xw== +"@zlooun/exceljs@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@zlooun/exceljs/-/exceljs-1.0.3.tgz#5ce9859304f6082f2482a8fa2dd03bb6b37d22f6" + integrity sha512-0bOI9DY9D/ybm38X05t+MvQAw26QxWN4xTmlZ6ritl9mfG0jec2Xh71HsZFvzghaYwlpIRpZJ0hTYoElDvKAvQ== + dependencies: + archiver "^5.0.0" + dayjs "^1.8.34" + fast-csv "^4.3.1" + jszip "^3.7.1" + readable-stream "^3.6.0" + saxes "^5.0.1" + tmp "^0.2.0" + unzipper "^0.10.11" + uuid "^8.3.0" + JSONStream@1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -1266,21 +1281,6 @@ event-stream@3.3.4: stream-combiner "~0.0.4" through "~2.3.1" -exceljs@4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/exceljs/-/exceljs-4.3.0.tgz#939bc0d4c59c200acadb7051be34d25c109853c4" - integrity sha512-hTAeo5b5TPvf8Z02I2sKIT4kSfCnOO2bCxYX8ABqODCdAjppI3gI9VYiGCQQYVcBaBSKlFDMKlAQRqC+kV9O8w== - dependencies: - archiver "^5.0.0" - dayjs "^1.8.34" - fast-csv "^4.3.1" - jszip "^3.5.0" - readable-stream "^3.6.0" - saxes "^5.0.1" - tmp "^0.2.0" - unzipper "^0.10.11" - uuid "^8.3.0" - fast-csv@^4.3.1: version "4.3.6" resolved "https://registry.yarnpkg.com/fast-csv/-/fast-csv-4.3.6.tgz#70349bdd8fe4d66b1130d8c91820b64a21bc4a63" @@ -1839,7 +1839,7 @@ jsonparse@^1.2.0: object.assign "^4.1.4" object.values "^1.1.6" -jszip@^3.5.0: +jszip@^3.7.1: version "3.10.1" resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== From 9acdf7940d99b6b000c8cb1cbc8269cde140bf42 Mon Sep 17 00:00:00 2001 From: Todd Kazakov Date: Tue, 12 Dec 2023 15:35:34 +0200 Subject: [PATCH 2/2] Fix --- index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 172541d..56d2b61 100644 --- a/index.js +++ b/index.js @@ -338,9 +338,10 @@ export default class TidepoolDataTools { } }; - const xlsxStream = es.map((data, callback) => addRow(data).then(callback, callback)) + const xlsxStream = es + .map((data, callback) => addRow(data).then(() => callback(), (err) => callback(err))) .pipe(es.through( - () => {}, + (data) => data, async function end() { // Worksheet 1 will always exist. // It's the ERROR sheet that we create at the beginning of this function.