diff --git a/nirc_ehr/resources/queries/dbo/q_clinremarks.sql b/nirc_ehr/resources/queries/dbo/q_clinremarks.sql index 2d34bada..488233b4 100644 --- a/nirc_ehr/resources/queries/dbo/q_clinremarks.sql +++ b/nirc_ehr/resources/queries/dbo/q_clinremarks.sql @@ -8,6 +8,10 @@ SELECT anmEvt.ANIMAL_EVENT_ID anmEvt.EVENT_ID.EVENT_ID AS category, anmEvt.DIAGNOSIS AS vetreview, anmCmt.TEXT AS remark, + CASE WHEN anmEvt.ATTACHMENT_PATH IS NOT NULL THEN + ('C:\Program Files\Labkey\labkey\files\NIRC\EHR\@files\attachments' + || substring(anmEvt.ATTACHMENT_PATH, LENGTH('N:\'), LENGTH(anmEvt.ATTACHMENT_PATH))) + ELSE NULL END AS attachmentFile, CAST(COALESCE(adt.modified, anmEvt.CREATED_DATETIME) AS TIMESTAMP) AS modified FROM ANIMAL_EVENT anmEvt LEFT JOIN ANIMAL_EVENT_COMMENT anmCmt ON anmEvt.ANIMAL_EVENT_ID = anmCmt.ANIMAL_EVENT_ID diff --git a/nirc_ehr/resources/queries/nirc_ehr/necropsyTasks/.qview.xml b/nirc_ehr/resources/queries/nirc_ehr/necropsyTasks/.qview.xml index fb474742..908527e7 100644 --- a/nirc_ehr/resources/queries/nirc_ehr/necropsyTasks/.qview.xml +++ b/nirc_ehr/resources/queries/nirc_ehr/necropsyTasks/.qview.xml @@ -3,6 +3,17 @@ + + + + + + + + + + + diff --git a/nirc_ehr/resources/queries/study/cases.js b/nirc_ehr/resources/queries/study/cases.js index 34a1fa2d..f5c88d96 100644 --- a/nirc_ehr/resources/queries/study/cases.js +++ b/nirc_ehr/resources/queries/study/cases.js @@ -42,7 +42,7 @@ EHR.Server.TriggerManager.registerHandlerForQuery(EHR.Server.TriggerManager.Even if (!qc) { console.error('Unable to find QCState: ' + row.QCState + '/' + row.QCStateLabel); } - else if (qc.Label == 'Completed' && row.caseid && row.Id && row.performedby && row.taskid && qc) { + else if ((qc.Label == 'Completed' || qc.Label == 'Review Required') && row.caseid && row.Id && row.performedby && row.taskid && qc) { var ordersInTransaction = helper.getProperty('ordersInTransaction'); var oit = []; if (ordersInTransaction && ordersInTransaction.length) { diff --git a/nirc_ehr/resources/queries/study/cases/All Clinical Cases.qview.xml b/nirc_ehr/resources/queries/study/cases/All Clinical Cases.qview.xml index 70c5f5bb..38e4ed5e 100644 --- a/nirc_ehr/resources/queries/study/cases/All Clinical Cases.qview.xml +++ b/nirc_ehr/resources/queries/study/cases/All Clinical Cases.qview.xml @@ -23,6 +23,8 @@ + + diff --git a/nirc_ehr/resources/queries/study/clinical_observationsSummary/Clinical.qview.xml b/nirc_ehr/resources/queries/study/clinical_observationsSummary/Clinical.qview.xml index fbcba011..be71773b 100644 --- a/nirc_ehr/resources/queries/study/clinical_observationsSummary/Clinical.qview.xml +++ b/nirc_ehr/resources/queries/study/clinical_observationsSummary/Clinical.qview.xml @@ -5,6 +5,7 @@ + @@ -12,6 +13,6 @@ - + \ No newline at end of file diff --git a/nirc_ehr/resources/queries/study/deathNotification.sql b/nirc_ehr/resources/queries/study/deathNotification.sql new file mode 100644 index 00000000..a1c0d6fc --- /dev/null +++ b/nirc_ehr/resources/queries/study/deathNotification.sql @@ -0,0 +1,8 @@ + +SELECT + Id, + date, + taskid, + performedBy.DisplayName AS performedBy, + reason.title AS reason +FROM study.deaths \ No newline at end of file diff --git a/nirc_ehr/resources/queries/study/demographicsCagemates.sql b/nirc_ehr/resources/queries/study/demographicsCagemates.sql index 1ea84560..3c8824d2 100644 --- a/nirc_ehr/resources/queries/study/demographicsCagemates.sql +++ b/nirc_ehr/resources/queries/study/demographicsCagemates.sql @@ -18,8 +18,7 @@ FROM study.housing h JOIN study.housing h2 ON (h2.Id.demographics.calculated_status = 'Alive' --- AND h.room = h2.room - AND (h.cage = h2.cage OR (h.cage IS NULL and h2.cage IS NULL))) + AND (h.cage = h2.cage)) WHERE h.enddateTimeCoalesced >= now() GROUP BY h.id, h.room, h.cage diff --git a/nirc_ehr/resources/queries/study/demographicsCurLocation.sql b/nirc_ehr/resources/queries/study/demographicsCurLocation.sql index c61f9625..441ed163 100644 --- a/nirc_ehr/resources/queries/study/demographicsCurLocation.sql +++ b/nirc_ehr/resources/queries/study/demographicsCurLocation.sql @@ -27,7 +27,9 @@ coalesce(d2.room, '') as room_order, d2.room_sortValue @hidden, coalesce(d2.cage, '') as cage_order, -d2.cage_sortValue @hidden +d2.cage_sortValue @hidden, + +d2.performedBy.displayName AS performedBy FROM study.housing d2 diff --git a/nirc_ehr/resources/queries/study/notificationAnimalProject.sql b/nirc_ehr/resources/queries/study/notificationAnimalProject.sql new file mode 100644 index 00000000..79593908 --- /dev/null +++ b/nirc_ehr/resources/queries/study/notificationAnimalProject.sql @@ -0,0 +1,8 @@ +-- Add more here to make a single query for animal details in notifications +SELECT + Id, + date, + project.name AS project, + enddate +FROM study.assignment +WHERE enddate IS NULL \ No newline at end of file diff --git a/nirc_ehr/resources/queries/study/observationSchedule.sql b/nirc_ehr/resources/queries/study/observationSchedule.sql index 2f97ed9a..ccab01b9 100644 --- a/nirc_ehr/resources/queries/study/observationSchedule.sql +++ b/nirc_ehr/resources/queries/study/observationSchedule.sql @@ -80,7 +80,7 @@ FROM LEFT JOIN ehr_lookups.treatment_frequency_times ft ON ft.frequency = t1.frequency.meaning - WHERE t1.date is not null AND t1.qcstate.publicdata = true + WHERE t1.date is not null ) s1 diff --git a/nirc_ehr/resources/queries/study/treatmentSchedule.sql b/nirc_ehr/resources/queries/study/treatmentSchedule.sql index ce3530df..d6ad5cd5 100644 --- a/nirc_ehr/resources/queries/study/treatmentSchedule.sql +++ b/nirc_ehr/resources/queries/study/treatmentSchedule.sql @@ -1,102 +1,68 @@ -/* - * Copyright (c) 2010-2014 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 - */ SELECT d.id, d.calculated_status, s.*, -s.objectid as treatmentid, -drug.qcstate.label as treatmentStatus, -s.objectid || '-pkSeparator-' || s.date as primaryKey -- -pkSeparator- is used to separate the two parts of the primary key in RecordTreatmentButton.js -FROM study.demographics d JOIN - - -(SELECT - s1.*, - timestampadd('SQL_TSI_MINUTE', ((s1.hours * 60) + s1.minutes), s1.origDate) as date, - ((s1.hours * 60) + s1.minutes) as timeOffset - -FROM ( - -SELECT - t1.lsid, - t1.objectid, - t1.dataset, - t1.id as animalid, - - coalesce(tt.time, ft.hourofday, ((hour(t1.date) * 100) + minute(t1.date))) as time, - (coalesce(tt.time, ft.hourofday, (hour(t1.date) * 100)) / 100) as hours, - CASE - WHEN (tt.time IS NOT NULL OR ft.hourofday IS NOT NULL) THEN (((coalesce(tt.time, ft.hourofday) / 100.0) - floor(coalesce(tt.time, ft.hourofday) / 100)) * 100) - ELSE minute(t1.date) - END as minutes, - dr.date as origDate, - CASE - WHEN (tt.time IS NULL) THEN 'Default' - ELSE 'Custom' - END as timeType, - - CASE - WHEN snomed.code IS NOT NULL THEN 'Diet' - ELSE IFDEFINED(t1.category) - END as category, - t1.frequency.meaning as frequency, - t1.date as startDate, - timestampdiff('SQL_TSI_DAY', cast(t1.dateOnly as timestamp), dr.dateOnly) + 1 as daysElapsed, - t1.enddate, - t1.code, - t1.treatmentRecord, - t1.volume, - t1.vol_units, - t1.concentration, - t1.conc_units, - t1.amount, - t1.amount_units, - t1.amountWithUnits, - t1.amountAndVolume, - t1.dosage, - t1.dosage_units, - t1.route, - IFDEFINED(t1.reason) AS reason, - t1.performedby, - t1.remark, - t1.caseid, - t1.orderedby, - - t1.qcstate - -FROM nirc_ehr.dateRange dr - -JOIN study."Treatment Orders" t1 - ON (dr.dateOnly >= t1.dateOnly and dr.dateOnly <= t1.enddateCoalesced AND - --technically the first day of the treatment is day 1, not day 0 - ((mod(CAST(timestampdiff('SQL_TSI_DAY', CAST(t1.dateOnly as timestamp), dr.dateOnly) as integer), t1.frequency.intervalindays) = 0 And t1.frequency.intervalindays is not null And t1.frequency.dayofweek is null )) -) - -LEFT JOIN ehr.treatment_times tt ON (tt.treatmentid = t1.objectid) -LEFT JOIN ehr_lookups.treatment_frequency_times ft ON (ft.frequency = t1.frequency.meaning AND tt.rowid IS NULL) - -LEFT JOIN ( +s.objectid AS treatmentid, +drug.qcstate.label AS treatmentStatus, +s.objectid || '-pkSeparator-' || s.date AS primaryKey -- -pkSeparator- is used to separate the two parts of the primary key in RecordTreatmentButton.js +FROM study.demographics d +JOIN( SELECT - sc.code - from ehr_lookups.snomed_subset_codes sc - WHERE sc.primaryCategory = 'Diet' - GROUP BY sc.code -) snomed ON snomed.code = t1.code - ---NOTE: if we run this report on a future interval, we want to include those treatments -WHERE t1.date is not null - -) s1 - -) s ON (s.animalid = d.id) LEFT JOIN -study.drug drug ON s.objectid = drug.treatmentid AND s.date = IFDEFINED(drug.scheduledDate) - -WHERE (d.lastDayatCenter Is Null or d.lastDayAtCenter > s.enddate OR s.enddate IS NULL) - - ---account for date/time in schedule -and s.date >= s.startDate and (s.enddate IS NULL OR s.date <= s.enddate) \ No newline at end of file + s1.*, + timestampadd('SQL_TSI_MINUTE', ((s1.hours * 60) + s1.minutes), s1.origDate) AS date, + ((s1.hours * 60) + s1.minutes) AS timeOffset + FROM ( + SELECT + t1.lsid, + t1.objectid, + t1.dataset, + t1.id AS animalid, + + COALESCE(ft.hourofday, ((hour(t1.date) * 100) + minute(t1.date))) AS time, + (COALESCE(ft.hourofday, (hour(t1.date) * 100)) / 100) AS hours, + CASE + WHEN (ft.hourofday IS NOT NULL) THEN (((ft.hourofday / 100.0) - floor(ft.hourofday / 100)) * 100) + ELSE minute(t1.date) + END AS minutes, + dr.date AS origDate, + t1.category, + t1.frequency.meaning AS frequency, + t1.date AS startDate, + timestampdiff('SQL_TSI_DAY', cast(t1.dateOnly AS timestamp), dr.dateOnly) + 1 AS daysElapsed, + t1.enddate, + t1.code, + t1.treatmentRecord, + t1.volume, + t1.vol_units, + t1.concentration, + t1.conc_units, + t1.amount, + t1.amount_units, + t1.amountWithUnits, + t1.amountAndVolume, + t1.dosage, + t1.dosage_units, + t1.route, + IFDEFINED(t1.reason) AS reason, + t1.performedby, + t1.remark, + t1.caseid, + t1.orderedby, + t1.qcstate + FROM nirc_ehr.dateRange dr + JOIN study."Treatment Orders" t1 ON (dr.dateOnly >= t1.dateOnly AND + --technically the first day of the treatment is day 1, not day 0 + ((mod(CAST(timestampdiff('SQL_TSI_DAY', CAST(t1.dateOnly AS timestamp), dr.dateOnly) AS integer), t1.frequency.intervalindays) = 0 AND t1.frequency.intervalindays IS NOT NULL AND t1.frequency.dayofweek IS NULL )) + ) + LEFT JOIN ehr_lookups.treatment_frequency_times ft ON ft.frequency = t1.frequency.meaning + --NOTE: if we run this report on a future interval, we want to include those treatments + WHERE t1.date IS NOT NULL + + ) s1 + +) s ON (s.animalid = d.id) +LEFT JOIN study.drug drug ON s.objectid = drug.treatmentid AND s.date = IFDEFINED(drug.scheduledDate) + +WHERE (d.lastDayatCenter IS NULL OR d.lastDayAtCenter > s.enddate OR s.enddate IS NULL) + AND s.date >= s.startDate AND (s.enddate IS NULL OR s.date <= s.enddate) \ No newline at end of file diff --git a/nirc_ehr/resources/queries/study/treatmentSchedule/.qview.xml b/nirc_ehr/resources/queries/study/treatmentSchedule/.qview.xml index d814bf44..2c883832 100644 --- a/nirc_ehr/resources/queries/study/treatmentSchedule/.qview.xml +++ b/nirc_ehr/resources/queries/study/treatmentSchedule/.qview.xml @@ -7,7 +7,6 @@ - diff --git a/nirc_ehr/resources/referenceStudy/study/datasets/datasets_metadata.xml b/nirc_ehr/resources/referenceStudy/study/datasets/datasets_metadata.xml index f34f32f6..3c70879e 100644 --- a/nirc_ehr/resources/referenceStudy/study/datasets/datasets_metadata.xml +++ b/nirc_ehr/resources/referenceStudy/study/datasets/datasets_metadata.xml @@ -379,6 +379,11 @@ varchar urn:ehr.labkey.org/#CaseId + + Attachment File + varchar + http://cpas.fhcrc.org/exp/xml#fileLink + @@ -643,6 +648,11 @@ urn:ehr.labkey.org/#CaseId + + + cage + +
diff --git a/nirc_ehr/src/org/labkey/nirc_ehr/NIRCOrchardFileGenerator.java b/nirc_ehr/src/org/labkey/nirc_ehr/NIRCOrchardFileGenerator.java index 36d02570..09278f5a 100644 --- a/nirc_ehr/src/org/labkey/nirc_ehr/NIRCOrchardFileGenerator.java +++ b/nirc_ehr/src/org/labkey/nirc_ehr/NIRCOrchardFileGenerator.java @@ -1,11 +1,14 @@ package org.labkey.nirc_ehr; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.labkey.api.data.CompareType; import org.labkey.api.data.Container; import org.labkey.api.data.DbScope; import org.labkey.api.data.SimpleFilter; import org.labkey.api.data.TableInfo; import org.labkey.api.data.TableSelector; +import org.labkey.api.exp.OntologyManager; import org.labkey.api.module.Module; import org.labkey.api.module.ModuleLoader; import org.labkey.api.module.ModuleProperty; @@ -16,6 +19,7 @@ import org.labkey.api.study.StudyService; import org.labkey.api.util.JobRunner; import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.util.logging.LogHelper; import org.labkey.api.writer.PrintWriters; import java.io.File; @@ -33,6 +37,7 @@ public class NIRCOrchardFileGenerator { public static final String NIRCOrchardFileLocation = "NIRCOrchardFileLocation"; + private static final Logger _log = LogHelper.getLogger(NIRCOrchardFileGenerator.class, "Orchard update on trigger."); public void generateOrchardFile(Container c, User u, String taskid) { @@ -80,6 +85,13 @@ public FileWriteTask(Container c, User u, String taskid, String orchardFileLocat @Override public void run() { + if (orchardFileLocation == null) + { + // Don't run if we don't have a location to write the file + _log.warn("Orchard file location is null, cannot generate Orchard file for taskid: " + taskid); + return; + } + JobRunner.getDefault().execute(() -> { TableInfo ti = getTableInfo(c, u, "study", "orchardData"); diff --git a/nirc_ehr/src/org/labkey/nirc_ehr/demographics/HousingDemographicsProvider.java b/nirc_ehr/src/org/labkey/nirc_ehr/demographics/HousingDemographicsProvider.java index d6d16da3..4a285f31 100644 --- a/nirc_ehr/src/org/labkey/nirc_ehr/demographics/HousingDemographicsProvider.java +++ b/nirc_ehr/src/org/labkey/nirc_ehr/demographics/HousingDemographicsProvider.java @@ -42,6 +42,7 @@ protected Collection getFieldKeys() keys.add(FieldKey.fromString("reason")); keys.add(FieldKey.fromString("remark")); keys.add(FieldKey.fromString("location")); + keys.add(FieldKey.fromString("performedBy")); return keys; } 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 4969a105..b3f86cd3 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 @@ -49,6 +49,7 @@ import java.time.LocalDateTime; import java.time.ZoneId; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -364,12 +365,13 @@ public void clinicalMoveNotification(final String animalId, final String date) } String remark = (String) EHRDemographicsService.get().getAnimal(container, animalId).getActiveHousing().get(0).get("remark"); + String performedBy = (String) EHRDemographicsService.get().getAnimal(container, animalId).getActiveHousing().get(0).get("performedBy"); //construct html for email notification final StringBuilder html = new StringBuilder(); html.append("Animal ").append(PageFlowUtil.filter(animalId)).append(" has been moved for Veterinary Treatment on ").append(date).append(".
"); - if (remark != null) - html.append("Remark: ").append(PageFlowUtil.filter(remark)).append("

"); + html.append("Performed By: ").append(PageFlowUtil.filter(performedBy)).append("
"); + html.append("Remark: ").append(PageFlowUtil.filter(remark)).append("

"); //append animal details appendAnimalDetails(html, animalId, container); @@ -410,16 +412,20 @@ public void sendDeathNotification(final String animalId) throws Exception } //get death info - TableInfo deaths = getTableInfo("study", "deaths"); - TableSelector deathsTs = new TableSelector(deaths, PageFlowUtil.set("Id", "date", "taskid"), new SimpleFilter(FieldKey.fromString("Id"), animalId), null); + TableInfo deaths = getTableInfo("study", "deathNotification"); + TableSelector deathsTs = new TableSelector(deaths, PageFlowUtil.set("Id", "date", "taskid", "performedBy", "reason"), new SimpleFilter(FieldKey.fromString("Id"), animalId), null); final Mutable deathDate = new MutableObject<>(); final Mutable taskId = new MutableObject<>(); + final Mutable performedBy = new MutableObject<>(); + final Mutable disposition = new MutableObject<>(); deathsTs.forEach(rs -> { if (rs.getString("date") != null) { Date date = ConvertHelper.convert(rs.getString("date"), Date.class); deathDate.setValue(date); taskId.setValue(rs.getString("taskid")); + performedBy.setValue(rs.getString("performedBy")); + disposition.setValue(rs.getString("reason")); } }); @@ -431,7 +437,9 @@ public void sendDeathNotification(final String animalId) throws Exception html.append("Death date not found. Please contact system administrator.").append("
"); return; } - html.append("Animal '").append(PageFlowUtil.filter(animalId)).append("' has been declared dead on '").append(_dateFormat.format(deathDate.getValue())).append("'.

"); + html.append("Animal '").append(PageFlowUtil.filter(animalId)).append("' has been declared dead on '").append(_dateFormat.format(deathDate.getValue())).append("'.
"); + html.append("Performed By: ").append(PageFlowUtil.filter(performedBy.getValue())).append("
"); + html.append("Disposition: ").append(PageFlowUtil.filter(disposition.getValue())).append("

"); //append animal details appendAnimalDetails(html, animalId, container); @@ -493,9 +501,8 @@ private void appendAnimalDetails(StringBuilder html, String id, final Container private String getProject(String id) { - TableInfo ti = getTableInfo("study", "assignment"); + TableInfo ti = getTableInfo("study", "notificationAnimalProject"); SimpleFilter filter = new SimpleFilter(FieldKey.fromString("Id"), id); - filter.addCondition(FieldKey.fromString("enddate"), null, CompareType.ISBLANK); TableSelector ts = new TableSelector(ti, PageFlowUtil.set("project"), filter, null); final Mutable project = new MutableObject<>(); ts.forEach(rs -> { 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 173fa82b..29320bc5 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 @@ -469,7 +469,7 @@ public void testClinicalObservation() Ext4GridRef observationOrders = _helper.getExt4GridForFormSection("Observation Orders"); _helper.addRecordToGrid(observationOrders); - observationOrders.setGridCell(1, "category", "Lameness/Disuse"); + observationOrders.setGridCell(1, "category", "Lameness"); observationOrders.setGridCell(1, "frequency", "QID"); submitForm("Submit Final", "Finalize"); @@ -479,7 +479,7 @@ public void testClinicalObservation() DataRegionTable table = new AnimalHistoryPage<>(getDriver()).getActiveReportDataRegion(); table.setFilter("Id", "Equals", animalId); - List expected = Arrays.asList("Activity", "Appetite", "BCS", "Hydration", "Stool", "Verified Id?", "Lameness/Disuse"); + List expected = Arrays.asList("Activity", "Appetite", "BCS", "Hydration", "Stool", "Verified Id?", "Lameness"); List actual = table.getColumnDataAsText("category"); Collections.sort(expected); Collections.sort(actual); @@ -492,7 +492,7 @@ public void testClinicalObservation() table = new AnimalHistoryPage<>(getDriver()).getActiveReportDataRegion(); table.setFilter("Id", "Equals", animalId); Assert.assertEquals("Incorrect rows in Today's Observation Schedule", 4, table.getDataRowCount()); - Assert.assertEquals("Incorrect observation title", "Daily Clinical Observations; Lameness/Disuse", table.getDataAsText(0, "observationList")); + Assert.assertEquals("Incorrect observation title", "Daily Clinical Observations; Lameness", table.getDataAsText(0, "observationList")); Assert.assertEquals("Status is not updated", "", table.getDataAsText(0, "observationStatus")); table.link(0, "observationRecord").click();