Skip to content

Commit da851aa

Browse files
Merge pull request #3180 from antgonza/select-metadata-for-analysis-GUI
adding how to select metadata for analysis via GUI
2 parents a4d982e + bbdf92e commit da851aa

File tree

7 files changed

+140
-12
lines changed

7 files changed

+140
-12
lines changed

qiita_db/analysis.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def get_by_status(cls, status):
111111

112112
@classmethod
113113
def create(cls, owner, name, description, from_default=False,
114-
merge_duplicated_sample_ids=False):
114+
merge_duplicated_sample_ids=False, categories=None):
115115
"""Creates a new analysis on the database
116116
117117
Parameters
@@ -129,6 +129,8 @@ def create(cls, owner, name, description, from_default=False,
129129
If the duplicated sample ids in the selected studies should be
130130
merged or prepended with the artifact ids. False (default) prepends
131131
the artifact id
132+
categories : set of str, optional
133+
If not None, use _only_ these categories for the metaanalysis
132134
133135
Returns
134136
-------
@@ -178,7 +180,8 @@ def create(cls, owner, name, description, from_default=False,
178180
params = qdb.software.Parameters.load(
179181
cmd, values_dict={
180182
'analysis': a_id,
181-
'merge_dup_sample_ids': merge_duplicated_sample_ids})
183+
'merge_dup_sample_ids': merge_duplicated_sample_ids,
184+
'categories': categories})
182185
job = qdb.processing_job.ProcessingJob.create(
183186
owner, params, True)
184187
sql = """INSERT INTO qiita.analysis_processing_job

qiita_db/support_files/patches/85.sql

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-- Feb 22, 2022
2+
-- adding a new parameter `categories` to build_analysis_files
3+
4+
DO $do$
5+
DECLARE
6+
cmd_id bigint;
7+
BEGIN
8+
SELECT command_id INTO cmd_id FROM qiita.software_command WHERE name = 'build_analysis_files';
9+
10+
INSERT INTO qiita.command_parameter (command_id, parameter_name, parameter_type, required, default_value)
11+
VALUES (cmd_id, 'categories', 'mchoice', True, NULL);
12+
END $do$;

qiita_pet/handlers/analysis_handlers/base_handlers.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,13 @@ def post(self):
2626
name = self.get_argument('name')
2727
desc = self.get_argument('description')
2828
mdsi = self.get_argument('merge_duplicated_sample_ids', False)
29+
metadata = self.request.arguments.get('analysis-metadata', None)
30+
2931
if mdsi in (b'on', 'on'):
3032
mdsi = True
3133
analysis = Analysis.create(
3234
self.current_user, name, desc, merge_duplicated_sample_ids=mdsi,
33-
from_default=True)
35+
from_default=True, categories=metadata)
3436

3537
self.redirect(u"%s/analysis/description/%s/"
3638
% (qiita_config.portal_dir, analysis.id))

qiita_pet/handlers/analysis_handlers/listing_handlers.py

+13-3
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ def get(self):
106106
# Format sel_data to get study IDs for the processed data
107107
sel_data = defaultdict(dict)
108108
proc_data_info = {}
109-
sel_samps = self.current_user.default_analysis.samples
110-
for aid, samples in sel_samps.items():
109+
analysis = self.current_user.default_analysis
110+
for aid, samples in analysis.samples.items():
111111
artifact = Artifact(aid)
112112
sel_data[artifact.study][aid] = samples
113113
proc_data_info[aid] = {
@@ -116,5 +116,15 @@ def get(self):
116116
'data_type': artifact.data_type
117117
}
118118

119+
# finding common metadata fields
120+
metadata = analysis.metadata_categories
121+
common = []
122+
for i, (_, m) in enumerate(metadata.items()):
123+
if i == 0:
124+
common = {'sample': set(m['sample']), 'prep': set(m['prep'])}
125+
else:
126+
common['sample'] = common['sample'] & set(m['sample'])
127+
common['prep'] = common['prep'] & set(m['prep'])
128+
119129
self.render("analysis_selected.html", sel_data=sel_data,
120-
proc_info=proc_data_info)
130+
proc_info=proc_data_info, metadata=metadata, common=common)

qiita_pet/templates/analysis_selected.html

+100-1
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,84 @@
6262
qiita_websocket.add_callback('clear', clear_from_html);
6363
$('#clear-button').on('click', clear);
6464
{% if sel_data %}$('#no-selected').hide(){% end %}
65+
66+
var common_sample_fields = {% raw list(common['sample']) %};
67+
var common_prep_fields = {% raw list(common['prep']) %};
68+
69+
$.each($(".chosen-select"), function (_, element){
70+
var is_sample = element.id.startsWith('sample-metadata');
71+
$.each(element.options, function (_, option){
72+
if (is_sample) {
73+
if (jQuery.inArray(option.text, common_sample_fields) >= 0) {
74+
option.selected=true;
75+
$('#analysis-metadata').append(
76+
$('<option>', { value: option.value, text: option.text,
77+
selected: true}));
78+
}
79+
} else {
80+
if (jQuery.inArray(option.text, common_prep_fields) >= 0) {
81+
option.selected=true;
82+
$('#analysis-metadata').append(
83+
$('<option>', { value: option.value, text: option.text,
84+
selected: true}));
85+
}
86+
}
87+
});
88+
});
89+
90+
$('#analysis-metadata').chosen({
91+
width: "95%"
92+
});
93+
94+
$(".chosen-select").chosen({
95+
width: "95%",
96+
no_results_text: "Oops, nothing found!",
97+
display_disabled_options: false,
98+
display_selected_options: false,
99+
}).change(function(event, object) {
100+
var item = $(this).attr('id');
101+
var key = Object.keys(object)[0];
102+
var toggle = key == 'selected';
103+
var selection = object[key];
104+
105+
if (toggle) {
106+
$('#analysis-metadata').append(
107+
$('<option>', { value: selection, text: selection,
108+
selected: true}));
109+
} else {
110+
$(".analysis-metadata option[value='" + selection + "']").remove();
111+
}
112+
// we need to update the chosen element AKA this line is needed for
113+
// things to work fine
114+
$("#analysis-metadata").trigger("chosen:updated");
115+
116+
if (jQuery.inArray(object[key], common_sample_fields) >= 0) {
117+
$.each($(".chosen-select"), function (_, element){
118+
if (item != element.id) {
119+
$.each(element.options, function (_, option){
120+
if (option.text == selection) {
121+
option.selected=toggle;
122+
}
123+
});
124+
}
125+
});
126+
} else if (jQuery.inArray(object[key], common_sample_fields) >= 0) {
127+
$.each($(".chosen-select"), function (_, element){
128+
if (item != element.id) {
129+
$.each(element.options, function (_, option){
130+
if (option.text == selection) {
131+
option.selected=toggle;
132+
}
133+
});
134+
}
135+
});
136+
}
137+
});
65138
});
66139
</script>
67140
{% end %}
68141

142+
69143
{% block content %}
70144
<h1>Selected Samples</h1>
71145
<span id="ws-error" style="color:red"></span>
@@ -139,6 +213,27 @@ <h4 class="modal-title" id="myModalLabel">Processed Data {{pid}}</h4>
139213
{% end %}
140214
{% end %}
141215
</table>
216+
<hr>
217+
<h4>Metadata Selection <small>Common fields for all studies are preselected</small></h4>
218+
<table border="0" style="width: 100%;">
219+
<tr>
220+
<td style="width: 50%;">
221+
Sample Information
222+
<select data-placeholder="Choose Sample Metadata..." multiple class="chosen-select" id="sample-metadata-{{study.id}}">
223+
{% for field in sorted(metadata[study.id]['sample']) %}
224+
<option value="{{field}}">{{field}}</option>
225+
{% end %}
226+
</select>
227+
</td>
228+
<td style="width: 50%;">
229+
Preparation Information
230+
<select data-placeholder="Choose Preparation Metadata..." multiple class="chosen-select" id="prep-metadata-{{study.id}}">
231+
{% for field in sorted(metadata[study.id]['prep']) %}
232+
<option value="{{field}}">{{field}}</option>
233+
{% end %}
234+
</td>
235+
</tr>
236+
</table>
142237
</div>
143238
</div>
144239
{% end %}
@@ -167,10 +262,14 @@ <h4 class="modal-title" id="myModalLabel">Create new analysis</h4>
167262
<label for="description">Merge samples with the same name <br/><small>useful when merging multiple preparation artifacts</small></label>
168263
<input type="checkbox" class="form-control" id="merge_duplicated_sample_ids" name="merge_duplicated_sample_ids">
169264
</div>
265+
<div class="form-group">
266+
<label for="description">Metadata selected <small>(to update use the main page)</small></label>
267+
<select name="analysis-metadata" id="analysis-metadata" class="analysis-metadata" multiple disabled></select>
268+
</div>
170269
</div>
171270
</div>
172271
<div class="modal-footer">
173-
<button type="submit" class="btn btn-primary">Create analysis</button>
272+
<button type="submit" onclick="$('#analysis-metadata').removeAttr('disabled');" "class="btn btn-primary">Create analysis</button>
174273
</div>
175274
</form>
176275
</div>

qiita_ware/private_plugin.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@ def build_analysis_files(job):
3434
with qdb.sql_connection.TRN:
3535
params = job.parameters.values
3636
analysis_id = params['analysis']
37+
categories = params['categories']
3738
merge_duplicated_sample_ids = params['merge_dup_sample_ids']
3839
analysis = qdb.analysis.Analysis(analysis_id)
39-
biom_files = analysis.build_files(merge_duplicated_sample_ids)
40+
biom_files = analysis.build_files(
41+
merge_duplicated_sample_ids, categories=categories)
4042

4143
cmd = qdb.software.Command.get_validator('BIOM')
4244
val_jobs = []

qiita_ware/test/test_private_plugin.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ def test_submit_to_EBI(self):
379379

380380
def test_build_analysis_files(self):
381381
job = self._create_job('build_analysis_files', {
382-
'analysis': 3, 'merge_dup_sample_ids': True})
382+
'analysis': 3, 'merge_dup_sample_ids': True, 'categories': None})
383383

384384
# testing shape and get_resource_allocation_info as
385385
# build_analysis_files is a special case
@@ -407,7 +407,7 @@ def _set_allocation(memory):
407407

408408
# now let's test something that will cause not a number input_size*N
409409
job = self._create_job('build_analysis_files', {
410-
'analysis': 3, 'merge_dup_sample_ids': True})
410+
'analysis': 3, 'merge_dup_sample_ids': True, 'categories': None})
411411
_set_allocation('{input_size}*N')
412412
self.assertEqual(job.get_resource_allocation_info(), 'Not valid')
413413
self.assertEqual(job.status, 'error')
@@ -416,7 +416,7 @@ def _set_allocation(memory):
416416

417417
# now let's test something that will return a negative number -samples
418418
job = self._create_job('build_analysis_files', {
419-
'analysis': 3, 'merge_dup_sample_ids': True})
419+
'analysis': 3, 'merge_dup_sample_ids': True, 'categories': None})
420420
_set_allocation('-{samples}')
421421
self.assertEqual(job.get_resource_allocation_info(), 'Not valid')
422422
self.assertEqual(job.status, 'error')
@@ -425,7 +425,7 @@ def _set_allocation(memory):
425425

426426
# now let's test a full build_analysis_files job
427427
job = self._create_job('build_analysis_files', {
428-
'analysis': 3, 'merge_dup_sample_ids': True})
428+
'analysis': 3, 'merge_dup_sample_ids': True, 'categories': None})
429429
job._set_status('in_construction')
430430
job.submit()
431431

0 commit comments

Comments
 (0)