diff --git a/plugins/tiddlywiki/xlsx-utils/controls.tid b/plugins/tiddlywiki/xlsx-utils/controls.tid index 5a92b432e..339237062 100644 --- a/plugins/tiddlywiki/xlsx-utils/controls.tid +++ b/plugins/tiddlywiki/xlsx-utils/controls.tid @@ -130,6 +130,8 @@ suffixed <$reveal state="""$(field)$!!import-field-source""" type="match" text="constant" default="column" tag="span"> <$edit-text tiddler=<> field="import-field-value" tag="input" placeholder="constant" default=""/> +<$checkbox tiddler=<> field="import-field-skip-tiddler-if-blank" checked="yes" unchecked="no" default="no"> +Skip this tiddler when field blank
Title: <$tiddler tiddler=<>> @@ -192,6 +194,13 @@ Title:
  • +Row type: +<$select tiddler=<> field="import-row-type" default="by-field"> + + + +
  • +
  • <$button class="tc-btn-invisible"> <$action-createtiddler $basetitle="$:/_ExcelImporter/ImportSpecifiers/Field" $savetitle="$:/temp/newtiddler" import-spec-role="field" import-field-name="fieldname" import-field-type="string" import-field-source="column" import-field-column="Column Name" /> <$action-listops $tiddler=<> $subfilter="[{$:/temp/newtiddler}] +[putfirst[]]"/> diff --git a/plugins/tiddlywiki/xlsx-utils/deserializer.js b/plugins/tiddlywiki/xlsx-utils/deserializer.js index 452126a63..94a4249dc 100644 --- a/plugins/tiddlywiki/xlsx-utils/deserializer.js +++ b/plugins/tiddlywiki/xlsx-utils/deserializer.js @@ -23,7 +23,7 @@ exports["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"] = f text: text }); // Return the output tiddlers - return importer.importTiddlers(); + return importer.getResults(); }; })(); diff --git a/plugins/tiddlywiki/xlsx-utils/importer.js b/plugins/tiddlywiki/xlsx-utils/importer.js index 7a595c603..fc430a340 100644 --- a/plugins/tiddlywiki/xlsx-utils/importer.js +++ b/plugins/tiddlywiki/xlsx-utils/importer.js @@ -21,103 +21,151 @@ var XLSXImporter = function(options) { this.filename = options.filename; this.text = options.text; this.importSpec = options.importSpec || $tw.wiki.getTiddlerText(DEFAULT_IMPORT_SPEC_TITLE); + this.logger = new $tw.utils.Logger("xlsx-utils"); + this.results = []; + if(JSZip) { + this.processWorkbook(); + } }; -XLSXImporter.prototype.importTiddlers = function() { - var self = this, - results = [], - workbook; - // Check for the JSZIP plugin - if(!JSZip) { - return results; - } +XLSXImporter.prototype.getResults = function() { + return this.results; +}; + +XLSXImporter.prototype.processWorkbook = function() { // Read the workbook if(this.filename) { - workbook = XLSX.readFile(this.filename); + this.workbook = XLSX.readFile(this.filename); } else if(this.text) { - workbook = XLSX.read(this.text,{type:"base64"}); + this.workbook = XLSX.read(this.text,{type:"base64"}); } // Read the root import specification - var rootImportSpec = $tw.wiki.getTiddler(this.importSpec); - if(rootImportSpec) { + this.rootImportSpec = $tw.wiki.getTiddler(this.importSpec); + if(this.rootImportSpec) { // Iterate through the sheets specified in the list field - $tw.utils.each(rootImportSpec.fields.list || [],function(sheetImportSpecTitle) { - // Get the sheet import specifier - var sheetImportSpec = $tw.wiki.getTiddler(sheetImportSpecTitle); - if(sheetImportSpec) { - var sheetName = sheetImportSpec.fields["import-sheet-name"], - sheet = workbook.Sheets[sheetName]; - // Get the size of the sheet - var sheetSize = self.measureSheet(sheet); - // Read the column names from the first row - var columnsByName = self.findColumns(sheet,sheetSize); - // Iterate through the rows - for(var row=sheetSize.startRow+1; row<=sheetSize.endRow; row++) { - // Iterate through the row import specifiers - $tw.utils.each(sheetImportSpec.fields.list || [],function(rowImportSpecTitle) { - var rowImportSpec = $tw.wiki.getTiddler(rowImportSpecTitle); - if(rowImportSpec) { - var tiddlerFields = {}; - // Iterate through the fields for the row - $tw.utils.each(rowImportSpec.fields.list || [],function(fieldImportSpecTitle) { - var fieldImportSpec = $tw.wiki.getTiddler(fieldImportSpecTitle); - if(fieldImportSpec) { - var fieldName = fieldImportSpec.fields["import-field-name"], - value; - switch(fieldImportSpec.fields["import-field-source"]) { - case "column": - var columnName = fieldImportSpec.fields["import-field-column"], - cell = sheet[XLSX.utils.encode_cell({c: columnsByName[columnName], r: row})]; - if(cell) { - switch(fieldImportSpec.fields["import-field-type"] || "string") { - case "date": - if(cell.t === "n") { - value = $tw.utils.stringifyDate(new Date((cell.v - (25567 + 2)) * 86400 * 1000)); - } - break; - case "string": - // Intentional fall-through - default: - value = cell.w; - break; - } - } - break; - case "constant": - value = fieldImportSpec.fields["import-field-value"] - break; - } - if(fieldImportSpec.fields["import-field-prefix"]) { - value = fieldImportSpec.fields["import-field-prefix"] + value; - } - if(fieldImportSpec.fields["import-field-suffix"]) { - value = value + fieldImportSpec.fields["import-field-suffix"]; - } - if(fieldImportSpec.fields["import-field-replace-blank"] && (value || "").trim() === "") { - value = fieldImportSpec.fields["import-field-replace-blank"]; - } - switch(fieldImportSpec.fields["import-field-list-op"] || "none") { - case "none": - tiddlerFields[fieldName] = value; - break; - case "append": - var list = $tw.utils.parseStringArray(tiddlerFields[fieldName] || ""); - $tw.utils.pushTop(list,value) - tiddlerFields[fieldName] = list; - break; - } - } - }); - results.push(tiddlerFields); - } - }); - } - } - }); + $tw.utils.each(this.rootImportSpec.fields.list || [],this.processSheet.bind(this)); } - return results; }; +XLSXImporter.prototype.processSheet = function(sheetImportSpecTitle) { + // Get the sheet import specifier + this.sheetImportSpec = $tw.wiki.getTiddler(sheetImportSpecTitle); + if(this.sheetImportSpec) { + this.sheetName = this.sheetImportSpec.fields["import-sheet-name"]; + this.sheet = this.workbook.Sheets[this.sheetName]; + if(!this.sheet) { + this.logger.alert("Missing sheet '" + this.sheetName + "'"); + } else { + // Get the size of the sheet + this.sheetSize = this.measureSheet(this.sheet); + // Read the column names from the first row + this.columnsByName = this.findColumns(this.sheet,this.sheetSize); + // Iterate through the rows + for(this.row=this.sheetSize.startRow+1; this.row<=this.sheetSize.endRow; this.row++) { + // Iterate through the row import specifiers + $tw.utils.each(this.sheetImportSpec.fields.list || [],this.processRow.bind(this)); + } + } + } +}; + +XLSXImporter.prototype.processRow = function(rowImportSpecTitle) { + this.rowImportSpec = $tw.wiki.getTiddler(rowImportSpecTitle); + if(this.rowImportSpec) { + this.tiddlerFields = {}; + this.skipTiddler = false; + // Determine the type of row + this.rowType = this.rowImportSpec.fields["import-row-type"] || "by-field"; + switch(this.rowType) { + case "by-column": + this.processRowByColumn(); + break; + case "by-field": + this.processRowByField(); + break; + } + // Save the tiddler if not skipped + if(!this.skipTiddler) { + if(!this.tiddlerFields.title) { + this.logger.alert("Missing title field for " + JSON.stringify(this.tiddlerFields)); + } + this.results.push(this.tiddlerFields); + } + } +}; + +XLSXImporter.prototype.processRowByColumn = function() { + var self = this; + // Iterate through the columns for the row + $tw.utils.each(this.columnsByName,function(index,name) { + var cell = self.sheet[XLSX.utils.encode_cell({c: self.columnsByName[name], r: self.row})]; + name = name.toLowerCase(); + if(cell && cell.w && $tw.utils.isValidFieldName(name)) { + self.tiddlerFields[name] = cell.w; + } + }); +}; + +XLSXImporter.prototype.processRowByField = function() { + // Iterate through the fields for the row + $tw.utils.each(this.rowImportSpec.fields.list || [],this.processField.bind(this)); +}; + +XLSXImporter.prototype.processField = function(fieldImportSpecTitle) { + var fieldImportSpec = $tw.wiki.getTiddler(fieldImportSpecTitle); + if(fieldImportSpec) { + var fieldName = fieldImportSpec.fields["import-field-name"], + value; + switch(fieldImportSpec.fields["import-field-source"]) { + case "column": + var columnName = fieldImportSpec.fields["import-field-column"], + cell = this.sheet[XLSX.utils.encode_cell({c: this.columnsByName[columnName], r: this.row})]; + if(cell) { + switch(fieldImportSpec.fields["import-field-type"] || "string") { + case "date": + if(cell.t === "n") { + value = $tw.utils.stringifyDate(new Date((cell.v - (25567 + 2)) * 86400 * 1000)); + } + break; + case "string": + // Intentional fall-through + default: + value = cell.w; + break; + } + } + break; + case "constant": + value = fieldImportSpec.fields["import-field-value"] + break; + } + if(fieldImportSpec.fields["import-field-prefix"]) { + value = fieldImportSpec.fields["import-field-prefix"] + value; + } + if(fieldImportSpec.fields["import-field-suffix"]) { + value = value + fieldImportSpec.fields["import-field-suffix"]; + } + if((value || "").trim() === "") { + if((fieldImportSpec.fields["import-field-skip-tiddler-if-blank"] || "").trim().toLowerCase() === "yes") { + this.skipTiddler = true; + } + if(fieldImportSpec.fields["import-field-replace-blank"]) { + value = fieldImportSpec.fields["import-field-replace-blank"]; + } + } + switch(fieldImportSpec.fields["import-field-list-op"] || "none") { + case "none": + this.tiddlerFields[fieldName] = value; + break; + case "append": + var list = $tw.utils.parseStringArray(this.tiddlerFields[fieldName] || ""); + $tw.utils.pushTop(list,value) + this.tiddlerFields[fieldName] = list; + break; + } + } +} + XLSXImporter.prototype.measureSheet = function(sheet) { var sheetRange = XLSX.utils.decode_range(sheet["!ref"]); return { diff --git a/plugins/tiddlywiki/xlsx-utils/xlsx-import-command.js b/plugins/tiddlywiki/xlsx-utils/xlsx-import-command.js index 9300ffd43..091778ce7 100644 --- a/plugins/tiddlywiki/xlsx-utils/xlsx-import-command.js +++ b/plugins/tiddlywiki/xlsx-utils/xlsx-import-command.js @@ -36,7 +36,7 @@ Command.prototype.execute = function() { filename: filename, importSpec: importSpec }); - $tw.wiki.addTiddlers(importer.importTiddlers()); + $tw.wiki.addTiddlers(importer.getResults()); return null; };