Skip to content

Commit 983d6d5

Browse files
Add case number to all cases (#1340)
* Case number trigger and action * Add caseNo to cases dataset * Admin page link * cleanup * lower batch size due to parameter constraint * test * CR feedback
1 parent b4ef45e commit 983d6d5

10 files changed

Lines changed: 264 additions & 2 deletions

File tree

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<query xmlns="http://labkey.org/data/xml/query">
2+
<metadata>
3+
<tables xmlns="http://labkey.org/data/xml">
4+
<table tableName="cases" tableDbType="TABLE">
5+
<tableUrl></tableUrl>
6+
<insertUrl />
7+
<importUrl />
8+
<updateUrl />
9+
<deleteUrl />
10+
<columns>
11+
<column columnName="date">
12+
<columnTitle>Open Date</columnTitle>
13+
<formatString>Date</formatString>
14+
</column>
15+
<column columnName="caseNo">
16+
<columnTitle>Case Number</columnTitle>
17+
</column>
18+
<column columnName="enddate">
19+
<columnTitle>Close Date</columnTitle>
20+
<isHidden>false</isHidden>
21+
</column>
22+
<column columnName="reviewdate">
23+
<columnTitle>Next Review Date</columnTitle>
24+
</column>
25+
<column columnName="category">
26+
<columnTitle>Case Category</columnTitle>
27+
<isUserEditable>false</isUserEditable>
28+
<nullable>false</nullable>
29+
</column>
30+
<column columnName="assignedvet">
31+
<columnTitle>Assigned Vet</columnTitle>
32+
<fk>
33+
<fkDbSchema>ehr_lookups</fkDbSchema>
34+
<fkTable>veterinarians</fkTable>
35+
<fkColumnName>UserId</fkColumnName>
36+
<fkDisplayColumnName>DisplayName</fkDisplayColumnName>
37+
</fk>
38+
</column>
39+
<column columnName="problem">
40+
<columnTitle>Master Problem(s)</columnTitle>
41+
</column>
42+
<column columnName="reviewfrequency">
43+
<columnTitle>Review Frequency</columnTitle>
44+
</column>
45+
<column columnName="remark">
46+
<columnTitle>Description/Notes</columnTitle>
47+
<inputType>textarea</inputType>
48+
</column>
49+
<column columnName="plan">
50+
<columnTitle>P2</columnTitle>
51+
<inputType>textarea</inputType>
52+
<isHidden>true</isHidden>
53+
</column>
54+
<column columnName="performedby">
55+
<columnTitle>Opened By</columnTitle>
56+
</column>
57+
<column columnName="project">
58+
<isHidden>true</isHidden>
59+
</column>
60+
</columns>
61+
</table>
62+
</tables>
63+
</metadata>
64+
</query>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
require("ehr/triggers").initScript(this);
3+
var triggerHelper = new org.labkey.onprc_ehr.query.ONPRC_EHRTriggerHelper(LABKEY.Security.currentUser.id, LABKEY.Security.currentContainer.id);
4+
5+
EHR.Server.TriggerManager.registerHandlerForQuery(EHR.Server.TriggerManager.Events.BEFORE_INSERT, 'study', 'Cases', function(helper, errors, row, oldRow){
6+
// Fill in case number on case insert
7+
if (!helper.isValidateOnly() && !helper.isETL() && !row.caseNo){
8+
row.caseNo = triggerHelper.getNextCaseDisplayId();
9+
}
10+
});

onprc_ehr/resources/referenceStudy/study/datasets/datasets_metadata.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,9 @@
520520
<column columnName="problem">
521521
<datatype>varchar</datatype>
522522
</column>
523+
<column columnName="caseNo">
524+
<datatype>integer</datatype>
525+
</column>
523526
</columns>
524527
<tableTitle>Cases</tableTitle>
525528
</table>

onprc_ehr/resources/views/ehrAdmin.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
{name: 'Verify Dataset Columns', url: '<%=contextPath%><%=containerPath%>/ehr-validateDataSetCols.view'},
2323
{name: 'Verify EHR Schema Indexes Are Compressed', url: '<%=contextPath%><%=containerPath%>/ehr-ensureEHRSchemaIndexes.view'},
2424
{name: 'Cache Demographics On All Living Animals', url: '<%=contextPath%><%=containerPath%>/ehr-cacheLivingAnimals.view'},
25-
{name: 'Prime/Refresh Data Entry Cache', url: '<%=contextPath%><%=containerPath%>/ehr-primeDataEntryCache.view'}
25+
{name: 'Prime/Refresh Data Entry Cache', url: '<%=contextPath%><%=containerPath%>/ehr-primeDataEntryCache.view'},
26+
{name: 'Populate Case Numbers', url: '<%=contextPath%><%=containerPath%>/onprc_ehr-populateCaseNo.view'}
2627
]
2728
},{
2829
header: 'Logs',
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<script type="text/javascript" nonce="<%=scriptNonce%>">
2+
LABKEY.Utils.onReady(function () {
3+
const el = document.getElementById('populate');
4+
el['onclick'] = function () { populateCaseNumbers(el); };
5+
});
6+
7+
function populateCaseNumbers() {
8+
const output = document.getElementById("output");
9+
output.innerHTML = "Starting. See site log for batch counts."
10+
11+
LABKEY.Ajax.request({
12+
url : LABKEY.ActionURL.buildURL('onprc_ehr', 'populateCaseNumbers'),
13+
method : 'POST',
14+
scope: this,
15+
failure: LABKEY.Utils.getCallbackWrapper((response) => {
16+
const output = document.getElementById("output");
17+
output.innerHTML += `<br><br>${response.exception}`;
18+
}),
19+
success: LABKEY.Utils.getCallbackWrapper((result) => {
20+
const output = document.getElementById("output");
21+
if (result.rows && result.rows > 0) {
22+
output.innerHTML += `<br><br>Successfully populated ${result.rows} case numbers.`
23+
} else {
24+
output.innerHTML += `<br><br>No cases found missing caseNo.`
25+
}
26+
})
27+
});
28+
}
29+
</script>
30+
31+
<div>
32+
<button id="populate">Populate Case Numbers</button>
33+
<br><br>
34+
<p>
35+
<span id="output"></span>
36+
</p>
37+
</div>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<view xmlns="http://labkey.org/data/xml/view" title="Populate Empty Case Numbers">
2+
<dependencies>
3+
<dependency path="ehr.context" />
4+
</dependencies>
5+
</view>

onprc_ehr/src/org/labkey/onprc_ehr/ONPRC_EHRController.java

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,36 +28,52 @@
2828
import org.labkey.api.action.ReadOnlyApiAction;
2929
import org.labkey.api.action.SimpleViewAction;
3030
import org.labkey.api.action.SpringActionController;
31+
import org.labkey.api.data.CompareType;
3132
import org.labkey.api.data.Container;
3233
import org.labkey.api.data.ContainerManager;
3334
import org.labkey.api.data.CoreSchema;
35+
import org.labkey.api.data.SQLFragment;
3436
import org.labkey.api.data.SimpleFilter;
37+
import org.labkey.api.data.Sort;
38+
import org.labkey.api.data.SqlExecutor;
39+
import org.labkey.api.data.Table;
3540
import org.labkey.api.data.TableInfo;
3641
import org.labkey.api.data.TableSelector;
42+
import org.labkey.api.ehr.EHRService;
3743
import org.labkey.api.ehr.security.EHRDataEntryPermission;
44+
import org.labkey.api.exp.property.Domain;
3845
import org.labkey.api.files.FileContentService;
3946
import org.labkey.api.query.FieldKey;
47+
import org.labkey.api.query.QueryService;
4048
import org.labkey.api.security.AdminConsoleAction;
49+
import org.labkey.api.security.LimitedUser;
4150
import org.labkey.api.security.RequiresPermission;
4251
import org.labkey.api.security.RequiresSiteAdmin;
4352
import org.labkey.api.security.User;
4453
import org.labkey.api.security.permissions.AdminPermission;
4554
import org.labkey.api.security.permissions.ReadPermission;
55+
import org.labkey.api.study.Dataset;
56+
import org.labkey.api.study.DatasetTable;
57+
import org.labkey.api.study.StudyService;
4658
import org.labkey.api.util.PageFlowUtil;
4759
import org.labkey.api.util.URLHelper;
4860
import org.labkey.api.view.ActionURL;
4961
import org.labkey.api.view.HtmlView;
5062
import org.labkey.api.view.NavTree;
63+
import org.springframework.dao.DataIntegrityViolationException;
5164
import org.springframework.validation.BindException;
5265
import org.springframework.validation.Errors;
5366
import org.springframework.web.servlet.ModelAndView;
5467

5568
import java.io.File;
5669
import java.lang.reflect.Method;
70+
import java.sql.ResultSet;
71+
import java.sql.SQLException;
5772
import java.util.ArrayList;
5873
import java.util.HashMap;
5974
import java.util.List;
6075
import java.util.Map;
76+
import java.util.Objects;
6177

6278
/**
6379
* User: bbimber
@@ -512,4 +528,112 @@ public void setStartingId(Integer startingId)
512528
_startingId = startingId;
513529
}
514530
}
531+
532+
@RequiresPermission(EHRDataEntryPermission.class)
533+
public class PopulateCaseNumbersAction extends MutatingApiAction<Object>
534+
{
535+
private String _casesProvisionedName;
536+
537+
@Override
538+
public void validateForm(Object o, Errors errors)
539+
{
540+
super.validateForm(o, errors);
541+
542+
StudyService ss = StudyService.get();
543+
if (ss == null)
544+
{
545+
errors.reject(ERROR_REQUIRED, "No study");
546+
return;
547+
}
548+
549+
int datasetId = ss.getDatasetIdByName(getContainer(), "cases");
550+
Dataset dataset = ss.getDataset(getContainer(), datasetId);
551+
552+
if (dataset == null)
553+
{
554+
errors.reject(ERROR_REQUIRED, "Cases dataset not found.");
555+
return;
556+
}
557+
558+
Domain domain = dataset.getDomain();
559+
if (domain == null)
560+
{
561+
errors.reject(ERROR_REQUIRED, "Cases dataset domain not found.");
562+
return;
563+
}
564+
565+
_casesProvisionedName = domain.getStorageTableName();
566+
if (_casesProvisionedName == null)
567+
{
568+
errors.reject(ERROR_REQUIRED, "Cases dataset provisioned name not found.");
569+
}
570+
}
571+
572+
@Override
573+
public ApiResponse execute(Object form, BindException errors) throws SQLException
574+
{
575+
Container c = EHRService.get().getEHRStudyContainer(getContainer());
576+
TableInfo ti = QueryService.get().getUserSchema(getUser(), c, "study").getTable("cases");
577+
578+
SQLFragment sqlStart = new SQLFragment("UPDATE c SET c.caseNo = updates.caseNo FROM studydataset.");
579+
sqlStart.appendIdentifier(_casesProvisionedName);
580+
sqlStart.append(" c JOIN (VALUES ");
581+
582+
SQLFragment sqlEnd = new SQLFragment(") AS updates(dsrowid, caseNo) ");
583+
sqlEnd.append("ON c.dsrowid = updates.dsrowid");
584+
585+
logger.info("Starting case number updates.");
586+
587+
// Stream cases with no case number, sorted by date
588+
SimpleFilter filter = SimpleFilter.createContainerFilter(getContainer());
589+
filter.addCondition(FieldKey.fromParts("caseNo"), null, CompareType.ISBLANK);
590+
ResultSet rs = new TableSelector(ti, filter, new Sort("date")).getResultSet(false, false);
591+
592+
boolean newBatch = true;
593+
int counter = 0;
594+
SQLFragment sql = new SQLFragment().append(sqlStart);
595+
596+
while( rs.next() )
597+
{
598+
if (newBatch) {
599+
newBatch = false;
600+
}
601+
else {
602+
sql.append(",");
603+
}
604+
sql.append("(?, ?)");
605+
sql.add(rs.getInt("dsrowid"));
606+
sql.add(ONPRC_EHRManager.get().getNextCaseNo(c));
607+
counter++;
608+
609+
// Batch boundary
610+
if (counter % 1000 == 0)
611+
{
612+
newBatch = true;
613+
sql.append(sqlEnd);
614+
615+
new SqlExecutor(ti.getSchema()).execute(sql);
616+
617+
logger.info(counter + " total case numbers added.");
618+
619+
sql = new SQLFragment().append(sqlStart);
620+
}
621+
}
622+
623+
if (!newBatch)
624+
{
625+
sql.append(sqlEnd);
626+
new SqlExecutor(ti.getSchema()).execute(sql);
627+
}
628+
629+
JSONObject ret = new JSONObject();
630+
ret.put("rows", counter);
631+
ret.put("success", true);
632+
633+
logger.info("Case number update completed. " + counter + " total case numbers updated.");
634+
635+
636+
return new ApiSimpleResponse(ret);
637+
}
638+
}
515639
}

onprc_ehr/src/org/labkey/onprc_ehr/ONPRC_EHRManager.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.labkey.onprc_ehr;
1717

1818
import org.labkey.api.data.Container;
19+
import org.labkey.api.data.DbSequenceManager;
1920
import org.labkey.api.data.PropertyManager;
2021
import org.labkey.api.data.PropertyManager.WritablePropertyMap;
2122
import org.labkey.api.query.Queryable;
@@ -90,6 +91,8 @@ public class ONPRC_EHRManager
9091
@Queryable
9192
public static final String CAGE_MEDICAL_EXEMPTION_FLAG = "Medical";
9293

94+
private static final String CASE_SEQUENCE = "org.labkey.onprc_ehr.cases";
95+
9396
private ONPRC_EHRManager()
9497
{
9598

@@ -147,5 +150,10 @@ public Map<String, Object> getAnimalLockProperties(Container c)
147150

148151
return ret;
149152
}
153+
154+
public long getNextCaseNo(Container c)
155+
{
156+
return DbSequenceManager.get(c, CASE_SEQUENCE).next();
157+
}
150158
}
151159

onprc_ehr/src/org/labkey/onprc_ehr/query/ONPRC_EHRTriggerHelper.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.labkey.api.data.ContainerManager;
3131
import org.labkey.api.data.DbSchema;
3232
import org.labkey.api.data.DbScope;
33+
import org.labkey.api.data.DbSequenceManager;
3334
import org.labkey.api.data.Results;
3435
import org.labkey.api.data.ResultsImpl;
3536
import org.labkey.api.data.SQLFragment;
@@ -2630,4 +2631,9 @@ public void recalculateAllVetAssignmentRecords()
26302631
{
26312632
EHRDemographicsService.get().recalculateForAllIdsInCache(_container, "onprc_ehr", "vet_assignment", true);
26322633
}
2634+
2635+
public long getNextCaseDisplayId()
2636+
{
2637+
return ONPRC_EHRManager.get().getNextCaseNo(_container);
2638+
}
26332639
}

onprc_ehr/test/src/org/labkey/test/tests/onprc_ehr/ONPRC_EHRTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
import java.util.Set;
7474

7575
import static org.junit.Assert.assertEquals;
76+
import static org.junit.Assert.assertNotNull;
7677
import static org.junit.Assert.assertTrue;
7778
import static org.junit.Assert.fail;
7879

@@ -1715,9 +1716,12 @@ public void testBehaviorRounds() throws Exception
17151716
SelectRowsCommand select = new SelectRowsCommand("study", "cases");
17161717
select.addFilter(new Filter("Id", SUBJECTS[0], Filter.Operator.EQUAL));
17171718
select.addFilter(new Filter("category", "Behavior", Filter.Operator.EQUAL));
1718-
select.setColumns(Arrays.asList("Id", "objectid"));
1719+
select.setColumns(Arrays.asList("Id", "objectid, caseNo"));
17191720
SelectRowsResponse resp = select.execute(getApiHelper().getConnection(), getContainerPath());
17201721
String caseId = (String)resp.getRows().get(0).get("objectid");
1722+
Integer caseNo = (Integer)resp.getRows().get(0).get("caseNo");
1723+
1724+
assertNotNull("Case number is missing.", caseNo);
17211725

17221726
getApiHelper().deleteAllRecords("study", "clinical_observations", new Filter("Id", SUBJECTS[0], Filter.Operator.EQUAL));
17231727
InsertRowsCommand insertRowsCommand = new InsertRowsCommand("study", "clinical_observations");

0 commit comments

Comments
 (0)