From 69cea2b719756fadd257d2078f710a8a93abae1f Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Tue, 30 Sep 2025 05:39:54 -0700 Subject: [PATCH 1/5] Incorporate performedby into workflow --- .../study/clinical_observations.query.xml | 1 - .../resources/queries/study/housing.query.xml | 3 +- .../web/nirc_ehr/model/sources/NIRCDefault.js | 42 +++++++++++++++---- .../table/NIRC_EHRSharedDatasetTrigger.java | 21 +++++++++- 4 files changed, 54 insertions(+), 13 deletions(-) diff --git a/nirc_ehr/resources/queries/study/clinical_observations.query.xml b/nirc_ehr/resources/queries/study/clinical_observations.query.xml index a33b6963..7b058cf6 100644 --- a/nirc_ehr/resources/queries/study/clinical_observations.query.xml +++ b/nirc_ehr/resources/queries/study/clinical_observations.query.xml @@ -34,7 +34,6 @@ Performed By - false Scheduled Date diff --git a/nirc_ehr/resources/queries/study/housing.query.xml b/nirc_ehr/resources/queries/study/housing.query.xml index 281899e0..b3db5e14 100644 --- a/nirc_ehr/resources/queries/study/housing.query.xml +++ b/nirc_ehr/resources/queries/study/housing.query.xml @@ -43,7 +43,8 @@ - + + diff --git a/nirc_ehr/resources/web/nirc_ehr/model/sources/NIRCDefault.js b/nirc_ehr/resources/web/nirc_ehr/model/sources/NIRCDefault.js index e332da94..15cdd8bd 100644 --- a/nirc_ehr/resources/web/nirc_ehr/model/sources/NIRCDefault.js +++ b/nirc_ehr/resources/web/nirc_ehr/model/sources/NIRCDefault.js @@ -11,6 +11,7 @@ EHR.model.DataModelManager.registerMetadata('Default', { }, performedby: { hidden: false, + allowBlank: true, defaultValue: LABKEY.Security.currentUser.id, getInitialValue: function (v, rec) { return LABKEY.Security.currentUser.id; @@ -112,15 +113,6 @@ EHR.model.DataModelManager.registerMetadata('Default', { 'enddate': { hidden: true }, - performedby: { - allowBlank: false, - lookup: { - schemaName: 'core', - queryName: 'users', - keyColumn: 'UserId', - displayColumn: 'DisplayName', - }, - }, reason: { defaultValue: null, allowBlank: false, @@ -131,6 +123,35 @@ EHR.model.DataModelManager.registerMetadata('Default', { filterArray: [LABKEY.Filter.create('date_disabled', null, LABKEY.Filter.Types.ISBLANK)] } }, + performedby: { + hidden: false, + allowBlank: true, + defaultValue: LABKEY.Security.currentUser.id, + lookup: { + schemaName: 'core', + queryName: 'users', + keyColumn: 'UserId', + displayColumn: 'DisplayName', + columns: 'UserId,DisplayName,FirstName,LastName', + sort: 'Type,DisplayName' + }, + getInitialValue: function (v, rec) { + return LABKEY.Security.currentUser.id; + }, + editorConfig: { + store: { + type: 'labkey-store', + schemaName: 'core', + + // 'performedby' is a text field in the dataset and its lookup to the userid is an int field - this mismatch causes it to disappear + // from the display when a value is selected from the dropdown even though the 'userid' value gets saved as a text (this behavior was only seen + // in the form panel but not in the grid panel). + // casting it as a varchar when loading the store fixes this issue. + sql: "SELECT CAST (UserId AS VARCHAR) AS UserId,DisplayName,FirstName,LastName FROM core.PrincipalsWithoutAdmin WHERE active = TRUE AND Type = 'u'", + autoLoad: true + } + } + }, }, 'study.arrival': { initialRoom: { @@ -190,6 +211,9 @@ EHR.model.DataModelManager.registerMetadata('Default', { treatmentid: { hidden: true, nullable: true + }, + performedby: { + allowBlank: true, } }, 'study.observation_order': { diff --git a/nirc_ehr/src/org/labkey/nirc_ehr/table/NIRC_EHRSharedDatasetTrigger.java b/nirc_ehr/src/org/labkey/nirc_ehr/table/NIRC_EHRSharedDatasetTrigger.java index 707796c9..a8d97b30 100644 --- a/nirc_ehr/src/org/labkey/nirc_ehr/table/NIRC_EHRSharedDatasetTrigger.java +++ b/nirc_ehr/src/org/labkey/nirc_ehr/table/NIRC_EHRSharedDatasetTrigger.java @@ -11,12 +11,12 @@ /** * Shared dataset trigger to add triggers to act on all the study datasets. - * */ + */ public class NIRC_EHRSharedDatasetTrigger implements Trigger { private void transformAnimalIdToUpperCase(Map row) { - if (row != null && row.containsKey("Id")) + if (row != null && row.containsKey("Id") && row.get("Id") != null) { row.put("Id", ((String) row.get("Id")).toUpperCase()); } @@ -26,5 +26,22 @@ private void transformAnimalIdToUpperCase(Map row) public void beforeInsert(TableInfo table, Container c, User user, @Nullable Map newRow, ValidationException errors, Map extraContext) throws ValidationException { transformAnimalIdToUpperCase(newRow); + if (newRow != null && newRow.containsKey("performedby") && newRow.get("performedby") == null) + { + if (newRow.containsKey("QCStateLabel") && newRow.get("QCStateLabel").equals("Completed")) + errors.addFieldError("performedby", "Performed by must be entered in all " + table.getTitle() + " records before submitting final."); + } + } + + @Override + public void beforeUpdate(TableInfo table, Container c, + User user, @Nullable Map newRow, @Nullable Map oldRow, + ValidationException errors, Map extraContext) throws ValidationException + { + if (newRow != null && newRow.containsKey("performedby") && newRow.get("performedby") == null) + { + if (newRow.containsKey("QCStateLabel") && newRow.get("QCStateLabel").equals("Completed")) + errors.addFieldError("performedby", "Performed by must be entered in all " + table.getTitle() + " records before submitting final."); + } } } From fc358891293e92f73112738386c0570250c15e53 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Tue, 30 Sep 2025 06:13:48 -0700 Subject: [PATCH 2/5] Further refactor --- .../table/NIRC_EHRSharedDatasetTrigger.java | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/nirc_ehr/src/org/labkey/nirc_ehr/table/NIRC_EHRSharedDatasetTrigger.java b/nirc_ehr/src/org/labkey/nirc_ehr/table/NIRC_EHRSharedDatasetTrigger.java index a8d97b30..9f157ab3 100644 --- a/nirc_ehr/src/org/labkey/nirc_ehr/table/NIRC_EHRSharedDatasetTrigger.java +++ b/nirc_ehr/src/org/labkey/nirc_ehr/table/NIRC_EHRSharedDatasetTrigger.java @@ -22,26 +22,33 @@ private void transformAnimalIdToUpperCase(Map row) } } - @Override - public void beforeInsert(TableInfo table, Container c, User user, @Nullable Map newRow, ValidationException errors, Map extraContext) throws ValidationException + private void verifyPerformedBy(TableInfo table, @Nullable Map newRow, ValidationException errors) { - transformAnimalIdToUpperCase(newRow); if (newRow != null && newRow.containsKey("performedby") && newRow.get("performedby") == null) { - if (newRow.containsKey("QCStateLabel") && newRow.get("QCStateLabel").equals("Completed")) - errors.addFieldError("performedby", "Performed by must be entered in all " + table.getTitle() + " records before submitting final."); + if (!newRow.containsKey("QCStateLabel") || newRow.get("QCStateLabel") == null) + { + errors.addFieldError("performedby", "Record in " + table.getTitle() + " cannot be submitted without Performed By if QCStateLabel is not found. Contact your administrator."); + } + else if (newRow.containsKey("QCStateLabel") && newRow.get("QCStateLabel").equals("Completed")) + { + errors.addFieldError("performedby", "Performed By must be entered in all records before submitting final. Table: " + table.getTitle()); + } } } + @Override + public void beforeInsert(TableInfo table, Container c, User user, @Nullable Map newRow, ValidationException errors, Map extraContext) throws ValidationException + { + transformAnimalIdToUpperCase(newRow); + verifyPerformedBy(table, newRow, errors); + } + @Override public void beforeUpdate(TableInfo table, Container c, User user, @Nullable Map newRow, @Nullable Map oldRow, ValidationException errors, Map extraContext) throws ValidationException { - if (newRow != null && newRow.containsKey("performedby") && newRow.get("performedby") == null) - { - if (newRow.containsKey("QCStateLabel") && newRow.get("QCStateLabel").equals("Completed")) - errors.addFieldError("performedby", "Performed by must be entered in all " + table.getTitle() + " records before submitting final."); - } + verifyPerformedBy(table, newRow, errors); } } From bfa51220148f153a106de8b965d57a9c4421038e Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Wed, 1 Oct 2025 11:29:19 -0700 Subject: [PATCH 3/5] Updates for required performedby field --- nirc_ehr/resources/queries/study/arrival.js | 15 +- nirc_ehr/resources/queries/study/birth.js | 14 +- .../nirc_ehr/query/NIRC_EHRTriggerHelper.java | 14 + .../study/study/datasets/datasetArrival.tsv | 12 +- .../study/datasets/datasetAssignment.tsv | 18 +- .../study/study/datasets/datasetBirth.tsv | 18 +- .../study/study/datasets/datasetBirth.tsv~ | 9 + .../study/study/datasets/datasetDeaths.tsv | 6 +- .../study/datasets/datasetDemographics.tsv | 28 +- .../study/study/datasets/datasetDeparture.tsv | 10 +- .../study/study/datasets/datasetFlags.tsv | 22 +- .../study/study/datasets/datasetHousing.tsv | 18 +- .../datasets/datasetProtocolAssignment.tsv | 18 +- .../study/study/datasets/datasetWeight.tsv | 1622 ++++++++--------- .../tests.nirc_ehr/NIRC_EHRTest.java | 83 + 15 files changed, 1015 insertions(+), 892 deletions(-) create mode 100644 nirc_ehr/test/sampledata/nirc_ehr/study/study/datasets/datasetBirth.tsv~ diff --git a/nirc_ehr/resources/queries/study/arrival.js b/nirc_ehr/resources/queries/study/arrival.js index 9a245644..3eccf2fd 100644 --- a/nirc_ehr/resources/queries/study/arrival.js +++ b/nirc_ehr/resources/queries/study/arrival.js @@ -51,7 +51,8 @@ EHR.Server.TriggerManager.registerHandlerForQuery(EHR.Server.TriggerManager.Even Id: row.Id, date: row.birth, qcstate: row.qcstate, - taskid: row.taskid + taskid: row.taskid, + performedby: row.performedby } var birthErrors = triggerHelper.saveBirthRecord(row.Id, birthInfo); @@ -67,7 +68,8 @@ EHR.Server.TriggerManager.registerHandlerForQuery(EHR.Server.TriggerManager.Even date: row.date, taskid: row.taskid, remark: row.remark, - qcstate: row.qcstate + qcstate: row.qcstate, + performedby: row.performedby } if (row.project) { @@ -89,7 +91,8 @@ EHR.Server.TriggerManager.registerHandlerForQuery(EHR.Server.TriggerManager.Even cage: row.cage, taskid: row.taskid, qcstate: row.qcstate, - reason: row.arrivalType + reason: row.arrivalType, + performedby: row.performedby } var housingErrors = triggerHelper.createHousingRecord(row.Id, housingRec, "arrival"); @@ -152,6 +155,12 @@ EHR.Server.TriggerManager.registerHandlerForQuery(EHR.Server.TriggerManager.Even hasUpdates = true; } + if (row.performedby && row.performedby !== data.performedby) + { + obj.performedby = row.performedby; + hasUpdates = true; + } + if (hasUpdates) { console.info("Arrival update for animal Id " + row.Id + " included demographic changes Demographic record updated."); diff --git a/nirc_ehr/resources/queries/study/birth.js b/nirc_ehr/resources/queries/study/birth.js index 12f4e7d9..2596d43b 100644 --- a/nirc_ehr/resources/queries/study/birth.js +++ b/nirc_ehr/resources/queries/study/birth.js @@ -38,7 +38,8 @@ EHR.Server.TriggerManager.registerHandlerForQuery(EHR.Server.TriggerManager.Even date: row.date, taskid: row.taskid, remark: row.remark, - qcstate: row.qcstate + qcstate: row.qcstate, + performedby: row.performedby } if (row.project) { @@ -62,7 +63,8 @@ EHR.Server.TriggerManager.registerHandlerForQuery(EHR.Server.TriggerManager.Even cage: row.cage, taskid: row.taskid, qcstate: row.qcstate, - reason: 'Husbandry' + reason: 'Husbandry', + performedby: row.performedby } var housingErrors = triggerHelper.createHousingRecord(row.Id, housingRec, "birth"); @@ -87,7 +89,8 @@ EHR.Server.TriggerManager.registerHandlerForQuery(EHR.Server.TriggerManager.Even gender: row['Id/demographics/gender'] || null, taskid: row.taskid, remark: row.remark, - QCStateLabel: row.QCStateLabel + QCStateLabel: row.QCStateLabel, + performedby: row.performedby }; //find dam, if provided @@ -136,6 +139,11 @@ EHR.Server.TriggerManager.registerHandlerForQuery(EHR.Server.TriggerManager.Even hasUpdates = true; } + if (obj.performedby && obj.performedby !== data.performedby) { + record.performedby = obj.performedby; + hasUpdates = true; + } + if (obj.QCStateLabel && obj.QCStateLabel !== data.QCStateLabel) { record.QCStateLabel = obj.QCStateLabel; hasUpdates = true; diff --git a/nirc_ehr/src/org/labkey/nirc_ehr/query/NIRC_EHRTriggerHelper.java b/nirc_ehr/src/org/labkey/nirc_ehr/query/NIRC_EHRTriggerHelper.java index af3d6d6c..3e88a76e 100644 --- a/nirc_ehr/src/org/labkey/nirc_ehr/query/NIRC_EHRTriggerHelper.java +++ b/nirc_ehr/src/org/labkey/nirc_ehr/query/NIRC_EHRTriggerHelper.java @@ -110,6 +110,7 @@ public String createHousingRecord(String id, Map row, String for Date date = ConvertHelper.convert(row.get("date"), Date.class); String location = ConvertHelper.convert(row.get("cage"), String.class); String reason = ConvertHelper.convert(row.get("reason"), String.class); + Integer performedby = ConvertHelper.convert(row.get("performedby"), Integer.class); if (id == null || date == null || location == null) return "Attempting to create a housing record with no id, date, or location"; @@ -166,6 +167,7 @@ else if (enddate == null || enddate.after(deathDate)) saveRow.put("taskId", taskId); saveRow.put("qcstate", qcstate); saveRow.put("reason", reason); + saveRow.put("performedby", performedby); if (updateRecord) saveRow.put("objectid", ts.getMap().get("objectid")); else @@ -220,6 +222,11 @@ public String saveBirthRecord(String id, Map row) throws QueryUp return "Attempting to create a birth record with no qcstate"; } + Integer performedby = ConvertHelper.convert(row.get("performedby"), Integer.class); + if (performedby == null) { + return "Attempting to create a birth record with no performedby"; + } + TableInfo ti = getTableInfo("study", "birth"); // If there is already a housing record for this task, update that record @@ -236,6 +243,7 @@ public String saveBirthRecord(String id, Map row) throws QueryUp saveRow.put("date", date); saveRow.put("taskId", taskId); saveRow.put("qcstate", qcstate); + saveRow.put("performedby", performedby); if (updateRecord) { saveRow.put("objectid", ts.getMap().get("objectid")); @@ -570,6 +578,11 @@ public String createAssignmentRecord(String dataset, String id, Map skipLinksForValidation() return links; } + @Override + protected void createTestSubjects() throws Exception + { + String[] fields; + Object[][] data; + SimplePostCommand insertCommand; + + //insert into demographics + log("Creating test subjects"); + fields = new String[]{"Id", "Species", "Birth", "Gender", "date", "calculated_status", "objectid", "performedby"}; + data = new Object[][]{ + {SUBJECTS[0], "Rhesus", (new Date()).toString(), getMale(), new Date(), "Alive", UUID.randomUUID().toString(), 1004}, + {SUBJECTS[1], "Cynomolgus", (new Date()).toString(), getMale(), new Date(), "Alive", UUID.randomUUID().toString(), 1004}, + {SUBJECTS[2], "Marmoset", (new Date()).toString(), getFemale(), new Date(), "Alive", UUID.randomUUID().toString(), 1004}, + {SUBJECTS[3], "Cynomolgus", (new Date()).toString(), getMale(), new Date(), "Alive", UUID.randomUUID().toString(), 1004}, + {SUBJECTS[4], "Cynomolgus", (new Date()).toString(), getMale(), new Date(), "Alive", UUID.randomUUID().toString(), 1004} + }; + insertCommand = getApiHelper().prepareInsertCommand("study", "demographics", "lsid", fields, data); + getApiHelper().deleteAllRecords("study", "demographics", new Filter("Id", StringUtils.join(SUBJECTS, ";"), Filter.Operator.IN)); + getApiHelper().doSaveRows(DATA_ADMIN.getEmail(), insertCommand, getExtraContext()); + + //for simplicity, also create the animals from MORE_ANIMAL_IDS right now + data = new Object[][]{ + {MORE_ANIMAL_IDS[0], "Rhesus", (new Date()).toString(), getMale(), new Date(), "Alive", UUID.randomUUID().toString(), 1004}, + {MORE_ANIMAL_IDS[1], "Cynomolgus", (new Date()).toString(), getMale(), new Date(), "Alive", UUID.randomUUID().toString(), 1004}, + {MORE_ANIMAL_IDS[2], "Marmoset", (new Date()).toString(), getFemale(), new Date(), "Alive", UUID.randomUUID().toString(), 1004}, + {MORE_ANIMAL_IDS[3], "Cynomolgus", (new Date()).toString(), getMale(), new Date(), "Alive", UUID.randomUUID().toString(), 1004}, + {MORE_ANIMAL_IDS[4], "Cynomolgus", (new Date()).toString(), getMale(), new Date(), "Alive", UUID.randomUUID().toString(), 1004} + }; + insertCommand = getApiHelper().prepareInsertCommand("study", "demographics", "lsid", fields, data); + getApiHelper().deleteAllRecords("study", "demographics", new Filter("Id", StringUtils.join(MORE_ANIMAL_IDS, ";"), Filter.Operator.IN)); + getApiHelper().doSaveRows(DATA_ADMIN.getEmail(), insertCommand, getExtraContext()); + + //used as initial dates + Date pastDate1 = TIME_FORMAT.parse("2012-01-03 09:30"); + Date pastDate2 = TIME_FORMAT.parse("2012-05-03 19:20"); + + //set housing + log("Creating initial housing records"); + fields = new String[]{"Id", "date", "enddate", "room", "cage", "performedby"}; + data = new Object[][]{ + {SUBJECTS[0], pastDate1, pastDate2, getRooms()[0], CAGES[0], 1004}, + {SUBJECTS[0], pastDate2, null, getRooms()[0], CAGES[0], 1004}, + {SUBJECTS[1], pastDate1, pastDate2, getRooms()[0], CAGES[0], 1004}, + {SUBJECTS[1], pastDate2, null, getRooms()[2], CAGES[2], 1004} + }; + insertCommand = getApiHelper().prepareInsertCommand("study", "Housing", "lsid", fields, data); + getApiHelper().deleteAllRecords("study", "Housing", new Filter("Id", StringUtils.join(SUBJECTS, ";"), Filter.Operator.IN)); + getApiHelper().doSaveRows(DATA_ADMIN.getEmail(), insertCommand, getExtraContext()); + + //set a base weight + log("Setting initial weights"); + fields = new String[]{"Id", "date", "weight", "QCStateLabel", "performedby"}; + data = new Object[][]{ + {SUBJECTS[0], pastDate2, 10.5, EHRQCState.COMPLETED.label, 1004}, + {SUBJECTS[0], new Date(), 12, EHRQCState.COMPLETED.label, 1004}, + {SUBJECTS[1], new Date(), 12, EHRQCState.COMPLETED.label, 1004}, + {SUBJECTS[2], new Date(), 12, EHRQCState.COMPLETED.label, 1004} + }; + insertCommand = getApiHelper().prepareInsertCommand("study", "Weight", "lsid", fields, data); + getApiHelper().deleteAllRecords("study", "Weight", new Filter("Id", StringUtils.join(SUBJECTS, ";"), Filter.Operator.IN)); + getApiHelper().doSaveRows(DATA_ADMIN.getEmail(), insertCommand, getExtraContext()); + + //set assignment + log("Setting initial assignments"); + fields = new String[]{"Id", "date", "enddate", "project", "performedby"}; + data = new Object[][]{ + {SUBJECTS[0], pastDate1, pastDate2, PROJECTS[0], 1004}, + {SUBJECTS[1], pastDate1, pastDate2, PROJECTS[0], 1004}, + {SUBJECTS[1], pastDate2, null, PROJECTS[2], 1004} + }; + insertCommand = getApiHelper().prepareInsertCommand("study", "Assignment", "lsid", fields, data); + getApiHelper().deleteAllRecords("study", "Assignment", new Filter("Id", StringUtils.join(SUBJECTS, ";"), Filter.Operator.IN)); + getApiHelper().doSaveRows(DATA_ADMIN.getEmail(), insertCommand, getExtraContext()); + + primeCaches(); + } + @Test public void testArrivalForm() { From 598f94da91cc994dc14548e7d509191ec904a864 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Wed, 1 Oct 2025 21:40:01 -0700 Subject: [PATCH 4/5] Couple more fixes --- nirc_ehr/resources/queries/study/necropsy.js | 3 +- .../nirc_ehr/query/NIRC_EHRTriggerHelper.java | 1 + .../tests.nirc_ehr/NIRC_EHRTest.java | 35 ++++++++++++++----- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/nirc_ehr/resources/queries/study/necropsy.js b/nirc_ehr/resources/queries/study/necropsy.js index 8550aae9..05522f36 100644 --- a/nirc_ehr/resources/queries/study/necropsy.js +++ b/nirc_ehr/resources/queries/study/necropsy.js @@ -54,7 +54,8 @@ function onUpsert(helper, scriptErrors, row, oldRow) { date: row.date, weight: row.necropsyWeight, taskid: row.taskid, - qcstate: qcstate + qcstate: qcstate, + performedby: row.performedby }; triggerHelper.upsertWeightRecord(weightRecord); } diff --git a/nirc_ehr/src/org/labkey/nirc_ehr/query/NIRC_EHRTriggerHelper.java b/nirc_ehr/src/org/labkey/nirc_ehr/query/NIRC_EHRTriggerHelper.java index 3e88a76e..57f72d1d 100644 --- a/nirc_ehr/src/org/labkey/nirc_ehr/query/NIRC_EHRTriggerHelper.java +++ b/nirc_ehr/src/org/labkey/nirc_ehr/query/NIRC_EHRTriggerHelper.java @@ -323,6 +323,7 @@ public void upsertWeightRecord(Map row) throws QueryUpdateServic saveRow.put("date", date); saveRow.put("taskid", taskId); saveRow.put("qcstate", row.get("qcstate")); + saveRow.put("performedby", row.get("performedby")); if (updateRecord) { saveRow.put("objectid", ts.getMap().get("objectid")); diff --git a/nirc_ehr/test/src/org.labkey.test/tests.nirc_ehr/NIRC_EHRTest.java b/nirc_ehr/test/src/org.labkey.test/tests.nirc_ehr/NIRC_EHRTest.java index c42b76c9..22720cd1 100644 --- a/nirc_ehr/test/src/org.labkey.test/tests.nirc_ehr/NIRC_EHRTest.java +++ b/nirc_ehr/test/src/org.labkey.test/tests.nirc_ehr/NIRC_EHRTest.java @@ -53,6 +53,7 @@ import org.labkey.test.util.LogMethod; import org.labkey.test.util.PortalHelper; import org.labkey.test.util.PostgresOnlyTest; +import org.labkey.test.util.ehr.EHRClientAPIHelper; import org.labkey.test.util.ext4cmp.Ext4FieldRef; import org.labkey.test.util.ext4cmp.Ext4GridRef; import org.openqa.selenium.Keys; @@ -98,6 +99,10 @@ public class NIRC_EHRTest extends AbstractGenericEHRTest implements PostgresOnly private static final String deadAnimalId = "D5454"; private static final String departedAnimalId = "H6767"; private static final String aliveAnimalId = "A4545"; + + private String[] weightFields = {"Id", "date", "enddate", "project", "weight", FIELD_QCSTATELABEL, FIELD_OBJECTID, FIELD_LSID, "_recordid", "performedby"}; + private Object[] weightData1 = {getExpectedAnimalIDCasing("TESTSUBJECT1"), EHRClientAPIHelper.DATE_SUBSTITUTION, null, null, "12", EHRQCState.IN_PROGRESS.label, null, null, "_recordID", 1004}; + DateTimeFormatter _dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd"); @BeforeClass @@ -455,6 +460,18 @@ protected void createTestSubjects() throws Exception primeCaches(); } + @Override + protected String[] getWeightFields() + { + return weightFields; + } + + @Override + protected Object[] getWeightData1() + { + return weightData1; + } + @Test public void testArrivalForm() { @@ -718,35 +735,35 @@ public void createSubjectsForDeathForm() throws IOException, CommandException goToSchemaBrowser(); log("Creating animals"); getApiHelper().doSaveRows(DATA_ADMIN.getEmail(), getApiHelper().prepareInsertCommand("study", "birth", "lsid", - new String[]{"Id", "Date", "gender", "QCStateLabel"}, + new String[]{"Id", "Date", "gender", "QCStateLabel", "performedby"}, new Object[][]{ - {aliveAnimalId, LocalDateTime.now().minusDays(30), "f", "Completed"}, - {deadAnimalId, LocalDateTime.now().minusDays(30), "m", "Completed"}, - {departedAnimalId, LocalDateTime.now().minusDays(30), "m", "Completed"}, + {aliveAnimalId, LocalDateTime.now().minusDays(30), "f", "Completed", 1004}, + {deadAnimalId, LocalDateTime.now().minusDays(30), "m", "Completed", 1004}, + {departedAnimalId, LocalDateTime.now().minusDays(30), "m", "Completed", 1004}, } ), getExtraContext()); log("Inserting rows in assignments, protocolAssignment and housing"); InsertRowsCommand protocol = new InsertRowsCommand("study", "protocolAssignment"); - protocol.addRow(Map.of("Id", aliveAnimalId, "date", LocalDateTime.now().minusDays(10), "protocol", "protocol101", "QCStateLabel", "Completed")); + protocol.addRow(Map.of("Id", aliveAnimalId, "date", LocalDateTime.now().minusDays(10), "protocol", "protocol101", "QCStateLabel", "Completed", "performedby", 1004)); protocol.execute(getApiHelper().getConnection(), getContainerPath()); InsertRowsCommand project = new InsertRowsCommand("study", "assignment"); - project.addRow(Map.of("Id", aliveAnimalId, "date", LocalDateTime.now().minusDays(10), "project", "640991", "QCStateLabel", "Completed")); + project.addRow(Map.of("Id", aliveAnimalId, "date", LocalDateTime.now().minusDays(10), "project", "640991", "QCStateLabel", "Completed", "performedby", 1004)); project.execute(getApiHelper().getConnection(), getContainerPath()); InsertRowsCommand housing = new InsertRowsCommand("study", "housing"); - housing.addRow(Map.of("Id", aliveAnimalId, "date", LocalDateTime.now().minusDays(10), "cage", "C4", "QCStateLabel", "Completed")); + housing.addRow(Map.of("Id", aliveAnimalId, "date", LocalDateTime.now().minusDays(10), "cage", "C4", "QCStateLabel", "Completed", "performedby", 1004)); housing.execute(getApiHelper().getConnection(), getContainerPath()); log("Marking an animal dead"); InsertRowsCommand deaths = new InsertRowsCommand("study", "deaths"); - deaths.addRow(Map.of("Id", deadAnimalId, "date", LocalDateTime.now().minusDays(10), "reason", "4")); + deaths.addRow(Map.of("Id", deadAnimalId, "date", LocalDateTime.now().minusDays(10), "reason", "4", "performedby", 1004)); deaths.execute(getApiHelper().getConnection(), getContainerPath()); log("Marking an animal departed"); InsertRowsCommand departure = new InsertRowsCommand("study", "departure"); - departure.addRow(Map.of("Id", departedAnimalId, "date", LocalDateTime.now().minusDays(1), "destination", "Oregon NPRC")); + departure.addRow(Map.of("Id", departedAnimalId, "date", LocalDateTime.now().minusDays(1), "destination", "Oregon NPRC", "performedby", 1004)); departure.execute(getApiHelper().getConnection(), getContainerPath()); } From aec486bacd8e2683171bf1b5e514f638a8d6a411 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Thu, 2 Oct 2025 05:37:35 -0700 Subject: [PATCH 5/5] Override test --- .../tests.nirc_ehr/NIRC_EHRTest.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/nirc_ehr/test/src/org.labkey.test/tests.nirc_ehr/NIRC_EHRTest.java b/nirc_ehr/test/src/org.labkey.test/tests.nirc_ehr/NIRC_EHRTest.java index 22720cd1..d43f33fb 100644 --- a/nirc_ehr/test/src/org.labkey.test/tests.nirc_ehr/NIRC_EHRTest.java +++ b/nirc_ehr/test/src/org.labkey.test/tests.nirc_ehr/NIRC_EHRTest.java @@ -472,6 +472,50 @@ protected Object[] getWeightData1() return weightData1; } + @Test + public void testWeightValidation() + { + //initialize weight of subject 3 + String[] fields; + Object[][] data; + SimplePostCommand insertCommand; + fields = new String[]{"Id", "date", "weight", "QCStateLabel", "performedby"}; + data = new Object[][]{ + {SUBJECTS[3], new Date(), 12, EHRQCState.COMPLETED.label, 1004}, + }; + insertCommand = getApiHelper().prepareInsertCommand("study", "Weight", "lsid", fields, data); + getApiHelper().doSaveRows(DATA_ADMIN.getEmail(), insertCommand, getExtraContext()); + + //expect weight out of range + data = new Object[][]{ + {SUBJECTS[3], new Date(), null, null, 120, EHRQCState.IN_PROGRESS.label, null, null, "recordID", 1004} + }; + Map> expected = new HashMap<>(); + expected.put("weight", Arrays.asList( + "WARN: Weight above the allowable value of 20.0 kg for Cynomolgus", + "INFO: Weight gain of >10%. Last weight 12 kg") + ); + getApiHelper().testValidationMessage(DATA_ADMIN.getEmail(), "study", "weight", getWeightFields(), data, expected); + + //expect INFO for +10% diff + data = new Object[][]{ + {SUBJECTS[3], new Date(), null, null, 20, EHRQCState.IN_PROGRESS.label, null, null, "recordID", 1004} + }; + expected = new HashMap<>(); + expected.put("weight", Collections.singletonList("INFO: Weight gain of >10%. Last weight 12 kg")); + getApiHelper().testValidationMessage(DATA_ADMIN.getEmail(), "study", "weight", getWeightFields(), data, expected); + + //expect INFO for -10% diff + data = new Object[][]{ + {SUBJECTS[3], new Date(), null, null, 5, EHRQCState.IN_PROGRESS.label, null, null, "recordID", 1004} + }; + expected = new HashMap<>(); + expected.put("weight", Collections.singletonList("INFO: Weight drop of >10%. Last weight 12 kg")); + getApiHelper().testValidationMessage(DATA_ADMIN.getEmail(), "study", "weight", getWeightFields(), data, expected); + + //TODO: test error threshold + } + @Test public void testArrivalForm() {