Skip to content

Commit 7709e8c

Browse files
Clinical Procedures Overdue Notification (#481)
* Procedure Overdue notification
1 parent 065e32c commit 7709e8c

7 files changed

Lines changed: 338 additions & 0 deletions

File tree

nirc_ehr/resources/queries/study/prcOverdue.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ SELECT
44
po.procedure,
55
po.windowStart,
66
po.windowEnd,
7+
timestampdiff('SQL_TSI_DAY', po.windowEnd, now()) as daysOverdue,
78
po.orderedby,
89
po.remark,
910
po.objectid,

nirc_ehr/resources/queries/study/prcOverdue/.qview.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,18 @@
77
<column name="Id/curLocation/cage"/>
88
<column name="date"/>
99
<column name="procedure"/>
10+
<column name="orderedby"/>
1011
<column name="windowStart"/>
1112
<column name="windowEnd"/>
13+
<column name="daysOverdue"/>
1214
<column name="remark"/>
1315
<column name="status"/>
1416
</columns>
1517
<filters>
1618
<filter column="Id/Demographics/calculated_status" operator="eq" value="Alive"/>
1719
</filters>
1820
<sorts>
21+
<sort column="daysOverdue" descending="true"/>
1922
<sort column="status" descending="false"/>
2023
<sort column="Id/curLocation/room"/>
2124
<sort column="Id/curLocation/cage"/>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
SELECT
2+
pod.Id,
3+
pod.Id.demographics.species.common_name AS species,
4+
pod.Id.curLocation.room.name AS room,
5+
pod.Id.curLocation.cage.cage AS cage,
6+
pod.procedure.name AS procedure,
7+
pod.orderedby.displayName AS orderedBy,
8+
pod.windowStart,
9+
pod.windowEnd,
10+
pod.daysOverdue,
11+
pod.remark,
12+
FROM prcOverdue pod
13+
ORDER BY pod.daysOverdue DESC
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<customView xmlns="http://labkey.org/data/xml/queryCustomView">
2+
<sorts>
3+
<sort column="daysOverdue" descending="true"/>
4+
</sorts>
5+
</customView>

nirc_ehr/src/org/labkey/nirc_ehr/NIRC_EHRModule.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import org.labkey.nirc_ehr.history.*;
5656
import org.labkey.nirc_ehr.notification.NIRCClinicalMoveNotification;
5757
import org.labkey.nirc_ehr.notification.NIRCDeathNotification;
58+
import org.labkey.nirc_ehr.notification.NIRCProcedureOverdueNotification;
5859
import org.labkey.nirc_ehr.query.NIRC_EHRUserSchema;
5960
import org.labkey.nirc_ehr.security.NIRCEHRVetTechRole;
6061
import org.labkey.nirc_ehr.table.NIRC_EHRCustomizer;
@@ -182,6 +183,7 @@ protected void doStartupAfterSpringConfig(ModuleContext moduleContext)
182183
registerDataEntry();
183184
NotificationService.get().registerNotification(new NIRCDeathNotification());
184185
NotificationService.get().registerNotification(new NIRCClinicalMoveNotification());
186+
NotificationService.get().registerNotification(new NIRCProcedureOverdueNotification());
185187

186188
// Ensure N: is mounted if it's configured, as it's being mapped in via a symlink/shortcut, so we can't
187189
// recognize paths using it based solely on their drive letter and mount just-in-time
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
package org.labkey.nirc_ehr.notification;
2+
3+
import org.jetbrains.annotations.Nullable;
4+
import org.labkey.api.data.Container;
5+
import org.labkey.api.data.TableInfo;
6+
import org.labkey.api.data.TableSelector;
7+
import org.labkey.api.ehr.notification.AbstractEHRNotification;
8+
import org.labkey.api.query.QueryService;
9+
import org.labkey.api.query.UserSchema;
10+
import org.labkey.api.security.User;
11+
import org.labkey.api.settings.AppProps;
12+
13+
import java.util.Date;
14+
import java.util.List;
15+
import java.util.Set;
16+
17+
18+
public class NIRCProcedureOverdueNotification extends AbstractEHRNotification
19+
{
20+
@Override
21+
public String getName()
22+
{
23+
return "NIRC Procedure Overdue Notification";
24+
}
25+
26+
@Override
27+
public String getScheduleDescription()
28+
{
29+
return "Weekly on Friday at 8:00 AM";
30+
}
31+
32+
@Override
33+
public String getCronString()
34+
{
35+
return "0 0 8 ? * 6";
36+
}
37+
38+
@Override
39+
public @Nullable String getMessageBodyHTML(Container c, User u)
40+
{
41+
List<ProcedureOverdue> procsOverdueList = getOverDueProcedures(c, u);
42+
StringBuilder html = new StringBuilder();
43+
html.append("<html>");
44+
45+
if (!procsOverdueList.isEmpty())
46+
{
47+
html.append("<h4>Animals with overdue procedures</h4>");
48+
appendTableHtml(c, html, procsOverdueList);
49+
}
50+
else
51+
{
52+
html.append("<h4>No overdue procedures</h4>");
53+
}
54+
55+
html.append("<br/>");
56+
57+
String procOverdueReportLink = AppProps.getInstance().getBaseServerUrl() + AppProps.getInstance().getContextPath() + c.getPath() + "/query-executeQuery.view?schemaName=study&query.queryName=prcOverdue";
58+
html.append("<a href='").append(procOverdueReportLink).append("'>");
59+
html.append("Click here to view Overdue Procedures report with additional actions.</a>");
60+
61+
html.append("</html>");
62+
return html.toString();
63+
}
64+
65+
private void appendTableHtml(Container c, StringBuilder html, List<ProcedureOverdue> procedureOverdueList)
66+
{
67+
html.append("<table style=\"border-collapse: collapse;\">");
68+
html.append("<tr>");
69+
html.append(NotificationHelper.getNotificationGridCellHeader(c, ("Id")));
70+
html.append(NotificationHelper.getNotificationGridCellHeader(c, ("Species")));
71+
html.append(NotificationHelper.getNotificationGridCellHeader(c,"Room"));
72+
html.append(NotificationHelper.getNotificationGridCellHeader(c,"Cage"));
73+
html.append(NotificationHelper.getNotificationGridCellHeader(c,"Procedure"));
74+
html.append(NotificationHelper.getNotificationGridCellHeader(c,"Ordered By"));
75+
html.append(NotificationHelper.getNotificationGridCellHeader(c,"Window Start"));
76+
html.append(NotificationHelper.getNotificationGridCellHeader(c,"Window End"));
77+
html.append(NotificationHelper.getNotificationGridCellHeader(c,"Days Overdue"));
78+
html.append(NotificationHelper.getNotificationGridCellHeader(c,"Remark"));
79+
80+
html.append("</tr>");
81+
82+
for (int i = 0; i < procedureOverdueList.size(); i++)
83+
{
84+
ProcedureOverdue pod = procedureOverdueList.get(i);
85+
String bgColor = i % 2 == 0 ? "#f2f2f2" : "#fff";
86+
html.append("<tr style=\"background-color:").append(bgColor).append("\">");
87+
88+
html.append(NotificationHelper.getNotificationGridCell(c, pod.getId(), null, false, true, false));
89+
html.append(NotificationHelper.getNotificationGridCell(c, pod.getSpecies()));
90+
html.append(NotificationHelper.getNotificationGridCell(c, pod.getRoom()));
91+
html.append(NotificationHelper.getNotificationGridCell(c, pod.getCage()));
92+
html.append(NotificationHelper.getNotificationGridCell(c, pod.getProcedure()));
93+
html.append(NotificationHelper.getNotificationGridCell(c, pod.getOrderedBy()));
94+
html.append(NotificationHelper.getNotificationGridCell(c, NotificationHelper.getFormattedDate(c, pod.getWindowStart())));
95+
html.append(NotificationHelper.getNotificationGridCell(c, NotificationHelper.getFormattedDate(c, pod.getWindowEnd())));
96+
html.append(NotificationHelper.getNotificationGridCell(c, String.valueOf(pod.getDaysOverdue())));
97+
html.append(NotificationHelper.getNotificationGridCell(c, pod.getRemark()));
98+
html.append("</tr>");
99+
}
100+
101+
html.append("</table>");
102+
}
103+
104+
private List<ProcedureOverdue> getOverDueProcedures(Container c, User u) throws IllegalStateException
105+
{
106+
UserSchema userSchema = QueryService.get().getUserSchema(u, c, "study");
107+
TableInfo tableInfo = userSchema.getTable("prcOverdueNotification", null);
108+
109+
if (null == tableInfo)
110+
{
111+
throw new IllegalStateException("Expected 'prcOverdueNotification' query for the 'NIRC Procedure Overdue' notification");
112+
}
113+
114+
TableSelector tableSelector = new TableSelector(tableInfo, Set.of("Id", "species", "room", "cage", "procedure", "orderedBy", "windowStart", "windowEnd", "daysOverdue", "remark"));
115+
return tableSelector.getArrayList(ProcedureOverdue.class);
116+
}
117+
118+
@Override
119+
public String getDescription()
120+
{
121+
return "Weekly notification sent Friday morning for animals with procedures past the scheduled window";
122+
}
123+
124+
@Override
125+
public String getEmailSubject(Container c)
126+
{
127+
return "Procedure overdue notification";
128+
}
129+
130+
public static class ProcedureOverdue
131+
{
132+
public String Id;
133+
public String species;
134+
public String room;
135+
public String cage;
136+
public String cageObjectId;
137+
public String procedure;
138+
public String orderedBy;
139+
public Date windowStart;
140+
public Date windowEnd;
141+
public int daysOverdue;
142+
public String remark;
143+
144+
public String getId()
145+
{
146+
return Id;
147+
}
148+
149+
public void setId(String id)
150+
{
151+
Id = id;
152+
}
153+
154+
public String getSpecies()
155+
{
156+
return species;
157+
}
158+
159+
public void setSpecies(String species)
160+
{
161+
this.species = species;
162+
}
163+
164+
public String getRoom()
165+
{
166+
return room;
167+
}
168+
169+
public void setRoom(String room)
170+
{
171+
this.room = room;
172+
}
173+
174+
public String getCage()
175+
{
176+
return cage;
177+
}
178+
179+
public void setCage(String cage)
180+
{
181+
this.cage = cage;
182+
}
183+
184+
public String getCageObjectId()
185+
{
186+
return cageObjectId;
187+
}
188+
189+
public void setCageObjectId(String cageObjectId)
190+
{
191+
this.cageObjectId = cageObjectId;
192+
}
193+
194+
public String getProcedure()
195+
{
196+
return procedure;
197+
}
198+
199+
public void setProcedure(String procedure)
200+
{
201+
this.procedure = procedure;
202+
}
203+
204+
public String getOrderedBy()
205+
{
206+
return orderedBy;
207+
}
208+
209+
public void setOrderedBy(String orderedBy)
210+
{
211+
this.orderedBy = orderedBy;
212+
}
213+
214+
public Date getWindowStart()
215+
{
216+
return windowStart;
217+
}
218+
219+
public void setWindowStart(Date windowStart)
220+
{
221+
this.windowStart = windowStart;
222+
}
223+
224+
public Date getWindowEnd()
225+
{
226+
return windowEnd;
227+
}
228+
229+
public void setWindowEnd(Date windowEnd)
230+
{
231+
this.windowEnd = windowEnd;
232+
}
233+
234+
public int getDaysOverdue()
235+
{
236+
return daysOverdue;
237+
}
238+
239+
public void setDaysOverdue(int daysOverdue)
240+
{
241+
this.daysOverdue = daysOverdue;
242+
}
243+
244+
public String getRemark()
245+
{
246+
return remark;
247+
}
248+
249+
public void setRemark(String remark)
250+
{
251+
this.remark = remark;
252+
}
253+
}
254+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package org.labkey.nirc_ehr.notification;
2+
3+
import jakarta.annotation.Nullable;
4+
import org.labkey.api.data.Container;
5+
import org.labkey.api.settings.AppProps;
6+
import org.labkey.api.settings.LookAndFeelProperties;
7+
import org.labkey.api.util.PageFlowUtil;
8+
9+
import java.text.SimpleDateFormat;
10+
import java.util.Date;
11+
12+
public class NotificationHelper
13+
{
14+
private static final String NOTIFICATION_GRID_TD_STYLE = "padding: 5px; vertical-align: top; border: 1px solid #000000;";
15+
16+
public static String getNotificationGridCell(Container c, String value, @Nullable String additionalStyle, boolean isHeader, boolean isId, boolean unsafePassThru)
17+
{
18+
String formattedValue = PageFlowUtil.filter(value);
19+
if (unsafePassThru)
20+
{
21+
formattedValue = value;
22+
}
23+
24+
if (isId)
25+
{
26+
String idLink = AppProps.getInstance().getBaseServerUrl() +
27+
AppProps.getInstance().getContextPath() +
28+
c.getPath() +
29+
"/ehr-participantView.view?participantId=" + formattedValue +
30+
"&inputType:singleSubject&showReport:0&activeReport:snapshot";
31+
32+
formattedValue = "<a href=\"" + idLink + "\">" + formattedValue + "</a>";
33+
}
34+
35+
if (isHeader)
36+
{
37+
return "<th style=\"" + NOTIFICATION_GRID_TD_STYLE + additionalStyle + "\"><b>" + formattedValue + "</b></th>";
38+
}
39+
else
40+
{
41+
return "<td style=\"" + NOTIFICATION_GRID_TD_STYLE + additionalStyle + "\">" + formattedValue + "</td>";
42+
}
43+
}
44+
45+
public static String getNotificationGridCell(Container c, String value)
46+
{
47+
return getNotificationGridCell(c, value, null, false, false, false);
48+
}
49+
50+
public static String getNotificationGridCellHeader(Container c, String value)
51+
{
52+
return getNotificationGridCell(c, value, null, true, false, false);
53+
}
54+
55+
public static String getFormattedDate(Container c, Date date)
56+
{
57+
SimpleDateFormat dateFormat = new SimpleDateFormat(LookAndFeelProperties.getInstance(c).getDefaultDateFormat());
58+
return dateFormat.format(date);
59+
}
60+
}

0 commit comments

Comments
 (0)