diff --git a/onprc_ehr/resources/queries/study/AssignmentsCreatedInPast1Day.query.xml b/onprc_ehr/resources/queries/study/AssignmentsCreatedInPast1Day.query.xml new file mode 100644 index 000000000..ba5b595d4 --- /dev/null +++ b/onprc_ehr/resources/queries/study/AssignmentsCreatedInPast1Day.query.xml @@ -0,0 +1,10 @@ + + + + + + Assignments created in past 24 hrs +
+
+
+
diff --git a/onprc_ehr/resources/queries/study/AssignmentsCreatedInPast1Day.sql b/onprc_ehr/resources/queries/study/AssignmentsCreatedInPast1Day.sql new file mode 100644 index 000000000..8a09aa654 --- /dev/null +++ b/onprc_ehr/resources/queries/study/AssignmentsCreatedInPast1Day.sql @@ -0,0 +1,19 @@ +SELECT + Id, + Id.demographics.gender as Sex, + Id.curlocation.room as Room, + Id.curlocation.cage as Cage, + project.displayname as project, + project.protocol.displayname as Protocol, + project.title as Title, + project.protocol.investigatorId.lastname as ProjectInvestigator, + CAST(date AS DATE) AS AssignDate, + CAST(enddate AS DATE) AS ReleaseDate, + CAST(projectedRelease AS DATE) AS ProjectedReleaseDate, + assignmentType, + assignCondition.meaning as AssignCondition, + projectedReleaseCondition.meaning as ProjectedReleaseCondition, + releaseCondition.meaning as ConditionAtRelease + +FROM Assignment +WHERE CAST(date AS DATE) >= TIMESTAMPADD('SQL_TSI_DAY', -1, NOW()) \ No newline at end of file diff --git a/onprc_ehr/resources/queries/study/AssignmentsReleasedInPast1Day.query.xml b/onprc_ehr/resources/queries/study/AssignmentsReleasedInPast1Day.query.xml new file mode 100644 index 000000000..bc2343418 --- /dev/null +++ b/onprc_ehr/resources/queries/study/AssignmentsReleasedInPast1Day.query.xml @@ -0,0 +1,10 @@ + + + + + + Assignments released in past 24 hrs +
+
+
+
diff --git a/onprc_ehr/resources/queries/study/AssignmentsReleasedInPast1Day.sql b/onprc_ehr/resources/queries/study/AssignmentsReleasedInPast1Day.sql new file mode 100644 index 000000000..b936474ae --- /dev/null +++ b/onprc_ehr/resources/queries/study/AssignmentsReleasedInPast1Day.sql @@ -0,0 +1,19 @@ +SELECT + Id, + Id.demographics.gender as Sex, + Id.curlocation.room as Room, + Id.curlocation.cage as Cage, + project.displayname as project, + project.protocol.displayname as Protocol, + project.title as Title, + project.protocol.investigatorId.lastname as ProjectInvestigator, + CAST(date AS DATE) AS AssignDate, + CAST(enddate AS DATE) AS ReleaseDate, + CAST(projectedRelease AS DATE) AS ProjectedReleaseDate, + assignmentType, + assignCondition.meaning as AssignCondition, + projectedReleaseCondition.meaning as ProjectedReleaseCondition, + releaseCondition.meaning as ConditionAtRelease + +FROM Assignment +WHERE CAST(enddate AS DATE) >= TIMESTAMPADD('SQL_TSI_DAY', -1, NOW()) \ No newline at end of file diff --git a/onprc_ehr/resources/queries/study/AssignmentsStartedPast1to7Days.query.xml b/onprc_ehr/resources/queries/study/AssignmentsStartedPast1to7Days.query.xml new file mode 100644 index 000000000..a65ccdd3d --- /dev/null +++ b/onprc_ehr/resources/queries/study/AssignmentsStartedPast1to7Days.query.xml @@ -0,0 +1,10 @@ + + + + + + Assignments started in past 1-7 days +
+
+
+
\ No newline at end of file diff --git a/onprc_ehr/resources/queries/study/AssignmentsStartedPast1to7Days.sql b/onprc_ehr/resources/queries/study/AssignmentsStartedPast1to7Days.sql new file mode 100644 index 000000000..020306760 --- /dev/null +++ b/onprc_ehr/resources/queries/study/AssignmentsStartedPast1to7Days.sql @@ -0,0 +1,20 @@ +SELECT + Id, + Id.demographics.gender as Sex, + Id.curlocation.room as Room, + Id.curlocation.cage as Cage, + project.displayname as project, + project.protocol.displayname as Protocol, + project.title as Title, + project.protocol.investigatorId.lastname as ProjectInvestigator, + CAST(date AS DATE) AS AssignDate, + CAST(enddate AS DATE) AS ReleaseDate, + CAST(projectedRelease AS DATE) AS ProjectedReleaseDate, + assignmentType, + assignCondition.meaning as AssignCondition, + projectedReleaseCondition.meaning as ProjectedReleaseCondition, + releaseCondition.meaning as ConditionAtRelease + +FROM Assignment +WHERE CAST(date AS DATE) >= TIMESTAMPADD('SQL_TSI_DAY', -1, NOW()) + AND CAST(date AS DATE) < TIMESTAMPADD('SQL_TSI_DAY', -7, NOW()) \ No newline at end of file diff --git a/onprc_ehr/resources/queries/study/AssignmentsStartingNext1to14Days.query.xml b/onprc_ehr/resources/queries/study/AssignmentsStartingNext1to14Days.query.xml new file mode 100644 index 000000000..165ef5863 --- /dev/null +++ b/onprc_ehr/resources/queries/study/AssignmentsStartingNext1to14Days.query.xml @@ -0,0 +1,10 @@ + + + + + + Assignments starting in next 1 to 14 days +
+
+
+
diff --git a/onprc_ehr/resources/queries/study/AssignmentsStartingNext1to14Days.sql b/onprc_ehr/resources/queries/study/AssignmentsStartingNext1to14Days.sql new file mode 100644 index 000000000..fcc73bac8 --- /dev/null +++ b/onprc_ehr/resources/queries/study/AssignmentsStartingNext1to14Days.sql @@ -0,0 +1,20 @@ +SELECT + Id, + Id.demographics.gender as Sex, + Id.curlocation.room as Room, + Id.curlocation.cage as Cage, + project.displayname as project, + project.protocol.displayname as Protocol, + project.title as Title, + project.protocol.investigatorId.lastname as ProjectInvestigator, + CAST(date AS DATE) AS AssignDate, + CAST(enddate AS DATE) AS ReleaseDate, + CAST(projectedRelease AS DATE) AS ProjectedReleaseDate, + assignmentType, + assignCondition.meaning as AssignCondition, + projectedReleaseCondition.meaning as ProjectedReleaseCondition, + releaseCondition.meaning as ConditionAtRelease + +FROM Assignment +WHERE CAST(date AS DATE) >= TIMESTAMPADD('SQL_TSI_DAY', 1, NOW()) + AND CAST(date AS DATE) < TIMESTAMPADD('SQL_TSI_DAY', 14, NOW()) \ No newline at end of file diff --git a/onprc_ehr/src/org/labkey/onprc_ehr/notification/BehaviorNotification.java b/onprc_ehr/src/org/labkey/onprc_ehr/notification/BehaviorNotification.java index 03f4a35f8..958d70a3f 100644 --- a/onprc_ehr/src/org/labkey/onprc_ehr/notification/BehaviorNotification.java +++ b/onprc_ehr/src/org/labkey/onprc_ehr/notification/BehaviorNotification.java @@ -17,10 +17,14 @@ import org.apache.commons.lang3.time.DateUtils; import org.json.JSONObject; +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.Results; +import org.labkey.api.data.ResultsImpl; import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.Selector; import org.labkey.api.data.SimpleFilter; import org.labkey.api.data.Sort; import org.labkey.api.data.SqlSelector; @@ -35,14 +39,18 @@ import org.labkey.api.security.User; import org.labkey.api.util.PageFlowUtil; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; /** * User: bimber @@ -107,6 +115,12 @@ public String getMessageBodyHTML(Container c, User u) NHPTraining_BehaviorAlert(c, u , msg); AlopeciaScoreAlert(c, u , msg); dcmNotesAlert(c, u , msg); + //Added by Kollil, Nov 2025 + //Refer to tkt # 13618 + assignmentsCreatedInPast1Day(c,u,msg); + assignmentsReleasedInPast1Day(c,u,msg); + assignmentsStartingNext1to14Days(c,u,msg); + assignmentsStartedPast1to7Days(c,u,msg); notesEndingToday(c, u, msg, Arrays.asList("BSU Notes"), null); saveValues(c, toSave); @@ -114,6 +128,212 @@ public String getMessageBodyHTML(Container c, User u) return msg.toString(); } + /* Added by Kollil Nov, 2025 + Priority 4: Add links to grids 3 and 4 in daily Behavior Alerts email (do not need to display full grid in email) + - for grid 3 - "There are __ assignments starting in the Next 1-14 days" with a link + Refer to tkt # 13618 + */ + private void assignmentsStartingNext1to14Days(final Container c, User u, final StringBuilder msg) + { + TableInfo ti = getStudySchema(c, u).getTable("AssignmentsStartingNext1to14Days"); + + TableSelector ts = new TableSelector(ti, null, new Sort("Id")); + long total = ts.getRowCount(); + + if (total > 0) + { + msg.append("Assignments starting in the next 1-14 days:

"); + msg.append( total + " entries found. "); + msg.append("Click here to view them\n"); + msg.append("


\n\n"); + } + else { + msg.append("WARNING: No assignments starting in the next 1-14 days!

\n"); + } + } + + /* Added by Kollil Nov, 2025 + Priority 4: Add links to grids 3 and 4 in daily Behavior Alerts email (do not need to display full grid in email) + - for grid 4 - "There were __ assignments started in the past 1-7 days" with a link + Refer to tkt # 13618 + */ + private void assignmentsStartedPast1to7Days(final Container c, User u, final StringBuilder msg) + { + TableInfo ti = getStudySchema(c, u).getTable("AssignmentsStartedPast1to7Days"); + + TableSelector ts = new TableSelector(ti, null, new Sort("Id")); + long total = ts.getRowCount(); + + if (total > 0) + { + msg.append("Assignments started in the past 1-7 days:

"); + msg.append( total + " entries found. "); + msg.append("Click here to view them\n"); + msg.append("


\n\n"); + } + else { + msg.append("WARNING: No assignments started in the past 1-7 days!

\n"); + } + } + + /* Added by Kollil Nov, 2025 + List of assignment records created by R&L in past 24hrs (this is the most important grid) + a. Replaces current grid “Assignments Modified in Past 1 Day” which captures any modifications to records in the last 24hrs + b. To clarify, not the same as records with the “assign date” within the past day + c. Include Day Leases + Refer to tkt # 13618 + */ + private void assignmentsCreatedInPast1Day(final Container c, User u, final StringBuilder msg) + { + 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; + } + //assignments query + TableInfo ti = QueryService.get().getUserSchema(u, c, "study").getTable("AssignmentsCreatedInPast1Day", ContainerFilter.Type.AllFolders.create(c, u)); + TableSelector ts = new TableSelector(ti, null, new Sort("Id")); + long count = ts.getRowCount(); + + //Get num of rows + if (count > 0) { + msg.append("Assignments created in the past 24hrs:

"); + msg.append( count + " entries found. "); + msg.append("Click here to view them in a separate window\n"); + msg.append("\n\n"); + + //Display the daily report in the email + Set columns = new HashSet<>(); + columns.add(FieldKey.fromString("Id")); + columns.add(FieldKey.fromString("Sex")); + columns.add(FieldKey.fromString("Room")); + columns.add(FieldKey.fromString("Cage")); + columns.add(FieldKey.fromString("project")); + columns.add(FieldKey.fromString("Protocol")); + columns.add(FieldKey.fromString("Title")); + columns.add(FieldKey.fromString("ProjectInvestigator")); + columns.add(FieldKey.fromString("AssignDate")); + columns.add(FieldKey.fromString("ReleaseDate")); + columns.add(FieldKey.fromString("ProjectedReleaseDate")); + columns.add(FieldKey.fromString("assignmentType")); + columns.add(FieldKey.fromString("assignCondition")); + columns.add(FieldKey.fromString("projectedReleaseCondition")); + columns.add(FieldKey.fromString("ConditionAtRelease")); + + final Map colMap = QueryService.get().getColumns(ti, columns); + TableSelector ts2 = new TableSelector(ti, colMap.values(), null, new Sort("Id")); + +// msg.append("


Assignments created in past 24hrs:

\n"); + msg.append(""); + msg.append(""); + msg.append(""); + + ts2.forEach(object -> { + Results rs = new ResultsImpl(object, colMap); + String url = getParticipantURL(c, rs.getString("Id")); + + msg.append(""); + msg.append("\n"); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + }); + msg.append("
Id Sex Room Cage Project Protocol Title Project Investigator Assign Date Release Date Projected Release Date Assignment Type Assign Condition Projected Release Condition Condition At Release
" + PageFlowUtil.filter(rs.getString("Id")) + " " + PageFlowUtil.filter(rs.getString("Sex")) + "" + PageFlowUtil.filter(rs.getString("Room")) + "" + PageFlowUtil.filter(rs.getString("Cage")) + "" + PageFlowUtil.filter(rs.getString("project")) + "" + PageFlowUtil.filter(rs.getString("Protocol")) + "" + PageFlowUtil.filter(rs.getString("Title")) + "" + PageFlowUtil.filter(rs.getString("ProjectInvestigator")) + "" + PageFlowUtil.filter(rs.getString("AssignDate")) + "" + PageFlowUtil.filter(rs.getString("ReleaseDate")) + "" + PageFlowUtil.filter(rs.getString("ProjectedReleaseDate")) + "" + PageFlowUtil.filter(rs.getString("assignmentType")) + "" + PageFlowUtil.filter(rs.getString("assignCondition")) + "" + PageFlowUtil.filter(rs.getString("projectedReleaseCondition")) + "" + PageFlowUtil.filter(rs.getString("ConditionAtRelease")) + "


"); + } + else { + msg.append("WARNING: No assignments created in the past 24hrs!

\n"); + } + } + + /* Added by Kollil Nov, 2025 + Grid 2: List of records with new “Release date” added within the last 24hrs + a. Omit Day Leases + Refer to tkt # 13618 + */ + private void assignmentsReleasedInPast1Day(final Container c, User u, final StringBuilder msg) + { + 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; + } + + //assignments query + TableInfo ti = QueryService.get().getUserSchema(u, c, "study").getTable("AssignmentsReleasedInPast1Day", ContainerFilter.Type.AllFolders.create(c, u)); + TableSelector ts = new TableSelector(ti, null, new Sort("Id")); + long count = ts.getRowCount(); + + //Get num of rows + if (count > 0) { + msg.append("Assignments with new \"Release date\" added in the last 24hrs:

"); + msg.append( count + " entries found. "); + msg.append("Click here to view them in a separate window\n"); + msg.append("\n\n"); + + //Display the daily report in the email + Set columns = new HashSet<>(); + columns.add(FieldKey.fromString("Id")); + columns.add(FieldKey.fromString("Sex")); + columns.add(FieldKey.fromString("Room")); + columns.add(FieldKey.fromString("Cage")); + columns.add(FieldKey.fromString("project")); + columns.add(FieldKey.fromString("Protocol")); + columns.add(FieldKey.fromString("Title")); + columns.add(FieldKey.fromString("ProjectInvestigator")); + columns.add(FieldKey.fromString("AssignDate")); + columns.add(FieldKey.fromString("ReleaseDate")); + columns.add(FieldKey.fromString("ProjectedReleaseDate")); + columns.add(FieldKey.fromString("assignmentType")); + columns.add(FieldKey.fromString("assignCondition")); + columns.add(FieldKey.fromString("projectedReleaseCondition")); + columns.add(FieldKey.fromString("ConditionAtRelease")); + + final Map colMap = QueryService.get().getColumns(ti, columns); + TableSelector ts2 = new TableSelector(ti, colMap.values(), null, new Sort("Id")); + +// msg.append("


Assignments with new \"Release date\" added within the last 24hrs:

\n"); + msg.append(""); + msg.append(""); + msg.append(""); + + ts2.forEach(object -> { + Results rs = new ResultsImpl(object, colMap); + String url = getParticipantURL(c, rs.getString("Id")); + + msg.append(""); + msg.append("\n"); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + }); + msg.append("
Id Sex Room Cage Project Protocol Title Project Investigator Assign Date Release Date Projected Release Date Assignment Type Assign Condition Projected Release Condition Condition At Release
" + PageFlowUtil.filter(rs.getString("Id")) + " " + PageFlowUtil.filter(rs.getString("Sex")) + "" + PageFlowUtil.filter(rs.getString("Room")) + "" + PageFlowUtil.filter(rs.getString("Cage")) + "" + PageFlowUtil.filter(rs.getString("project")) + "" + PageFlowUtil.filter(rs.getString("Protocol")) + "" + PageFlowUtil.filter(rs.getString("Title")) + "" + PageFlowUtil.filter(rs.getString("ProjectInvestigator")) + "" + PageFlowUtil.filter(rs.getString("AssignDate")) + "" + PageFlowUtil.filter(rs.getString("ReleaseDate")) + "" + PageFlowUtil.filter(rs.getString("ProjectedReleaseDate")) + "" + PageFlowUtil.filter(rs.getString("assignmentType")) + "" + PageFlowUtil.filter(rs.getString("assignCondition")) + "" + PageFlowUtil.filter(rs.getString("projectedReleaseCondition")) + "" + PageFlowUtil.filter(rs.getString("ConditionAtRelease")) + "


"); + } + else { + msg.append("WARNING: No assignments released in the past 24hrs!

\n"); + } + } /* Added by Kollil 08/22/2025 New alert/notification for when an animal receives an alopecia score of 4 or 5, but does not have an open behavioral case for alopecia. @@ -128,16 +348,15 @@ private void AlopeciaScoreAlert(final Container c, User u, final StringBuilder m msg.append("Animals with alopecia score of 4 or 5, but does not have an open behavioral case for alopecia:

"); if (total > 0) { - msg.append("There are " + total + " entries found. "); - msg.append("

Click here to view them

\n"); + msg.append( total + " entries found. "); + msg.append("Click here to view them\n"); msg.append("
\n\n"); } else { msg.append("WARNING: No animals found with alopecia score of 4 or 5, but does not have an open behavioral case for alopecia!

\n"); } - - } + } // Added by Kollil 11/04/2020 private void NHPTraining_BehaviorAlert(final Container c, User u, final StringBuilder msg) @@ -155,14 +374,13 @@ private void NHPTraining_BehaviorAlert(final Container c, User u, final StringBu if (total > 0) { msg.append("There are " + total + " entries found. "); - msg.append("

Click here to view them

\n"); + msg.append("Click here to view them\n"); msg.append("
\n\n"); } else { msg.append("WARNING: There are no NHP_Training entries where \"Training Result = In Progress\" for over 60 days!

\n"); } - } /* @@ -184,9 +402,9 @@ protected void dcmNotesAlert(final Container c, User u, final StringBuilder msg) long count = ts.getRowCount(); if (count > 0) { - msg.append("WARNING: There are " + count + " DCM action items.
\n"); - msg.append("

Click here to view them
\n\n"); - msg.append("



"); + msg.append("WARNING: There are " + count + " DCM action items.
\n"); + msg.append("Click here to view them
\n\n"); + msg.append("

"); } else { @@ -209,8 +427,8 @@ protected void dcmNotesAlert(final Container c, User u, final StringBuilder msg) msg.append("DCM Alerts:

"); if (count1 > 0) { msg.append("" + count1 + " DCM notes entries added yesterday where \"Category = Notes pertaining to DAR\".
\n"); - msg.append("

Click here to view them
\n\n"); - msg.append("



\n\n"); + msg.append("Click here to view them
\n\n"); + msg.append("

\n\n"); } else { @@ -228,8 +446,8 @@ New alert for DCM notes (category = notes pertaining to DAR) removed the previou if (count4 > 0) { msg.append("" + count4 + " DCM notes entries removed yesterday where \"Category = Notes pertaining to DAR\".
\n"); - msg.append("

Click here to view them
\n\n"); - msg.append("



\n\n"); + msg.append("Click here to view them
\n\n"); + msg.append("

\n\n"); } else { msg.append("WARNING: No DCM notes ended yesterday where \"Category = Notes pertaining to DAR\"!

"); @@ -243,8 +461,8 @@ New alert for DCM notes (category = notes pertaining to DAR) removed the previou if (count2 > 0) { msg.append("There are " + count2 + " flag(s) added yesterday.
\n"); - msg.append("

Click here to view them
\n\n"); - msg.append("


"); + msg.append("Click here to view them
\n\n"); + msg.append("
"); } else { @@ -258,8 +476,8 @@ New alert for DCM notes (category = notes pertaining to DAR) removed the previou long count3 = ts3.getRowCount(); if (count3 > 0) { msg.append("" + count3 + " flag(s) removed yesterday.
\n"); - msg.append("

Click here to view them
\n\n"); - msg.append("


"); + msg.append("Click here to view them
\n\n"); + msg.append("
"); } else { msg.append("WARNING: There are no flags removed yesterday!

");