Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tattoo snomed addition #546

Open
wants to merge 15 commits into
base: release22.7-SNAPSHOT
Choose a base branch
from
Open
5 changes: 5 additions & 0 deletions onprc_ehr/resources/queries/study/TattooAlert.sql
Original file line number Diff line number Diff line change
@@ -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
9 changes: 9 additions & 0 deletions onprc_ehr/resources/scripts/onprc_ehr/onprc_triggers.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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();
}
Expand All @@ -112,7 +122,8 @@ protected void animalsWithoutRounds(final Container c, User u, final StringBuild
long count = ts.getRowCount();
if (count > 0)
{
msg.append("<b>WARNING: There are " + count + " active cases that do not have obs entered today.</b><br>");
msg.append("<b>Clinical Rounds Alerts: Active cases that do not have observations.</b><br>");
msg.append("<b>WARNING: " + count + " active case(s) found that do not have obs entered today.</b><br>");
msg.append("<table border=1 style='border-collapse: collapse;'>");
msg.append("<tr style='font-weight: bold;'><td>Room</td><td>Cage</td><td>Id</td><td>Assigned Vet</td><td>Problem(s)</td><td>Days Since Last Rounds</td></tr>");

Expand Down Expand Up @@ -163,7 +174,8 @@ protected void animalsWithoutVetReview(final Container c, User u, final StringBu
long count = ts.getRowCount();
if (count > 0)
{
msg.append("<b>WARNING: There are " + count + " active cases that have not been vet reviewed in the past 7 days.</b><br>");
msg.append("<b>Clinical Rounds Alerts: Active cases with no Vet review.</b><br>");
msg.append("<b>WARNING: " + count + " active case(s) found that have not been vet reviewed in the past 7 days.</b><br>");
msg.append("<table border=1 style='border-collapse: collapse;'>");
msg.append("<tr style='font-weight: bold;'><td>Room</td><td>Cage</td><td>Id</td><td>Assigned Vet</td><td>Problem(s)</td><td>Days Since last Vet Review</td></tr>");

Expand All @@ -188,4 +200,108 @@ public void exec(ResultSet object) throws SQLException
msg.append("<hr>\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<FieldKey> 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<FieldKey, ColumnInfo> 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("<b>Clinical Rounds Process Alerts: Active cases that have observations.</b><br>");
msg.append("<b>CONFIRMATION: " + count + " active case(s) found that have their obs entered today.</b><br>");
msg.append("<table border=1 style='border-collapse: collapse;'>");
msg.append("<tr style='font-weight: bold;'><td>Room</td><td>Cage</td><td>Id</td><td>Assigned Vet</td><td>Problem(s)</td></tr>");

ts.forEach(new Selector.ForEachBlock<ResultSet>()
{
@Override
public void exec(ResultSet object) throws SQLException
{
Results rs = new ResultsImpl(object, cols);
msg.append("<tr>");
msg.append("<td>" + safeAppend(rs.getString(FieldKey.fromString("Id/curLocation/room")), "None") + "</td>");
msg.append("<td>" + safeAppend(rs.getString(FieldKey.fromString("Id/curLocation/cage")), "") + "</td>");
msg.append("<td>" + rs.getString(FieldKey.fromString("Id")) + "</td>");
msg.append("<td>" + safeAppend(rs.getString(FieldKey.fromString("assignedvet/DisplayName")), "None") + "</td>");
msg.append("<td>" + safeAppend(rs.getString(FieldKey.fromString("allProblemCategories")), "None") + "</td>");

msg.append("</tr>");
}
});

msg.append("</table>");
msg.append("<hr>\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<FieldKey> 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<FieldKey, ColumnInfo> 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("<b>Clinical Rounds Process Alerts: Active cases that do not have observations entered today.</b><br>");
// msg.append("<b>WARNING: There are " + count + " active cases that do not have obs entered today.</b><br>");
// msg.append("<table border=1 style='border-collapse: collapse;'>");
// msg.append("<tr style='font-weight: bold;'><td>Room</td><td>Cage</td><td>Id</td><td>Assigned Vet</td><td>Problem(s)</td><td>Days Since Last Rounds</td></tr>");
//
// ts.forEach(new Selector.ForEachBlock<ResultSet>()
// {
// @Override
// public void exec(ResultSet object) throws SQLException
// {
// Results rs = new ResultsImpl(object, cols);
// msg.append("<tr>");
// msg.append("<td>" + safeAppend(rs.getString(FieldKey.fromString("Id/curLocation/room")), "None") + "</td>");
// msg.append("<td>" + safeAppend(rs.getString(FieldKey.fromString("Id/curLocation/cage")), "") + "</td>");
// msg.append("<td>" + rs.getString(FieldKey.fromString("Id")) + "</td>");
// msg.append("<td>" + safeAppend(rs.getString(FieldKey.fromString("assignedvet/DisplayName")), "None") + "</td>");
// msg.append("<td>" + safeAppend(rs.getString(FieldKey.fromString("allProblemCategories")), "None") + "</td>");
// msg.append("<td>" + safeAppend(rs.getString(FieldKey.fromString("daysSinceLastRounds")), "") + "</td>");
// msg.append("</tr>");
// }
// });
//
// msg.append("</table>");
// msg.append("<hr>\n");
// }
// }

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -295,4 +295,47 @@ protected void getAnimalsNotWeightedInPast60Days(StringBuilder msg, Container c,

msg.append("<hr>\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("<b>Animals with missing Tattoo procedure:</b><br><br>\n");

if (QueryService.get().getUserSchema(u, c, "study") == null) {
msg.append("<b>Warning: The study schema has not been enabled in this folder, so the alert cannot run!<p><hr>");
return;
}
//Tattoo query
TableInfo ti = QueryService.get().getUserSchema(u, c, "study").getTable("TattooAlert", ContainerFilter.Type.AllFolders.create(c, u));
Set<ColumnInfo> 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("<b><a href='" + url + "'>Click here to view them.</a></b><br><br>\n");

msg.append("Summary by area:<br>\n");
msg.append("<table>");
Map<String, Integer> areaMap = getAreaMap(ts, cols);
for (String area : areaMap.keySet())
{
String newUrl = url + "&query.Id/curLocation/area~eq=" + area;
msg.append("<tr><td>" + area + ": </td><td><a href='" + newUrl + "'>" + areaMap.get(area) + "</a></td></tr>\n");
}
msg.append("</table>");
msg.append("<br><br>");
}
else
{
msg.append("<b> All live animals at the center are tattooed! </b>");
}
msg.append("<hr>\n");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ public Map<String, Object> 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<Map<String, Object>> records) throws Exception
{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<String, Object> row) throws QueryUpdateServiceException, DuplicateKeyException, SQLException, BatchValidationException
{
if (id != null)
{
//_log.info("Animal Id thats tattooed: " + id);
Map<String, Object> snomedProps = new HashMap<String, Object>();

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<String, Object> new_row = new CaseInsensitiveHashMap<>();
new_row.putAll(snomedProps);

List<Map<String, Object>> 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<String, Object> onAnimalArrival_AddDemographics(String id, Map<String, Object> row) throws QueryUpdateServiceException, DuplicateKeyException, SQLException, BatchValidationException
{
Expand Down Expand Up @@ -1228,6 +1269,7 @@ public void createBirthRecord_ONPRC(String id, Map<String, Object> 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
{
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}

Expand Down Expand Up @@ -2337,7 +2378,6 @@ public void exec(ResultSet rs) throws SQLException

}


//Added 3-6-2019 Blasa
public void sendProjectNotifications(Integer projectid)
{
Expand Down