Skip to content

Commit ae3dc0c

Browse files
authored
Merge pull request #2288 from antgonza/redbiom-per-sample
redbiom now adds per sample studies to analysis
2 parents 91f0051 + 38e5cd7 commit ae3dc0c

File tree

7 files changed

+116
-88
lines changed

7 files changed

+116
-88
lines changed

qiita_pet/handlers/qiita_redbiom.py

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# -----------------------------------------------------------------------------
88

99
from requests import ConnectionError
10+
from future.utils import viewitems
1011
from collections import defaultdict
1112
import redbiom.summarize
1213
import redbiom.search
@@ -42,35 +43,38 @@ def _redbiom_metadata_search(self, query, contexts):
4243
'Not a valid search: "%s", your query is too small '
4344
'(too few letters), try a longer query' % query)
4445
if not message:
45-
sids = set([s.split('.', 1)[0] for s in samples])
46-
for s in sids:
47-
study_artifacts[s] = [a.id for a in Study(s).artifacts(
48-
artifact_type='BIOM')]
46+
study_samples = defaultdict(list)
47+
for s in samples:
48+
study_samples[s.split('.', 1)[0]].append(s)
49+
for sid, samps in viewitems(study_samples):
50+
study_artifacts[sid] = {
51+
a.id: samps for a in Study(sid).artifacts(
52+
artifact_type='BIOM')}
4953

5054
return message, study_artifacts
5155

5256
def _redbiom_feature_search(self, query, contexts):
53-
study_artifacts = defaultdict(list)
57+
study_artifacts = defaultdict(lambda: defaultdict(list))
5458
query = [f for f in query.split(' ')]
5559
for ctx in contexts:
5660
for idx in redbiom.util.ids_from(query, True, 'feature', ctx):
57-
aid, sid = idx.split('_', 1)
58-
sid = sid.split('.', 1)[0]
59-
study_artifacts[sid].append(aid)
61+
aid, sample_id = idx.split('_', 1)
62+
sid = sample_id.split('.', 1)[0]
63+
study_artifacts[sid][aid].append(sample_id)
6064

6165
return '', study_artifacts
6266

6367
def _redbiom_taxon_search(self, query, contexts):
64-
study_artifacts = defaultdict(list)
68+
study_artifacts = defaultdict(lambda: defaultdict(list))
6569
for ctx in contexts:
6670
# find the features with those taxonomies and then search
6771
# those features in the samples
6872
features = redbiom.fetch.taxon_descendents(ctx, query)
6973
for idx in redbiom.util.ids_from(features, True, 'feature',
7074
ctx):
71-
aid, sid = idx.split('_', 1)
72-
sid = sid.split('.', 1)[0]
73-
study_artifacts[sid].append(aid)
75+
aid, sample_id = idx.split('_', 1)
76+
sid = sample_id.split('.', 1)[0]
77+
study_artifacts[sid][aid].append(sample_id)
7478

7579
return '', study_artifacts
7680

@@ -98,8 +102,8 @@ def _redbiom_search(self, query, search_on, callback):
98102
study_artifacts.keys(), True)
99103
# inserting the artifact_biom_ids to the results
100104
for i in range(len(results)):
101-
results[i]['artifact_biom_ids'] = list(set(
102-
study_artifacts[str(results[i]['study_id'])]))
105+
results[i]['artifact_biom_ids'] = study_artifacts[
106+
str(results[i]['study_id'])]
103107
else:
104108
message = "No samples where found! Try again ..."
105109
else:

qiita_pet/static/js/qiita.js

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -127,25 +127,35 @@ function delete_analysis(aname, analysis_id) {
127127
* Note that we have a list of artifact ids cause the user can select one single
128128
* artifact to add or all study artifacts
129129
*/
130-
function send_samples_to_analysis(button, aids) {
130+
function send_samples_to_analysis(button, aids, samples = null) {
131131
button.value = 'Adding';
132132
button.disabled = true;
133133
$(button).addClass("btn-info");
134134
bootstrapAlert('We are adding ' + aids.length + ' artifact(s) to the analysis. This ' +
135135
'might take some time based on the number of samples on each artifact.', "warning", 10000);
136-
$.get('/artifact/samples/', {ids:aids})
137-
.done(function ( data ) {
138-
if (data['status']=='success') {
139-
qiita_websocket.send('sel', data['data']);
140-
button.value = 'Added';
141-
$(button).removeClass("btn-info");
142-
} else {
143-
bootstrapAlert('ERROR: ' + data['msg'], "danger");
144-
button.value = 'There was an error, scroll up to see it';
145-
button.disabled = false;
146-
$(button).addClass("btn-danger");
147-
}
136+
if (samples === null) {
137+
$.get('/artifact/samples/', {ids:aids})
138+
.done(function ( data ) {
139+
if (data['status']=='success') {
140+
qiita_websocket.send('sel', data['data']);
141+
button.value = 'Added';
142+
$(button).removeClass("btn-info");
143+
} else {
144+
bootstrapAlert('ERROR: ' + data['msg'], "danger");
145+
button.value = 'There was an error, scroll up to see it';
146+
button.disabled = false;
147+
$(button).addClass("btn-danger");
148+
}
148149
});
150+
} else {
151+
$.each(aids, function(i, aid) {
152+
var to_send = {};
153+
to_send[aid] = samples.split(',');
154+
qiita_websocket.send('sel', to_send);
155+
});
156+
button.value = 'Added';
157+
$(button).removeClass("btn-info");
158+
}
149159
}
150160

151161
/**
@@ -366,25 +376,51 @@ function show_alert(data) {
366376
$('#dflt-sel-info').css('color', 'rgb(0, 160, 0)');
367377
}
368378

369-
function format_biom_rows(data, row) {
379+
function format_biom_rows(data, row, for_study_list = true, samples = null) {
370380
var proc_data_table = '<table class="table" cellpadding="0" cellspacing="0" border="0" style="padding-left:0px;width:95%">';
371381
proc_data_table += '<tr>';
382+
if (for_study_list) {
383+
proc_data_table += '<th></th>';
384+
}
372385
proc_data_table += '<th>Name</th>';
386+
if (for_study_list) {
387+
proc_data_table += '<th>Data type</th>';
388+
}
373389
proc_data_table += '<th>Processing method</th>';
374390
proc_data_table += '<th>Parameters</th>';
391+
if (for_study_list) {
392+
proc_data_table += '<th>Samples in Prep Info</th>';
393+
}
375394
proc_data_table += '<th>Files</th>';
376395
proc_data_table += '</tr>';
396+
377397
$.each(data, function (idx, info) {
378398
if (typeof info !== 'string' && !(info instanceof String)) {
379399
proc_data_table += '<tr>';
400+
if (for_study_list) {
401+
if (samples === null) {
402+
proc_data_table += '<td><input type="button" class="btn btn-sm" value="Add" onclick="send_samples_to_analysis(this, [' + info.artifact_id + '])"></td>';
403+
} else {
404+
proc_data_table += '<td><input type="button" class="btn btn-sm" value="Add" onclick="send_samples_to_analysis(this, [' + info.artifact_id + "], '" + samples[info.artifact_id].join(',') + "'" + ')"></td>';
405+
}
406+
}
380407
proc_data_table += '<td>' + info.name + ' (' + info.artifact_id + ' - ' + info.timestamp.split('.')[0] + ')</td>';
408+
409+
if (for_study_list) {
410+
proc_data_table += '<td>' + info.data_type + ' (' + info.target_subfragment.join(', ') + ')</td>';
411+
}
381412
proc_data_table += '<td>' + info.algorithm + '</td>';
413+
382414
var params = '';
383415
for (var key in info.parameters) {
384416
params += '<i>' + key + '</i>: ' + info.parameters[key] + '<br/>';
385417
}
386418
proc_data_table += '<td><small>' + params + '</small></td>';
419+
if (for_study_list) {
420+
proc_data_table += '<td>' + info.prep_samples + '</td>';
421+
}
387422
proc_data_table += '<td><small>' + info.files.join('<br/>') + '</small></td>';
423+
388424
proc_data_table += '</tr>';
389425
}
390426
});

qiita_pet/templates/artifact_ajax/artifact_summary.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ <h4>
108108
{% if editable %}
109109
<a class="btn btn-default btn-sm" data-toggle="modal" data-target="#update-artifact-name"><span class="glyphicon glyphicon-pencil"></span> Edit</a>
110110
{% if artifact_type == 'BIOM' %}
111-
<a class="btn btn-default btn-sm" onclick="send_samples_to_analysis(this, [{{artifact_id}}]);"> Add to analysis</a>
111+
<input type="button" class="btn btn-default btn-sm" value="Add to Analysis" onclick="send_samples_to_analysis(this, [{{artifact_id}}]);">
112112
{% else %}
113113
<a class="btn btn-default btn-sm" onclick="load_process_artifact_ui({{artifact_id}});"><span class="glyphicon glyphicon-play"></span> Process</a>
114114
{% end %}

qiita_pet/templates/list_analyses.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
$.get('/artifact/info/', {ids: JSON.parse(row.data()[0]), only_biom: 'False' })
4343
.done(function ( data ) {
4444
if (data['status']=='success') {
45-
$('td', row.child()).html(format_biom_rows(data.data, row.index())).show();
45+
$('td', row.child()).html(format_biom_rows(data.data, row.index(), for_study_list=false)).show();
4646
} else {
4747
bootstrapAlert('ERROR: ' + data['msg'], "danger", 10000);
4848
}

qiita_pet/templates/list_studies.html

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -54,41 +54,6 @@
5454
$("#search-waiting").hide();
5555
qiita_websocket.init(window.location.host + '{% raw qiita_config.portal_dir %}/study/list/socket/', error, error);
5656
qiita_websocket.add_callback('sel', show_alert);
57-
function format_biom_rows(data, row) {
58-
var proc_data_table = '<table class="table" cellpadding="0" cellspacing="0" border="0" style="padding-left:0px;width:95%">';
59-
proc_data_table += '<tr>';
60-
proc_data_table += '<th></th>';
61-
proc_data_table += '<th>Name</th>';
62-
proc_data_table += '<th>Data type</th>';
63-
proc_data_table += '<th>Processing method</th>';
64-
proc_data_table += '<th>Parameters</th>';
65-
proc_data_table += '<th>Samples in Prep Info</th>';
66-
proc_data_table += '<th>Files</th>';
67-
proc_data_table += '</tr>';
68-
69-
$.each(data, function (idx, info) {
70-
if (typeof info !== 'string' && !(info instanceof String)) {
71-
proc_data_table += '<tr>';
72-
proc_data_table += '<td><input type="button" class="btn btn-sm" value="Add" onclick="send_samples_to_analysis(this, [' + info.artifact_id + '])"></td>';
73-
proc_data_table += '<td>' + info.name + ' (' + info.artifact_id + ' - ' + info.timestamp.split('.')[0] + ')</td>';
74-
proc_data_table += '<td>' + info.data_type + ' (' + info.target_subfragment.join(', ') + ')</td>';
75-
proc_data_table += '<td>' + info.algorithm + '</td>';
76-
77-
var params = '';
78-
for (var key in info.parameters) {
79-
params += '<i>' + key + '</i>: ' + info.parameters[key] + '<br/>';
80-
}
81-
proc_data_table += '<td><small>' + params + '</small></td>';
82-
proc_data_table += '<td>' + info.prep_samples + '</td>';
83-
proc_data_table += '<td><small>' + info.files.join('<br/>') + '</small></td>';
84-
85-
proc_data_table += '</tr>';
86-
}
87-
});
88-
89-
proc_data_table += '</table>';
90-
return proc_data_table;
91-
}
9257

9358
$('#user-studies-table').dataTable({
9459
"lengthMenu": [[5, 10, 50, -1], [5, 10, 50, "All"]],

qiita_pet/templates/redbiom.html

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,42 +15,55 @@
1515
// adding total features found
1616

1717
var api = this.api(), data;
18-
var total_features = api
18+
var artifacts_samples = api
1919
.column(0)
2020
.data()
21-
.reduce( function (a, b) {return a + b.length}, 0);
22-
21+
.reduce( function (a, b) {
22+
var artifacts = 0, samples = 0;
23+
$.each( b, function( key, value ) {
24+
artifacts += 1;
25+
samples += value.length;
26+
});
27+
return [artifacts, samples]}, [0, 0]);
28+
var artifacts = artifacts_samples[0];
29+
var samples = artifacts_samples[1];
2330
$('.footer').addClass("col-md-12 text-right");
24-
if (total_features == 0) {
31+
if (artifacts == 0) {
2532
text = '';
2633
} else {
27-
text = 'Found ' + total_features + ' artifacts.';
34+
text = 'Found ' + artifacts + ' artifacts with ' + samples + ' samples.';
2835
}
2936
$('.footer').html(text)
3037
},
3138
"columns": [
32-
{ "orderable": false, "width": "15%", "data": "artifact_biom_ids"},
39+
{ "orderable": false, "width": "20%", "data": "artifact_biom_ids"},
3340
{ "data": "study_title", "width": "70%" },
3441
{ "data": "study_abstract", "width": "0%" },
35-
{ "data": "study_id", "width": "15%" },
42+
{ "data": "study_id", "width": "10%" },
3643
{ "data": "study_alias", "width": "0%" }],
3744
columnDefs: [
3845
// {type:'natural', targets:[2,6,7]},
3946
{"targets": [ 2, 4 ], "visible": false},
4047
// render zero
4148
{"render": function ( data, type, row, meta ) {
42-
if (data !== null && data !== undefined && data.length != 0){
43-
return '<div class="container" style="max-width: 5em;">'+
44-
'<div class="row justify-content-md-center">' +
45-
{% if current_user is not None %}
46-
'<div class="col-md-1 text-center details-control">&nbsp;</div>' +
47-
{% end %}
48-
'<div class="col-md-1 text-center">' + data.length + '</div>' +
49-
'</div>' +
50-
'</div>';
51-
} else {
52-
return 'No BIOMs';
49+
if (data !== null && data !== undefined){
50+
var artifacts = 0, samples = 0;
51+
for (var d in data) { if (data.hasOwnProperty(d)) { artifacts++; } }
52+
if (artifacts != 0) {
53+
return '<div class="container" style="max-width: 15em;">'+
54+
'<div class="row justify-content-md-center">' +
55+
{% if current_user is not None %}
56+
'<div class="col-md-12 text-center details-control">&nbsp;</div>' +
57+
{% end %}
58+
'<div class="col-md-12 text-center">' +
59+
'Artifacts: ' + artifacts + ' | ' +
60+
'Samples: ' + artifacts +
61+
'</div>' +
62+
'</div>' +
63+
'</div>';
64+
}
5365
}
66+
return 'No BIOMs';
5467
}, targets: [0]},
5568
// render the title cell
5669
{"render": function ( data, type, row, meta ) {
@@ -85,10 +98,14 @@
8598
// modified from: https://jsfiddle.net/8rejaL88/2/
8699
tr.addClass('shown');
87100
row.child('<p><center><img src="{% raw qiita_config.portal_dir %}/static/img/waiting.gif" style="display:block;margin-left: auto;margin-right: auto"/></center></p>', 'no-padding' ).show();
88-
$.get('/artifact/info/', {ids: row.data().artifact_biom_ids})
101+
102+
var artifact_biom_ids = row.data().artifact_biom_ids;
103+
var artifact_biom_ids_keys = []
104+
$.each(artifact_biom_ids, function(e){ artifact_biom_ids_keys.push(e) });
105+
$.get('/artifact/info/', {ids: artifact_biom_ids_keys })
89106
.done(function ( data ) {
90107
if (data['status']=='success') {
91-
$('td', row.child()).html(format_biom_rows(data.data, row.index())).show();
108+
$('td', row.child()).html(format_biom_rows(data.data, row.index(), true, samples=artifact_biom_ids)).show();
92109
} else {
93110
bootstrapAlert('ERROR: ' + data['msg'], "danger", 10000);
94111
}

qiita_pet/test/test_qiita_redbiom.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ def test_post_metadata(self):
2727
response = self.post('/redbiom/', post_args)
2828
self.assertEqual(response.code, 200)
2929
data = DATA
30-
data[0]['artifact_biom_ids'] = [4, 5, 6, 7]
30+
samples = ['1.SKD6.640190', '1.SKD9.640182', '1.SKD8.640184',
31+
'1.SKD5.640186', '1.SKD2.640178', '1.SKD4.640185',
32+
'1.SKD1.640179', '1.SKD3.640198', '1.SKD7.640191']
33+
data[0]['artifact_biom_ids'] = {
34+
'5': samples, '4': samples, '7': samples, '6': samples}
3135
exp = {'status': 'success', 'message': '', 'data': data}
3236
self.assertEqual(loads(response.body), exp)
3337

@@ -59,7 +63,8 @@ def test_post_features(self):
5963
}
6064
response = self.post('/redbiom/', post_args)
6165
data = DATA
62-
data[0]['artifact_biom_ids'] = ['5', '4']
66+
data[0]['artifact_biom_ids'] = {
67+
'5': ['1.SKM3.640197'], '4': ['1.SKM3.640197']}
6368
exp = {'status': 'success', 'message': '', 'data': data}
6469
self.assertEqual(response.code, 200)
6570
self.assertEqual(loads(response.body), exp)
@@ -80,7 +85,8 @@ def test_post_taxon(self):
8085
'search_on': 'taxon'
8186
}
8287
data = DATA
83-
data[0]['artifact_biom_ids'] = ['5', '4']
88+
data[0]['artifact_biom_ids'] = {
89+
'5': ['1.SKM3.640197'], '4': ['1.SKM3.640197']}
8490
response = self.post('/redbiom/', post_args)
8591
exp = {'status': 'success', 'message': '', 'data': data}
8692
self.assertEqual(response.code, 200)

0 commit comments

Comments
 (0)