Skip to content

Commit 1d3058d

Browse files
committed
Merge 26.5 to develop
2 parents 0e4b76f + 8b7e081 commit 1d3058d

269 files changed

Lines changed: 217 additions & 47980 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ehr/resources/queries/study/demographicsAge.sql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ ROUND(CONVERT(age_in_months(d.birth, COALESCE(d.lastDayAtCenter, now())), DOUBLE
1717

1818
ROUND(CONVERT(age_in_months(d.birth, COALESCE(d.lastDayAtCenter, now())), DOUBLE) / 12, 1) AS ageInYears,
1919

20-
TIMESTAMPDIFF('SQL_TSI_DAY', d.birth, COALESCE(d.lastDayAtCenter, now())) as ageInDays,
20+
age_in_days(d.birth, COALESCE(d.lastDayAtCenter, now())) as ageInDays,
2121

2222
case
2323
when (age_in_months(d.birth, COALESCE(d.lastDayAtCenter, now()))) < 1
24-
then (CONVERT(CONVERT(TIMESTAMPDIFF('SQL_TSI_DAY', d.birth, COALESCE(d.lastDayAtCenter, now())), float), VARCHAR) || ' days')
24+
then (CONVERT(CONVERT(age_in_days(d.birth, COALESCE(d.lastDayAtCenter, now())), float), VARCHAR) || ' days')
2525
when (age_in_months(d.birth, COALESCE(d.lastDayAtCenter, now()))) < 12
2626
then (CONVERT(CONVERT(ROUND(age_in_months(d.birth, COALESCE(d.lastDayAtCenter, now())), 1), float), VARCHAR) || ' months')
2727
else
28-
(CONVERT(CONVERT(FLOOR(age_in_months(d.birth, COALESCE(d.lastDayAtCenter, now())) / 12), SQL_INTEGER), VARCHAR) || '.' || CONVERT(MOD(CONVERT(ROUND(age_in_months(d.birth, COALESCE(d.death, now())) / 12.0 * 10.0, 0), SQL_INTEGER), 10), VARCHAR) || ' years')
28+
(CONVERT(CONVERT(FLOOR(age_in_months(d.birth, COALESCE(d.lastDayAtCenter, now())) / 12), SQL_INTEGER), VARCHAR) || '.' || CONVERT(MOD(CONVERT(ROUND(age_in_months(d.birth, COALESCE(d.lastDayAtCenter, now())) / 12.0 * 10.0, 0), SQL_INTEGER), 10), VARCHAR) || ' years')
2929
end as ageFriendly
3030

3131
FROM study.Demographics d

ehr/resources/web/ehr/data/StoreCollection.js

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Ext4.define('EHR.data.StoreCollection', {
1212
serverStores: null,
1313
hasLoaded: false, //will be set true after initial load
1414
clientDataChangeBuffer: 150,
15+
validationRequestsInFlight: 0,
1516
ignoredClientEvents: {},
1617

1718
constructor: function(){
@@ -20,7 +21,7 @@ Ext4.define('EHR.data.StoreCollection', {
2021
this.serverStores = Ext4.create('Ext.util.MixedCollection', false, this.getKey);
2122

2223
this.callParent(arguments);
23-
this.addEvents('commitcomplete', 'commitexception', 'validation', 'initialload', 'load', 'clientdatachanged', 'serverdatachanged');
24+
this.addEvents('commitcomplete', 'commitexception', 'beforevalidation', 'validationstart', 'validation', 'validationcomplete', 'initialload', 'load', 'clientdatachanged', 'serverdatachanged');
2425

2526
this.on('clientdatachanged', this.onClientDataChanged, this, {buffer: this.clientDataChangeBuffer});
2627
},
@@ -218,24 +219,48 @@ Ext4.define('EHR.data.StoreCollection', {
218219
}
219220
else
220221
{
221-
//this really isnt the right event to fire, but it will force a recalulation of buttons on the panel
222+
//this really isn't the right event to fire, but it will force a recalculation of buttons on the panel
222223
this.fireEvent('validation', this);
223224
}
224225
},
225226

226227
validateAll: function(){
228+
if(this.fireEvent('beforevalidation', this)===false)
229+
return;
227230
this.serverStores.each(function(serverStore){
228231
serverStore.validateRecords(serverStore.getRange(), true);
229232
}, this);
230233
},
231234

232235
validateRecords: function(recordMap){
236+
if(this.fireEvent('beforevalidation', this)===false)
237+
return;
233238
for (var serverStoreId in recordMap){
234239
var serverStore = this.serverStores.get(serverStoreId);
235240
serverStore.validateRecords(Ext4.Object.getValues(recordMap[serverStoreId]), true);
236241
}
237242
},
238243

244+
onValidationRequestStart: function(){
245+
this.validationRequestsInFlight++;
246+
247+
if (this.validationRequestsInFlight === 1){
248+
this.fireEvent('validationstart', this);
249+
}
250+
},
251+
252+
onValidationRequestComplete: function(){
253+
if (!this.validationRequestsInFlight){
254+
return;
255+
}
256+
257+
this.validationRequestsInFlight--;
258+
259+
if (this.validationRequestsInFlight === 0){
260+
this.fireEvent('validationcomplete', this);
261+
}
262+
},
263+
239264
serverToClientDataMap: null,
240265

241266
getServerToClientDataMap: function(){
@@ -472,11 +497,31 @@ Ext4.define('EHR.data.StoreCollection', {
472497
if (EHR.debug)
473498
console.log(commands);
474499

500+
var success = this.getOnCommitSuccess(recordsArr, validateOnly, retainErrors);
501+
var failure = this.getOnCommitFailure(recordsArr, validateOnly);
475502
var cfg = {
476503
url : LABKEY.ActionURL.buildURL('query', 'saveRows', this.containerPath),
477504
method : 'POST',
478-
success: this.getOnCommitSuccess(recordsArr, validateOnly, retainErrors),
479-
failure: this.getOnCommitFailure(recordsArr, validateOnly),
505+
success: function(response, options){
506+
try {
507+
success.call(this, response, options);
508+
}
509+
finally {
510+
if (validateOnly){
511+
this.onValidationRequestComplete();
512+
}
513+
}
514+
},
515+
failure: function(response, options){
516+
try {
517+
failure.call(this, response, options);
518+
}
519+
finally {
520+
if (validateOnly){
521+
this.onValidationRequestComplete();
522+
}
523+
}
524+
},
480525
scope: this,
481526
timeout: 5000000, //a little extreme?
482527
transacted: true,
@@ -498,9 +543,20 @@ Ext4.define('EHR.data.StoreCollection', {
498543
if (validateOnly){
499544
cfg.jsonData.validateOnly = true;
500545
cfg.jsonData.extraContext.isValidateOnly = true;
546+
this.onValidationRequestStart();
547+
}
548+
549+
var request;
550+
try {
551+
request = LABKEY.Ajax.request(cfg);
501552
}
553+
catch (e){
554+
if (validateOnly){
555+
this.onValidationRequestComplete();
556+
}
502557

503-
var request = LABKEY.Ajax.request(cfg);
558+
throw e;
559+
}
504560

505561
Ext4.Array.forEach(recordsArr, function(command){
506562
Ext4.Array.forEach(command, function(rec){
@@ -893,4 +949,4 @@ Ext4.define('EHR.data.StoreCollection', {
893949
s.checkForServerErrorChanges();
894950
}, this);
895951
}
896-
});
952+
});

ehr/resources/web/ehr/panel/DataEntryErrorPanel.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Ext4.define('EHR.panel.DataEntryErrorPanel', {
1919
this.callParent(arguments);
2020

2121
this.mon(this.storeCollection, 'validation', this.updateErrorMessages, this, {buffer: 1000});
22+
this.mon(this.storeCollection, 'validationcomplete', this.updateErrorMessages, this, {buffer: 50});
2223
this.mon(this.storeCollection, 'commitcomplete', this.updateErrorMessages, this, {buffer: 200});
2324
this.mon(this.storeCollection, 'commitexception', this.updateErrorMessages, this, {buffer: 200});
2425
},

ehr/resources/web/ehr/panel/DataEntryPanel.js

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Ext4.define('EHR.panel.DataEntryPanel', {
1212
storeCollection: null,
1313
hideErrorPanel: false,
1414
useSectionBorder: true,
15+
validationInProgress: false,
1516

1617
layout: 'anchor',
1718
border: false,
@@ -33,7 +34,10 @@ Ext4.define('EHR.panel.DataEntryPanel', {
3334
this.storeCollection.on('initialload', this.onStoreCollectionInitialLoad, this);
3435
this.storeCollection.on('commitcomplete', this.onStoreCollectionCommitComplete, this);
3536
this.storeCollection.on('validation', this.onStoreCollectionValidation, this);
37+
this.storeCollection.on('validationstart', this.onValidationStart, this);
38+
this.storeCollection.on('validationcomplete', this.onValidationComplete, this);
3639
this.storeCollection.on('beforecommit', this.onStoreCollectionBeforeCommit, this);
40+
this.storeCollection.on('beforevalidation', this.onBeforeValidation, this);
3741
this.storeCollection.on('commitexception', this.onStoreCollectionCommitException, this);
3842
//this.storeCollection.on('serverdatachanged', this.onStoreCollectionServerDataChanged, this);
3943

@@ -83,13 +87,67 @@ Ext4.define('EHR.panel.DataEntryPanel', {
8387
}
8488
},
8589

90+
onBeforeValidation: function(sc){
91+
function processItem(item) {
92+
if(item.disableOn) {
93+
item.setDisabled(true);
94+
if (item.setTooltip)
95+
item.setTooltip('Disabled waiting on validation. Select "More Actions" -> "Re-Validate" if this is not clearing.');
96+
}
97+
98+
if (item.menu) {
99+
item.menu.items.each(function (menuItem) {
100+
processItem(menuItem);
101+
}, this);
102+
}
103+
}
104+
105+
var ehrContext = LABKEY.getModuleContext('ehr');
106+
if (ehrContext && !ehrContext.isSubmitEnabledOnValidation) {
107+
var btns = this.getToolbarItems();
108+
if (btns) {
109+
Ext4.Array.forEach(btns, function (toolbar) {
110+
toolbar.items.each(function (item) {
111+
processItem(item);
112+
}, this);
113+
}, this);
114+
}
115+
}
116+
},
117+
118+
onValidationStart: function(){
119+
// Suppress the indicator during initial form load/background reconciliation.
120+
if (!this.hasStoreCollectionLoaded || !this.storeCollection || !this.storeCollection.hasLoaded){
121+
return;
122+
}
123+
124+
this.validationInProgress = true;
125+
this.setValidationIndicatorVisible(true);
126+
},
127+
128+
onValidationComplete: function(){
129+
this.validationInProgress = false;
130+
this.setValidationIndicatorVisible(false);
131+
132+
var errorPanel = this.getErrorPanel();
133+
if (errorPanel){
134+
errorPanel.updateErrorMessages();
135+
}
136+
137+
this.onStoreCollectionValidation(this.storeCollection);
138+
},
139+
86140
onStoreCollectionValidation: function(sc){
87141
if (!this.hasStoreCollectionLoaded){
88142
return;
89143
}
90144

91145
this.updateDirtyStateMessage();
92146

147+
if (this.storeCollection && this.storeCollection.validationRequestsInFlight > 0){
148+
return;
149+
}
150+
93151
var maxSeverity = sc.getMaxErrorSeverity();
94152

95153
if(EHR.debug && maxSeverity)
@@ -276,6 +334,19 @@ Ext4.define('EHR.panel.DataEntryPanel', {
276334
},
277335
items: this.getItemConfig(),
278336
dockedItems: [{
337+
xtype: 'toolbar',
338+
dock: 'bottom',
339+
itemId: 'validationIndicator',
340+
hidden: true,
341+
border: false,
342+
plain: true,
343+
style: 'background-color: transparent; padding: 12px 0 0 0;',
344+
items: [{
345+
xtype: 'container',
346+
html: '<span><i class="fa fa-spinner fa-pulse"></i> Validating...</span>',
347+
style: 'font: bold 13px tahoma,arial,verdana,sans-serif; line-height: 16px; color: #C33;'
348+
}]
349+
},{
279350
xtype: 'toolbar',
280351
dock: 'bottom',
281352
ui: 'footer',
@@ -509,6 +580,29 @@ Ext4.define('EHR.panel.DataEntryPanel', {
509580
return this.dirtyStateArea;
510581
},
511582

583+
getErrorPanel: function(){
584+
if (!this.errorPanel || this.errorPanel.isDestroyed){
585+
this.errorPanel = this.down('#errorPanel');
586+
}
587+
588+
return this.errorPanel;
589+
},
590+
591+
getValidationIndicator: function(){
592+
if (!this.validationIndicator || this.validationIndicator.isDestroyed){
593+
this.validationIndicator = this.down('#validationIndicator');
594+
}
595+
596+
return this.validationIndicator;
597+
},
598+
599+
setValidationIndicatorVisible: function(visible){
600+
var indicator = this.getValidationIndicator();
601+
if (indicator){
602+
indicator.setVisible(visible);
603+
}
604+
},
605+
512606
getButtons: function(){
513607
var buttons = [{
514608
xtype: 'container',

ehr/src/org/labkey/ehr/EHRManager.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ public class EHRManager
136136

137137
public static final String EXPERIMENTAL_REACT_PARTICIPANT_REPORTS = "ehrReactParticipantReports";
138138

139+
public static final String EXPERIMENTAL_SUBMIT_ENABLED_ON_VALIDATION = "ehrSubmitEnabledDuringValidation";
140+
139141
// Column name constants to reduce hardcoding
140142
private static final class ColumnNames
141143
{

ehr/src/org/labkey/ehr/EHRModule.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,11 @@ public void moduleStartupComplete(ServletContext servletContext)
296296
"Use React EHR participant history",
297297
"Links on animal Ids will go to the new React participant history.",
298298
false);
299+
300+
OptionalFeatureService.get().addExperimentalFeatureFlag(EHRManager.EXPERIMENTAL_SUBMIT_ENABLED_ON_VALIDATION,
301+
"Enable 'Submit' buttons during validation",
302+
"User can submit form while validation is in progress",
303+
false);
299304
}
300305

301306
@Override
@@ -337,6 +342,9 @@ public JSONObject getPageContextJson(ContainerUser context)
337342
// Expose the experimental React participant reports flag to client-side JavaScript
338343
ret.put("isReactAnimalHistoryEnabled", AppProps.getInstance().isOptionalFeatureEnabled(EHRManager.EXPERIMENTAL_REACT_PARTICIPANT_REPORTS));
339344

345+
// Expose the experimental 'submit enabled on validation' flag to client-side JavaScript
346+
ret.put("isSubmitEnabledOnValidation", AppProps.getInstance().isOptionalFeatureEnabled(EHRManager.EXPERIMENTAL_SUBMIT_ENABLED_ON_VALIDATION));
347+
340348
if (map.containsKey(EHRManager.EHRStudyContainerPropName) && map.get(EHRManager.EHRStudyContainerPropName) != null)
341349
{
342350
User u = context.getUser();

0 commit comments

Comments
 (0)