Files
enviPy-bayer/static/js/pps.js
2025-10-03 00:07:30 +13:00

787 lines
27 KiB
JavaScript

/////////////////////////////////////////
// Functions to keep after refactoring //
/////////////////////////////////////////
var syncKetcherLock = 0;
var ignoreKetcherUpdates = 0
var ignoreTextFieldUpdates = 0
// The SMILES generated by Ketcher might look different than the one inserted
// however we need this for internal comparison to check whether the SMILES taken
// from the TextInput generates the same smiles.
var SMILES = "";
function syncTextInputToKetcher(textInputId, ketcherId) {
var ketcher = getKetcher(ketcherId);
var textInputSMILES = $('#' + textInputId).val();
data = {
"struct": textInputSMILES,
"output_format": "chemical/x-mdl-molfile",
"options": {
"smart-layout": true,
"ignore-stereochemistry-errors": true,
"mass-skip-error-on-pseudoatoms": false,
"gross-formula-add-rsites": true
}
}
ketcher.server.layout(data).then(
function(response) {
if(response.struct != '') {
console.log("Writing " + textInputSMILES + " to Ketcher");
ketcher.setMolecule(textInputSMILES);
// Wait for ketcher to accept the change and set global SMILES
setTimeout(function() {
SMILES = ketcher.getSmiles();
}, 250);
}
}
).catch(
function(res) {
console.log("Promise failed" + res);
}
);
}
function syncKetcherToTextInput(ketcherId, textInputId) {
// Obtain values
var ketcher = getKetcher(ketcherId);
// Get the SMILES of the molecule currently drawn in Ketcher
var tempKetcherSMILES;
try {
tempKetcherSMILES = ketcher.getSmiles();
} catch(err) {
console.log(err);
return;
}
// compare to SMILES from global scope
if(SMILES == tempKetcherSMILES) {
// There was no change originating from TextInput
return
} else {
SMILES = tempKetcherSMILES;
$("#" + textInputId).val(SMILES);
}
}
function syncKetcherAndTextInput(origin, ketcherId, textInputId) {
// check if function is "locked"
if(origin == 'ketcher') {
// Early exit - no op
if (ignoreKetcherUpdates == 1) {
return;
}
// Lock updates triggered by textInput
ignoreTextFieldUpdates = 1;
// Do the sync
syncKetcherToTextInput(ketcherId, textInputId);
ignoreTextFieldUpdates = 0;
} else {
// Early exit - no op
if(ignoreTextFieldUpdates == 1) {
return;
}
ignoreKetcherUpdates = 1;
// Do the sync
syncTextInputToKetcher(textInputId, ketcherId);
ignoreKetcherUpdates = 0;
}
}
// Returns the ketcher object usually located in an iframe
function getKetcher(idToUse){
if (idToUse === undefined) {
console.log("getKetcher() called without id!");
idToUse = 'ifKetcher';
}
var ketcherFrame = document.getElementById(idToUse);
if ('contentDocument' in ketcherFrame) {
return ketcherFrame.contentWindow.ketcher;
} else { // IE7
return document.frames[idToUse].window.ketcher;
}
}
function enterKeyPressed(event){
// 13 means return/enter
if (event.which == 13 || event.keyCode == 13) {
return false;
}
return true
};
// Function to attach an RSS feed available at feedUrl and attaches it to attachElement
function loadRSSFeed(attachElement, feedUrl) {
$.ajax(feedUrl, {
accepts:{
xml:"application/rss+xml"
},
dataType:"xml",
success:function(data) {
$(data).find("item").each(function () {
var el = $(this);
document.getElementById(attachElement).innerHTML += '<li class="list-group-item">';
+ '<h4 class="list-group-item-heading">'
+ '<a target="blank" href="' + el.find("link").text() + '">'
+ el.find("title").text()
+ "</a></h4><h6>"
+ el.find("pubDate").text()
+ "</h6><p>"
+ el.find("description").text()
+ "</p></li>";
});
}
});
}
function hasValue(id) {
var element = document.getElementById(id);
if(element.value === ""
|| element.value === undefined
|| element.value === null
|| element.value === "undefined"){
return false;
}
return true;
}
function beforePredict(){
// TODO make it a parameter
if(!hasValue("smilesinput")) {
return;
}
encodedSMILES = encodeURIComponent($('#smilesinput').val());
// TODO Fix via templating to set host
url = "/pathway?rootNode="+ encodedSMILES;
$.getJSON(url, function(result) {
// If something is found the returned dict contains the key "pathway" if not it contains "object"
if(result.pathway){
// TODO set as param
attachObjectsAsList("foundPathways", result.pathway);
} else {
$("#index-form").submit();
}
});
}
function attachObjectsAsList(elementId, objects){
var content ='<ul class="list-group">';
var foundone = false;
for (var obj in objects) {
if(objects[obj].reviewStatus == "reviewed"){
content += '<a class="list-group-item" href="' + objects[obj].id + '">';
content += objects[obj].name;
content += '</a>';
foundone = true;
}
}
content += '</ul>';
console.log(foundone);
if(foundone){
$("#foundPathways").append(content);
$("#foundMatching").modal();
} else {
$("#index-form").submit();
}
}
class Setting {
constructor(nameInputId, selectedPackagesId, relativeReasoningSelectId, cutoffInputId, evaluationTypeSelectId,
availableTSSelectId, formsId, tableId, summaryTableId) {
this.nameInputId = nameInputId;
this.selectedPackagesId = selectedPackagesId;
this.relativeReasoningSelectId = relativeReasoningSelectId;
this.cutoffInputId = cutoffInputId;
this.evaluationTypeSelectId = evaluationTypeSelectId;
this.availableTSSelectId = availableTSSelectId;
this.formsId = formsId;
this.tableId = tableId;
this.summaryTableId = summaryTableId;
// General settings
this.name = null;
this.selectedPackages = [];
// Relative Reasoning related
this.selectedRelativeReasoning = null;
this.cutoff = null;
this.evaluationType = null;
// parameters such as { "lowPH": 7, "highPH": 8 }
this.tsParams = {};
}
extractName() {
var tempName = $('#' + this.nameInputId).val()
if (tempName == '') {
console.log("Name was empty...");
return;
}
this.name = tempName;
}
extractSelectedPackages() {
var selPacks = $("#" + this.selectedPackagesId + " :selected");
var ref = this;
ref.selectedPackages = [];
selPacks.each(function () {
var obj = {}
obj['id'] = this.value;
obj['name'] = this.text;
ref.selectedPackages.push(obj);
});
}
extractRelativeReasoning() {
var tempRR = $('#' + this.relativeReasoningSelectId + " :selected").val()
if (tempRR == '') {
console.log("RR was empty...");
return;
}
var obj = {}
obj['id'] = $('#' + this.relativeReasoningSelectId + " :selected").val()
obj['name'] = $('#' + this.relativeReasoningSelectId + " :selected").text()
this.selectedRelativeReasoning = obj;
}
extractCutoff() {
var tempCutoff = $('#' + this.cutoffInputId).val()
if (tempCutoff == '') {
console.log("Cutoff was empty...");
return;
}
this.cutoff = tempCutoff;
}
extractEvaluationType() {
var tempEvaluationType = $('#' + this.evaluationTypeSelectId).val()
if (tempEvaluationType == '') {
console.log("EvaluationType was empty...");
return;
}
this.evaluationType = tempEvaluationType;
}
addTruncator() {
// triggered by "Add"
// will extract values and afterwards updates table + summary
var type = $("#" + this.availableTSSelectId + " :selected").val();
var text = $("#" + this.availableTSSelectId + " :selected").text();
var form = $("#" + type + "_form > :input")
// flag to check whether at least one input had a valid value
var addedValue = false;
// reference being used in each
var ref = this;
form.each(function() {
if(this.value == "" || this.value === "undefined"){
console.log(this);
console.log("Skipping " + this.name);
} else {
var obj = {}
obj[this.name] = this.value;
obj['text'] = text;
ref.tsParams[type] = obj
}
});
this.updateTable();
this.updateSummaryTable();
}
removeTruncator(rowId) {
var summary = rowId.startsWith("sum") ? true : false;
// plain key
var key = rowId.replace(summary ? "sum" : "trunc", "").replace("row", "");
console.log("Removing " + key);
// remove the rows
$("#trunc"+ key + "row").remove();
if($("#sum"+ key + "row").length > 0) {
$("#sum"+ key + "row").remove();
}
delete this.tsParams[key];
}
updateTable() {
// remove all children
$('#'+this.tableId + "Body").empty()
var innerHTML = "<tr>" +
"<td>Name</td>" +
"<td>Value</td>" +
"<td width='10%'>Action</td>" +
"</tr>";
for (var x in this.tsParams) {
var val = "";
for (var y in this.tsParams[x]){
if (y == 'text') {
continue;
}
val += this.tsParams[x][y]
}
innerHTML += "<tr id='trunc" + x + "row'>" +
"<td>" + this.tsParams[x]['text'] + "</td>" +
"<td>" + val + "</td>" +
"<td width='10%'>"+
"<button type='button' id='" + x + "button' class='form-control' onclick='s.removeTruncator(\"trunc" + x + "row\")'>Remove</button>" +
"</td>" +
"</tr>";
}
$('#'+this.tableId + "Body").append(innerHTML);
}
packageRows() {
var res = '';
for(var p in this.selectedPackages) {
var obj = this.selectedPackages[p];
res += "<tr>" +
"<td>Package</td>" +
"<td>" + obj['name'] + "</td>" +
"<td width='10%'>" +
// "<button type='button' id='relativereasoningbutton' class='form-control' onclick='s.removeTruncator(\"Relative Reasoning\")'>Remove</button>" +
"</td>" +
"</tr>";
}
return res;
}
modelRow() {
if(this.selectedRelativeReasoning == null) {
return '';
}
return "<tr>" +
"<td>Relative Reasoning</td>" +
"<td>" + this.selectedRelativeReasoning['name'] + " " + (this.evaluationType == "singleGen" ? "SG" : "MG") + " with t=" + this.cutoff + " </td>" +
"<td width='10%'>" +
// "<button type='button' id='relativereasoningbutton' class='form-control' onclick='s.removeTruncator(\"Relative Reasoning\")'>Remove</button>" +
"</td>" +
"</tr>";
}
updateSummaryTable() {
// remove all children
$('#'+this.summaryTableId + "Body").empty()
var innerHTML = "<tr>" +
"<td>Name</td>" +
"<td>Value</td>" +
"<td width='10%'>Action</td>" +
"</tr>";
innerHTML += this.packageRows();
innerHTML += this.modelRow();
// var innerHTML = ''
for (var x in this.tsParams) {
var val = "";
for (var y in this.tsParams[x]){
if (y == 'text') {
continue;
}
val += this.tsParams[x][y]
}
innerHTML += "<tr id='sum" + x + "row'>" +
"<td>" + this.tsParams[x]['text'] + "</td>" +
"<td>" + val + "</td>" +
"<td width='10%'>"+
"<button type='button' id='" + x + "button' class='form-control' onclick='s.removeTruncator(\"sum" + x + "row\")'>Remove</button>" +
"</td>" +
"</tr>";
}
$('#'+this.summaryTableId + "Body").append(innerHTML);
}
}
// TODO
function queryResultToTable(appendId, result) {
$('#' + appendId).empty();
var table = '<table class="table">' +
'<thead class="thead-dark">' +
'<tr>';
var header = '';
for (var r in result['result']) {
var keys = Object.keys(result['result'][r]);
for (var k in keys) {
header += '<th>' + keys[k] + '</th>';
}
break;
}
table += header;
table += '</tr>';
table += '</thead>';
table += '<tbody>';
var body = '';
for (var r in result['result']) {
body += '<tr>';
for (var k in result['result'][r]) {
body += '<td>' + result['result'][r][k] + '</td>';
}
body += '</tr>';
}
table += body;
table += '</tbody>';
table += '</table>';
$('#' + appendId).append(table);
}
function makeLoadingGif(attachOb, src) {
// TODO provide SERVER_BASE from outside
img_src = src === undefined ? 'http://localhost:8080' : src
var loadingGif = "<div style='text-align:center;'><div class='loading' align='middle'>"
+ "<img id='image-wait' src='" + img_src + "' alt='Loading'" +
" title='Loading'" +
" style='text-align:left;margin:0px auto;' align='middle'/>"
+ "</div></div>";
$(attachOb).html(loadingGif);
//document.getElementById("image-wait").src = getBasePath() + "/" + "images/wait.gif";
}
function cleanIdName(idName) {
return idName.replace(/[ \-()]/g, "")
}
function makeAccordionHead(accordionId, accordionTitle, reviewStatus) {
return "<div class='panel-group' id='" + accordionId + "'>"
+ "<div class='panel panel-default'>"
+ "<div class='panel-heading' id='headingPanel' style='font-size:2rem;height: 46px'>"
+ accordionTitle + reviewStatus
+ "</div><div id='descDiv'></div>"
+ "</div>";
}
function makeAccordionPanel(accordionId, panelName, panelContent, collapsed, id) {
if(panelContent == "" || panelContent == "no description") {
return "";
}
if(collapsed == true) {
collapsed = "in";
} else {
collapsed = "";
}
var panelId = typeof id === "undefined" ? cleanIdName(panelName) : id;
return "<div class='panel panel-default panel-heading list-group-item' style='background-color:silver'>"
+ "<h4 class='panel-title'>"
+ "<a id=" + panelId + "Link" + " data-toggle='collapse' data-parent='#" + accordionId + "' href='#" + panelId + "'>"
+ panelName
+ "</a>"
+ "</h4>"
+ "</div>"
+ "<div id='" + panelId + "' class='panel-collapse collapse " + collapsed + "'>"
+ "<div class='panel-body list-group-item' id='"+panelId+"Content'>"
+ panelContent
+ "</div>"
+ "</div>";
}
function fillPRCurve(modelUri, onclick){
if (modelUri == '') {
return;
}
// clear div
$('#plotDiv').empty();
// for the time being add animation
makeLoadingGif('#plotDiv');
$.getJSON(modelUri + "?prcurve", function (result) {
// remove loading gif
$('#plotDiv').empty();
// check if model is in a proper state
if(result.hasOwnProperty('error')){
var content = '<div>'+result.error+' \nYou can check the model <a href="' + modelUri + '">here</a>. </div>';
$('#plotDiv').append(content);
return
}
var chartPanel = '<div id="chart" class="panel-group"></div>';
$('#plotDiv').append(chartPanel);
// Labels
var x = ['Recall'];
var y = ['Precision'];
var thres = ['threshold'];
// Compare function for the given array
function compare(a, b) {
if (a.threshold < b.threshold)
return -1;
else if (a.threshold > b.threshold)
return 1;
else
return 0;
}
// gets the index for a certain value
function getIndexForValue(data, val, val_name) {
for(var idx in data) {
if(data[idx][val_name] == val) {
return idx;
}
}
return -1;
}
var data = result.prdata;
var dataLength = Object.keys(data).length;
data.sort(compare);
// collect data in 3 individual arrays
for (var idx in data) {
var d = data[idx];
x.push(d.recall);
y.push(d.precision);
thres.push(d.threshold);
}
// generate the actual chart with values collected above
var chart = c3.generate({
bindto: '#chart',
data: {
onclick: function (d, e) {
var idx = d.index;
var thresh = data[dataLength-idx-1].threshold;
onclick(thresh)
},
x: 'Recall',
y: 'Precision',
columns: [
x,
y,
//thres
]
},
size: {
height: 400, // TODO: Make variable to current modal width
width: 480
},
axis: {
x: {
max: 1,
min: 0,
label: 'Recall',
padding: 0,
tick: {
fit: true,
values: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
}
},
y: {
max: 1,
min: 0,
label: 'Precision',
padding: 0,
tick: {
fit: true,
values: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
}
}
},
point: {
r: 4
},
tooltip: {
format: {
title: function (recall) {
idx = getIndexForValue(data, recall, "recall");
if(idx != -1) {
return "Threshold: " + data[idx].threshold;
}
return "";
},
value: function (precision, ratio, id) {
return undefined;
}
}
},
zoom: {
enabled: true
}
});
});
}
function handleAssessmentResponse(depict_url, data) {
var inside_app_domain = "<a class='list-group-item'>This compound is " + (data["assessment"]["inside_app_domain"] ? "inside" : "outside") + " the Applicability Domain derived from the chemical (PCA) space constructed using the training data." + "</a>";
var functionalGroupsImgSrc = null;
var reactivityCentersImgSrc = null;
if (data['assessment']['node'] !== undefined) {
functionalGroupsImgSrc = "<img width='400' src='" + data['assessment']['node']['image'] + "'>";
reactivityCentersImgSrc = "<img width='400' src='" + data['assessment']['node']['image'] + "'>"
} else {
functionalGroupsImgSrc = "<img width='400' src=\"" + depict_url + "?smiles=" + encodeURIComponent(data['assessment']['smiles']) + "\">";
reactivityCentersImgSrc = "<img width='400' src=\"" + depict_url + "?smiles=" + encodeURIComponent(data['assessment']['smiles']) + "\">"
}
tpl = `<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
<h4 class="panel-title">
<a id="app-domain-assessment-functional-groups-link" data-toggle="collapse" data-parent="#app-domain-assessment" href="#app-domain-assessment-functional-groups">Functional Groups Covered by Model</a>
</h4>
</div>
<div id="app-domain-assessment-functional-groups" class="panel-collapse collapse">
<div class="panel-body list-group-item">
${inside_app_domain}
<p></p>
<div id="image-div" align="center">
${functionalGroupsImgSrc}
</div>
</div>
</div>
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
<h4 class="panel-title">
<a id="app-domain-assessment-reactivity-centers-link" data-toggle="collapse" data-parent="#app-domain-assessment" href="#app-domain-assessment-reactivity-centers">Reactivity Centers</a>
</h4>
</div>
<div id="app-domain-assessment-reactivity-centers" class="panel-collapse collapse">
<div class="panel-body list-group-item">
<div id="image-div" align="center">
${reactivityCentersImgSrc}
</div>
</div>
</div>`
var transformations = '';
for (t in data['assessment']['transformations']) {
transObj = data['assessment']['transformations'][t];
var neighbors = '';
for (n in transObj['neighbors']) {
neighObj = transObj['neighbors'][n];
var neighImg = "<img width='100%' src='" + transObj['rule']['url'] + "?smiles=" + encodeURIComponent(neighObj['smiles']) + "'>";
var objLink = `<a class='list-group-item' href="${neighObj['url']}">${neighObj['name']}</a>`
var neighPredProb = "<a class='list-group-item'>Predicted probability: " + neighObj['probability'].toFixed(2) + "</a>";
var pwLinks = '';
for (pw in neighObj['related_pathways']) {
var pwObj = neighObj['related_pathways'][pw];
pwLinks += "<a class='list-group-item' href=" + pwObj['url'] + ">" + pwObj['name'] + "</a>";
}
var expPathways = `
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
<h4 class="panel-title">
<a id="transformation-${t}-neighbor-${n}-exp-pathway-link" data-toggle="collapse" data-parent="#transformation-${t}-neighbor-${n}" href="#transformation-${t}-neighbor-${n}-exp-pathway">Experimental Pathways</a>
</h4>
</div>
<div id="transformation-${t}-neighbor-${n}-exp-pathway" class="panel-collapse collapse">
<div class="panel-body list-group-item">
${pwLinks}
</div>
</div>
`
if (pwLinks === '') {
expPathways = ''
}
neighbors += `
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
<h4 class="panel-title">
<a id="transformation-${t}-neighbor-${n}-link" data-toggle="collapse" data-parent="#transformation-${t}" href="#transformation-${t}-neighbor-${n}">Analog Transformation on ${neighObj['name']}</a>
</h4>
</div>
<div id="transformation-${t}-neighbor-${n}" class="panel-collapse collapse">
<div class="panel-body list-group-item">
${objLink}
${neighPredProb}
${expPathways}
<p></p>
<div id="image-div" align="center">
${neighImg}
</div>
</div>
</div>
`
}
var panelName = null;
var objLink = null;
if (transObj['is_predicted']) {
panelName = `Predicted Transformation by ${transObj['rule']['name']}`;
for (e in transObj['edges']) {
objLink = `<a class='list-group-item' href="${transObj['edges'][e]['url']}">${transObj['edges'][e]['name']}</a>`
break;
}
} else {
panelName = `Potential Transformation by applying ${transObj['rule']['name']}`;
objLink = `<a class='list-group-item' href="${transObj['rule']['url']}">${transObj['rule']['name']}</a>`
}
var predProb = "<a class='list-group-item'>Predicted probability: " + transObj['probability'].toFixed(2) + "</a>";
var timesTriggered = "<a class='list-group-item'>This rule has triggered " + transObj['times_triggered'] + " times in the training set</a>";
var reliability = "<a class='list-group-item'>Reliability: " + transObj['reliability'].toFixed(2) + " (" + (transObj['reliability'] > data['ad_params']['reliability_threshold'] ? "&gt" : "&lt") + " Reliability Threshold of " + data['ad_params']['reliability_threshold'] + ") </a>";
var localCompatibility = "<a class='list-group-item'>Local Compatibility: " + transObj['local_compatibility'].toFixed(2) + " (" + (transObj['local_compatibility'] > data['ad_params']['local_compatibility_threshold'] ? "&gt" : "&lt") + " Local Compatibility Threshold of " + data['ad_params']['local_compatibility_threshold'] + ")</a>";
var transImg = "<img width='100%' src='" + transObj['rule']['url'] + "?smiles=" + encodeURIComponent(data['assessment']['smiles']) + "'>";
var transformation = `
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
<h4 class="panel-title">
<a id="transformation-${t}-link" data-toggle="collapse" data-parent="#transformation-${t}" href="#transformation-${t}">${panelName}</a>
</h4>
</div>
<div id="transformation-${t}" class="panel-collapse collapse">
<div class="panel-body list-group-item">
${objLink}
${predProb}
${timesTriggered}
${reliability}
${localCompatibility}
<p></p>
<div id="image-div" align="center">
${transImg}
</div>
<p></p>
${neighbors}
</div>
</div>
`
transformations += transformation;
}
res = tpl + transformations;
$("#appDomainAssessmentResultTable").append(res);
}