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

[bot] Merge 24.11 to 25.1 #728

Merged
merged 6 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@
<column name="mostRecentWeight" />
</columns>
<sorts>
<sort column="date" />
<sort column="Id/curLocation/room"/>
<sort column="Id/curLocation/cage"/>
<sort column="date" />
<sort column="Id" />
</sorts>
<!--<filters>-->
Expand Down
68 changes: 65 additions & 3 deletions WNPRC_EHR/resources/web/wnprc_ehr/datasetButtons.js
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,25 @@ WNPRC_EHR.DatasetButtons = new function(){
}
}
},

checkBloodSchedule: function(records) {

return new Promise((resolve, reject) => {
LABKEY.Ajax.request({
url: LABKEY.ActionURL.buildURL('WNPRC_EHR', 'CompareBloodSchedules'),
jsonData: { records: records },
callback: function (config, success, xhr) {
if (success) {
resolve(xhr.responseText);
} else {
reject('Couldn\'t compare blood schedule, internal error.');
}
}
})
});

},

/**
* This add a handler to a dataset that allows the user to change the QCState of the records, designed to approve or deny blood requests.
* It also captures values for 'billedBy' and 'instructions'.
Expand Down Expand Up @@ -710,14 +729,26 @@ WNPRC_EHR.DatasetButtons = new function(){
title: 'Change Request Status',
width: 430,
autoHeight: true,
id: 'change-request-window',
items: [{
xtype: 'form',
height: '100%',
ref: 'theForm',
id: 'change-request-form',
autoHeight: true,
bodyStyle: 'padding: 5px;',
defaults: {
border: false
},
items: [{
items: [

{
id:'bloodCompareResponseWrapper',
height: '100%',
html: '<div id="bloodCompareResponse"><i class="fa fa-spinner fa-pulse"></i> loading..</div>',
tag: 'div'
},
{
html: 'Total Records: '+checked.length+'<br><br>',
tag: 'div'
},{
Expand Down Expand Up @@ -767,10 +798,11 @@ WNPRC_EHR.DatasetButtons = new function(){
}],
buttons: [{
text:'Submit',
disabled:false,
disabled:true,
formBind: true,
ref: '../submit',
scope: this,
id: 'submitButton',
handler: function(o){
var win = o.up('window');
var form = win.down('form');
Expand Down Expand Up @@ -848,7 +880,37 @@ WNPRC_EHR.DatasetButtons = new function(){
handler: function(o){
o.ownerCt.ownerCt.close();
}
}]
}],
listeners: {
afterrender: () => {
this.checkBloodSchedule(records).then(response => {
let resp = document.getElementById('bloodCompareResponse');
let rsp = JSON.parse(response).message;
let txt = '';
if (rsp) {
for (let item of rsp) {
txt += '<li>' + item.message + ' (Project(s): ' + item.projects + '; Contacts: <a href="mailto:' + item.emails + '">' + item.emails + '</a>' + ')</li>';
}
}
if (txt.length > 0){
txt = '<ul>' + txt + '</ul>'
resp.innerHTML = '<span style="background:#acac16"> Warning:</span> ' + txt;
} else {
resp.innerHTML = '';
}
Ext4.getCmp('submitButton').enable()
// Have to reset the height since the form has a set height after initial rendering,
// but when text is dynamically added the height does not change.
Ext4.getCmp('change-request-form').setHeight('100%')
}).catch(error => {
Ext4.getCmp('submitButton').enable()
console.error(error);
let resp = document.getElementById('bloodCompareResponse');
resp.innerHTML = '<p>Error checking blood schedule, you are still able to submit. Please contact EHR admins with details: ' + new Date() + ' ' + JSON.parse(error) + '</p>'
Ext4.getCmp('change-request-form').setHeight('100%')
});
}
}
}).show();
}
},
Expand Down
200 changes: 200 additions & 0 deletions WNPRC_EHR/src/org/labkey/wnprc_ehr/WNPRC_EHRController.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.labkey.api.action.SimpleRedirectAction;
import org.labkey.api.action.SpringActionController;
import org.labkey.api.data.ColumnInfo;
import org.labkey.api.data.CompareType;
import org.labkey.api.data.Container;
import org.labkey.api.data.CoreSchema;
import org.labkey.api.data.DbSchema;
Expand Down Expand Up @@ -76,11 +77,13 @@
import org.labkey.api.security.RequiresPermission;
import org.labkey.api.security.RequiresSiteAdmin;
import org.labkey.api.security.User;
import org.labkey.api.security.UserManager;
import org.labkey.api.security.permissions.AdminPermission;
import org.labkey.api.security.permissions.ReadPermission;
import org.labkey.api.study.Dataset;
import org.labkey.api.study.StudyService;
import org.labkey.api.util.ExceptionUtil;
import org.labkey.api.util.PageFlowUtil;
import org.labkey.api.util.Path;
import org.labkey.api.util.ResultSetUtil;
import org.labkey.api.util.URLHelper;
Expand Down Expand Up @@ -122,13 +125,16 @@
import java.io.InputStreamReader;
import java.nio.file.Paths;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -2105,4 +2111,198 @@ public Object execute(Object o, BindException errors) throws Exception
return response;
}
}

public static class CompareBloodSchedulesForm
{
private List<Map<String,Object>> _records;
public List<Map<String,Object>> getRecords()
{
return _records;
}

public void setRecords(List<Map<String,Object>> records)
{
_records = records;
}
}

public static String convertSetToString(Set<?> set, String delimiter) {
StringBuilder sb = new StringBuilder();
Iterator<?> iterator = set.iterator();

while (iterator.hasNext()) {
Object element = iterator.next();
sb.append(element);
if (iterator.hasNext()) {
sb.append(delimiter + " ");
}
}

return sb.toString();
}

@ActionNames("CompareBloodSchedules")
@RequiresNoPermission()
public class comparebloodSchedulesAction extends ReadOnlyApiAction<CompareBloodSchedulesForm>
{

@Override
public ApiResponse execute(CompareBloodSchedulesForm form, BindException errors) throws IOException, InvalidFormatException
{

ApiSimpleResponse response = new ApiSimpleResponse();
Map<String, List<Map<String, Object>>> groupedById = new HashMap<>();
Map<Timestamp, List<Map<String, Object>>> groupedByDate = new HashMap<>();
List<String> lsids = new ArrayList<>();

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
Date parsedDate;

for (Map<String,Object> mp : form.getRecords())
{
String id = (String) mp.get("Id");
try
{
//convert the date to Timestamp since this is what comes from the db later
parsedDate = dateFormat.parse((String) mp.get("date"));
Timestamp date = new Timestamp(parsedDate.getTime());
mp.put("date", date);
groupedById.computeIfAbsent(id, k -> new ArrayList<>()).add(mp);
groupedByDate.computeIfAbsent(date, k -> new ArrayList<>()).add(mp);
}
catch (ParseException e)
{
throw new RuntimeException(e);
}
}


Set<String> ids = groupedById.keySet();
Set<Timestamp> dates = groupedByDate.keySet();

//get min date to query DB later
java.time.LocalDate minDate= java.time.LocalDate.now().plusYears(100);
for (Timestamp dateTime : dates)
{
java.time.LocalDate date1 = dateTime.toLocalDateTime().toLocalDate();
minDate = date1.isBefore(minDate) ? date1 : minDate;
}

//get max date to query DB later
java.time.LocalDate maxDate = java.time.LocalDate.now().minusYears(100);
for (Timestamp dateTime : dates)
{
java.time.LocalDate date1 = dateTime.toLocalDateTime().toLocalDate();
maxDate = date1.isAfter(maxDate) ? date1 : maxDate;
}

//get any blood draws that have potentially the same date & id
Set<Integer> qcStates = new HashSet<>();
qcStates.add(EHRService.get().getQCStates(getContainer()).get(EHRService.QCSTATES.RequestApproved.getLabel()).getRowId());
qcStates.add(EHRService.get().getQCStates(getContainer()).get(EHRService.QCSTATES.Scheduled.getLabel()).getRowId());

SimpleFilter bloodFilter = new SimpleFilter(FieldKey.fromString("lsid"), String.join("; ",ids), CompareType.CONTAINS_ONE_OF);
bloodFilter.addCondition(FieldKey.fromString("date"), minDate, CompareType.DATE_GTE);
bloodFilter.addCondition(FieldKey.fromString("date"), maxDate, CompareType.DATE_LTE);
bloodFilter.addCondition(FieldKey.fromString("qcstate"), qcStates, CompareType.IN);
//Runs query with updated info.
TableInfo bloodTi = QueryService.get().getUserSchema(getUser(), getContainer(), "study").getTable("Blood Draws");
TableSelector bloodTable = new TableSelector(bloodTi, PageFlowUtil.set("lsid", "date", "project", "Id", "requestor", "createdby"), bloodFilter, null);
Map<String, Object>[] bloodRows = bloodTable.getMapArray();

//add the records from the db to our groupedById array
for (Map<String, Object> row : bloodRows)
{
for (Map.Entry<String, List<Map<String, Object>>> entry: groupedById.entrySet())
{
if (entry.getKey().equals(row.get("Id")))
{
entry.getValue().add(row);
}
}
}


List<Map<String,Object>> messageList = new ArrayList<>();

for (Map.Entry<String, List<Map<String, Object>>> entry : groupedById.entrySet())
{
Map<String, Object> theMessage = new HashMap<>();

List<Map<String, Object>> records = entry.getValue();

Set<java.time.LocalTime> uniqueTimes = new HashSet<>();
Set<String> uniqueRequestors = new HashSet<>();
Set<Integer> uniqueProjects = new HashSet<>();
Set<Integer> uniqueCreatedBy = new HashSet<>();


// Create a Set to track unique dates
for (int i = 0; i < records.size() - 1; i++)
{
Map<String, Object> record1 = records.get(i);
Timestamp dateTime1 = (Timestamp) record1.get("date");

lsids.add((String) record1.get("lsid"));

for (int j = i + 1; j < records.size(); j++)
{
Map<String, Object> record2 = records.get(j);
Timestamp dateTime2 = (Timestamp) record2.get("date");
lsids.add((String) record1.get("lsid"));


java.time.LocalDate date1 = dateTime1.toLocalDateTime().toLocalDate();
java.time.LocalTime time1 = dateTime1.toLocalDateTime().toLocalTime();

java.time.LocalDate date2 = dateTime2.toLocalDateTime().toLocalDate();
java.time.LocalTime time2 = dateTime2.toLocalDateTime().toLocalTime();

if (date1.isEqual(date2) && !time1.equals(time2))
{
uniqueTimes.add(time1);
uniqueTimes.add(time2);
}
}
}
if (uniqueTimes.size() > 0)
{
//get requestor and project information

SimpleFilter myFilter = new SimpleFilter(FieldKey.fromString("lsid"), String.join("; ", lsids), CompareType.CONTAINS_ONE_OF);
//Runs query with updated info.
TableInfo ti = QueryService.get().getUserSchema(getUser(), getContainer(), "study").getTable("Blood Draws");
TableSelector myTable = new TableSelector(ti, PageFlowUtil.set("lsid", "project", "Id", "requestor", "createdby"), myFilter, null);
Map<String, Object>[] rows = myTable.getMapArray();
for (Map<String, Object> row : rows)
{
uniqueRequestors.add((String) row.get("requestor"));
uniqueProjects.add((Integer) row.get("project"));
uniqueCreatedBy.add((Integer) row.get("createdBy"));
}

List<String> emails = new ArrayList<>();
for (var userId : uniqueCreatedBy)
{
var user = UserManager.getUser(userId);

if (user != null)
emails.add(user.getEmail());
}

//pull out requestors email/userid
theMessage.put("emails", String.join(",", emails));
theMessage.put("projects", convertSetToString(uniqueProjects, ","));
theMessage.put("message",entry.getKey() + " has "+ uniqueTimes.size() + " draws on the same day but at different times.");

messageList.add(theMessage);
}
}
response.put("message", messageList);

return response;

}
}

}
Loading
Loading