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("| 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 |
");
+
+ ts2.forEach(object -> {
+ Results rs = new ResultsImpl(object, colMap);
+ String url = getParticipantURL(c, rs.getString("Id"));
+
+ msg.append("");
+ msg.append("| " + PageFlowUtil.filter(rs.getString("Id")) + " | \n");
+ msg.append("" + PageFlowUtil.filter(rs.getString("Sex")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("Room")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("Cage")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("project")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("Protocol")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("Title")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("ProjectInvestigator")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("AssignDate")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("ReleaseDate")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("ProjectedReleaseDate")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("assignmentType")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("assignCondition")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("projectedReleaseCondition")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("ConditionAtRelease")) + " | ");
+ msg.append("
");
+ });
+ msg.append("
");
+ }
+ 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("| 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 |
");
+
+ ts2.forEach(object -> {
+ Results rs = new ResultsImpl(object, colMap);
+ String url = getParticipantURL(c, rs.getString("Id"));
+
+ msg.append("");
+ msg.append("| " + PageFlowUtil.filter(rs.getString("Id")) + " | \n");
+ msg.append("" + PageFlowUtil.filter(rs.getString("Sex")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("Room")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("Cage")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("project")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("Protocol")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("Title")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("ProjectInvestigator")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("AssignDate")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("ReleaseDate")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("ProjectedReleaseDate")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("assignmentType")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("assignCondition")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("projectedReleaseCondition")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("ConditionAtRelease")) + " | ");
+ msg.append("
");
+ });
+ msg.append("
");
+ }
+ 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!
");