From 1774af0d306409ac2cd50df79aa7d3103782d436 Mon Sep 17 00:00:00 2001 From: Binal Patel Date: Fri, 18 Apr 2025 11:23:39 -0700 Subject: [PATCH 1/3] Move FormBulkAddWindow to ehr so that it can be used by other centers. --- .../web/ehr/window/FormBulkAddWindow.js | 252 ++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 ehr/resources/web/ehr/window/FormBulkAddWindow.js diff --git a/ehr/resources/web/ehr/window/FormBulkAddWindow.js b/ehr/resources/web/ehr/window/FormBulkAddWindow.js new file mode 100644 index 000000000..d5614948c --- /dev/null +++ b/ehr/resources/web/ehr/window/FormBulkAddWindow.js @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2018-2019 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ +/** + * @cfg targetStore + */ +Ext4.define('EHR.window.FormBulkAddWindow', { + extend: 'Ext.window.Window', + + modal: true, + closeAction: 'destroy', + title: 'Bulk Add Data', + bodyStyle: 'padding: 5px;', + width: 1000, + defaults: { + border: false + }, + + fieldConfigs: [], + fieldNames: [], + requiredFieldConfigs: [], + requiredFieldNames: [], + + initComponent: function(){ + const section = this.targetStore?.sectionCfg; + if (!section) { + Ext4.Msg.alert('Error', 'Unable to find form section. Check that the form is configured correctly.'); + return; + } + const allConfigs = EHR.model.DefaultClientModel.getFieldConfigs(section.fieldConfigs, section.configSources, this.extraMetaData); + this.fieldConfigs = allConfigs.filter((f) => { + return !f.hidden && !f.isHidden && f.name.toLowerCase() !== 'taskid' && f.name.toLowerCase() !== "qcstate"; + }); + if (!this.fieldConfigs) { + Ext4.Msg.alert('Error', 'Unable to find fields in target store. Check that the form is configured correctly.'); + return; + } + + this.fieldNames = this.fieldConfigs.map((f) => { + const alias = f.importAliases?.[0]; // If the import alias matches the label, use that in the template + if (alias?.toLowerCase() === f.label?.toLowerCase()) { + return alias; + } + return f.name; + }); + + this.requiredFieldConfigs = this.fieldConfigs.filter((f) => { + return f.required; + }); + + this.requiredFieldNames = this.requiredFieldConfigs.map((f) => { + return f.name; + }); + + this.items = [{ + html : 'This allows you to import data using a simple Excel or TSV file. To import, cut/paste the contents of the Excel or TSV file (Ctl + A is a good way to select all) into the box below and hit submit. The limit for import is 250 rows.', + style: 'padding-bottom: 10px;' + },{ + xtype: 'ldk-linkbutton', + text: '[Download Template]', + scope: this, + linkTarget: '_blank', + handler: function(){ + LABKEY.Utils.convertToExcel({ + fileName: this.targetStore.sectionCfg.label + '.xlsx', + sheets: [{ + name: 'Requests', + data: [ + this.fieldNames + ] + }] + }); + } + },{ + xtype: 'textarea', + width: 970, + height: 400, + itemId: 'textField' + }]; + + this.buttons = [{ + text: 'Submit', + scope: this, + handler: this.onSubmit + },{ + text: 'Cancel', + handler: function(btn){ + btn.up('window').close(); + } + }]; + + this.projectStore = EHR.DataEntryUtils.getProjectStore(); + + this.callParent(arguments); + }, + + onSubmit: function(){ + const text = this.down('#textField').getValue(); + if (!text){ + Ext4.Msg.alert('Error', 'Must paste the records into the text area'); + return; + } + + const parsed = LDK.Utils.CSVToArray(Ext4.String.trim(text), '\t'); + if (!parsed){ + Ext4.Msg.alert('Error', 'There was an error parsing the data.'); + return; + } + + if (parsed.length < 2){ + Ext4.Msg.alert('Error', 'There are not enough rows in the text, there was an error parsing the data.'); + return; + } + + this.doParse(parsed); + }, + + doParse: function(parsed){ + const errors = []; + const records = []; + + //first get global values: + Ext4.Msg.wait('Processing...'); + + const dataRowCount = parsed.length - 1; + if (dataRowCount > 250) { + errors.push('Row Count - ' + dataRowCount + ': Import maximum is 250 rows. Please split your import into multiple uploads and submit the form between each upload.'); + } + else { + + for (let i = 1; i < parsed.length; i++) { + const row = parsed[i]; + if (!row || row.length < this.requiredFieldNames.length) { + errors.push('Row ' + i + ': not enough items in row'); + continue; + } + + const newRow = this.processRow(parsed[0], row, errors, i); + if (newRow) { + records.push(this.targetStore.createModel(newRow)); + } + } + + Ext4.Msg.hide(); + } + + if (errors.length){ + Ext4.Msg.alert('Error', 'There following errors were found:

' + errors.join('
')); + return; + } + + if (records.length){ + this.targetStore.add(records); + } + + this.close(); + }, + + resolveLookup: function(field, value, errors){ + if (!field || !field.lookup) + return value; + + if (!field.lookup.store) { + const storeId = LABKEY.ext4.Util.getLookupStoreId(field); + + field.lookup.store = Ext4.StoreMgr.get(storeId); + if (!field.lookup.store) { + console.log('Unable to find lookup store for ' + storeId); + + return value; + } + } + + const lookupRecord = field.lookup.store.findRecord(field.lookup.displayColumn, value); + if (lookupRecord) { + return lookupRecord.data[field.lookup.keyColumn]; + } + + return value; + }, + + processRow: function(headers, row, errors, rowIdx){ + const obj = { + Id: row[headers.indexOf('Id')], + date: LDK.ConvertUtils.parseDate(row[headers.indexOf('date')]), + project: this.resolveProjectByName(row[headers.indexOf('project')], errors, rowIdx) + } + + Ext4.each(this.fieldConfigs, function(field) { + if (!obj[field.name]) { + let index = headers.indexOf(field.name); + if (index === -1 && field.importAliases?.[0]) { + index = headers.indexOf(field.importAliases?.[0]); + } + if (index !== -1) { + obj[field.name] = this.resolveLookup(field, row[index], errors); + } + } + }, this); + + if (!this.checkRequired(this.requiredFieldNames, obj, errors, rowIdx)){ + return obj; + } + }, + + checkRequired: function(fields, row, errors, rowIdx){ + let hasErrors = false, fieldName; + + for (let i=0;i Date: Fri, 18 Apr 2025 11:24:12 -0700 Subject: [PATCH 2/3] Minor update --- ehr/resources/web/ehr/window/FormBulkAddWindow.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ehr/resources/web/ehr/window/FormBulkAddWindow.js b/ehr/resources/web/ehr/window/FormBulkAddWindow.js index d5614948c..06cf9323b 100644 --- a/ehr/resources/web/ehr/window/FormBulkAddWindow.js +++ b/ehr/resources/web/ehr/window/FormBulkAddWindow.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019 LabKey Corporation + * Copyright (c) 2018-2025 LabKey Corporation * * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 */ From 932b1c4205e2e15a6afde13b6bf4d6dc9ed7bc72 Mon Sep 17 00:00:00 2001 From: Binal Patel Date: Fri, 18 Apr 2025 21:22:24 -0700 Subject: [PATCH 3/3] Add 'upperCaseAnimalId' property with default set to false. --- ehr/resources/web/ehr/window/AddAnimalsWindow.js | 3 ++- ehr/resources/web/ehr/window/FormBulkAddWindow.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ehr/resources/web/ehr/window/AddAnimalsWindow.js b/ehr/resources/web/ehr/window/AddAnimalsWindow.js index 8352874ff..dd00c66e6 100644 --- a/ehr/resources/web/ehr/window/AddAnimalsWindow.js +++ b/ehr/resources/web/ehr/window/AddAnimalsWindow.js @@ -17,6 +17,7 @@ Ext4.define('EHR.window.AddAnimalsWindow', { MAX_ANIMALS: 350, bulkEditCheckDisabled: false, + upperCaseAnimalId: false, initComponent: function(){ Ext4.apply(this, { @@ -170,7 +171,7 @@ Ext4.define('EHR.window.AddAnimalsWindow', { var records = []; Ext4.Array.forEach(subjectList, function(s){ - var model = Ext4.isObject(s) ? s : {Id: s}; + var model = Ext4.isObject(s) ? s : { Id: this.upperCaseAnimalId ? s.toUpperCase() : s }; if (date) { model.date = date; } diff --git a/ehr/resources/web/ehr/window/FormBulkAddWindow.js b/ehr/resources/web/ehr/window/FormBulkAddWindow.js index 06cf9323b..71ec6c945 100644 --- a/ehr/resources/web/ehr/window/FormBulkAddWindow.js +++ b/ehr/resources/web/ehr/window/FormBulkAddWindow.js @@ -22,6 +22,7 @@ Ext4.define('EHR.window.FormBulkAddWindow', { fieldNames: [], requiredFieldConfigs: [], requiredFieldNames: [], + upperCaseAnimalId: false, initComponent: function(){ const section = this.targetStore?.sectionCfg; @@ -183,7 +184,7 @@ Ext4.define('EHR.window.FormBulkAddWindow', { processRow: function(headers, row, errors, rowIdx){ const obj = { - Id: row[headers.indexOf('Id')], + Id: this.upperCaseAnimalId ? row[headers.indexOf('Id')].toUpperCase() : row[headers.indexOf('Id')], date: LDK.ConvertUtils.parseDate(row[headers.indexOf('date')]), project: this.resolveProjectByName(row[headers.indexOf('project')], errors, rowIdx) }