diff --git a/onprc_ehr/resources/queries/study/TattooAlert.sql b/onprc_ehr/resources/queries/study/TattooAlert.sql new file mode 100644 index 000000000..46018da83 --- /dev/null +++ b/onprc_ehr/resources/queries/study/TattooAlert.sql @@ -0,0 +1,5 @@ +-- Displays all animals with tattoo procedure missing in the encounters table. Excluding the purchased animals +Select Id, birth, gender from study.demographics a +Where a.calculated_status = 'Alive' + And a.id not in (Select id from study.arrival where acquisitiontype = 6) --6= Purchase + And a.id not in (Select id from study.encounters where procedureid = 760) -- TATTOO procedure \ No newline at end of file diff --git a/onprc_ehr/resources/scripts/onprc_ehr/onprc_triggers.js b/onprc_ehr/resources/scripts/onprc_ehr/onprc_triggers.js index 6781fe524..4cb521606 100644 --- a/onprc_ehr/resources/scripts/onprc_ehr/onprc_triggers.js +++ b/onprc_ehr/resources/scripts/onprc_ehr/onprc_triggers.js @@ -721,6 +721,15 @@ exports.init = function(EHR){ } }); + //Added: By Kollil on 5/31/2022 + //When the Tattoo procedure is selected, add snomedcode: P-12090 TATTOOING + EHR.Server.TriggerManager.registerHandlerForQuery(EHR.Server.TriggerManager.Events.AFTER_UPSERT, 'study', 'encounters', function(helper, errors, row, oldRow){ + if (row.procedureid == 760){ + //Add snomed code. + triggerHelper.addTattooSnomedCode(row.Id, row); + } + }); + EHR.Server.TriggerManager.registerHandlerForQuery(EHR.Server.TriggerManager.Events.BEFORE_UPSERT, 'study', 'drug', function(helper, scriptErrors, row, oldRow){ if (row.outcome && row.outcome != 'Normal' && !row.remark){ EHR.Server.Utils.addError(scriptErrors, 'remark', 'A remark is required if a non-normal outcome is reported', 'WARN'); diff --git a/onprc_ehr/src/org/labkey/onprc_ehr/notification/ClinicalRoundsNotification.java b/onprc_ehr/src/org/labkey/onprc_ehr/notification/ClinicalRoundsNotification.java index 2c719c49d..e1324b62b 100644 --- a/onprc_ehr/src/org/labkey/onprc_ehr/notification/ClinicalRoundsNotification.java +++ b/onprc_ehr/src/org/labkey/onprc_ehr/notification/ClinicalRoundsNotification.java @@ -62,21 +62,27 @@ public String getEmailSubject(Container c) } @Override +// public String getCronString() +// { +// return "0 0 15 * * ?"; +// } + + //Kollil 10/13: Changed the daily alert to Tuesdays and Thursdays public String getCronString() { - return "0 0 15 * * ?"; + return "0 0 15 ? * TUE,THU"; } @Override public String getScheduleDescription() { - return "every day at 3PM"; + return "every Tuesday & Thursday at 3PM"; } @Override public String getDescription() { - return "The report is designed alert if there are any animals without rounds observations entered or lacking vet review"; + return "The report is designed to alert if there are any animals without rounds observations entered or lacking vet review. Also, contains the report to alert for Clinical rounds observations entered today, and not entered recently"; } @Override @@ -86,7 +92,11 @@ public String getMessageBodyHTML(Container c, User u) duplicateCases(c, u, msg); animalsWithoutRounds(c, u, msg); - //animalsWithoutVetReview(c, u, msg); + animalsWithoutVetReview(c, u, msg); + + //Clinical process alerts : Kollil, 10/13/22 + animalsWithRounds(c, u, msg); //Added: 8-22-2016 R.Blasa + //animalsWithoutRounds2(c, u, msg); //Added 8-29-2016 return msg.toString(); } @@ -112,7 +122,8 @@ protected void animalsWithoutRounds(final Container c, User u, final StringBuild long count = ts.getRowCount(); if (count > 0) { - msg.append("WARNING: There are " + count + " active cases that do not have obs entered today.
"); + msg.append("Clinical Rounds Alerts: Active cases that do not have observations.
"); + msg.append("WARNING: " + count + " active case(s) found that do not have obs entered today.
"); msg.append(""); msg.append(""); @@ -163,7 +174,8 @@ protected void animalsWithoutVetReview(final Container c, User u, final StringBu long count = ts.getRowCount(); if (count > 0) { - msg.append("WARNING: There are " + count + " active cases that have not been vet reviewed in the past 7 days.
"); + msg.append("Clinical Rounds Alerts: Active cases with no Vet review.
"); + msg.append("WARNING: " + count + " active case(s) found that have not been vet reviewed in the past 7 days.
"); msg.append("
RoomCageIdAssigned VetProblem(s)Days Since Last Rounds
"); msg.append(""); @@ -188,4 +200,108 @@ public void exec(ResultSet object) throws SQLException msg.append("
\n"); } } -} + + //Clinical process alerts +// + + //Modified: 8-15-2016 R.Blasa Show Clinical open cases that were entered + protected void animalsWithRounds(final Container c, User u, final StringBuilder msg) + { + SimpleFilter filter = new SimpleFilter(FieldKey.fromString("daysSinceLastRounds"), 0, CompareType.EQUAL); + filter.addCondition(FieldKey.fromString("isActive"), true, CompareType.EQUAL); + filter.addCondition(FieldKey.fromString("category"), "Clinical", CompareType.EQUAL); + filter.addCondition(FieldKey.fromString("Id/demographics/calculated_status"), "Alive", CompareType.EQUAL); + + TableInfo ti = getStudySchema(c, u).getTable("cases"); + Set keys = new HashSet<>(); + keys.add(FieldKey.fromString("Id")); + keys.add(FieldKey.fromString("Id/curLocation/room")); + keys.add(FieldKey.fromString("Id/curLocation/cage")); + keys.add(FieldKey.fromString("daysSinceLastRounds")); + keys.add(FieldKey.fromString("assignedvet/DisplayName")); + keys.add(FieldKey.fromString("allProblemCategories")); + final Map cols = QueryService.get().getColumns(ti, keys); + + TableSelector ts = new TableSelector(ti, cols.values(), filter, new Sort("Id/curLocation/room_sortValue,Id/curLocation/cage_sortValue")); + long count = ts.getRowCount(); + + if (count > 0) + { + msg.append("Clinical Rounds Process Alerts: Active cases that have observations.
"); + msg.append("CONFIRMATION: " + count + " active case(s) found that have their obs entered today.
"); + msg.append("
RoomCageIdAssigned VetProblem(s)Days Since last Vet Review
"); + msg.append(""); + + ts.forEach(new Selector.ForEachBlock() + { + @Override + public void exec(ResultSet object) throws SQLException + { + Results rs = new ResultsImpl(object, cols); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + + msg.append(""); + } + }); + + msg.append("
RoomCageIdAssigned VetProblem(s)
" + safeAppend(rs.getString(FieldKey.fromString("Id/curLocation/room")), "None") + "" + safeAppend(rs.getString(FieldKey.fromString("Id/curLocation/cage")), "") + "" + rs.getString(FieldKey.fromString("Id")) + "" + safeAppend(rs.getString(FieldKey.fromString("assignedvet/DisplayName")), "None") + "" + safeAppend(rs.getString(FieldKey.fromString("allProblemCategories")), "None") + "
"); + msg.append("
\n"); + } + } + + + // protected void animalsWithoutRounds2(final Container c, User u, final StringBuilder msg) +// { +// SimpleFilter filter = new SimpleFilter(FieldKey.fromString("daysSinceLastRounds"), 0, CompareType.GT); +// filter.addCondition(FieldKey.fromString("isActive"), true, CompareType.EQUAL); +// filter.addCondition(FieldKey.fromString("category"), "Clinical", CompareType.EQUAL); +// filter.addCondition(FieldKey.fromString("Id/demographics/calculated_status"), "Alive", CompareType.EQUAL); +// +// TableInfo ti = getStudySchema(c, u).getTable("cases"); +// Set keys = new HashSet<>(); +// keys.add(FieldKey.fromString("Id")); +// keys.add(FieldKey.fromString("Id/curLocation/room")); +// keys.add(FieldKey.fromString("Id/curLocation/cage")); +// keys.add(FieldKey.fromString("daysSinceLastRounds")); +// keys.add(FieldKey.fromString("assignedvet/DisplayName")); +// keys.add(FieldKey.fromString("allProblemCategories")); +// final Map cols = QueryService.get().getColumns(ti, keys); +// +// TableSelector ts = new TableSelector(ti, cols.values(), filter, new Sort("Id/curLocation/room_sortValue,Id/curLocation/cage_sortValue")); +// long count = ts.getRowCount(); +// +// if (count > 0) +// { +// msg.append("Clinical Rounds Process Alerts: Active cases that do not have observations entered today.
"); +// msg.append("WARNING: There are " + count + " active cases that do not have obs entered today.
"); +// msg.append(""); +// msg.append(""); +// +// ts.forEach(new Selector.ForEachBlock() +// { +// @Override +// public void exec(ResultSet object) throws SQLException +// { +// Results rs = new ResultsImpl(object, cols); +// msg.append(""); +// msg.append(""); +// msg.append(""); +// msg.append(""); +// msg.append(""); +// msg.append(""); +// msg.append(""); +// msg.append(""); +// } +// }); +// +// msg.append("
RoomCageIdAssigned VetProblem(s)Days Since Last Rounds
" + safeAppend(rs.getString(FieldKey.fromString("Id/curLocation/room")), "None") + "" + safeAppend(rs.getString(FieldKey.fromString("Id/curLocation/cage")), "") + "" + rs.getString(FieldKey.fromString("Id")) + "" + safeAppend(rs.getString(FieldKey.fromString("assignedvet/DisplayName")), "None") + "" + safeAppend(rs.getString(FieldKey.fromString("allProblemCategories")), "None") + "" + safeAppend(rs.getString(FieldKey.fromString("daysSinceLastRounds")), "") + "
"); +// msg.append("
\n"); +// } +// } + + } diff --git a/onprc_ehr/src/org/labkey/onprc_ehr/notification/RoutineClinicalTestsNotification.java b/onprc_ehr/src/org/labkey/onprc_ehr/notification/RoutineClinicalTestsNotification.java index e3ccb637a..66359d2ab 100644 --- a/onprc_ehr/src/org/labkey/onprc_ehr/notification/RoutineClinicalTestsNotification.java +++ b/onprc_ehr/src/org/labkey/onprc_ehr/notification/RoutineClinicalTestsNotification.java @@ -18,6 +18,7 @@ import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.CompareType; import org.labkey.api.data.Container; +import org.labkey.api.data.ContainerFilter; import org.labkey.api.data.ResultsImpl; import org.labkey.api.data.Selector; import org.labkey.api.data.SimpleFilter; @@ -92,8 +93,7 @@ public String getMessageBodyHTML(Container c, User u) getTbAlerts(sb, c, u); getPEAlerts(sb, c, u); getAnimalsNotWeightedInPast60Days(sb, c, u); - - //getVirologyAlerts(sb, c, u); + getTattooAlerts(sb, c, u); return sb.toString(); } @@ -295,4 +295,47 @@ protected void getAnimalsNotWeightedInPast60Days(StringBuilder msg, Container c, msg.append("
\n"); } + + /** + * Kollil, 6/01/22 : List the animalIds with missing Tattoos, i.e, Animals with no Tattoo procedure, ProcedureId = 760 + */ + protected void getTattooAlerts(StringBuilder msg, Container c, User u) + { + msg.append("Animals with missing Tattoo procedure:

\n"); + + if (QueryService.get().getUserSchema(u, c, "study") == null) { + msg.append("Warning: The study schema has not been enabled in this folder, so the alert cannot run!


"); + return; + } + //Tattoo query + TableInfo ti = QueryService.get().getUserSchema(u, c, "study").getTable("TattooAlert", ContainerFilter.Type.AllFolders.create(c, u)); + Set cols = appendLocationCols(ti); + TableSelector ts = new TableSelector(ti, cols, null, null); + long count = ts.getRowCount(); + + //Get num of rows + if (count > 0) + { + msg.append("ALERT: " + count + " animals found with no Tattoo procedure recorded in Prime:"); + //String url = getExecuteQueryUrl(c, "study", "Demographics", "By Location") + "&query.Id/MostRecentWeight/DaysSinceWeight~gt=75&query.calculated_status~eq=Alive&query.Id/curLocation/Room/housingType/value~eq=Cage Location"; + String url = getExecuteQueryUrl(c, "study", "TattooAlert", null) + "&query.containerFilterName=AllFolders"; + msg.append("Click here to view them.

\n"); + + msg.append("Summary by area:
\n"); + msg.append(""); + Map areaMap = getAreaMap(ts, cols); + for (String area : areaMap.keySet()) + { + String newUrl = url + "&query.Id/curLocation/area~eq=" + area; + msg.append("\n"); + } + msg.append("
" + area + ": " + areaMap.get(area) + "
"); + msg.append("

"); + } + else + { + msg.append(" All live animals at the center are tattooed! "); + } + msg.append("
\n"); + } } diff --git a/onprc_ehr/src/org/labkey/onprc_ehr/notification/VetReviewNotification.java b/onprc_ehr/src/org/labkey/onprc_ehr/notification/VetReviewNotification.java index 3e30b8c7e..e454b213d 100644 --- a/onprc_ehr/src/org/labkey/onprc_ehr/notification/VetReviewNotification.java +++ b/onprc_ehr/src/org/labkey/onprc_ehr/notification/VetReviewNotification.java @@ -63,15 +63,20 @@ public String getEmailSubject(Container c) } @Override +// public String getCronString() +// { +// return "0 0 15 * * ?"; +// } + //Kollil 10/13: Changed the daily alert to once a week, Wednesdays public String getCronString() { - return "0 0 15 * * ?"; + return "0 0 15 ? * WED"; } @Override public String getScheduleDescription() { - return "daily at 3PM"; + return "every Wednesday at 3PM"; } @Override diff --git a/onprc_ehr/src/org/labkey/onprc_ehr/query/ONPRC_EHRTriggerHelper.java b/onprc_ehr/src/org/labkey/onprc_ehr/query/ONPRC_EHRTriggerHelper.java index d70ffb5d5..ca58d900a 100644 --- a/onprc_ehr/src/org/labkey/onprc_ehr/query/ONPRC_EHRTriggerHelper.java +++ b/onprc_ehr/src/org/labkey/onprc_ehr/query/ONPRC_EHRTriggerHelper.java @@ -185,7 +185,8 @@ public Map getExtraContext() return map; } - //Added by Kolli 3/3/20 + + //Added by Kolli 3/3/20 //This function automatically sets the study details endDate if previous study details endDate is empty when a new study is created. public void closeStudyDetailsRecords(List> records) throws Exception { @@ -1093,6 +1094,7 @@ private Date maxDate(Date d1, Date d2) return (d1.after(d2)) ? d1 : d2; } + public void closeActiveAssignmentRecords(String id, Date deathDate, String causeOfDeath) throws Exception { try @@ -1147,6 +1149,45 @@ public void closeActiveAssignmentRecords(String id, Date deathDate, String cause } } + /*Added by Kolli 6/3/22 + When a tattoo procedure is entered into study.encounters table enter a snomed code into ehr.snomed_tags + Id - Animalid + Date - now() + code - TATTOOING + code/code - P-12090 + */ + public void addTattooSnomedCode(String id, @NotNull Map row) throws QueryUpdateServiceException, DuplicateKeyException, SQLException, BatchValidationException + { + if (id != null) + { + //_log.info("Animal Id thats tattooed: " + id); + Map snomedProps = new HashMap(); + + snomedProps.put("Id", row.get("Id")); + snomedProps.put("date", row.get("date")); + snomedProps.put("code", "P-12090"); + snomedProps.put("objectId", new GUID()); + snomedProps.put("container", _container.getId()); + + //_log.info("props: " + snomedProps); + + TableInfo ti = getTableInfo("ehr", "snomed_tags"); + Map new_row = new CaseInsensitiveHashMap<>(); + new_row.putAll(snomedProps); + + List> rows = new ArrayList<>(); + rows.add(new_row); + + BatchValidationException errors = new BatchValidationException(); + //Insert row into the db table + ti.getUpdateService().insertRows(getUser(), getContainer(), rows, errors, null, getExtraContext()); + + if (errors.hasErrors()) + throw errors; + + } + } + //Added on 10/5/2016, L.Kolli public Map onAnimalArrival_AddDemographics(String id, Map row) throws QueryUpdateServiceException, DuplicateKeyException, SQLException, BatchValidationException { @@ -1228,6 +1269,7 @@ public void createBirthRecord_ONPRC(String id, Map props) throws } } + // Added: 6-27-2017 F.Blasa Process when transitioning from Prenatal - Fetus to Live public void doBirthConditionAfterPrenatal(String id, Date date, String dam, Date Arrival_Date, String birthCondition, boolean isBecomingPublic) throws Exception { @@ -2119,6 +2161,7 @@ public String validateCaseNo(String caseNo, String type, String objectid) { SimpleFilter filter = new SimpleFilter(FieldKey.fromString("caseno"), caseNo); filter.addCondition(FieldKey.fromString("type"), type); + if (objectid != null) { filter.addCondition(FieldKey.fromString("objectid"), objectid, CompareType.NEQ_OR_NULL); @@ -2128,8 +2171,6 @@ public String validateCaseNo(String caseNo, String type, String objectid) { return "There is already an existing case with the number: " + caseNo; } - - return null; } @@ -2337,7 +2378,6 @@ public void exec(ResultSet rs) throws SQLException } - //Added 3-6-2019 Blasa public void sendProjectNotifications(Integer projectid) {