From 8c881e9f34c47480fb152440534f51caef56501d Mon Sep 17 00:00:00 2001 From: ciatph Date: Sun, 11 Dec 2022 15:45:27 +0800 Subject: [PATCH 1/2] feat: Convert text list to HTML tags from excel files, #1 --- .env.example | 1 + .gitignore | 12 ++- README.md | 8 ++ package-lock.json | 172 ++++++++++++++++++++++++++++++ package.json | 5 + src/01_recommendations/index.js | 47 ++++++++ src/lib/stringlisttohtml/index.js | 98 +++++++++++++++++ src/lib/xlsxwrapper/index.js | 76 +++++++++++++ 8 files changed, 414 insertions(+), 5 deletions(-) create mode 100644 .env.example create mode 100644 src/01_recommendations/index.js create mode 100644 src/lib/stringlisttohtml/index.js create mode 100644 src/lib/xlsxwrapper/index.js diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..57e5fd5 --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +EXCEL_FILENAME=somefile.xlsx \ No newline at end of file diff --git a/.gitignore b/.gitignore index e34ee9b..689d49e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ node_modules/ -.xls -.xlsx -.csv -.zip -.env +.vscode +*.xls +*.xlsx +*.csv +*.zip +*.json +*.env diff --git a/README.md b/README.md index dd31f8f..04a815e 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,10 @@ The following requirements were used for this project. Feel free to use other de 2. Install dependencies.
`npm install` +3. Create a `.env` file from the `.env.example` file. Replace `EXCEL_FILENAME` with a target excel file's filename relative to the `/src/01_recommendations` directory. + +4. See the excel parser script's example usage on `/src/01_recommendations/index.js` + ## Available scripts ### `npm run lint` @@ -32,5 +36,9 @@ Check source codes for lint errors. Fix lint errors. +### `npm run parse` + +Normalize an unconventional, complex excel file into an array of simple JS objects with columns containing messy (ordered and unordered) bullet lists converted into organized HTML list tags. + @ciatph
20221205 diff --git a/package-lock.json b/package-lock.json index 19129a9..c4889d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,10 @@ "name": "excel-parser-uploader", "version": "1.0.0", "license": "ISC", + "dependencies": { + "dotenv": "^16.0.3", + "xlsx": "^0.18.5" + }, "devDependencies": { "eslint": "^8.29.0", "eslint-config-standard": "^17.0.0", @@ -133,6 +137,14 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -267,6 +279,18 @@ "node": ">=6" } }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -283,6 +307,14 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -307,6 +339,17 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -373,6 +416,14 @@ "node": ">=6.0.0" } }, + "node_modules/dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "engines": { + "node": ">=12" + } + }, "node_modules/es-abstract": { "version": "1.20.4", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", @@ -907,6 +958,14 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1882,6 +1941,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/string.prototype.trimend": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", @@ -2071,6 +2141,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -2086,6 +2172,26 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -2193,6 +2299,11 @@ "dev": true, "requires": {} }, + "adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==" + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2296,6 +2407,15 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, + "cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "requires": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + } + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2306,6 +2426,11 @@ "supports-color": "^7.1.0" } }, + "codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==" + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2327,6 +2452,11 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==" + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2373,6 +2503,11 @@ "esutils": "^2.0.2" } }, + "dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" + }, "es-abstract": { "version": "1.20.4", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", @@ -2774,6 +2909,11 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3452,6 +3592,14 @@ "object-inspect": "^1.9.0" } }, + "ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "requires": { + "frac": "~1.1.2" + } + }, "string.prototype.trimend": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", @@ -3593,6 +3741,16 @@ "is-symbol": "^1.0.3" } }, + "wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==" + }, + "word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==" + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -3605,6 +3763,20 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "requires": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + } + }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/package.json b/package.json index f13d5fa..ef4d0e1 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "index.js", "scripts": { "start": "node src/scripts/main.js", + "parse": "node src/01_recommendations/index.js", "lint": "eslint src", "lint:fix": "eslint src --fix" }, @@ -22,5 +23,9 @@ "eslint": "^8.29.0", "eslint-config-standard": "^17.0.0", "eslint-plugin-promise": "^6.1.1" + }, + "dependencies": { + "dotenv": "^16.0.3", + "xlsx": "^0.18.5" } } diff --git a/src/01_recommendations/index.js b/src/01_recommendations/index.js new file mode 100644 index 0000000..ef252bf --- /dev/null +++ b/src/01_recommendations/index.js @@ -0,0 +1,47 @@ +require('dotenv').config() +const path = require('path') +const XLSXWrapper = require('../lib/xlsxwrapper') +const StringListToHTML = require('../lib/stringlisttohtml') + +// Excel file column names +const COLUMN_NAMES = { + CROP_STAGE: 'crop_stage', + FARM_OPERATION: 'farm_operation', + FORECAST: 'forecast', + IMPACT: 'impact', + IMPACT_TAGALOG: 'impact_tagalog', + PRACTICE: 'practice', + PRACTICE_TAGALOG: 'practice_tagalog' +} + +const main = () => { + // Excel file path + const filePath = path.join(__dirname, process.env.EXCEL_FILENAME) + + const excel = new XLSXWrapper(filePath) + const textToHTML = new StringListToHTML() + + // Read data from excel file + const seasonalData = excel.getDataSheet(0) + + // Normalize, clean and convert list text content to HTML tags + const data = seasonalData.reduce((list, item, index) => { + const t = Object.values(item) + if (index > 0) { + list.push({ + [COLUMN_NAMES.CROP_STAGE]: t[0], + [COLUMN_NAMES.FARM_OPERATION]: t[1], + [COLUMN_NAMES.FORECAST]: t[2], + [COLUMN_NAMES.IMPACT]: textToHTML.convert(t[3]), + [COLUMN_NAMES.IMPACT_TAGALOG]: textToHTML.convert(t[4]), + [COLUMN_NAMES.PRACTICE]: textToHTML.convert(t[5]), + [COLUMN_NAMES.PRACTICE_TAGALOG]: textToHTML.convert(t[6]) + }) + } + return list + }, []) + + console.log(data) +} + +main() diff --git a/src/lib/stringlisttohtml/index.js b/src/lib/stringlisttohtml/index.js new file mode 100644 index 0000000..9093548 --- /dev/null +++ b/src/lib/stringlisttohtml/index.js @@ -0,0 +1,98 @@ +class StringListToHTML { + // Constants + IS_ITEM = 'is_item' + IS_SUB_ITEM = 'is_sub_item' + BIG_BULLET_SYMBOL = '●' + SMALL_BULLET_SYMBOL = '•' + NUMBER = '{{number}}' + SUBITEM = '{{sub}}' + + /** + * Get the row-dominant bullet character symbol + * Sometimes there's a big bullet symbol, sometimes a small symbol. Arrrggggh excel so messy >.< + * @param {String} string - Long text that contains numbers and bullets + * @returns {String} Returns the dominant bullet-type symbol BIG_BULLET_SYMBOL or SMALL_BULLET_SYMBOL + */ + getBulletSymbol = (string) => { + let separator = this.BIG_BULLET_SYMBOL + + if (string.includes(this.SMALL_BULLET_SYMBOL)) { + separator = this.SMALL_BULLET_SYMBOL + } + + return separator + } + + /** + * Converts text that contains 1-level nested ORDERED or UNORDERED list (numbers and bullets) to HTML tags + * @param {String} string - Long text that contains numbers and bullets + * @returns {String} 1-level input list text converted to HTML list tags + */ + textToHTML = (stringArr) => { + let html = '' + let LASTITEM + + const isOrdered = stringArr.find(x => x.includes(this.NUMBER)) + const isUnorderer = stringArr.find(x => x.includes(this.SUBITEM)) + + // Long text or paragraph + if (!isOrdered && !isUnorderer) { + return stringArr[0] + } + + html += (isOrdered) ? '
    ' : '' + } + + html += `
  1. ${stringArr[i].replace(this.NUMBER, '').trim()}
  2. ` + LASTITEM = this.IS_ITEM + } else if (stringArr[i].includes(this.SUBITEM)) { + if (LASTITEM === this.IS_ITEM) { + html += '' + } + } + } + + // Close the ordered list + if (isOrdered) { + html += '
' + } + + return html + } + + /** + * Converts text that contains full nested ORDERED or UNORDERED list (numbers and bullets) to HTML tags + * @param {String} string - Long text that contains numbers and bullets + * @returns {String} Full input list text converted to HTML list tags + */ + convert = (string) => { + let mainItems = [string] + + // Replace bullet list symbols with common markers + mainItems = string.replace(/[\d]+\./g, `{{split}}${this.NUMBER}`) + mainItems = mainItems.replace(/[●•]/g, `{{split}}${this.SUBITEM}`) + + mainItems = mainItems + .split('{{split}}') + .filter(item => item !== '') + + return this.textToHTML(mainItems) + } +} + +module.exports = StringListToHTML diff --git a/src/lib/xlsxwrapper/index.js b/src/lib/xlsxwrapper/index.js new file mode 100644 index 0000000..95ad217 --- /dev/null +++ b/src/lib/xlsxwrapper/index.js @@ -0,0 +1,76 @@ +const XLSX = require('xlsx') + +/** + * Wrapper class for the "xlsx" (sheetjs) excel file parser library. + */ +class XLSXWrapper { + #workbook = null + #sheets = null + + /** + * Initialize the XLSXWrapper class with a full file path to an excel file + * @param {String} filepath + */ + constructor (filepath) { + try { + this.#workbook = XLSX.readFile(filepath) + this.#sheets = this.#workbook.SheetNames + } catch (err) { + throw new Error(err.message) + } + } + + // @returns xlsx workbook + get workbook () { + return this.#workbook + } + + // @returns xlsx sheet names + get sheets () { + return this.#sheets + } + + /** + * Get the JSON conversion of an xlsx sheet + * @param {Number} sheetNum excel file sheet number + * @returns {Object} JSON conversion of an xlsx sheet + */ + getDataSheet (sheetNum) { + return XLSX.utils.sheet_to_json(this.#workbook.Sheets[this.#sheets[sheetNum]]) + } + + /** + * Extract the numerical data into a Float32Array using low-level operations + * Reference: https://docs.sheetjs.com/docs/demos/ml/#importing-data-from-a-spreadsheet + * @param {Number} sheetNum excel file sheet number + * @returns {Float32Array[]} Numerical data from all rows and cells + */ + getRawData (sheetNum) { + const range = XLSX.utils.decode_range(this.#workbook.Sheets[this.#sheets[sheetNum]]['!ref']) + const out = [] + + for (let C = range.s.c; C <= range.e.c; ++C) { + // Create the typed array + const ta = new Float32Array(range.e.r - range.s.r + 1) + + // Walk the rows + for (let R = range.s.r; R <= range.e.r; ++R) { + // Find the cell, skip it if thte cell isn't numeric or boolean + const cell = this.#workbook.Sheets[this.#sheets[sheetNum]][XLSX.utils.encode_cell({ r: R, c: C })] + + if (!cell || (cell.t !== 'n' && cell.t !== 'b')) { + continue + } + + // Assign to the typed array + ta[R - range.s.r] = cell.v + } + + out.push(ta) + } + + return out + } +} + +module.exports = XLSXWrapper From b23f56b39361a494d2c6eb9a5052f97aff98e850 Mon Sep 17 00:00:00 2001 From: ciatph Date: Sun, 11 Dec 2022 16:07:33 +0800 Subject: [PATCH 2/2] chore: Update README --- README.md | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 04a815e..145e4c9 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,84 @@ The following requirements were used for this project. Feel free to use other de - node v16.14.2 - npm v8.5.0 +## Notes + +- The bullet list symbols: `●`, `•` and a dash `-` can be used interchangeably per main item group (same for a single cell, but can differ across rows and columns). +- Expect the excel bullet text to be riddled with special characters (newlines, tabs and others). +- The script should be able to handle and convert mixed cases of unconventional and regular bullet-list formatting in all excel cells to HTML tags.The bullet list (per excel cell) can contain formats: + +### Messy Format 1 + +Sub item is in the same line as the main ordered item. + +``` +1. main item 1 ● sub item 1 + ● sub item 2 + ● sub item 3 + +2. main item 2 + ● sub item 1 + ● sub item 2 + ● sub item 3 +``` + +### Messy Format 2 + +Sub item is in the same line as the unordered main item. + +``` +● main item 1 ● main item 2 +● main item 3 +● main item 4 +``` + +### Messy Format 3 (Uses a dash symbol) + +Sub item is in the same line as the unordered main item. + +``` +● main item 1 + - sub item 1 + - sub item 2 + - sub item 3 +● main item 2 + - sub item 1 + - sub item 2 +● main item 3 +● main item 4 +``` + +### Conventional Format 1 (Number and bullets) + +``` +1. main item 1 + ● sub item 1 + ● sub item 2 + ● sub item 3 +``` + +### Conventional Format 2 (All bullets) + +``` +● main item 1 +● main item 2 +● main item 3 +``` + +### Conventional Format 3 (Number and bullets, starts with a bullet) + +``` +● main item + 1. sub item 2 + 2. sub item 3 + +● main item 2 + 1. sub item 1 + 2. sub item 2 + 3. sub item 3 +``` + + ## Installation 1. Clone this repository.
@@ -24,7 +102,8 @@ The following requirements were used for this project. Feel free to use other de 3. Create a `.env` file from the `.env.example` file. Replace `EXCEL_FILENAME` with a target excel file's filename relative to the `/src/01_recommendations` directory. -4. See the excel parser script's example usage on `/src/01_recommendations/index.js` +4. See the excel parser script's example usage on
+`/src/01_recommendations/index.js` ## Available scripts @@ -40,5 +119,7 @@ Fix lint errors. Normalize an unconventional, complex excel file into an array of simple JS objects with columns containing messy (ordered and unordered) bullet lists converted into organized HTML list tags. +> **WARNING:** The script does not support `-` (dash) symbols. + @ciatph
20221205