Skip to content

Commit 40e64ad

Browse files
Merge 22.7 to develop
2 parents 2be172c + c10c69c commit 40e64ad

14 files changed

Lines changed: 1405 additions & 361 deletions

File tree

snprc_ehr/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,9 @@ dependencies
1313
BuildUtils.addLabKeyDependency(project: project, config: "modules", depProjectPath: ":server:modules:ehrModules:ehr", depProjectConfig: "published", depExtension: "module")
1414
BuildUtils.addLabKeyDependency(project: project, config: "modules", depProjectPath: ":server:modules:dataintegration", depProjectConfig: "published", depExtension: "module")
1515
BuildUtils.addLabKeyDependency(project: project, config: "modules", depProjectPath: ":server:modules:snd", depProjectConfig: "published", depExtension: "module")
16+
17+
external "org.apache.commons:commons-text:1.9"
18+
compileOnly "org.projectlombok:lombok:1.18.22"
19+
annotationProcessor "org.projectlombok:lombok:1.18.22"
20+
1621
}

snprc_ehr/package-lock.json

Lines changed: 786 additions & 357 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# direct external dependencies for project :server:modules:snprcEHRModules:snprc_ehr
2+
commons-text-1.9.jar
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{table}
2+
Filename|Component|Version|Source|License|SNPRC Dev|Purpose
3+
commons-text-1.9.jar|Apache Commons Text|1.9|{link:GitHub|https://github.com/apache/commons-text}|{link:Apache License 2.0|https://github.com/apache/commons-text/blob/master/LICENSE.txt}|jallen|A library focused on algorithms working on strings
4+
{table}

snprc_ehr/resources/data/reports.tsv

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ idHistory General query ID History true study idHistory id_date false false
5353
generalTb General query TB Test Results true study tb date false false TB Test results
5454
demographics General query Demographics true study demographics By Location false false qcstate/publicdata This report displays the demographics data about each animal including species, gender and birth
5555
reproSummary Reproductive Management js Reproductive Summary true study underDevelopment date false false qcstate/publicdata
56-
menses Reproductive Management js Menses true study underDevelopment date false false qcstate/publicdata
5756
freezerworks General query Freezer/Sample Data true study Freezerworks date false false qcstate/publicdata
5857
currentBlood General js Current Blood true study currentBlood date false false This report contains a summary of the current available blood for each animal
5958
Obscan Reproductive Management query Obscan true study ObScan date false false qcstate/publicdata

snprc_ehr/resources/queries/ehr_lookups/ageclass.query.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<fk>
1616
<fkDbSchema>snprc_ehr</fkDbSchema>
1717
<fkTable>species</fkTable>
18-
<fkColumnName>arc_species_code.code</fkColumnName>
18+
<fkColumnName>arc_species_code</fkColumnName>
1919
</fk>
2020
</column>
2121
<column columnName="ageclass">

snprc_ehr/resources/queries/study/CycleDaily/.qview.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
</column>
1414
<column name="VaginalBleeding">
1515
<properties>
16-
<property name = "columnTitle" value="VI"/>
16+
<property name = "columnTitle" value="VB"/>
1717
</properties>
1818
</column>
1919
<column name="PurpleColor">

snprc_ehr/resources/queries/study/PotentialSires.sql

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ FROM study.demographics AS d
2121
gender,
2222
label
2323
FROM ehr_lookups.ageclass AS ac
24-
) as x ON d.species.arc_species_code = x.species and x.label = 'Adult' AND (x.gender = 'M' OR x.gender is NULL)
24+
) as x ON d.species.arc_species_code = x.species
25+
AND (x.label = 'Adult' OR (d.species.arc_species_code = 'CJ' AND x.label = 'Senior' ))
26+
AND (x.gender = 'M' OR x.gender is NULL)
2527

2628
-- hard code until the gestation data is available in LK - use 185 if we don't have a value
2729
INNER JOIN (
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package org.labkey.snprc_ehr.constants;
2+
3+
import org.apache.commons.text.CaseUtils;
4+
5+
/**
6+
* String constants used for database queries. Contains table names, schema names, column names, SQL aueries,
7+
* and other database query information
8+
*/
9+
public class QueryConstants
10+
{
11+
public static final String AGE_AT_TIME_COLUMN = "Age At Time";
12+
public static final String AGE_AT_TIME_YEARS_COLUMN = "Age At Time Years";
13+
public static final String AGE_AT_TIME_YEARS_ROUNDED_COLUMN = "Age At Time Years Rounded";
14+
public static final String AGE_AT_TIME_DAYS_COLUMN = "Age At Time Days";
15+
public static final String AGE_AT_TIME_MONTHS_COLUMN = "Age At Time Months";
16+
public static final String AGE_CLASS_AT_TIME_COLUMN = "Age Class At Time";
17+
public static final String ASSIGNMENT_AT_TIME_COLUMN = "Assignment At Time";
18+
public static final String STUDY_SCHEMA = "study";
19+
public static final String CODE_COLUMN = "code";
20+
public static final String SPECIES_TABLE = "species";
21+
public static final String PROJECT_TABLE = "project";
22+
public static final String ANIMAL_TABLE = "animal";
23+
public static final String ASSIGNMENT_TABLE = "assignment";
24+
public static final String DATE_COLUMN = "date";
25+
public static final String DEMOGRAPHICS_TABLE = "demographics";
26+
public static final String ID_COLUMN = "Id";
27+
public static final String ROW_ID_COLUMN = "rowid";
28+
public static final String PROTOCOLS_AT_TIME_CALCULATED = "Protocols At Time";
29+
public static final String ACCOUNTS_AT_TIME_CALCULATED = "Accounts At Time";
30+
public static final String PRIMARY_KEY_VARIABLE = "primaryKeyColumnName";
31+
public static final String SCHEMA_VARIABLE = "schemaName";
32+
public static final String EHR_PATH_VARIABLE = "ehrPath";
33+
public static final String QUERY_VARIABLE = "queryName";
34+
public static final String ID_COLUMN_VARIABLE = "idColumnName";
35+
public static final String TARGET_CONTAINER_VARIABLE ="targetContainerName";
36+
public static final String DATE_COLUMN_VARIABLE = "dateColumnName";
37+
public static final String EHR_LOOKUPS_SCHEMA = "ehr_lookups";
38+
public static final String AGE_CLASS_LOOKUP_URL = "/detailsQueryRow.view?schemaName=ehr_lookups&query.queryName=ageclass&rowid=";
39+
40+
41+
public static final String AGE_CLASS_TABLE = "ageClass";
42+
public static final String ASSIGNMENT_AT_TIME_SQL = "SELECT\n" +
43+
"sd.${" + PRIMARY_KEY_VARIABLE + "},\n" +
44+
"group_concat(DISTINCT h.protocol.displayName, chr(10)) as " + CaseUtils.toCamelCase(PROTOCOLS_AT_TIME_CALCULATED, true) + ",\n" +
45+
"group_concat(DISTINCT ao.account.account, chr(10)) as " + CaseUtils.toCamelCase(ACCOUNTS_AT_TIME_CALCULATED, true) + ",\n" +
46+
"FROM \"${" + SCHEMA_VARIABLE + "}\".\"${" + QUERY_VARIABLE + "}\" sd\n" +
47+
"JOIN \"${" + TARGET_CONTAINER_VARIABLE + "}\".study.assignment h\n" +
48+
" ON (sd.id = h.id AND h.dateOnly <= CAST(sd.${" + DATE_COLUMN_VARIABLE + "} AS DATE) AND (CAST(sd.${" + DATE_COLUMN_VARIABLE +"} AS DATE) <= h.enddateCoalesced) AND h.qcstate.publicdata = true)\n" +
49+
"JOIN \"${" + TARGET_CONTAINER_VARIABLE + "}\".study.animalAccounts ao\n" +
50+
" ON (sd.id = ao.id AND ao.dateOnly <= CAST(sd.${" + DATE_COLUMN_VARIABLE + "} AS DATE) AND (CAST(sd.${" + DATE_COLUMN_VARIABLE +"} AS DATE) <= ao.enddateCoalesced) AND ao.qcstate.publicdata = true)\n" +
51+
"group by sd.${"+ PRIMARY_KEY_VARIABLE + "}";
52+
53+
public static final String AGE_AT_TIME_SQL = "SELECT\n" +
54+
"c.${" + PRIMARY_KEY_VARIABLE + "},\n" +
55+
"\n" +
56+
"CAST(\n" +
57+
"CASE\n" +
58+
"WHEN d.birth is null or c.${" + DATE_COLUMN_VARIABLE + "} is null\n" +
59+
" THEN null\n" +
60+
"WHEN (d.lastDayAtCenter IS NOT NULL AND d.lastDayAtCenter < c.${" + DATE_COLUMN_VARIABLE + "}) THEN\n" +
61+
" ROUND(CONVERT(age_in_months(d.birth, d.lastDayAtCenter), DOUBLE) / 12, 1)\n" +
62+
"ELSE\n" +
63+
" ROUND(CONVERT(age_in_months(d.birth, CAST(c.${" + DATE_COLUMN_VARIABLE + "} as DATE)), DOUBLE) / 12, 1)\n" +
64+
"END AS float) as " + CaseUtils.toCamelCase(AGE_AT_TIME_COLUMN, true) + ",\n" +
65+
"\n" +
66+
67+
"CAST(\n" +
68+
"CASE\n" +
69+
"WHEN d.birth is null or c.${" + DATE_COLUMN_VARIABLE + "} is null\n" +
70+
" THEN null\n" +
71+
"WHEN (d.lastDayAtCenter IS NOT NULL AND d.lastDayAtCenter < c.${" + DATE_COLUMN_VARIABLE + "}) THEN\n" +
72+
" ROUND(CONVERT(timestampdiff('SQL_TSI_DAY', d.birth, d.lastDayAtCenter), DOUBLE) / 365.25, 2)\n" +
73+
"ELSE\n" +
74+
" ROUND(CONVERT(timestampdiff('SQL_TSI_DAY', d.birth, CAST(c.${" + DATE_COLUMN_VARIABLE + "} as DATE)), DOUBLE) / 365.25, 2)\n" +
75+
"END AS float) as " + CaseUtils.toCamelCase(AGE_AT_TIME_YEARS_COLUMN, true) + ",\n" +
76+
"\n" +
77+
"CAST(\n" +
78+
"CASE\n" +
79+
"WHEN d.birth is null or c.${" + DATE_COLUMN_VARIABLE + "} is null\n" +
80+
" THEN null\n" +
81+
"WHEN (d.lastDayAtCenter IS NOT NULL AND d.lastDayAtCenter < c.${" + DATE_COLUMN_VARIABLE + "}) THEN\n" +
82+
" floor(age(d.birth, d.lastDayAtCenter))\n" +
83+
"ELSE\n" +
84+
" floor(age(d.birth, CAST(c.${" + DATE_COLUMN_VARIABLE + "} as DATE)))\n" +
85+
"END AS float) as " + CaseUtils.toCamelCase(AGE_AT_TIME_YEARS_ROUNDED_COLUMN, true) + ",\n" +
86+
"\n" +
87+
//Added 'Age at time Days' by kollil on 02/15/2019
88+
"CAST(\n" +
89+
"CASE\n" +
90+
"WHEN d.birth is null or c.${" + DATE_COLUMN_VARIABLE + "} is null\n" +
91+
" THEN null\n" +
92+
"WHEN (d.lastDayAtCenter IS NOT NULL AND d.lastDayAtCenter < c.${" + DATE_COLUMN_VARIABLE + "}) THEN\n" +
93+
" CONVERT(TIMESTAMPDIFF('SQL_TSI_DAY',d.birth, d.lastDayAtCenter), INTEGER)\n" +
94+
"ELSE\n" +
95+
" CONVERT(TIMESTAMPDIFF('SQL_TSI_DAY',d.birth, CAST(c.${" + DATE_COLUMN_VARIABLE + "} AS DATE)), INTEGER)\n" +
96+
"END AS float) as " + CaseUtils.toCamelCase(AGE_AT_TIME_DAYS_COLUMN, true) + ",\n" +
97+
"\n" +
98+
//
99+
"CAST(\n" +
100+
"CASE\n" +
101+
"WHEN d.birth is null or c.${" + DATE_COLUMN_VARIABLE + "} is null\n" +
102+
" THEN null\n" +
103+
"WHEN (d.lastDayAtCenter IS NOT NULL AND d.lastDayAtCenter < c.${" + DATE_COLUMN_VARIABLE + "}) THEN\n" +
104+
" CONVERT(age_in_months(d.birth, d.lastDayAtCenter), INTEGER)\n" +
105+
"ELSE\n" +
106+
" CONVERT(age_in_months(d.birth, CAST(c.${" + DATE_COLUMN_VARIABLE + "} AS DATE)), INTEGER)\n" +
107+
"END AS float) as " + CaseUtils.toCamelCase(AGE_AT_TIME_MONTHS_COLUMN, true) + ",\n" +
108+
//NOTE: written as subselect so we ensure a single row returned in case data in ehr_lookups.ageclass has rows that allow dupes
109+
"(SELECT ac.label FROM ehr_lookups.ageclass ac\n" +
110+
" WHERE " +
111+
" (CONVERT(age_in_months(d.birth, COALESCE(d.lastDayAtCenter, now())), DOUBLE) / 12) >= ac.\"min\" AND\n" +
112+
" ((CONVERT(age_in_months(d.birth, COALESCE(d.lastDayAtCenter, now())), DOUBLE) / 12) < ac.\"max\" OR ac.\"max\" is null) AND\n" +
113+
" d.species.arc_species_code = ac.species AND\n" +
114+
" (d.gender = ac.gender OR ac.gender IS NULL)\n" +
115+
") AS " + CaseUtils.toCamelCase(AGE_CLASS_AT_TIME_COLUMN, true) + " \n" +
116+
"FROM \"${" + SCHEMA_VARIABLE + "}\".\"${" + QUERY_VARIABLE + "}\" c " +
117+
"LEFT JOIN \"${" + EHR_PATH_VARIABLE + "}\".study.demographics d ON (d.Id = c.${" + ID_COLUMN_VARIABLE + "})";
118+
119+
120+
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
package org.labkey.snprc_ehr.helpers;
2+
3+
import org.apache.commons.lang.StringUtils;
4+
import org.apache.commons.lang.text.StrSubstitutor;
5+
import org.apache.commons.text.CaseUtils;
6+
import org.labkey.api.data.AbstractTableInfo;
7+
import org.labkey.api.data.ColumnInfo;
8+
import org.labkey.api.data.TableInfo;
9+
import org.labkey.api.data.WrappedColumn;
10+
import org.labkey.api.query.UserSchema;
11+
import org.labkey.snprc_ehr.model.CalculatedColumn;
12+
import org.labkey.snprc_ehr.model.CalculatedColumnQueryInfo;
13+
import org.labkey.snprc_ehr.query.CalculatedColumnForeignKey;
14+
15+
import java.util.HashMap;
16+
import java.util.List;
17+
import java.util.Map;
18+
import java.util.Set;
19+
20+
import static org.labkey.snprc_ehr.constants.QueryConstants.DATE_COLUMN_VARIABLE;
21+
import static org.labkey.snprc_ehr.constants.QueryConstants.EHR_PATH_VARIABLE;
22+
import static org.labkey.snprc_ehr.constants.QueryConstants.ID_COLUMN;
23+
import static org.labkey.snprc_ehr.constants.QueryConstants.ID_COLUMN_VARIABLE;
24+
import static org.labkey.snprc_ehr.constants.QueryConstants.PRIMARY_KEY_VARIABLE;
25+
import static org.labkey.snprc_ehr.constants.QueryConstants.QUERY_VARIABLE;
26+
import static org.labkey.snprc_ehr.constants.QueryConstants.SCHEMA_VARIABLE;
27+
import static org.labkey.snprc_ehr.constants.QueryConstants.TARGET_CONTAINER_VARIABLE;
28+
29+
/**
30+
* Class that provides methods to build a table from a SQL query that contains columns to be calculated
31+
*/
32+
public class CustomizerQueryProvider
33+
{
34+
/**
35+
* Constructor
36+
*/
37+
public CustomizerQueryProvider() {
38+
39+
}
40+
41+
/**
42+
* Checks if the passed table exists, the primary key and id columns,
43+
* and builds table with calculated fields from SQL query. Returns false if any checks fail and
44+
* true if all checks pass and the table is successfully built
45+
* @param tableInfo
46+
* @param columnName
47+
* @param dateColumnName
48+
* @param queryString
49+
* @param ehrSchema
50+
* @param calculatedColumns
51+
* @param isRemovingDefaultCustomizerColumns
52+
* @return
53+
*/
54+
public AbstractTableInfo getCalculatedColumnsTable(AbstractTableInfo tableInfo, String columnName, String dateColumnName,
55+
String queryString, UserSchema ehrSchema,
56+
Set<CalculatedColumn> calculatedColumns, boolean isRemovingDefaultCustomizerColumns) {
57+
/* Check if the column already exists in the table */
58+
if (tableInfo.getColumn(CaseUtils.toCamelCase(columnName, false), false) != null)
59+
{
60+
/* Check if the table should be removed so that it can be rebuilt. Applies to tables that require
61+
* queries that are different from the DefaultEHRCustomizer's query */
62+
if (isRemovingDefaultCustomizerColumns)
63+
tableInfo.removeColumn(tableInfo.getColumn(CaseUtils.toCamelCase(columnName, false)));
64+
else
65+
return null;
66+
}
67+
68+
final ColumnInfo primaryKeyColumn = getPrimaryKeyColumn(tableInfo);
69+
if (primaryKeyColumn == null)
70+
return null;
71+
final ColumnInfo idColumn = tableInfo.getColumn(ID_COLUMN);
72+
if(idColumn == null)
73+
return null;
74+
75+
/* Build a query info object to be used by the foreign key class */
76+
CalculatedColumnQueryInfo queryInfo = getQueryInfo(tableInfo, primaryKeyColumn, idColumn, ehrSchema, columnName,
77+
calculatedColumns);
78+
79+
/* Build a wrapped column object for the table */
80+
WrappedColumn calculatedColumn = getWrappedCalculatedColumn(queryInfo, mapQueryStringValues(queryString, queryInfo,
81+
dateColumnName));
82+
tableInfo.addColumn(calculatedColumn);
83+
return tableInfo;
84+
}
85+
86+
/**
87+
* Obtain the column info for the primary key of a given table
88+
* @param tableInfo
89+
* @return
90+
*/
91+
private ColumnInfo getPrimaryKeyColumn(TableInfo tableInfo) {
92+
List<ColumnInfo> primaryKeys = tableInfo.getPkColumns();
93+
return (primaryKeys.size() != 1) ? null : primaryKeys.get(0);
94+
}
95+
96+
/**
97+
* Returns a new query info object to be used for calculating a column's info
98+
* @param tableInfo
99+
* @param primaryKeyColumn
100+
* @param idColumn
101+
* @param ehrSchema
102+
* @param label
103+
* @param calculatedColumns
104+
* @return
105+
*/
106+
private CalculatedColumnQueryInfo getQueryInfo(AbstractTableInfo tableInfo, ColumnInfo primaryKeyColumn,
107+
ColumnInfo idColumn, UserSchema ehrSchema,
108+
String label, Set<CalculatedColumn> calculatedColumns)
109+
{
110+
CalculatedColumnQueryInfo queryInfo = new CalculatedColumnQueryInfo();
111+
queryInfo.setTableInfo(tableInfo);
112+
queryInfo.setCalculatedColumns(calculatedColumns);
113+
queryInfo.setPrimaryKeyColumn(primaryKeyColumn);
114+
queryInfo.setIdColumn(idColumn);
115+
queryInfo.setEhrSchema(ehrSchema);
116+
queryInfo.setLabel(label);
117+
118+
return queryInfo;
119+
}
120+
121+
/**
122+
* Returns a wrapped column object from a given query and it's information
123+
* @param queryInfo
124+
* @param queryString
125+
* @return
126+
*/
127+
private WrappedColumn getWrappedCalculatedColumn(CalculatedColumnQueryInfo queryInfo, String queryString) {
128+
WrappedColumn column = new WrappedColumn(queryInfo.getPrimaryKeyColumn(), CaseUtils.toCamelCase(queryInfo.getLabel(),
129+
false));
130+
column.setLabel(queryInfo.getLabel());
131+
column.setReadOnly(true);
132+
column.setIsUnselectable(true);
133+
column.setUserEditable(false);
134+
/* Invokes the CalculatedColumnForeignKey class */
135+
column.setFk(new CalculatedColumnForeignKey(queryInfo, queryString));
136+
return column;
137+
}
138+
139+
/**
140+
* Maps values from the query object's data to the variable names within the SQL query string
141+
* (formatted for the Apache Commons Lang StrSubstitor where ${variableName} maps to variableName)
142+
* @param queryString
143+
* @param queryInfo
144+
* @param dateColumnName
145+
* @return
146+
*/
147+
private String mapQueryStringValues(String queryString, CalculatedColumnQueryInfo queryInfo, String dateColumnName) {
148+
Map<String, String> queryByValues = new HashMap<>();
149+
if(StringUtils.contains(queryString, PRIMARY_KEY_VARIABLE))
150+
queryByValues.put(PRIMARY_KEY_VARIABLE, queryInfo.getPrimaryKeyColumn().getFieldKey().toSQLString());
151+
if(StringUtils.contains(queryString, SCHEMA_VARIABLE))
152+
queryByValues.put(SCHEMA_VARIABLE, queryInfo.getTableInfo().getPublicSchemaName());
153+
if(StringUtils.contains(queryString, QUERY_VARIABLE))
154+
queryByValues.put(QUERY_VARIABLE, queryInfo.getTableInfo().getName());
155+
if(StringUtils.contains(queryString, EHR_PATH_VARIABLE))
156+
queryByValues.put(EHR_PATH_VARIABLE, queryInfo.getEhrSchema().getContainer().getPath());
157+
if(StringUtils.contains(queryString, ID_COLUMN_VARIABLE))
158+
queryByValues.put(ID_COLUMN_VARIABLE, queryInfo.getIdColumn().getFieldKey().toSQLString());
159+
if(StringUtils.contains(queryString, TARGET_CONTAINER_VARIABLE))
160+
queryByValues.put(TARGET_CONTAINER_VARIABLE, queryInfo.getTableInfo().getUserSchema().getName());
161+
if(StringUtils.contains(queryString, DATE_COLUMN_VARIABLE))
162+
queryByValues.put(DATE_COLUMN_VARIABLE, dateColumnName);
163+
return StrSubstitutor.replace(queryString, queryByValues);
164+
165+
}
166+
167+
168+
169+
170+
171+
}

0 commit comments

Comments
 (0)