diff --git a/shrine-webclient/src/main/html/js-i2b2/cells/plugins/MedCo/MedCo_QryTool.js b/shrine-webclient/src/main/html/js-i2b2/cells/plugins/MedCo/MedCo_QryTool.js
index cc32f5f30..d33234bdf 100644
--- a/shrine-webclient/src/main/html/js-i2b2/cells/plugins/MedCo/MedCo_QryTool.js
+++ b/shrine-webclient/src/main/html/js-i2b2/cells/plugins/MedCo/MedCo_QryTool.js
@@ -1,677 +1,687 @@
i2b2.MedCo.QT = {};
var pieResultParam = {
"header": {
"title": {
"font": "cinzel",
"text": "#patients",
"color": "#000000",
"fontSize": 24
},
"location": "pie-center",
},
"size": {
"canvasHeight": 400,
"canvasWidth": 600,
"pieInnerRadius": "59%",
"pieOuterRadius": "63%"
},
"data": {
"sortOrder": "label-desc",
"content": {}
},
"labels": {
"outer": {
"format": "label-value2",
"pieDistance": 20
},
"inner": {
"format": "none"
},
"mainLabel": {
"font": "cinzel",
"fontSize": 15
},
"percentage": {
"font": "cinzel",
"color": "#999999",
"fontSize": 8,
"decimalPlaces": 0
},
"value": {
"font": "cinzel",
"color": "#000000",
"fontSize": 15,
"fontWeight": 600
},
"lines": {
"enabled": true,
},
"truncation": {
"enabled": true
}
},
"tooltips": {
"enabled": true,
"type": "placeholder",
"string": "{label}: {value}, {percentage}%",
"styles": {
"fontSize": 15,
"backgroundOpacity": 0.70,
"borderRadius": 15,
"padding": 7
}
},
"effects": {
"load": {
"speed": 500
},
"pullOutSegmentOnClick": {
"effect": "none",
},
"highlightLuminosity": 0.3
},
"misc": {
"colors": {
"segmentStroke": "#000000"
},
"pieCenterOffset": {
"y": -50
}
}
}
var currId = 0; // used to give a unique id to the panels
function getPanelIndex(panel){
while (panel.className != 'MedCo-QueryPanel'){
if (!panel.parentElement){
return -1
}
panel = panel.parentElement
}
var index = Array.prototype.indexOf.call(panel.parentNode.children, panel);
return index
}
function htmlToElement(html) {
var template = document.createElement('template');
html = html.trim(); // Never return a text node of whitespace as the result
template.innerHTML = html;
return template.content.firstChild;
}
i2b2.MedCo.QT.ctrlr = {};
i2b2.MedCo.QT.ctrlr.init = function(){
// Initialization of the query tool.
var num_panels = 3; // start with 3 panels
i2b2.MedCo.QT.panels = [];
for (var i=0; i < num_panels; i++) {
i2b2.MedCo.QT.ctrlr.appendPanel()
}
// i2b2.MedCo.QT.query.init();
};
i2b2.MedCo.QT.ctrlr.clear = function (){
// todo and ignore a possible query result?
// clear all the panels
var panels = i2b2.MedCo.QT.panels;
for(var i = 0; i < panels.length; i++){
panels[i].ctrlr.clear()
}
};
i2b2.MedCo.QT.timer = {
// number of milliseconds between two updates
step: 100,
// store the start time (in milliseconds)
startTime: 0,
//store the pointer to the interval object so to stop it
timeinterval: null,
start: function () {
// hide the gif and start
document.getElementById("medcoTimer").style.display = 'inline-block';
document.getElementById("MedCo-loadingGif").style.display = 'none';
var context = i2b2.MedCo.QT.timer;
context.clear();
context.startTime = new Date().getTime();
context.timeinterval = setInterval(context.update, context.step);
},
update: function () {
var now = new Date().getTime();
document.querySelector("#medcoTimer").innerHTML = ((now-i2b2.MedCo.QT.timer.startTime)/1000).toFixed(2);
},
stop: function () {
clearInterval(i2b2.MedCo.QT.timer.timeinterval);
i2b2.MedCo.QT.timer.timeinterval = null;
},
clear: function(){
i2b2.MedCo.QT.timer.stop();
document.querySelector("#medcoTimer").innerHTML = 0;
},
wait: function(){
// show the gif
document.getElementById("medcoTimer").style.display = 'none';
document.getElementById("MedCo-loadingGif").style.display = 'inline';
}
};
i2b2.MedCo.QT.query = {
// timeout used to wait until we received all the variant ids
timeout: null,
// You should call this function to run a query so to make sure you received and encrypted all
// the annotation ids you queried
waitThenRun: function(){
// clear any previous timeout (in case the user keeps pressing "run query" we want to send only one query)
clearTimeout(i2b2.MedCo.QT.query.timeout);
// clear the timer in any case
i2b2.MedCo.QT.timer.clear();
// show that we are preparing to send the query
i2b2.MedCo.QT.timer.wait();
// wait until you receive all the variants you requested and all of them have been encrypted
if (i2b2.MedCo.ctrlr.pendingVariantsQueries != 0 || !i2b2.MedCo.ctrlr.background.encryptionDone()) { // todo change names
// try again later to run the query
i2b2.MedCo.QT.query.timeout = setTimeout(i2b2.MedCo.QT.query.waitThenRun, 500);
return
}
// the query can be build and sent
i2b2.MedCo.QT.query.run();
},
// build and send the query
run: function(){
// start the timer
i2b2.MedCo.QT.timer.start();
// callback processor to run the query from definition
this.MedCocallback = new i2b2_scopedCallback();
this.MedCocallback.scope = this;
this.MedCocallback.callback = function(results) {
// // "results" object contains the following attributes:
// // refXML: xmlDomObject <--- for data processing
// // msgRequest: xml (string)
// // msgResponse: xml (string)
// // error: boolean
// // errorStatus: string [only with error=true]
// // errorMsg: string [only with error=true]
// stop the timer and the gif
i2b2.MedCo.QT.timer.stop();
try{
if (results.error) {
alert(results.errorMsg);
return;
} else {
// extract and show the patient counts
i2b2.MedCo.QT.query.showResponse(results, "MedCo-QueryResults");
}
} catch(e){
alert("Error when extracting the results of the query: " + e.message)
}
};
+ // todo change the result_output_list with the selected options:
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+
this.MedCoparams = {};
this.MedCoparams.psm_query_definition = i2b2.MedCo.QT.query.buildQueryDefinition("MedCo_query");
this.MedCoparams.psm_result_output = "\n\n";
i2b2.CRC.ajax.runQueryInstance_fromQueryDefinition("PLUGIN:MedCo", this.MedCoparams, this.MedCocallback);
},
showResponse: function(response, resultDiv){
response = response.refXML.getElementsByTagName('query_result_instance');
// first extract the data
var institutions = new Array(response.length);
for (var i = 0; i < response.length; i++) {
try {
var currInstitution = JSON.parse(i2b2.h.getXNodeVal(response[i], 'description'));
var instName = Object.keys(currInstitution)[0];
i2b2.MedCo.ctrlr.background.toBeDecrypted([currInstitution[instName]["enc_count_result"]]);
institutions[i] = {
label: instName,
enc_count_result: currInstitution[instName]["enc_count_result"]
}
} catch (e) {
alert("Error in extracting the response." + i2b2.h.getXNodeVal(response[i], 'summary'));
return
}
}
// now wait until all ciphertext are decrypted
var checkThenShow = function(){
if (i2b2.MedCo.ctrlr.background.decryptionDone()) {
var resultPanel = document.getElementById(resultDiv)
while (resultPanel.firstChild) {
resultPanel.removeChild(resultPanel.firstChild);
}
var totPatientCount = 0;
for (var i = 0; i < institutions.length; i++){
institutions[i].value = i2b2.MedCo.ctrlr.background.decryptionCache[institutions[i].enc_count_result];
totPatientCount += institutions[i].value;
}
// if there are no patients that match the query then do not display the pie chart
if (totPatientCount == 0) {
resultPanel.innerHTML = "No patient matches the query"
return
}
pieResultParam.data.content = institutions;
new d3pie(resultDiv, pieResultParam);
}
else{
setTimeout(checkThenShow, 100)
}
};
checkThenShow();
},
// build the xml of the query from i2b2.MedCo.QT.panels
buildQueryDefinition: function(name){
// name: name for the query
// get the time of the query (we concatenate the time to the name of the query)
var d = new Date();
var time =
d.getHours().toString() + ":" +
d.getMinutes().toString() + ":" +
d.getSeconds().toString() + ":" +
d.getMilliseconds().toString();
var rand = Math.random()+""
var queryxml = "\n\t" + name + "@" + time + ":" + rand + "\n\tANY\n\t0";
var panels = i2b2.MedCo.QT.panels;
for (var i = 0 ; i < panels.length; i++){ // there are 3 panels
if (panels[i].ctrlr.isEmpty()) {continue} // if in the panel there are no parameters the go to the next panel
// open a panel object in which we put all its concepts
queryxml +=
"\n\t\n\t\t" +
"" + i + "\n\t\t" +
"100\n\t\t" +
"" + (panels[i].model.exclude ? 1 : 0) + "\n\t\t" +
"ANY\n\t\t" +
"1\n\t\t";
// put the all the concepts in the query
for (var j = 0; j < panels[i].model.content.length; j++){
// take one "row" at a time and take encrypted values if sensitive
var item_keys = [];
var row = panels[i].model.content[j];
var dateConstraint = panels[i].model.date.model;
if (row.sensitive){
// the row contains sensitive values
for (var z=0; z\n\t\t\t" +
"" + item_keys[z] + "\n\t\t"
if (dateConstraint.dateFrom || dateConstraint.dateTo){
queryxml +=
"\t" +
(dateConstraint.dateFrom ? "\n\t\t\t\t" + dateConstraint.dateFrom.Year + "-" + dateConstraint.dateFrom.Month + "-" + dateConstraint.dateFrom.Day + "T00:00:00.000-05:00'\n\t\t\t" : "") +
(dateConstraint.dateTo ? "\n\t\t\t\t" + dateConstraint.dateTo.Year + "-" + dateConstraint.dateTo.Month + "-" + dateConstraint.dateTo.Day + "T00:00:00.000-05:00'\n\t\t\t\t" : "") +
"\n\t\t"
}
queryxml += "\n";
}
}
queryxml +="\n\t\n"
}
queryxml += "\n\n";
return queryxml
},
};
i2b2.MedCo.QT.ctrlr.appendPanel = function(){
// Each element (row) visualized on a panel is bound either to a concept (which can
// be either sensitive or non-sensitive) or to a list of variant ids (always sensitive).
// create a new panel, add it to the list of panels and bind to it model, view and controller
var panel = {};
panel.view = {};
panel.ctrlr = {};
panel.model = {};
i2b2.MedCo.QT.panels.push(panel);
// ------ initialize the model of the panel ------
panel.model = {
// indicates whether the panel is excluded or not
"exclude": false,
// contains a list of {"sensitive": bool, "values"=[ list of concepts/variant ids bound to i-th row of the panel ]}
"content": [],
// date constraint model and controller (to show the popup)
"date": {},
// id of the whole html panel
"panelId": 'MedCo-QueryPanel' + currId,
// id of the html panel containing dropped concepts
"conceptPanelId": "MedCo-QueryPanelConcepts" + currId
};
currId++;
// bind the panel to a date constraint model
i2b2.MedCo.QT.dateConstraint.ctrlr.init(panel)
// ------ initialize the view of the panel ------
document.getElementById("MedCo-QueryPanels").appendChild(htmlToElement(
"
" +
"" +
"
" +
"
" +
"
" +
//
"
" +
"
" +
"
" +
"
" +
"
" +
"
"));
// // create and add the div for the dropped concepts
i2b2.sdx.Master.AttachType(panel.model.conceptPanelId , 'CONCPT', {dropTarget: true});
i2b2.sdx.Master.setHandlerCustom(panel.model.conceptPanelId, 'CONCPT', 'DropHandler', function (sdxData) {panel.ctrlr.doDrop(sdxData);});
/* Instantiate a ContextMenu for this panel*/
YAHOO.util.Event.onContentReady(panel.model.conceptPanelId, function () {
var conceptContextMenu = new YAHOO.widget.ContextMenu(
panel.model.conceptPanelId+"ContextMenu",
{
trigger: document.getElementById(panel.model.conceptPanelId).childNodes,
itemdata: ["Delete"],
lazyload: true
}
);
// whenever the content of the panel changes, update the trigger property
$(panel.model.conceptPanelId).on("DOMSubtreeModified",function(){
conceptContextMenu.cfg.setProperty("trigger", document.getElementById(panel.model.conceptPanelId).childNodes);
});
function onContextMenuClick(p_sType, p_aArgs) {
//p_aArgs[1]: MenuItem instance that was the target of the "click" event.
var oItem = p_aArgs[1], // The MenuItem that was clicked
oTarget = this.contextEventTarget,
oLI;
if (oItem) {
// todo: also verify the class (sdxDefaultCONCPT)?
oLI = oTarget.nodeName.toUpperCase() == "DIV" ?
oTarget : YAHOO.util.Dom.getAncestorByClassName(oTarget, "sdxDefaultCONCPT");
switch (oItem.index) {
case 0: // Delete
// remove the concept from the view and from the model
var panel_view = document.getElementById(panel.model.conceptPanelId)
var index = Array.from(panel_view.children).indexOf(oLI)
panel.model.content.splice(index, 1)
panel_view.removeChild(oLI)
break;
// add here other cases if there are more menu items
}
}
}
// "render" event handler for the ewe context menu
function onContextMenuRender(p_sType, p_aArgs) {
// Add a "click" event handler to the ewe context menu
this.subscribe("click", onContextMenuClick);
}
conceptContextMenu.subscribe("render", onContextMenuRender);
// a bit rude bug fix... (hide menu when clicking on the panel)
document.getElementById(panel.model.conceptPanelId).onclick = function () {conceptContextMenu.hide.call(conceptContextMenu)}
});
panel.view.appendConcept = function (concept, output){
var conceptDiv =
"" +
((panel.model.exclude) ? "
NOT" : "") +
"
" +
concept +
((output)? (" (
)") : "") +
"
";
// var panel.model.conceptPanelId = panel.view.getElementsByClassName('MedCo-QueryPanelConcepts')[0].id;
var conceptPanel = document.getElementById(panel.model.conceptPanelId)
conceptPanel.innerHTML += conceptDiv;
return conceptPanel.lastChild
};
panel.view.clear = function(){
panel.model.exclude = false;
var excludeButton = document.getElementById(panel.model.panelId).getElementsByClassName("exclude")[0]
excludeButton.style.textDecoration = panel.model.exclude? "underline":"none";
var concepts = document.getElementById(panel.model.conceptPanelId);
while (concepts.firstChild) {
concepts.removeChild(concepts.firstChild);
}
};
// ------ initialize the controller ------
panel.ctrlr.doDrop = function (sdxData) {
var concept = sdxData[0]; // only interested in first record
// CHECK: some useful functions:
// alert(i2b2.h.getXNodeVal(sdxData.origData.xmlOrig, "level"))
// alert(Object.getOwnPropertyNames(sdxData.origData));
// optimization to prevent requerying the hive for new results if the input dataset has not changed
// i2b2.ExampTabs.model.dirtyResultsData = true;
// check whether to show a popup or directly append the concept
switch(concept.sdxInfo.sdxDisplayName) {
case "Gene Name":
i2b2.MedCo.popups.ByGene.ctrlr.show(i2b2.MedCo.QT.panels.indexOf(panel));
return;
case "Protein Position":
i2b2.MedCo.popups.ByProteinPosition.ctrlr.show(i2b2.MedCo.QT.panels.indexOf(panel));
return;
case "Variant Name":
i2b2.MedCo.popups.ByVariantName.ctrlr.show(i2b2.MedCo.QT.panels.indexOf(panel));
return;
}
var keyval = concept.sdxInfo.sdxKeyValue;
var sensitive = !keyval.includes("nonsensitive") && !keyval.includes("non-sensitive");
var conceptModel = sensitive ? concept.origData.basecode.split(":")[1] : (concept.origData.key).replace(/ NOT
var not = document.createElement("span")
not.className = "itemExclude"
not.title = "This item is being excluded"
not.innerHTML = "NOT"
conceptsDiv[i].insertBefore(not, conceptsDiv[i].firstChild);
}
}
else {
// remove the "NOT" in front of the concepts in the div
for (var i = 0; i < conceptsDiv.length; i++) {
conceptsDiv[i].removeChild(conceptsDiv[i].getElementsByClassName("itemExclude")[0])
}
}
};
panel.ctrlr.delete = function(){
var index = i2b2.MedCo.QT.panels.indexOf(panel)
if (index == -1){
alert("Impossible to delete the panel, panel inexistent.")
return
}
// delete the panel from the model
i2b2.MedCo.QT.panels.splice(index, 1)
// delete the panel from the view
var panels_container = document.getElementById("MedCo-QueryPanels")
var panels = panels_container.childElements()
panels_container.removeChild(panels[index]);
// update the group number in the header of the panels
panels = panels_container.childElements()
for (var i = 0; i < panels.length; i++) {
// i2b2.MedCo.QT.panels[i].group = (i + 1);
var header = panels[i].getElementsByClassName("groupId")[0]
header.innerHTML = 'Group ' + (i+1);
}
if (i2b2.MedCo.QT.panels.length != panels.length){
alert("Something went wrong: model and view are inconsistent.")
}
}
panel.ctrlr.isEmpty = function(){
// check there is at least a row which contains something
for (var i=0; i < panel.model.content.length; i++) {
if (panel.model.content[i].values.length > 0) {
return false;
}
}
return true;
}
};
i2b2.MedCo.QT.ctrlr.deletePanel = function(HTMLelem){
// HTMLelem: (clicked) html element
// first get the index of the panel
var index = getPanelIndex(HTMLelem);
if (index < 0){
alert("Panel not found");
return
}
// then call the delete function on it
i2b2.MedCo.QT.panels[index].ctrlr.delete()
};
i2b2.MedCo.QT.ctrlr.excludePanel = function(HTMLelem){
// HTMLelem: (clicked) html element
// first get the index of the panel
var index = getPanelIndex(HTMLelem)
if (index < 0){
alert("Panel not found")
return
}
// then call the exclude function on it
i2b2.MedCo.QT.panels[index].ctrlr.exclude(HTMLelem)
};
i2b2.MedCo.QT.ctrlr.showDateConstraint = function(HTMLelem){
var index = getPanelIndex(HTMLelem)
if (index < 0){
alert("Panel not found")
return
}
// alert("panel index:"+index)
i2b2.MedCo.QT.panels[index].model.date.ctrlr.show()
}
\ No newline at end of file