diff --git a/onprc_ehr/resources/queries/study/Demographics_NotInMMA.query.xml b/onprc_ehr/resources/queries/study/Demographics_NotInMMA.query.xml
new file mode 100644
index 000000000..48f834dca
--- /dev/null
+++ b/onprc_ehr/resources/queries/study/Demographics_NotInMMA.query.xml
@@ -0,0 +1,9 @@
+
+
+
+
+ Demographics (Excluding animals in Weight MMA regimen)
+
+
+
+
diff --git a/onprc_ehr/resources/queries/study/Demographics_NotInMMA.sql b/onprc_ehr/resources/queries/study/Demographics_NotInMMA.sql
new file mode 100644
index 000000000..f197e4ee1
--- /dev/null
+++ b/onprc_ehr/resources/queries/study/Demographics_NotInMMA.sql
@@ -0,0 +1,54 @@
+/*
+ Created by Kollil in Dec 2025
+ Tkt # 13461
+ Added two filters to the Demographics dataset:
+ 1. Filter out any animal with the following SNOMED Codes:
+ Begin active weight management regimen (P-YY961)
+ However, we would need to include animals that have this additional SNOMED Code if it's entered AFTER the one above
+ Release from active weight management regimen (P-YY960)
+ 2. Remove Shelters, Corral and Hospital locations from the lists
+ */
+
+SELECT
+ d.Id.curlocation.area AS Area,
+ d.Id.curlocation.room AS Room,
+ d.Id.curlocation.cage AS Cage,
+ d.Id,
+ d.Id.utilization.use AS ProjectsAndGroups,
+ d.species,
+ d.geographic_origin,
+ d.gender AS Sex,
+ d.calculated_status,
+ d.birth,
+ d.Id.Age.YearAndDays,
+ d.Id.MostRecentWeight.MostRecentWeight,
+ d.Id.MostRecentWeight.MostRecentWeightDate,
+ d.Id.viral_status.viralStatus,
+ d.history
+FROM Demographics d
+WHERE d.Id.curlocation.area NOT IN ('Shelters', 'Corral', 'Hospital')-- Exclude animals from these locations
+ AND NOT (-- Exclude females under 5yrs, males under 7yrs
+ (d.gender.code = 'f' AND d.Id.age.ageInYears < 5)
+ OR (d.gender.code = 'm' AND d.Id.age.ageInYears < 7)
+ )
+ AND NOT EXISTS (
+ -- -- Find animals whose latest 'Weight MMA BEGIN' has no later 'Weight MMA RELEASE'
+ SELECT 1
+ FROM study.WeightManagementMMAData b
+ WHERE b.Id = d.Id
+ AND b.code = 'P-YY961'
+ AND b.date = (
+ SELECT MAX(b2.date)
+ FROM study.WeightManagementMMAData b2
+ WHERE b2.Id = d.Id
+ AND b2.code = 'P-YY961'
+ )
+ AND NOT EXISTS (
+ SELECT 1
+ FROM study.WeightManagementMMAData r
+ WHERE r.Id = d.Id
+ AND r.code = 'P-YY960'
+ AND r.date > b.date
+ )
+)
+
diff --git a/onprc_ehr/resources/queries/study/weightConsecutiveDrops_NotInMMA.query.xml b/onprc_ehr/resources/queries/study/weightConsecutiveDrops_NotInMMA.query.xml
new file mode 100644
index 000000000..c781c7ded
--- /dev/null
+++ b/onprc_ehr/resources/queries/study/weightConsecutiveDrops_NotInMMA.query.xml
@@ -0,0 +1,50 @@
+
+
+
+
+ Weight Change, Relative to Previous Weight (Excluding the animals enrolled in MMA)
+ This query shows the percent change of each weight, relative to the weight immediately prior to it
+
+
+ true
+ true
+
+
+ /query/executeQuery.view?schemaName=study&
+ query.queryName=weight&
+ query.id~eq=${id}&
+ query.sort=-date
+
+
+
+ /query/executeQuery.view?schemaName=study&
+ query.queryName=weight&
+ query.date~eq=${PrevDate}&
+ query.sort=-date
+
+
+
+ /query/executeQuery.view?schemaName=study&
+ query.queryName=weight&
+ query.lsid~eq=${lsid}&
+
+ Percent Change
+
+
+ Interval (Days)
+
+
+ Current Weight (kg)
+
+
+ Weight Date
+
+
+
+
+
+ PctChange
+
+
+
+
diff --git a/onprc_ehr/resources/queries/study/weightConsecutiveDrops_NotInMMA.sql b/onprc_ehr/resources/queries/study/weightConsecutiveDrops_NotInMMA.sql
new file mode 100644
index 000000000..13fbd4571
--- /dev/null
+++ b/onprc_ehr/resources/queries/study/weightConsecutiveDrops_NotInMMA.sql
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2013-2019 LabKey Corporation
+ *
+ * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
+ */
+--this query contains a handful of calculated fields for the weights table
+--it is designed to be joined into weights using lsid
+
+SELECT
+ w.lsid,
+ w.Id,
+ w.date,
+ w.weight AS curWeight,
+ pd1.PrevDate as prevDate1,
+ pw1.weight AS prevWeight1,
+ Round(((w.weight - pw1.weight) * 100 / w.weight), 1) AS pctChange1,
+ timestampdiff('SQL_TSI_DAY', pw1.date, w.date) AS interval1,
+ pd2.PrevDate as PrevDate2,
+ pw2.weight AS PrevWeight2,
+ Round(((pw1.weight - pw2.weight) * 100 / pw1.weight), 1) AS PctChange2,
+ timestampdiff('SQL_TSI_DAY', pw2.date, pw1.date) AS Interval2
+FROM study.weight w
+ --Find the next most recent weight date before this one
+ JOIN
+ (SELECT T2.Id, T2.date, max(T1.date) as PrevDate
+ FROM study.weight T1
+ JOIN study.weight T2 ON (T1.Id = T2.Id AND T1.date < T2.date)
+ WHERE t1.qcstate.publicdata = true AND t2.qcstate.publicdata = true
+ GROUP BY T2.Id, T2.date) pd1
+ ON (w.Id = pd1.Id AND w.date = pd1.Date)
+
+ --and the weight associated with that date
+ JOIN study.weight pw1
+ ON (w.Id = pw1.Id AND pw1.date = pd1.prevdate AND pw1.qcstate.publicdata = true)
+
+ --then find the next most recent date
+ LEFT JOIN
+ (SELECT T2.Id, T2.date, max(T1.date) as PrevDate
+ FROM study.weight T1
+ JOIN study.weight T2 ON (T1.Id = T2.Id AND T1.date < T2.date)
+ WHERE t1.qcstate.publicdata = true AND t2.qcstate.publicdata = true
+ GROUP BY T2.Id, T2.date
+ ) pd2
+ ON (pd1.Id = pd2.Id AND pd1.PrevDate = pd2.date)
+
+ --and the weight associated with that date
+ JOIN study.weight pw2
+ ON (w.Id = pw2.Id AND pw2.date = pd2.prevdate AND pw2.qcstate.publicdata = true)
+WHERE
+ w.qcstate.publicdata = true
+ AND pd1.date is not null
+ AND pd2.date is not null
+ --only include drops
+ AND w.weight < pw1.weight
+ AND pw1.weight < pw2.weight
+ AND ((w.weight - pw2.weight) * 100 / w.weight) < -3.0
+
+ AND w.Id.curlocation.area NOT IN ('Shelters', 'Corral', 'Hospital')-- Exclude animals from these locations
+ AND NOT (-- Exclude females under 5yrs, males under 7yrs
+ (w.Id.demographics.gender.code = 'f' AND w.Id.age.ageInYears < 5)
+ OR (w.Id.demographics.gender.code = 'm' AND w.Id.age.ageInYears < 7)
+ )
+ AND w.Id NOT IN (
+ SELECT 1 -- Find animals whose latest 'Weight MMA BEGIN' has no later 'Weight MMA RELEASE'
+ FROM study.WeightManagementMMAData b
+ WHERE b.Id = w.Id
+ AND b.code = 'P-YY961'
+ AND b.date = (
+ SELECT MAX(b2.date)
+ FROM study.WeightManagementMMAData b2
+ WHERE b2.Id = w.Id
+ AND b2.code = 'P-YY961'
+ )
+ AND NOT EXISTS (
+ SELECT 1
+ FROM study.WeightManagementMMAData r
+ WHERE r.Id = w.Id
+ AND r.code = 'P-YY960'
+ AND r.date > b.date
+ )
+ )
diff --git a/onprc_ehr/resources/queries/study/weightRelChange_NotInMMA.query.xml b/onprc_ehr/resources/queries/study/weightRelChange_NotInMMA.query.xml
new file mode 100644
index 000000000..5b6170eca
--- /dev/null
+++ b/onprc_ehr/resources/queries/study/weightRelChange_NotInMMA.query.xml
@@ -0,0 +1,68 @@
+
+
+
+
+ Weight Change, Relative to Current Weight (Excluding the animals enrolled in MMA)
+ This query shows the percent change of each weight, relative to the current weight
+
+
+ true
+ true
+
+
+ true
+
+ study
+ animal
+ id
+
+
+
+ true
+ Date of Last Weight
+
+
+ Old Weight (kg)
+
+
+ % Change Relative To Current
+
+
+
+
+
+ true
+ FF0000
+
+
+
+
+
+ true
+ 458B00
+
+
+
+
+ Days Since Weight
+
+
+ Months Since Weight
+
+
+ Latest Weight (kg)
+ /query/executeQuery.view?schemaName=study&
+ query.queryName=weight&
+ query.date~eq=${LatestWeightDate}&
+ query.sort=-date
+
+
+
+ Latest Weight Date
+
+
+ PctChange
+
+
+
+
diff --git a/onprc_ehr/resources/queries/study/weightRelChange_NotInMMA.sql b/onprc_ehr/resources/queries/study/weightRelChange_NotInMMA.sql
new file mode 100644
index 000000000..884348e4c
--- /dev/null
+++ b/onprc_ehr/resources/queries/study/weightRelChange_NotInMMA.sql
@@ -0,0 +1,62 @@
+/*
+ Created by Kollil in Dec 2025
+ Tkt # 13461
+ Added two filters to the Demographics dataset:
+ 1. Filter out any animal with the following SNOMED Codes:
+ Begin active weight management regimen (P-YY961)
+ However, we would need to include animals that have this additional SNOMED Code if it's entered AFTER the one above
+ Release from active weight management regimen (P-YY960)
+ 2. Remove Shelters, Corral and Hospital locations from the lists
+ */
+
+SELECT
+ w.lsid,
+ w.Id,
+ w.date,
+ w.Id.MostRecentWeight.MostRecentWeightDate as LatestWeightDate,
+ w.Id.MostRecentWeight.MostRecentWeight AS LatestWeight,
+
+ timestampdiff('SQL_TSI_DAY', w.date, w.Id.MostRecentWeight.MostRecentWeightDate) AS IntervalInDays,
+ age_in_months(w.date, w.Id.MostRecentWeight.MostRecentWeightDate) AS IntervalInMonths,
+
+ w.weight,
+ CASE WHEN w.date >= timestampadd('SQL_TSI_DAY', -730, w.Id.MostRecentWeight.MostRecentWeightDate) THEN
+ Round(((w.Id.MostRecentWeight.MostRecentWeight - w.weight) * 100 / w.weight), 1)
+ ELSE
+ null
+ END AS PctChange,
+
+ CASE WHEN w.date >= timestampadd('SQL_TSI_DAY', -730, w.Id.MostRecentWeight.MostRecentWeightDate) THEN
+ Abs(Round(((w.Id.MostRecentWeight.MostRecentWeight - w.weight) * 100 / w.weight), 1))
+ else
+ null
+ END AS AbsPctChange,
+ w.qcstate
+FROM study.weight w
+WHERE w.Id.curlocation.area NOT IN ('Shelters', 'Corral', 'Hospital')-- Exclude animals from these locations
+ AND NOT (-- Exclude females under 5yrs, males under 7yrs
+ (w.Id.demographics.gender.code = 'f' AND w.Id.age.ageInYears < 5)
+ OR (w.Id.demographics.gender.code = 'm' AND w.Id.age.ageInYears < 7)
+ )
+ AND w.qcstate.publicdata = true
+ AND NOT EXISTS (
+ -- -- Find animals whose latest 'Weight MMA BEGIN' has no later 'Weight MMA RELEASE'
+ SELECT 1
+ FROM study.WeightManagementMMAData b
+ WHERE b.Id = w.Id
+ AND b.code = 'P-YY961'
+ AND b.date = (
+ SELECT MAX(b2.date)
+ FROM study.WeightManagementMMAData b2
+ WHERE b2.Id = w.Id
+ AND b2.code = 'P-YY961'
+ )
+ AND NOT EXISTS (
+ SELECT 1
+ FROM study.WeightManagementMMAData r
+ WHERE r.Id = w.Id
+ AND r.code = 'P-YY960'
+ AND r.date > b.date
+ )
+ )
+
diff --git a/onprc_ehr/src/org/labkey/onprc_ehr/notification/WeightAlertsNotification.java b/onprc_ehr/src/org/labkey/onprc_ehr/notification/WeightAlertsNotification.java
index b661a3a64..b16ec0b25 100644
--- a/onprc_ehr/src/org/labkey/onprc_ehr/notification/WeightAlertsNotification.java
+++ b/onprc_ehr/src/org/labkey/onprc_ehr/notification/WeightAlertsNotification.java
@@ -20,6 +20,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.Results;
import org.labkey.api.data.ResultsImpl;
import org.labkey.api.data.Selector;
@@ -50,6 +51,10 @@
* User: bbimber
* Date: 7/23/12
* Time: 7:41 PM
+ *
+ * Modified by Kollil as per ticket # 13461
+ * Date: Jan 2026
+ *
*/
public class WeightAlertsNotification extends AbstractEHRNotification
{
@@ -79,13 +84,13 @@ public String getEmailSubject(Container c)
@Override
public String getCronString()
{
- return "0 15 9 ? * MON";
- }
+ return "0 0 12 ? * THU";
+ } //Made changes to the alert by Kollil, Refer to tkt # 13461
@Override
public String getScheduleDescription()
{
- return "every Monday, at 9:15 AM";
+ return "every Thursday, at 12pm";
}
@Override
@@ -97,7 +102,11 @@ public String getMessageBodyHTML(Container c, User u)
Date now = new Date();
msg.append("This email contains alerts of significant weight changes. It was run on: " + getDateFormat(c).format(now) + " at " + _timeFormat.format(now) + ".
");
- getLivingWithoutWeight(c, u, msg);
+ /*
+ Changed by Kollil: 1/2026, tkt #13461
+ 1. Delete the "animals do not have a weight" since that's captured on the colony management emails already.
+ */
+ //getLivingWithoutWeight(c, u, msg);
generateCombinedWeightTable(c, u, msg);
@@ -110,68 +119,36 @@ private void generateCombinedWeightTable(final Container c, User u, final String
//first weight drops
Set dropDistinctIds = new HashSet<>();
- processWeights(c, u, sb, 0, 30, CompareType.LTE, -10, dropDistinctIds);
- consecutiveWeightDrops(c, u, sb, dropDistinctIds);
+ processWeights(c, u, sb, 0, 100, CompareType.LTE, -10, dropDistinctIds);
+ /*
+ Changed by Kollil: 1/2026
+ Disabling this section as per ticket #13461
+ */
+// consecutiveWeightDrops(c, u, sb, dropDistinctIds);
if (!dropDistinctIds.isEmpty())
{
- String url = getExecuteQueryUrl(c, "study", "Demographics", "By Location") + "&query.calculated_status~eq=Alive&query.Id~in=" + (StringUtils.join(new ArrayList(dropDistinctIds), ";"));
+ String url = getExecuteQueryUrl(c, "study", "Demographics_NotInMMA", null ) + "&query.calculated_status~eq=Alive&query.Id~in=" + (StringUtils.join(new ArrayList(dropDistinctIds), ";"));
sb.insert(0, "WARNING: There are " + dropDistinctIds.size() + " animals that experienced either a large weight loss, or 3 consecutive weight drops. Click here to view this list, or view the data below.
");
}
//also weight gains
Set gainDistinctIds = new HashSet<>();
- processWeights(c, u, sb, 0, 30, CompareType.GTE, 10, gainDistinctIds);
+ processWeights(c, u, sb, 0, 100, CompareType.GTE, 10, gainDistinctIds);
if (!gainDistinctIds.isEmpty())
{
- String url = getExecuteQueryUrl(c, "study", "Demographics", "By Location") + "&query.calculated_status~eq=Alive&query.Id~in=" + (StringUtils.join(new ArrayList(gainDistinctIds), ";"));
+ String url = getExecuteQueryUrl(c, "study", "Demographics_NotInMMA", null) + "&query.calculated_status~eq=Alive&query.Id~in=" + (StringUtils.join(new ArrayList(gainDistinctIds), ";"));
sb.insert(0, "WARNING: There are " + gainDistinctIds.size() + " animals that experienced large weight gain (>10%). Click here to view this list, or view the data below.
");
}
msg.append(sb);
}
- private void getLivingWithoutWeight(final Container c, User u, final StringBuilder msg)
- {
- SimpleFilter filter = new SimpleFilter(FieldKey.fromString("calculated_status"), "Alive");
- filter.addCondition(FieldKey.fromString("Id/MostRecentWeight/MostRecentWeightDate"), null, CompareType.ISBLANK);
- Sort sort = new Sort(getStudy(c).getSubjectColumnName());
-
- TableInfo ti = getStudySchema(c, u).getTable("Demographics");
- List colKeys = new ArrayList<>();
- colKeys.add(FieldKey.fromString(getStudy(c).getSubjectColumnName()));
- colKeys.add(FieldKey.fromString("Id/age/AgeFriendly"));
- final Map columns = QueryService.get().getColumns(ti, colKeys);
-
- TableSelector ts = new TableSelector(ti, columns.values(), filter, sort);
- if (ts.exists())
- {
- msg.append("WARNING: The animals listed below do not have a weight.\n");
- msg.append(" Click here to view these animals
\n");
-
- ts.forEach(new TableSelector.ForEachBlock<>()
- {
- @Override
- public void exec(ResultSet rs) throws SQLException
- {
- Results results = new ResultsImpl(rs, columns);
- msg.append(rs.getString(getStudy(c).getSubjectColumnName()));
- String age = results.getString(FieldKey.fromString("Id/age/AgeFriendly"));
- if (age != null)
- msg.append(" (Age: " + age + ")");
-
- msg.append("
\n");
- }
- });
-
- msg.append("
\n");
- }
- }
-
private void processWeights(Container c, User u, final StringBuilder msg, int min, int max, CompareType ct, double pct, @Nullable Set distinctIds)
{
- TableInfo ti = getStudySchema(c, u).getTable("weightRelChange");
+ TableInfo ti = QueryService.get().getUserSchema(u, c, "study").getTable("weightRelChange_NotInMMA", ContainerFilter.Type.AllFolders.create(c, u));
+// TableInfo ti = getStudySchema(c, u).getTable("weightRelChange");
assert ti != null;
final FieldKey areaKey = FieldKey.fromString("Id/curLocation/Area");
@@ -180,7 +157,7 @@ private void processWeights(Container c, User u, final StringBuilder msg, int mi
final FieldKey ageKey = FieldKey.fromString("Id/age/AgeFriendly");
final FieldKey problemKey = FieldKey.fromString("Id/openProblems/problems");
final FieldKey investKey = FieldKey.fromString("Id/activeAssignments/investigators");
- final FieldKey vetsKey = FieldKey.fromString("Id/activeAssignments/vets");
+ final FieldKey vetsKey = FieldKey.fromString("Id/assignedVet/AssignedVet");
final FieldKey peKey = FieldKey.fromString("Id/physicalExamHistory/daysSinceExam");
List colKeys = new ArrayList<>();
@@ -206,7 +183,7 @@ private void processWeights(Container c, User u, final StringBuilder msg, int mi
filter.addCondition(FieldKey.fromString("IntervalInDays"), max, CompareType.LTE);
Calendar date = Calendar.getInstance();
- date.add(Calendar.DATE, -30);
+ date.add(Calendar.DATE, -100); /// change to last 100 days
filter.addCondition(FieldKey.fromString("LatestWeightDate"), getDateFormat(c).format(date.getTime()), CompareType.DATE_GTE);
TableSelector ts = new TableSelector(ti, columns.values(), filter, null);
@@ -262,14 +239,13 @@ public void exec(ResultSet object) throws SQLException
if (!summary.isEmpty())
{
- msg.append("Click here to view these " + distinctAnimals.size() + " animals
\n");
+ msg.append("Click here to view these " + distinctAnimals.size() + " animals
\n");
msg.append("| Area | Room | Cage | Id | Investigators | Responsible Vet | Open Problems | Days Since Last PE | Weight Dates | Days Between | Weight (kg) | Percent Change |
");
for (String area : summary.keySet())
{
Map>> areaValue = summary.get(area);
- for (String room : areaValue.keySet())
- {
+ for (String room : areaValue.keySet()) {
List
\n");
msg.append("
");
}
- else
- {
+ else {
msg.append("There are no changes during this period.
");
}
@@ -314,6 +289,7 @@ public void exec(ResultSet object) throws SQLException
distinctIds.addAll(distinctAnimals);
}
+
protected void consecutiveWeightDrops(final Container c, User u, final StringBuilder msg, @Nullable Set distinctIds)
{
SimpleFilter filter = new SimpleFilter(FieldKey.fromString("Id/dataset/demographics/calculated_status"), "Alive");
@@ -326,7 +302,7 @@ protected void consecutiveWeightDrops(final Container c, User u, final StringBui
sort.appendSortColumn(new Sort.SortField(FieldKey.fromString("Id/curLocation/room"), Sort.SortDirection.ASC));
sort.appendSortColumn(new Sort.SortField(FieldKey.fromString("Id/curLocation/cage"), Sort.SortDirection.ASC));
- TableInfo ti = getStudySchema(c, u).getTable("weightConsecutiveDrops");
+ TableInfo ti = getStudySchema(c, u).getTable("weightConsecutiveDrops_NotInMMA");
assert ti != null;
List colKeys = new ArrayList<>();
@@ -404,7 +380,7 @@ public void exec(ResultSet rs) throws SQLException
tableMsg.append("\n");
- msg.append("Click here to view these " + animalIds.size() + " animals
\n");
+ msg.append("Click here to view these " + animalIds.size() + " animals
\n");
msg.append(tableMsg);
msg.append("
\n");
@@ -413,6 +389,43 @@ public void exec(ResultSet rs) throws SQLException
}
}
+ // private void getLivingWithoutWeight(final Container c, User u, final StringBuilder msg)
+// {
+// SimpleFilter filter = new SimpleFilter(FieldKey.fromString("calculated_status"), "Alive");
+// filter.addCondition(FieldKey.fromString("Id/MostRecentWeight/MostRecentWeightDate"), null, CompareType.ISBLANK);
+// Sort sort = new Sort(getStudy(c).getSubjectColumnName());
+//
+// TableInfo ti = getStudySchema(c, u).getTable("Demographics");
+// List colKeys = new ArrayList<>();
+// colKeys.add(FieldKey.fromString(getStudy(c).getSubjectColumnName()));
+// colKeys.add(FieldKey.fromString("Id/age/AgeFriendly"));
+// final Map columns = QueryService.get().getColumns(ti, colKeys);
+//
+// TableSelector ts = new TableSelector(ti, columns.values(), filter, sort);
+// if (ts.exists())
+// {
+// msg.append("WARNING: The animals listed below do not have a weight.\n");
+// msg.append(" Click here to view these animals\n");
+//
+// ts.forEach(new TableSelector.ForEachBlock<>()
+// {
+// @Override
+// public void exec(ResultSet rs) throws SQLException
+// {
+// Results results = new ResultsImpl(rs, columns);
+// msg.append(rs.getString(getStudy(c).getSubjectColumnName()));
+// String age = results.getString(FieldKey.fromString("Id/age/AgeFriendly"));
+// if (age != null)
+// msg.append(" (Age: " + age + ")");
+//
+// msg.append("
\n");
+// }
+// });
+//
+// msg.append("
\n");
+// }
+// }
+
private String getValue(Results rs, String prop) throws SQLException
{
String val = rs.getString(FieldKey.fromString(prop));