Commit 4f7d3571 authored by Mikaël Salson's avatar Mikaël Salson
Browse files

-- Merge Query to the CloneDB through a dedicated controller

See vdj#210
parents 8a94597b a26da971
Pipeline #1330 passed with stages
in 2 minutes and 19 seconds
......@@ -1149,10 +1149,18 @@ Clone.prototype = {
}
//IMGT info
if (this.seg.imgt != null) {
html += "<tr><td class='header' colspan='" + (time_length + 1) + "'> Results of <a href='http://www.imgt.org/IMGT_vquest/share/textes/'>IMGT/V-QUEST</a> " + "</td></tr>";
for (var item in this.seg.imgt) {
html += "<tr><td> " + item + "</td><td> " + this.seg.imgt[item] + "</td></tr>";
var other_infos = {"imgt": "<a href='http://www.imgt.org/IMGT_vquest/share/textes/'>IMGT/V-QUEST</a>",
"clonedb": "<a href='http://ecngs.vidjil.org/clonedb'>CloneDB</a>"};
for (var external_tool in other_infos) {
if (typeof this.seg[external_tool] != 'undefined'
&& this.seg[external_tool] != null) {
html += "<tr><td class='header' colspan='" + (time_length + 1) + "'> Results of "+other_infos[external_tool]+ "</td></tr>";
for (var item in this.seg[external_tool]) {
if (! (this.seg[external_tool][item] instanceof Object)
&& ! (this.seg[external_tool][item] instanceof Array)) {
html += "<tr><td> " + item + "</td><td> " + this.seg[external_tool][item] + "</td></tr>";
}
}
}
}
......
......@@ -4,6 +4,7 @@ DB_TIMEOUT_GET_DATA = 20000 // Get patient/sample .data
DB_TIMEOUT_GET_CUSTOM_DATA = 1200000 // Launch custum fused sample .data
NOTIFICATION_PERIOD = 30000 // Time interval to check for notifications periodically
var SEQ_LENGTH_CLONEDB = 40; // Length of the sequence retrieved for CloneDB
/**
*
......@@ -85,9 +86,20 @@ Database.prototype = {
this.div.appendChild(close_popup)
this.div.appendChild(this.msg)
this.connected = undefined;
document.body.appendChild(this.div);
},
/**
* @return - true iff the last connection was successful
* - false if not
* - undefined if no connection was attempted yet
*/
is_connected: function() {
return this.connected;
},
/**
* check ssl certificate validity
......@@ -142,8 +154,10 @@ Database.prototype = {
success: function (result) {
result = jQuery.parseJSON(result)
setTimeout(function(){ self.waitProcess(result.processId, 5000, callback)}, 5000);
self.connected = true;
},
error: function (request, status, error) {
self.connected = false;
if (status === "timeout") {
console.log({"type": "flash", "default" : "database_timeout", "priority": 2});
} else {
......@@ -171,6 +185,7 @@ Database.prototype = {
timeout: DB_TIMEOUT_CALL,
xhrFields: {withCredentials: true},
success: function (result) {
self.connected = true;
result = jQuery.parseJSON(result)
if (result.status == "COMPLETED"){
callback(result.data);
......@@ -182,6 +197,7 @@ Database.prototype = {
},
error: function (request, status, error) {
self.connected = false;
if (status === "timeout") {
console.log({"type": "flash", "default" : "database_timeout", "priority": 2});
} else {
......@@ -236,8 +252,10 @@ Database.prototype = {
xhrFields: {withCredentials: true},
success: function (result) {
self.display_result(result, url, args)
self.connected = true;
},
error: function (request, status, error) {
self.connected = false;
if (status === "timeout") {
console.log({"type": "flash", "default" : "database_timeout", "priority": 2});
} else {
......@@ -249,6 +267,41 @@ Database.prototype = {
});
},
callCloneDB: function(clones) {
var windows = [];
var self = this;
var kept_clones = [];
for (var i = 0; i < clones.length; i++) {
var clone = this.m.clones[clones[i]];
if (clone.hasSeg('5', '3')) {
var middle_pos = Math.round((clone.seg['5']['stop'] + clone.seg['3']['start'])/2);
windows.push(clone.sequence.substr(middle_pos - Math.round(SEQ_LENGTH_CLONEDB/2), SEQ_LENGTH_CLONEDB));
kept_clones.push(clones[i]);
}
}
$.ajax({
type: "POST",
url: self.db_address+"clonedb",
data: "sequences="+windows.join()+"&sample_set_id="+self.m.sample_set_id,
xhrFields: {withCredentials: true},
success: function (result) {
self.connected = true;
for (var i = 0; i < kept_clones.length; i++) {
self.m.clones[kept_clones[i]].seg.clonedb = processCloneDBContents(result[i]);
}
},
error: function() {
self.connected = false;
console.log({
"type": "flash",
"msg": "Error while requesting CloneDB",
"priority": 2
});
}
});
},
pre_process_onChange : function () {
......@@ -368,8 +421,10 @@ Database.prototype = {
xhrFields: {withCredentials: true},
success: function (result) {
self.display_result(result, $(this).attr('action'))
self.connected = true;
},
error: function (request, status, error) {
self.connected = false;
if (status === "timeout") {
console.log({"type": "flash", "default" : "database_timeout", "priority": 2});
} else {
......@@ -665,8 +720,11 @@ Database.prototype = {
success: function (result) {
self.display_result(result, "", args);
console.log('=== load_data: success ===');
self.connected = true;
// self.callCloneDB(_.range(self.m.clones.length));
},
error: function (request, status, error) {
self.connected = false;
if (status === "timeout") {
console.log({"type": "flash", "default" : "database_timeout", "msg" : " - unable to access patient data" , "priority": 2});
} else {
......@@ -721,8 +779,10 @@ Database.prototype = {
success: function (result) {
self.m.resume()
self.display_result(result, "", args);
self.connected = true;
},
error: function (request, status, error) {
self.connected = false;
self.m.resume()
if (status === "timeout") {
console.log({"type": "flash", "default" : "database_timeout", "msg": " - unable to access patient data" , "priority": 2});
......
......@@ -182,6 +182,17 @@ Segment.prototype = {
span.appendChild(document.createTextNode("❯ to ARResT/CJ"));
div_menu.appendChild(span);
//toCloneDB button
span = document.createElement('span');
span.id = "toCloneDB";
span.setAttribute('title', 'Send sequences to EC-NGS/CloneDB in the background')
span.className = "button devel-mode";
span.onclick = function () {
self.db.callCloneDB(self.m.getSelected());
};
span.appendChild(document.createTextNode("❯ to CloneDB"));
div_menu.appendChild(span);
//toBlast button
span = document.createElement('span');
span.id = "toBlast";
......
......@@ -93,6 +93,58 @@ function correctIMGTPositionsForInsertions(data) {
}
}
/**
* Take in parameter the JSON result of CloneDB for one clone
* Return a hash whose keys are URLs to sample sets and configs.
* An additional key (termed 'original') corresponds to the original
* results as returned by CloneDB.
*/
function processCloneDBContents(results) {
var existing_urls = {};
var final_results = {};
var count_non_viewable = 0;
for (var clone in results) {
if (typeof results[clone]['tags'] != 'undefined'
&& typeof results[clone]['tags']['sample_set'] != 'undefined'
&& typeof results[clone]['tags']['sample_set_viewable'] != 'undefined') {
var nb_samples = results[clone]['tags']['sample_set'].length;
for (var i = 0; i < nb_samples; i++) {
if (results[clone]['tags']['sample_set_viewable'][i]) {
var name = results[clone]['tags']['sample_set_name'][i];
if (name == null)
name = results[clone]['tags']['sample_set'][i];
var config_name = results[clone]['tags']['config_name'];
if (typeof config_name != 'undefined'
&& typeof config_name[0] != 'undefined')
config_name = config_name[0];
else
config_name = 'unknown';
url = '?sample_set_id='+results[clone]['tags']['sample_set'][i]+'&config='+results[clone]['tags']['config_id'][0];
var msg = '<a href="'+url+'">'+name+'</a> ('+config_name+')';
if (! (url in existing_urls)) {
existing_urls[url] = true;
final_results[msg] = results[clone]['occ'];
} else{
final_results[msg] += results[clone]['occ'];
}
} else {
count_non_viewable += 1;
}
}
} else {
count_non_viewable += 1;
}
}
for (msg in final_results) {
final_results[msg] = final_results[msg]+' clone'+((final_results[msg] == 1) ? '' : 's');
}
if (count_non_viewable > 0)
final_results['Non viewable samples'] = count_non_viewable;
final_results['original'] = results;
return final_results;
}
/**
* extract information from htlm page
*
......
......@@ -34,6 +34,78 @@ QUnit.test("test rounding functions", function(assert) {
}
);
QUnit.test("processCloneDBContents", function(assert) {
var emptyResult = [];
assert.deepEqual(processCloneDBContents(emptyResult), {'original': []},
"processing empty result");
var singleResult = [{'tags': {'sample_set_viewable': [true, true],
'sample_set_name': ['patient', null],
'sample_set': ["15", "152"],
'config_id': ['1024'],
'config_name': ['config'],
'sample_name': ['toto']},
'occ': 3,
'V' : 'IGHV1*02',
'J' : 'IGHJ3*01'}];
assert.deepEqual(processCloneDBContents(singleResult),
{'<a href="?sample_set_id=15&config=1024">patient</a> (config)': "3 clones",
'<a href="?sample_set_id=152&config=1024">152</a> (config)': "3 clones",
'original': singleResult}, "processing one result");
var multipleResults = [{'tags': {'sample_set_viewable': [true, true],
'sample_set_name': ['patient', null],
'sample_set': ["15", "152"],
'config_id': ['1024'],
'config_name': ['config'],
'sample_name': ['toto']},
'occ': 3,
'V' : 'IGHV1*02',
'J' : 'IGHJ3*01'},
// Different clone from the previous one but a common sample set
{'tags': {'sample_set_viewable': [true],
'sample_set_name': [null],
'sample_set': ["152"],
'config_id': ['1024'],
'config_name': ['config'],
'sample_name': ['toto']},
'occ': 2,
'V' : 'IGHV1*01',
'J' : 'IGHJ2*01'},
// Test that the not viewable are not taken into account
{'tags': {'sample_set_viewable': [false],
'sample_set_name': ['secret'],
'sample_set': ["666"],
'config_id': ['10'],
'config_name': ['[old] config'],
'sample_name': ['toto']},
'occ': 100,
'V' : 'IGHV1*02',
'J' : 'IGHJ3*01'}];
var results = processCloneDBContents(multipleResults);
assert.equal(results['<a href="?sample_set_id=152&config=1024">152</a> (config)'], '5 clones', "multiple results");
assert.equal(results['<a href="?sample_set_id=15&config=1024">patient</a> (config)'], '3 clones', "multiple results, one entry");
assert.equal(results['Non viewable samples'], 1, "One non viewable sample");
var count = 0;
for (var item in results) {
count += 1;
}
assert.equal(count, 4, "Two results plus one non-viewable plus original entry");
// Test missing viewable property
var missingViewable = [{'tags': {'sample_set_name': ['patient', null],
'sample_set': ["15", "152"],
'config_id': ['1024'],
'config_name': ['config'],
'sample_name': ['toto']},
'occ': 3,
'V' : 'IGHV1*02',
'J' : 'IGHJ3*01'}];
assert.deepEqual(processCloneDBContents(missingViewable), {'Non viewable samples': 1, 'original': missingViewable},
"processing missingViewable");
});
QUnit.test("processImgtContents", function(assert) {
var ready = assert.async();
assert.expect(5);
......
# coding: utf8
import gluon.contrib.simplejson, datetime
import vidjil_utils
import time
import os
import sys
import imp
if request.env.http_origin:
response.headers['Access-Control-Allow-Origin'] = request.env.http_origin
response.headers['Access-Control-Allow-Credentials'] = 'true'
response.headers['Access-Control-Max-Age'] = 86400
def index():
'''
The request should receive two parameters:
- sequences: a list of comma-separated DNA sequences to be searched in the CloneDB
- sample_set_id: the sample set we're coming from
'''
if not auth.user:
return response.json({'error': 'Access denied'})
if request.vars['sequences'] == None or request.vars['sequences'] == ''\
or request.vars['sample_set_id'] == None:
return response.json({'error': 'Malformed request'})
return search_clonedb(request.vars['sequences'].split(','), int(request.vars['sample_set_id']))
def search_clonedb(sequences, sample_set_id):
sys.path.insert(1, os.path.abspath(defs.DIR_CLONEDB))
import grep_clones
clonedb = imp.load_source('clonedb', defs.DIR_CLONEDB+os.path.sep+'clonedb.cgi')
results = []
for sequence in sequences:
options = clonedb.build_grep_clones_options({'sequence': sequence+' -sample_set:%d' % sample_set_id})
args = grep_clones.parser.parse_args(options)
log.debug(args)
occurrences = grep_clones.launch_search(args)
for occ in occurrences:
if 'tags' in occ and 'sample_set' in occ['tags']:
info = get_info_of_viewable_sample_set([int(sample_id) for sample_id in occ['tags']['sample_set']], int(occ['tags']['config_id'][0]))
occ['tags']['sample_set_viewable'] = info['viewable']
occ['tags']['sample_set_name'] = info['name']
config_db = db.config[occ['tags']['config_id'][0]]
occ['tags']['config_name'] = [config_db.name if config_db else None]
results.append(occurrences)
return response.json(results)
def get_info_of_viewable_sample_set(sample_sets, config):
info = {'viewable': [], 'name': []}
for sample_id in sample_sets:
viewable = auth.can_view_sample_set(sample_id, auth.user)
info['viewable'].append(viewable)
if viewable:
info['name'].append(get_sample_name(sample_id))
else:
info['name'].append(None)
return info
......@@ -89,3 +89,23 @@ class SampleSet(object):
def get_data(self, sample_set_id):
return db(db.generic.sample_set_id == sample_set_id).select()[0]
def get_sample_name(sample_set_id):
'''
Return the name associated with a sample set (eg. a run or a
patient) if any
'''
sample = db.sample_set[sample_set_id]
if sample is None or (sample.sample_type != defs.SET_TYPE_PATIENT \
and sample.sample_type != defs.SET_TYPE_RUN):
return None
sample_type = sample.sample_type
patient_or_run = db[sample_type](db[sample_type].sample_set_id == sample_set_id)
if patient_or_run is None:
return None
if sample.sample_type == defs.SET_TYPE_PATIENT:
return vidjil_utils.anon_ids(patient_or_run.id)
return patient_or_run.name
......@@ -42,6 +42,7 @@ DIR_VIDJIL = '../../'
DIR_FUSE = '../../tools'
DIR_MIXCR = '../../'
DIR_PEAR = '../../'
DIR_CLONEDB = '../../../clonedb'
### Directory that store the germlines used by Vidjil
DIR_GERMLINE = DIR_VIDJIL + 'germline/'
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment