8
8
from __future__ import division
9
9
from json import dumps
10
10
from future .utils import viewitems
11
+ from collections import defaultdict
11
12
12
13
from tornado .web import authenticated , HTTPError
13
14
from tornado .gen import coroutine , Task
21
22
from qiita_db .exceptions import QiitaDBIncompatibleDatatypeError
22
23
from qiita_db .util import get_table_cols
23
24
from qiita_db .data import ProcessedData
25
+ from qiita_core .exceptions import IncompetentQiitaDeveloperError
24
26
25
27
from qiita_pet .handlers .base_handlers import BaseHandler
26
28
from qiita_pet .handlers .util import study_person_linkifier , pubmed_linkifier
@@ -42,74 +44,149 @@ def _get_shared_links_for_study(study):
42
44
return ", " .join (shared )
43
45
44
46
45
- def _build_study_info (user , results = None ):
46
- """builds list of dicts for studies table, with all html formatted"""
47
+ def _build_single_study_info (study , info , study_proc , proc_samples ):
48
+ """Clean up and add to the study info for HTML purposes
49
+
50
+ Parameters
51
+ ----------
52
+ study : Study object
53
+ The study to build information for
54
+ info : dict
55
+ Information from Study.get_info
56
+ study_proc : dict of dict of lists
57
+ Dictionary keyed on study_id that lists all processed data associated
58
+ with that study. This list of processed data ids is keyed by data type
59
+ proc_samples : dict of lists
60
+ Dictionary keyed on proc_data_id that lists all samples associated with
61
+ that processed data.
62
+
63
+ Returns
64
+ -------
65
+ dict
66
+ info-information + extra information for the study,
67
+ slightly HTML formatted
68
+ """
69
+ PI = StudyPerson (info ['principal_investigator_id' ])
70
+ status = study .status
71
+ if info ['pmid' ] is not None :
72
+ info ['pmid' ] = ", " .join ([pubmed_linkifier ([p ])
73
+ for p in info ['pmid' ]])
74
+ else :
75
+ info ['pmid' ] = ""
76
+ if info ["number_samples_collected" ] is None :
77
+ info ["number_samples_collected" ] = 0
78
+ info ["shared" ] = _get_shared_links_for_study (study )
79
+ info ["num_raw_data" ] = len (study .raw_data ())
80
+ info ["status" ] = status
81
+ info ["study_id" ] = study .id
82
+ info ["pi" ] = study_person_linkifier ((PI .email , PI .name ))
83
+ del info ["principal_investigator_id" ]
84
+ del info ["email" ]
85
+ # Build the proc data info list for the child row in datatable
86
+ info ["proc_data_info" ] = []
87
+ for data_type , proc_datas in viewitems (study_proc [study .id ]):
88
+ info ["proc_data_info" ].extend ([
89
+ _build_single_proc_data_info (pd_id , data_type , proc_samples [pd_id ])
90
+ for pd_id in proc_datas ])
91
+ return info
92
+
93
+
94
+ def _build_single_proc_data_info (proc_data_id , data_type , samples ):
95
+ """Build the proc data info list for the child row in datatable
96
+
97
+ Parameters
98
+ ----------
99
+ proc_data_id : int
100
+ The processed data attached to he study, in the form
101
+ {study_id: [proc_data_id, proc_data_id, ...], ...}
102
+ data_type : str
103
+ Data type of the processed data
104
+ proc_samples : dict of lists
105
+ The samples available in the processed data, in the form
106
+ {proc_data_id: [samp1, samp2, ...], ...}
107
+
108
+ Returns
109
+ -------
110
+ dict
111
+ The information for the processed data, in the form {info: value, ...}
112
+ """
113
+ proc_data = ProcessedData (proc_data_id )
114
+ proc_info = proc_data .processing_info
115
+ proc_info ['pid' ] = proc_data_id
116
+ proc_info ['data_type' ] = data_type
117
+ proc_info ['samples' ] = sorted (samples )
118
+ proc_info ['processed_date' ] = str (proc_info ['processed_date' ])
119
+ return proc_info
120
+
121
+
122
+ def _build_study_info (user , study_proc = None , proc_samples = None ):
123
+ """Builds list of dicts for studies table, with all HTML formatted
124
+
125
+ Parameters
126
+ ----------
127
+ user : User object
128
+ logged in user
129
+ study_proc : dict of lists, optional
130
+ Dictionary keyed on study_id that lists all processed data associated
131
+ with that study. Required if proc_samples given.
132
+ proc_samples : dict of lists, optional
133
+ Dictionary keyed on proc_data_id that lists all samples associated with
134
+ that processed data. Required if study_proc given.
135
+
136
+ Returns
137
+ -------
138
+ infolist: list of dict of lists and dicts
139
+ study and processed data info for JSON serialiation for datatables
140
+ Each dict in the list is a single study, and contains the text
141
+
142
+ Notes
143
+ -----
144
+ Both study_proc and proc_samples must be passed, or neither passed.
145
+ """
146
+ build_samples = False
147
+ # Logic check to make sure both needed parts passed
148
+ if study_proc is not None and proc_samples is None :
149
+ raise IncompetentQiitaDeveloperError (
150
+ 'Must pass proc_samples when study_proc given' )
151
+ elif proc_samples is not None and study_proc is None :
152
+ raise IncompetentQiitaDeveloperError (
153
+ 'Must pass study_proc when proc_samples given' )
154
+ elif study_proc is None :
155
+ build_samples = True
156
+
47
157
# get list of studies for table
48
- study_list = user .user_studies .union (
158
+ study_set = user .user_studies .union (
49
159
Study .get_by_status ('public' )).union (user .shared_studies )
50
- if results is not None :
51
- study_list = study_list .intersection (results )
52
- if not study_list :
160
+ if study_proc is not None :
161
+ study_set = study_set .intersection (study_proc )
162
+ if not study_set :
53
163
# No studies left so no need to continue
54
164
return []
55
165
56
166
# get info for the studies
57
167
cols = ['study_id' , 'email' , 'principal_investigator_id' ,
58
168
'pmid' , 'study_title' , 'metadata_complete' ,
59
169
'number_samples_collected' , 'study_abstract' ]
60
- study_info = Study .get_info (study_list , cols )
170
+ study_info = Study .get_info (study_set , cols )
61
171
62
172
infolist = []
63
- for row , info in enumerate (study_info ):
173
+ for info in study_info :
174
+ # Convert DictCursor to proper dict
175
+ info = dict (info )
64
176
study = Study (info ['study_id' ])
65
- status = study .status
66
- # Just passing the email address as the name here, since
67
- # name is not a required field in qiita.qiita_user
68
- PI = StudyPerson (info ['principal_investigator_id' ])
69
- PI = study_person_linkifier ((PI .email , PI .name ))
70
- if info ['pmid' ] is not None :
71
- pmids = ", " .join ([pubmed_linkifier ([p ])
72
- for p in info ['pmid' ]])
73
- else :
74
- pmids = ""
75
- if info ["number_samples_collected" ] is None :
76
- info ["number_samples_collected" ] = "0"
77
- shared = _get_shared_links_for_study (study )
78
- meta_complete_glyph = "ok" if info ["metadata_complete" ] else "remove"
79
- # build the HTML elements needed for table cell
80
- title = ("<a href='#' data-toggle='modal' "
81
- "data-target='#study-abstract-modal' "
82
- "onclick='fillAbstract(\" studies-table\" , {0})'>"
83
- "<span class='glyphicon glyphicon-file' "
84
- "aria-hidden='true'></span></a> | "
85
- "<a href='/study/description/{1}' "
86
- "id='study{0}-title'>{2}</a>" ).format (
87
- str (row ), str (study .id ), info ["study_title" ])
88
- meta_complete = "<span class='glyphicon glyphicon-%s'></span>" % \
89
- meta_complete_glyph
90
- if status == 'public' :
91
- shared = "Not Available"
92
- else :
93
- shared = ("<span id='shared_html_{0}'>{1}</span><br/>"
94
- "<a class='btn btn-primary btn-xs' data-toggle='modal' "
95
- "data-target='#share-study-modal-view' "
96
- "onclick='modify_sharing({0});'>Modify</a>" .format (
97
- study .id , shared ))
98
-
99
- infolist .append ({
100
- "checkbox" : "<input type='checkbox' value='%d' />" % study .id ,
101
- "id" : study .id ,
102
- "title" : title ,
103
- "meta_complete" : meta_complete ,
104
- "num_samples" : info ["number_samples_collected" ],
105
- "shared" : shared ,
106
- "num_raw_data" : len (study .raw_data ()),
107
- "pi" : PI ,
108
- "pmid" : pmids ,
109
- "status" : status ,
110
- "abstract" : info ["study_abstract" ]
111
-
112
- })
177
+ # Build the processed data info for the study if none passed
178
+ if build_samples :
179
+ proc_data_list = study .processed_data ()
180
+ proc_samples = {}
181
+ study_proc = {study .id : defaultdict (list )}
182
+ for pid in proc_data_list :
183
+ proc_data = ProcessedData (pid )
184
+ study_proc [study .id ][proc_data .data_type ()].append (pid )
185
+ proc_samples [pid ] = proc_data .samples
186
+
187
+ study_info = _build_single_study_info (study , info , study_proc ,
188
+ proc_samples )
189
+ infolist .append (study_info )
113
190
return infolist
114
191
115
192
@@ -195,12 +272,12 @@ def get(self, ignore):
195
272
196
273
if user != self .current_user .id :
197
274
raise HTTPError (403 , 'Unauthorized search!' )
198
- res = None
199
275
if query :
200
276
# Search for samples matching the query
201
277
search = QiitaStudySearch ()
202
278
try :
203
- res , meta = search (query , self .current_user )
279
+ search (query , self .current_user )
280
+ study_proc , proc_samples , _ = search .filter_by_processed_data ()
204
281
except ParseException :
205
282
self .clear ()
206
283
self .set_status (400 )
@@ -223,9 +300,10 @@ def get(self, ignore):
223
300
info = {'User' : self .current_user .id ,
224
301
'query' : query })
225
302
return
226
- if not res :
227
- res = {}
228
- info = _build_study_info (self .current_user , results = res )
303
+ else :
304
+ study_proc = proc_samples = None
305
+ info = _build_study_info (self .current_user , study_proc = study_proc ,
306
+ proc_samples = proc_samples )
229
307
# build the table json
230
308
results = {
231
309
"sEcho" : echo ,
0 commit comments