|
a |
|
b/qiita_db/study.py |
|
|
1 |
r""" |
|
|
2 |
Study and StudyPerson objects (:mod:`qiita_db.study`) |
|
|
3 |
===================================================== |
|
|
4 |
|
|
|
5 |
.. currentmodule:: qiita_db.study |
|
|
6 |
|
|
|
7 |
This module provides the implementation of the Study and StudyPerson classes. |
|
|
8 |
The study class allows access to all basic information including name and |
|
|
9 |
pmids associated with the study, as well as returning ids for the data, |
|
|
10 |
sample template, owner, and shared users. It is the central hub for creating, |
|
|
11 |
deleting, and accessing a study in the database. |
|
|
12 |
|
|
|
13 |
Contacts are taken care of by the StudyPerson class. This holds the contact's |
|
|
14 |
name, email, address, and phone of the various persons in a study, e.g. The PI |
|
|
15 |
or lab contact. |
|
|
16 |
|
|
|
17 |
Classes |
|
|
18 |
------- |
|
|
19 |
|
|
|
20 |
.. autosummary:: |
|
|
21 |
:toctree: generated/ |
|
|
22 |
|
|
|
23 |
Study |
|
|
24 |
StudyPerson |
|
|
25 |
""" |
|
|
26 |
|
|
|
27 |
# ----------------------------------------------------------------------------- |
|
|
28 |
# Copyright (c) 2014--, The Qiita Development Team. |
|
|
29 |
# |
|
|
30 |
# Distributed under the terms of the BSD 3-clause License. |
|
|
31 |
# |
|
|
32 |
# The full license is in the file LICENSE, distributed with this software. |
|
|
33 |
# ----------------------------------------------------------------------------- |
|
|
34 |
from collections import defaultdict |
|
|
35 |
from copy import deepcopy |
|
|
36 |
from itertools import chain |
|
|
37 |
import warnings |
|
|
38 |
|
|
|
39 |
from qiita_core.exceptions import IncompetentQiitaDeveloperError |
|
|
40 |
from qiita_core.qiita_settings import qiita_config |
|
|
41 |
import qiita_db as qdb |
|
|
42 |
|
|
|
43 |
|
|
|
44 |
class Study(qdb.base.QiitaObject): |
|
|
45 |
r"""Study object to access to the Qiita Study information |
|
|
46 |
|
|
|
47 |
Attributes |
|
|
48 |
---------- |
|
|
49 |
data_types |
|
|
50 |
info |
|
|
51 |
investigation |
|
|
52 |
name |
|
|
53 |
pmids |
|
|
54 |
shared_with |
|
|
55 |
sample_template |
|
|
56 |
status |
|
|
57 |
title |
|
|
58 |
owner |
|
|
59 |
autoloaded |
|
|
60 |
|
|
|
61 |
Methods |
|
|
62 |
------- |
|
|
63 |
raw_data |
|
|
64 |
preprocessed_data |
|
|
65 |
processed_data |
|
|
66 |
add_pmid |
|
|
67 |
exists |
|
|
68 |
has_access |
|
|
69 |
share |
|
|
70 |
unshare |
|
|
71 |
|
|
|
72 |
Notes |
|
|
73 |
----- |
|
|
74 |
All setters raise QiitaDBStatusError if trying to change a public study. |
|
|
75 |
You should not be doing that. |
|
|
76 |
""" |
|
|
77 |
_table = "study" |
|
|
78 |
_portal_table = "study_portal" |
|
|
79 |
# The following columns are considered not part of the study info |
|
|
80 |
_non_info = frozenset(["email", "study_title", "ebi_study_accession", |
|
|
81 |
"autoloaded"]) |
|
|
82 |
|
|
|
83 |
def _lock_non_sandbox(self): |
|
|
84 |
"""Raises QiitaDBStatusError if study is non-sandboxed""" |
|
|
85 |
if self.status != 'sandbox': |
|
|
86 |
raise qdb.exceptions.QiitaDBStatusError( |
|
|
87 |
"Illegal operation on non-sandbox study!") |
|
|
88 |
|
|
|
89 |
@classmethod |
|
|
90 |
def from_title(cls, title): |
|
|
91 |
"""Instantiate Study from title |
|
|
92 |
|
|
|
93 |
Parameters |
|
|
94 |
---------- |
|
|
95 |
title : str |
|
|
96 |
Tht title to search for |
|
|
97 |
|
|
|
98 |
Returns |
|
|
99 |
------- |
|
|
100 |
Study |
|
|
101 |
The study with the given title |
|
|
102 |
|
|
|
103 |
Raises |
|
|
104 |
------ |
|
|
105 |
QiitaDBUnknownIDError |
|
|
106 |
If the title doesn't exist |
|
|
107 |
""" |
|
|
108 |
with qdb.sql_connection.TRN: |
|
|
109 |
sql = """SELECT study_id |
|
|
110 |
FROM qiita.{} |
|
|
111 |
WHERE study_title = %s""".format(cls._table) |
|
|
112 |
|
|
|
113 |
qdb.sql_connection.TRN.add(sql, [title]) |
|
|
114 |
sid = qdb.sql_connection.TRN.execute_fetchflatten() |
|
|
115 |
|
|
|
116 |
if not sid: |
|
|
117 |
raise qdb.exceptions.QiitaDBUnknownIDError( |
|
|
118 |
cls._table, f'"{title}" does not exist') |
|
|
119 |
|
|
|
120 |
return qdb.study.Study(sid[0]) |
|
|
121 |
|
|
|
122 |
@classmethod |
|
|
123 |
def iter(cls): |
|
|
124 |
"""Iterate over all studies in the database |
|
|
125 |
|
|
|
126 |
Returns |
|
|
127 |
------- |
|
|
128 |
generator |
|
|
129 |
Yields a `Study` object for each study in the database, |
|
|
130 |
in order of ascending study_id |
|
|
131 |
""" |
|
|
132 |
with qdb.sql_connection.TRN: |
|
|
133 |
sql = """SELECT study_id FROM qiita.{} |
|
|
134 |
ORDER BY study_id""".format(cls._table) |
|
|
135 |
qdb.sql_connection.TRN.add(sql) |
|
|
136 |
|
|
|
137 |
ids = qdb.sql_connection.TRN.execute_fetchflatten() |
|
|
138 |
|
|
|
139 |
for id_ in ids: |
|
|
140 |
yield Study(id_) |
|
|
141 |
|
|
|
142 |
@property |
|
|
143 |
def status(self): |
|
|
144 |
r"""The status is inferred by the status of its artifacts""" |
|
|
145 |
with qdb.sql_connection.TRN: |
|
|
146 |
# Get the status of all its artifacts |
|
|
147 |
sql = """SELECT DISTINCT visibility |
|
|
148 |
FROM qiita.visibility |
|
|
149 |
JOIN qiita.artifact USING (visibility_id) |
|
|
150 |
JOIN qiita.study_artifact USING (artifact_id) |
|
|
151 |
WHERE study_id = %s and visibility_id NOT IN %s""" |
|
|
152 |
qdb.sql_connection.TRN.add( |
|
|
153 |
sql, [self._id, qdb.util.artifact_visibilities_to_skip()]) |
|
|
154 |
return qdb.util.infer_status( |
|
|
155 |
qdb.sql_connection.TRN.execute_fetchindex()) |
|
|
156 |
|
|
|
157 |
@staticmethod |
|
|
158 |
def all_data_types(): |
|
|
159 |
"""Returns list of all the data types available in the system |
|
|
160 |
|
|
|
161 |
Returns |
|
|
162 |
------- |
|
|
163 |
list of str |
|
|
164 |
All the data types available in the system |
|
|
165 |
""" |
|
|
166 |
with qdb.sql_connection.TRN: |
|
|
167 |
sql = "SELECT DISTINCT data_type FROM qiita.data_type" |
|
|
168 |
qdb.sql_connection.TRN.add(sql) |
|
|
169 |
return qdb.sql_connection.TRN.execute_fetchflatten() |
|
|
170 |
|
|
|
171 |
@classmethod |
|
|
172 |
def get_ids_by_status(cls, status): |
|
|
173 |
"""Returns study id for all Studies with given status |
|
|
174 |
|
|
|
175 |
Parameters |
|
|
176 |
---------- |
|
|
177 |
status : str |
|
|
178 |
Status setting to search for |
|
|
179 |
|
|
|
180 |
Returns |
|
|
181 |
------- |
|
|
182 |
set of qiita_db.study.Study |
|
|
183 |
All studies in the database that match the given status |
|
|
184 |
""" |
|
|
185 |
with qdb.sql_connection.TRN: |
|
|
186 |
sql = """SELECT DISTINCT study_id |
|
|
187 |
FROM qiita.study_artifact |
|
|
188 |
JOIN qiita.artifact USING (artifact_id) |
|
|
189 |
JOIN qiita.visibility USING (visibility_id) |
|
|
190 |
JOIN qiita.study_portal USING (study_id) |
|
|
191 |
JOIN qiita.portal_type USING (portal_type_id) |
|
|
192 |
WHERE visibility = %s AND portal = %s""" |
|
|
193 |
qdb.sql_connection.TRN.add(sql, [status, qiita_config.portal]) |
|
|
194 |
sids = set(qdb.sql_connection.TRN.execute_fetchflatten()) |
|
|
195 |
# If status is sandbox, all the studies that are not present in the |
|
|
196 |
# study_artifact table are also sandbox |
|
|
197 |
if status == 'sandbox': |
|
|
198 |
sql = """SELECT study_id |
|
|
199 |
FROM qiita.study |
|
|
200 |
JOIN qiita.study_portal USING (study_id) |
|
|
201 |
JOIN qiita.portal_type USING (portal_type_id) |
|
|
202 |
WHERE portal = %s AND study_id NOT IN ( |
|
|
203 |
SELECT study_id |
|
|
204 |
FROM qiita.study_artifact)""" |
|
|
205 |
qdb.sql_connection.TRN.add(sql, [qiita_config.portal]) |
|
|
206 |
sids = sids.union( |
|
|
207 |
qdb.sql_connection.TRN.execute_fetchflatten()) |
|
|
208 |
|
|
|
209 |
return sids |
|
|
210 |
|
|
|
211 |
@classmethod |
|
|
212 |
def get_by_status(cls, status): |
|
|
213 |
"""Returns study id for all Studies with given status |
|
|
214 |
|
|
|
215 |
Parameters |
|
|
216 |
---------- |
|
|
217 |
status : str |
|
|
218 |
Status setting to search for |
|
|
219 |
|
|
|
220 |
Returns |
|
|
221 |
------- |
|
|
222 |
set of qiita_db.study.Study |
|
|
223 |
All studies in the database that match the given status |
|
|
224 |
""" |
|
|
225 |
return set(cls(sid) for sid in cls.get_ids_by_status(status)) |
|
|
226 |
|
|
|
227 |
@classmethod |
|
|
228 |
def get_info(cls, study_ids=None, info_cols=None): |
|
|
229 |
"""Returns study data for a set of study_ids |
|
|
230 |
|
|
|
231 |
Parameters |
|
|
232 |
---------- |
|
|
233 |
study_ids : list of ints, optional |
|
|
234 |
Studies to get information for. Defauls to all studies |
|
|
235 |
info_cols: list of str, optional |
|
|
236 |
Information columns to retrieve. Defaults to all study data |
|
|
237 |
|
|
|
238 |
Returns |
|
|
239 |
------- |
|
|
240 |
list of DictCursor |
|
|
241 |
Table-like structure of metadata, one study per row. Can be |
|
|
242 |
accessed as a list of dictionaries, keyed on column name. |
|
|
243 |
""" |
|
|
244 |
# The following tables are considered part of info |
|
|
245 |
_info_cols = frozenset(chain( |
|
|
246 |
qdb.util.get_table_cols('study'), |
|
|
247 |
qdb.util.get_table_cols('study_status'), |
|
|
248 |
qdb.util.get_table_cols('timeseries_type'), |
|
|
249 |
# placeholder for table study_publication |
|
|
250 |
['publications'])) |
|
|
251 |
|
|
|
252 |
if info_cols is None: |
|
|
253 |
info_cols = _info_cols |
|
|
254 |
elif not _info_cols.issuperset(info_cols): |
|
|
255 |
warnings.warn("Non-info columns passed: %s" % ", ".join( |
|
|
256 |
set(info_cols) - _info_cols)) |
|
|
257 |
|
|
|
258 |
search_cols = ",".join(sorted(_info_cols.intersection(info_cols))) |
|
|
259 |
|
|
|
260 |
with qdb.sql_connection.TRN: |
|
|
261 |
sql = """SELECT {0} |
|
|
262 |
FROM qiita.study |
|
|
263 |
LEFT JOIN ( |
|
|
264 |
SELECT study_id, |
|
|
265 |
array_agg(row_to_json((publication, is_doi), true)) |
|
|
266 |
AS publications |
|
|
267 |
FROM qiita.study_publication |
|
|
268 |
GROUP BY study_id) |
|
|
269 |
AS full_publications |
|
|
270 |
USING (study_id) |
|
|
271 |
JOIN qiita.timeseries_type USING (timeseries_type_id) |
|
|
272 |
JOIN qiita.study_portal USING (study_id) |
|
|
273 |
JOIN qiita.portal_type USING (portal_type_id) |
|
|
274 |
WHERE portal = %s""".format(search_cols) |
|
|
275 |
args = [qiita_config.portal] |
|
|
276 |
if study_ids is not None: |
|
|
277 |
sql = "{0} AND study_id IN %s".format(sql) |
|
|
278 |
args.append(tuple(study_ids)) |
|
|
279 |
|
|
|
280 |
qdb.sql_connection.TRN.add(sql, args) |
|
|
281 |
rows = qdb.sql_connection.TRN.execute_fetchindex() |
|
|
282 |
if study_ids is not None and len(rows) != len(study_ids): |
|
|
283 |
raise qdb.exceptions.QiitaDBError( |
|
|
284 |
'Non-portal-accessible studies asked for!') |
|
|
285 |
|
|
|
286 |
res = [] |
|
|
287 |
for r in rows: |
|
|
288 |
r = dict(r) |
|
|
289 |
if 'ebi_study_accession' in info_cols: |
|
|
290 |
r['ebi_submission_status'] = cls( |
|
|
291 |
r['study_id']).ebi_submission_status |
|
|
292 |
res.append(r) |
|
|
293 |
|
|
|
294 |
return res |
|
|
295 |
|
|
|
296 |
@classmethod |
|
|
297 |
def exists(cls, study_title): |
|
|
298 |
"""Check if a study exists based on study_title, which is unique |
|
|
299 |
|
|
|
300 |
Parameters |
|
|
301 |
---------- |
|
|
302 |
study_title : str |
|
|
303 |
The title of the study to search for in the database |
|
|
304 |
|
|
|
305 |
Returns |
|
|
306 |
------- |
|
|
307 |
bool |
|
|
308 |
""" |
|
|
309 |
with qdb.sql_connection.TRN: |
|
|
310 |
sql = """SELECT EXISTS( |
|
|
311 |
SELECT study_id |
|
|
312 |
FROM qiita.{} |
|
|
313 |
WHERE study_title = %s)""".format(cls._table) |
|
|
314 |
qdb.sql_connection.TRN.add(sql, [study_title]) |
|
|
315 |
return qdb.sql_connection.TRN.execute_fetchlast() |
|
|
316 |
|
|
|
317 |
@classmethod |
|
|
318 |
def create(cls, owner, title, info, investigation=None): |
|
|
319 |
"""Creates a new study on the database |
|
|
320 |
|
|
|
321 |
Parameters |
|
|
322 |
---------- |
|
|
323 |
owner : User object |
|
|
324 |
the study's owner |
|
|
325 |
title : str |
|
|
326 |
Title of the study |
|
|
327 |
info : dict |
|
|
328 |
the information attached to the study. All "*_id" keys must pass |
|
|
329 |
the objects associated with them. |
|
|
330 |
investigation : Investigation object, optional |
|
|
331 |
If passed, the investigation to associate with. Defaults to None. |
|
|
332 |
|
|
|
333 |
Raises |
|
|
334 |
------ |
|
|
335 |
QiitaDBColumnError |
|
|
336 |
Non-db columns in info dictionary |
|
|
337 |
All required keys not passed |
|
|
338 |
IncompetentQiitaDeveloperError |
|
|
339 |
email, study_id, study_status_id, or study_title passed as a key |
|
|
340 |
QiitaDBDuplicateError |
|
|
341 |
If a study with the given title already exists |
|
|
342 |
|
|
|
343 |
Notes |
|
|
344 |
----- |
|
|
345 |
All keys in info, must be equal to columns in qiita.study table in the |
|
|
346 |
database. |
|
|
347 |
""" |
|
|
348 |
# make sure not passing non-info columns in the info dict |
|
|
349 |
if cls._non_info.intersection(info): |
|
|
350 |
raise qdb.exceptions.QiitaDBColumnError( |
|
|
351 |
"non info keys passed: %s" % cls._non_info.intersection(info)) |
|
|
352 |
|
|
|
353 |
# cleaning up title, this is also done in JS for the GUI but rather |
|
|
354 |
# be safe than sorry |
|
|
355 |
title = ' '.join(title.split()).strip() |
|
|
356 |
|
|
|
357 |
with qdb.sql_connection.TRN: |
|
|
358 |
if cls.exists(title): |
|
|
359 |
raise qdb.exceptions.QiitaDBDuplicateError( |
|
|
360 |
"Study", "title: %s" % title) |
|
|
361 |
|
|
|
362 |
# add default values to info |
|
|
363 |
insertdict = deepcopy(info) |
|
|
364 |
insertdict['email'] = owner.id |
|
|
365 |
insertdict['study_title'] = title |
|
|
366 |
if "reprocess" not in insertdict: |
|
|
367 |
insertdict['reprocess'] = False |
|
|
368 |
|
|
|
369 |
# No nuns allowed |
|
|
370 |
insertdict = {k: v for k, v in insertdict.items() |
|
|
371 |
if v is not None} |
|
|
372 |
|
|
|
373 |
# make sure dictionary only has keys for available columns in db |
|
|
374 |
qdb.util.check_table_cols(insertdict, cls._table) |
|
|
375 |
# make sure reqired columns in dictionary |
|
|
376 |
qdb.util.check_required_columns(insertdict, cls._table) |
|
|
377 |
|
|
|
378 |
# Insert study into database |
|
|
379 |
sql = """INSERT INTO qiita.{0} ({1}) |
|
|
380 |
VALUES ({2}) RETURNING study_id""".format( |
|
|
381 |
cls._table, ','.join(insertdict), |
|
|
382 |
','.join(['%s'] * len(insertdict))) |
|
|
383 |
|
|
|
384 |
# make sure data in same order as sql column names, |
|
|
385 |
# and ids are used |
|
|
386 |
data = [] |
|
|
387 |
for col in insertdict: |
|
|
388 |
if isinstance(insertdict[col], qdb.base.QiitaObject): |
|
|
389 |
data.append(insertdict[col].id) |
|
|
390 |
else: |
|
|
391 |
data.append(insertdict[col]) |
|
|
392 |
|
|
|
393 |
qdb.sql_connection.TRN.add(sql, data) |
|
|
394 |
study_id = qdb.sql_connection.TRN.execute_fetchlast() |
|
|
395 |
|
|
|
396 |
# Add to both QIITA and given portal (if not QIITA) |
|
|
397 |
portal_id = qdb.util.convert_to_id( |
|
|
398 |
qiita_config.portal, 'portal_type', 'portal') |
|
|
399 |
sql = """INSERT INTO qiita.study_portal (study_id, portal_type_id) |
|
|
400 |
VALUES (%s, %s)""" |
|
|
401 |
args = [[study_id, portal_id]] |
|
|
402 |
if qiita_config.portal != 'QIITA': |
|
|
403 |
qp_id = qdb.util.convert_to_id( |
|
|
404 |
'QIITA', 'portal_type', 'portal') |
|
|
405 |
args.append([study_id, qp_id]) |
|
|
406 |
qdb.sql_connection.TRN.add(sql, args, many=True) |
|
|
407 |
qdb.sql_connection.TRN.execute() |
|
|
408 |
|
|
|
409 |
# add study to investigation if necessary |
|
|
410 |
if investigation: |
|
|
411 |
sql = """INSERT INTO qiita.investigation_study |
|
|
412 |
(investigation_id, study_id) |
|
|
413 |
VALUES (%s, %s)""" |
|
|
414 |
qdb.sql_connection.TRN.add(sql, [investigation.id, study_id]) |
|
|
415 |
|
|
|
416 |
qdb.sql_connection.TRN.execute() |
|
|
417 |
|
|
|
418 |
return cls(study_id) |
|
|
419 |
|
|
|
420 |
@classmethod |
|
|
421 |
def delete(cls, id_): |
|
|
422 |
r"""Deletes the study from the database |
|
|
423 |
|
|
|
424 |
Parameters |
|
|
425 |
---------- |
|
|
426 |
id_ : integer |
|
|
427 |
The object identifier |
|
|
428 |
|
|
|
429 |
Raises |
|
|
430 |
------ |
|
|
431 |
QiitaDBError |
|
|
432 |
If the sample_(id_) table exists means a sample template exists |
|
|
433 |
""" |
|
|
434 |
with qdb.sql_connection.TRN: |
|
|
435 |
# checking that the id_ exists |
|
|
436 |
cls(id_) |
|
|
437 |
|
|
|
438 |
if qdb.util.exists_table('sample_%d' % id_): |
|
|
439 |
raise qdb.exceptions.QiitaDBError( |
|
|
440 |
'Study "%s" cannot be erased because it has a ' |
|
|
441 |
'sample template' % cls(id_).title) |
|
|
442 |
|
|
|
443 |
args = [id_] |
|
|
444 |
|
|
|
445 |
sql = "DELETE FROM qiita.study_portal WHERE study_id = %s" |
|
|
446 |
qdb.sql_connection.TRN.add(sql, args) |
|
|
447 |
|
|
|
448 |
sql = "DELETE FROM qiita.study_publication WHERE study_id = %s" |
|
|
449 |
qdb.sql_connection.TRN.add(sql, args) |
|
|
450 |
|
|
|
451 |
sql = """DELETE FROM qiita.study_environmental_package |
|
|
452 |
WHERE study_id = %s""" |
|
|
453 |
qdb.sql_connection.TRN.add(sql, args) |
|
|
454 |
|
|
|
455 |
sql = "DELETE FROM qiita.study_users WHERE study_id = %s" |
|
|
456 |
qdb.sql_connection.TRN.add(sql, args) |
|
|
457 |
|
|
|
458 |
sql = "DELETE FROM qiita.investigation_study WHERE study_id = %s" |
|
|
459 |
qdb.sql_connection.TRN.add(sql, args) |
|
|
460 |
|
|
|
461 |
sql = "DELETE FROM qiita.per_study_tags WHERE study_id = %s" |
|
|
462 |
qdb.sql_connection.TRN.add(sql, args) |
|
|
463 |
|
|
|
464 |
sql = "DELETE FROM qiita.study WHERE study_id = %s" |
|
|
465 |
qdb.sql_connection.TRN.add(sql, args) |
|
|
466 |
|
|
|
467 |
qdb.sql_connection.TRN.execute() |
|
|
468 |
|
|
|
469 |
@classmethod |
|
|
470 |
def get_tags(cls): |
|
|
471 |
"""Returns the available study tags |
|
|
472 |
|
|
|
473 |
Returns |
|
|
474 |
------- |
|
|
475 |
list of DictCursor |
|
|
476 |
Table-like structure of metadata, one tag per row. Can be |
|
|
477 |
accessed as a list of dictionaries, keyed on column name. |
|
|
478 |
""" |
|
|
479 |
with qdb.sql_connection.TRN: |
|
|
480 |
sql = """SELECT qiita.user_level.name AS user_level, |
|
|
481 |
array_agg(study_tag ORDER BY study_tag) |
|
|
482 |
FROM qiita.study_tags |
|
|
483 |
LEFT JOIN qiita.qiita_user USING (email) |
|
|
484 |
LEFT JOIN qiita.user_level USING (user_level_id) |
|
|
485 |
GROUP BY qiita.user_level.name""" |
|
|
486 |
|
|
|
487 |
qdb.sql_connection.TRN.add(sql) |
|
|
488 |
results = dict(qdb.sql_connection.TRN.execute_fetchindex()) |
|
|
489 |
# when the system is empty, |
|
|
490 |
# it's possible to get an empty dict, fixing |
|
|
491 |
if 'admin' not in results: |
|
|
492 |
results['admin'] = [] |
|
|
493 |
if 'user' not in results: |
|
|
494 |
results['user'] = [] |
|
|
495 |
|
|
|
496 |
return results |
|
|
497 |
|
|
|
498 |
@classmethod |
|
|
499 |
def insert_tags(cls, user, tags): |
|
|
500 |
"""Insert available study tags |
|
|
501 |
|
|
|
502 |
Parameters |
|
|
503 |
---------- |
|
|
504 |
user : qiita_db.user.User |
|
|
505 |
The user adding the tags |
|
|
506 |
tags : list of str |
|
|
507 |
The list of tags to add |
|
|
508 |
""" |
|
|
509 |
with qdb.sql_connection.TRN: |
|
|
510 |
email = user.email |
|
|
511 |
sql = """INSERT INTO qiita.study_tags (email, study_tag) |
|
|
512 |
SELECT %s, %s WHERE NOT EXISTS ( |
|
|
513 |
SELECT 1 FROM qiita.study_tags WHERE study_tag = %s)""" |
|
|
514 |
sql_args = [[email, tag, tag] for tag in tags] |
|
|
515 |
qdb.sql_connection.TRN.add(sql, sql_args, many=True) |
|
|
516 |
qdb.sql_connection.TRN.execute() |
|
|
517 |
|
|
|
518 |
# --- Attributes --- |
|
|
519 |
@property |
|
|
520 |
def autoloaded(self): |
|
|
521 |
"""Returns if the study was autoloaded |
|
|
522 |
|
|
|
523 |
Returns |
|
|
524 |
------- |
|
|
525 |
bool |
|
|
526 |
If the study was autoloaded or not |
|
|
527 |
""" |
|
|
528 |
with qdb.sql_connection.TRN: |
|
|
529 |
sql = """SELECT autoloaded FROM qiita.{0} |
|
|
530 |
WHERE study_id = %s""".format(self._table) |
|
|
531 |
qdb.sql_connection.TRN.add(sql, [self._id]) |
|
|
532 |
return qdb.sql_connection.TRN.execute_fetchlast() |
|
|
533 |
|
|
|
534 |
@autoloaded.setter |
|
|
535 |
def autoloaded(self, value): |
|
|
536 |
"""Sets the autoloaded status of the study |
|
|
537 |
|
|
|
538 |
Parameters |
|
|
539 |
---------- |
|
|
540 |
value : bool |
|
|
541 |
Whether the study was autoloaded |
|
|
542 |
""" |
|
|
543 |
sql = """UPDATE qiita.{0} SET autoloaded = %s |
|
|
544 |
WHERE study_id = %s""".format(self._table) |
|
|
545 |
qdb.sql_connection.perform_as_transaction(sql, [value, self._id]) |
|
|
546 |
|
|
|
547 |
@property |
|
|
548 |
def title(self): |
|
|
549 |
"""Returns the title of the study |
|
|
550 |
|
|
|
551 |
Returns |
|
|
552 |
------- |
|
|
553 |
str |
|
|
554 |
Title of study |
|
|
555 |
""" |
|
|
556 |
with qdb.sql_connection.TRN: |
|
|
557 |
sql = """SELECT study_title FROM qiita.{0} |
|
|
558 |
WHERE study_id = %s""".format(self._table) |
|
|
559 |
qdb.sql_connection.TRN.add(sql, [self._id]) |
|
|
560 |
return qdb.sql_connection.TRN.execute_fetchlast() |
|
|
561 |
|
|
|
562 |
@title.setter |
|
|
563 |
def title(self, title): |
|
|
564 |
"""Sets the title of the study |
|
|
565 |
|
|
|
566 |
Parameters |
|
|
567 |
---------- |
|
|
568 |
title : str |
|
|
569 |
The study title |
|
|
570 |
""" |
|
|
571 |
sql = """UPDATE qiita.{0} SET study_title = %s |
|
|
572 |
WHERE study_id = %s""".format(self._table) |
|
|
573 |
qdb.sql_connection.perform_as_transaction(sql, [title, self._id]) |
|
|
574 |
|
|
|
575 |
@property |
|
|
576 |
def notes(self): |
|
|
577 |
"""Returns the notes of the study |
|
|
578 |
|
|
|
579 |
Returns |
|
|
580 |
------- |
|
|
581 |
str |
|
|
582 |
Study notes |
|
|
583 |
""" |
|
|
584 |
with qdb.sql_connection.TRN: |
|
|
585 |
sql = """SELECT notes FROM qiita.{0} |
|
|
586 |
WHERE study_id = %s""".format(self._table) |
|
|
587 |
qdb.sql_connection.TRN.add(sql, [self._id]) |
|
|
588 |
return qdb.sql_connection.TRN.execute_fetchlast() |
|
|
589 |
|
|
|
590 |
@notes.setter |
|
|
591 |
def notes(self, notes): |
|
|
592 |
"""Sets the notes of the study |
|
|
593 |
|
|
|
594 |
Parameters |
|
|
595 |
---------- |
|
|
596 |
notes : str |
|
|
597 |
The study notes |
|
|
598 |
""" |
|
|
599 |
sql = """UPDATE qiita.{0} SET notes = %s |
|
|
600 |
WHERE study_id = %s""".format(self._table) |
|
|
601 |
qdb.sql_connection.perform_as_transaction(sql, [notes, self._id]) |
|
|
602 |
|
|
|
603 |
@property |
|
|
604 |
def public_raw_download(self): |
|
|
605 |
"""Returns if the study's raw data is available for download |
|
|
606 |
|
|
|
607 |
Returns |
|
|
608 |
------- |
|
|
609 |
str |
|
|
610 |
public_raw_download of study |
|
|
611 |
""" |
|
|
612 |
with qdb.sql_connection.TRN: |
|
|
613 |
sql = """SELECT public_raw_download FROM qiita.{0} |
|
|
614 |
WHERE study_id = %s""".format(self._table) |
|
|
615 |
qdb.sql_connection.TRN.add(sql, [self._id]) |
|
|
616 |
return qdb.sql_connection.TRN.execute_fetchlast() |
|
|
617 |
|
|
|
618 |
@public_raw_download.setter |
|
|
619 |
def public_raw_download(self, public_raw_download): |
|
|
620 |
"""Sets if the study's raw data is available for download |
|
|
621 |
|
|
|
622 |
Parameters |
|
|
623 |
---------- |
|
|
624 |
public_raw_download : bool |
|
|
625 |
The study public_raw_download |
|
|
626 |
""" |
|
|
627 |
sql = """UPDATE qiita.{0} SET public_raw_download = %s |
|
|
628 |
WHERE study_id = %s""".format(self._table) |
|
|
629 |
qdb.sql_connection.perform_as_transaction( |
|
|
630 |
sql, [public_raw_download, self._id]) |
|
|
631 |
|
|
|
632 |
@property |
|
|
633 |
def info(self): |
|
|
634 |
"""Dict with all information attached to the study |
|
|
635 |
|
|
|
636 |
Returns |
|
|
637 |
------- |
|
|
638 |
dict |
|
|
639 |
info of study keyed to column names |
|
|
640 |
""" |
|
|
641 |
with qdb.sql_connection.TRN: |
|
|
642 |
sql = "SELECT * FROM qiita.{0} WHERE study_id = %s".format( |
|
|
643 |
self._table) |
|
|
644 |
qdb.sql_connection.TRN.add(sql, [self._id]) |
|
|
645 |
info = dict(qdb.sql_connection.TRN.execute_fetchindex()[0]) |
|
|
646 |
# remove non-info items from info |
|
|
647 |
for item in self._non_info: |
|
|
648 |
info.pop(item) |
|
|
649 |
# removed because redundant to the id already stored in the object |
|
|
650 |
info.pop('study_id') |
|
|
651 |
|
|
|
652 |
if info['principal_investigator_id']: |
|
|
653 |
info['principal_investigator'] = qdb.study.StudyPerson( |
|
|
654 |
info["principal_investigator_id"]) |
|
|
655 |
else: |
|
|
656 |
info['principal_investigator'] = None |
|
|
657 |
del info['principal_investigator_id'] |
|
|
658 |
|
|
|
659 |
if info['lab_person_id']: |
|
|
660 |
info['lab_person'] = qdb.study.StudyPerson( |
|
|
661 |
info["lab_person_id"]) |
|
|
662 |
else: |
|
|
663 |
info['lab_person'] = None |
|
|
664 |
del info['lab_person_id'] |
|
|
665 |
|
|
|
666 |
return info |
|
|
667 |
|
|
|
668 |
@info.setter |
|
|
669 |
def info(self, info): |
|
|
670 |
"""Updates the information attached to the study |
|
|
671 |
|
|
|
672 |
Parameters |
|
|
673 |
---------- |
|
|
674 |
info : dict |
|
|
675 |
information to change/update for the study, keyed to column name |
|
|
676 |
|
|
|
677 |
Raises |
|
|
678 |
------ |
|
|
679 |
IncompetentQiitaDeveloperError |
|
|
680 |
Empty dict passed |
|
|
681 |
QiitaDBColumnError |
|
|
682 |
Unknown column names passed |
|
|
683 |
""" |
|
|
684 |
if not info: |
|
|
685 |
raise IncompetentQiitaDeveloperError("Need entries in info dict!") |
|
|
686 |
|
|
|
687 |
if 'study_id' in info: |
|
|
688 |
raise qdb.exceptions.QiitaDBColumnError("Cannot set study_id!") |
|
|
689 |
|
|
|
690 |
if self._non_info.intersection(info): |
|
|
691 |
raise qdb.exceptions.QiitaDBColumnError( |
|
|
692 |
"non info keys passed: %s" % self._non_info.intersection(info)) |
|
|
693 |
|
|
|
694 |
with qdb.sql_connection.TRN: |
|
|
695 |
if 'timeseries_type_id' in info: |
|
|
696 |
# We only lock if the timeseries type changes |
|
|
697 |
self._lock_non_sandbox() |
|
|
698 |
|
|
|
699 |
# make sure dictionary only has keys for available columns in db |
|
|
700 |
qdb.util.check_table_cols(info, self._table) |
|
|
701 |
|
|
|
702 |
sql_vals = [] |
|
|
703 |
data = [] |
|
|
704 |
# build query with data values in correct order for SQL statement |
|
|
705 |
for key, val in info.items(): |
|
|
706 |
sql_vals.append("{0} = %s".format(key)) |
|
|
707 |
if isinstance(val, qdb.base.QiitaObject): |
|
|
708 |
data.append(val.id) |
|
|
709 |
else: |
|
|
710 |
data.append(val) |
|
|
711 |
data.append(self._id) |
|
|
712 |
|
|
|
713 |
sql = "UPDATE qiita.{0} SET {1} WHERE study_id = %s".format( |
|
|
714 |
self._table, ','.join(sql_vals)) |
|
|
715 |
qdb.sql_connection.TRN.add(sql, data) |
|
|
716 |
qdb.sql_connection.TRN.execute() |
|
|
717 |
|
|
|
718 |
@property |
|
|
719 |
def shared_with(self): |
|
|
720 |
"""list of users the study is shared with |
|
|
721 |
|
|
|
722 |
Returns |
|
|
723 |
------- |
|
|
724 |
list of qiita_db.user.User |
|
|
725 |
Users the study is shared with |
|
|
726 |
""" |
|
|
727 |
with qdb.sql_connection.TRN: |
|
|
728 |
sql = """SELECT email FROM qiita.{0}_users |
|
|
729 |
WHERE study_id = %s""".format(self._table) |
|
|
730 |
qdb.sql_connection.TRN.add(sql, [self._id]) |
|
|
731 |
return [qdb.user.User(uid) |
|
|
732 |
for uid in qdb.sql_connection.TRN.execute_fetchflatten()] |
|
|
733 |
|
|
|
734 |
@property |
|
|
735 |
def publications(self): |
|
|
736 |
""" Returns list of publications from this study |
|
|
737 |
|
|
|
738 |
Returns |
|
|
739 |
------- |
|
|
740 |
list of (str, str) |
|
|
741 |
list of all the DOI and pubmed ids |
|
|
742 |
""" |
|
|
743 |
with qdb.sql_connection.TRN: |
|
|
744 |
sql = """SELECT publication, is_doi |
|
|
745 |
FROM qiita.study_publication |
|
|
746 |
WHERE study_id = %s""" |
|
|
747 |
qdb.sql_connection.TRN.add(sql, [self._id]) |
|
|
748 |
return qdb.sql_connection.TRN.execute_fetchindex() |
|
|
749 |
|
|
|
750 |
@publications.setter |
|
|
751 |
def publications(self, values): |
|
|
752 |
"""Sets the pmids for the study |
|
|
753 |
|
|
|
754 |
Parameters |
|
|
755 |
---------- |
|
|
756 |
values : list of (str, str) |
|
|
757 |
The list of (DOI, pubmed id) to associate with the study |
|
|
758 |
|
|
|
759 |
Raises |
|
|
760 |
------ |
|
|
761 |
TypeError |
|
|
762 |
If values is not a list |
|
|
763 |
""" |
|
|
764 |
# Check that a list is actually passed |
|
|
765 |
if not isinstance(values, list): |
|
|
766 |
raise TypeError('publications should be a list') |
|
|
767 |
|
|
|
768 |
with qdb.sql_connection.TRN: |
|
|
769 |
# Delete the previous pmids associated with the study |
|
|
770 |
sql = "DELETE FROM qiita.study_publication WHERE study_id = %s" |
|
|
771 |
qdb.sql_connection.TRN.add(sql, [self._id]) |
|
|
772 |
|
|
|
773 |
# Set the new ones |
|
|
774 |
sql = """INSERT INTO qiita.study_publication |
|
|
775 |
(study_id, publication, is_doi) |
|
|
776 |
VALUES (%s, %s, %s)""" |
|
|
777 |
sql_args = [[self._id, pub, is_doi] for pub, is_doi in values] |
|
|
778 |
qdb.sql_connection.TRN.add(sql, sql_args, many=True) |
|
|
779 |
qdb.sql_connection.TRN.execute() |
|
|
780 |
|
|
|
781 |
@property |
|
|
782 |
def investigation(self): |
|
|
783 |
""" Returns Investigation this study is part of |
|
|
784 |
|
|
|
785 |
If the study doesn't have an investigation associated with it, it will |
|
|
786 |
return None |
|
|
787 |
|
|
|
788 |
Returns |
|
|
789 |
------- |
|
|
790 |
qiita_db.investigation.Investigation or None |
|
|
791 |
""" |
|
|
792 |
with qdb.sql_connection.TRN: |
|
|
793 |
sql = """SELECT investigation_id FROM qiita.investigation_study |
|
|
794 |
WHERE study_id = %s""" |
|
|
795 |
qdb.sql_connection.TRN.add(sql, [self._id]) |
|
|
796 |
inv = qdb.sql_connection.TRN.execute_fetchindex() |
|
|
797 |
# If this study belongs to an investigation it will be in |
|
|
798 |
# the first value of the first row [0][0] |
|
|
799 |
return qdb.investigation.Investigation(inv[0][0]) if inv else None |
|
|
800 |
|
|
|
801 |
@property |
|
|
802 |
def sample_template(self): |
|
|
803 |
"""Returns sample_template information |
|
|
804 |
|
|
|
805 |
If the study doesn't have a sample template associated with it, it will |
|
|
806 |
return None |
|
|
807 |
|
|
|
808 |
Returns |
|
|
809 |
------- |
|
|
810 |
qiita_db.metadata_template.sample_template.SampleTemplate or None |
|
|
811 |
""" |
|
|
812 |
with qdb.sql_connection.TRN: |
|
|
813 |
sql = """SELECT EXISTS(SELECT * |
|
|
814 |
FROM qiita.study_sample |
|
|
815 |
WHERE study_id = %s)""" |
|
|
816 |
qdb.sql_connection.TRN.add(sql, [self.id]) |
|
|
817 |
exists = qdb.sql_connection.TRN.execute_fetchlast() |
|
|
818 |
return (qdb.metadata_template.sample_template.SampleTemplate(self._id) |
|
|
819 |
if exists else None) |
|
|
820 |
|
|
|
821 |
@property |
|
|
822 |
def data_types(self): |
|
|
823 |
"""Returns list of the data types for this study |
|
|
824 |
|
|
|
825 |
Returns |
|
|
826 |
------- |
|
|
827 |
list of str |
|
|
828 |
""" |
|
|
829 |
with qdb.sql_connection.TRN: |
|
|
830 |
sql = """SELECT DISTINCT data_type |
|
|
831 |
FROM qiita.study_prep_template |
|
|
832 |
JOIN qiita.prep_template USING (prep_template_id) |
|
|
833 |
JOIN qiita.data_type USING (data_type_id) |
|
|
834 |
WHERE study_id = %s""" |
|
|
835 |
qdb.sql_connection.TRN.add(sql, [self._id]) |
|
|
836 |
return qdb.sql_connection.TRN.execute_fetchflatten() |
|
|
837 |
|
|
|
838 |
@property |
|
|
839 |
def owner(self): |
|
|
840 |
"""Gets the owner of the study |
|
|
841 |
|
|
|
842 |
Returns |
|
|
843 |
------- |
|
|
844 |
qiita_db.user.User |
|
|
845 |
The user that owns this study |
|
|
846 |
""" |
|
|
847 |
with qdb.sql_connection.TRN: |
|
|
848 |
sql = """SELECT email FROM qiita.{} WHERE study_id = %s""".format( |
|
|
849 |
self._table) |
|
|
850 |
qdb.sql_connection.TRN.add(sql, [self._id]) |
|
|
851 |
return qdb.user.User(qdb.sql_connection.TRN.execute_fetchlast()) |
|
|
852 |
|
|
|
853 |
@property |
|
|
854 |
def environmental_packages(self): |
|
|
855 |
"""Gets the environmental packages associated with the study |
|
|
856 |
|
|
|
857 |
Returns |
|
|
858 |
------- |
|
|
859 |
list of str |
|
|
860 |
The environmental package names associated with the study |
|
|
861 |
""" |
|
|
862 |
with qdb.sql_connection.TRN: |
|
|
863 |
sql = """SELECT environmental_package_name |
|
|
864 |
FROM qiita.study_environmental_package |
|
|
865 |
WHERE study_id = %s""" |
|
|
866 |
qdb.sql_connection.TRN.add(sql, [self._id]) |
|
|
867 |
return qdb.sql_connection.TRN.execute_fetchflatten() |
|
|
868 |
|
|
|
869 |
@environmental_packages.setter |
|
|
870 |
def environmental_packages(self, values): |
|
|
871 |
"""Sets the environmental packages for the study |
|
|
872 |
|
|
|
873 |
Parameters |
|
|
874 |
---------- |
|
|
875 |
values : list of str |
|
|
876 |
The list of environmental package names to associate with the study |
|
|
877 |
|
|
|
878 |
Raises |
|
|
879 |
------ |
|
|
880 |
TypeError |
|
|
881 |
If values is not a list |
|
|
882 |
ValueError |
|
|
883 |
If any environmental packages listed on values is not recognized |
|
|
884 |
""" |
|
|
885 |
with qdb.sql_connection.TRN: |
|
|
886 |
# The environmental packages can be changed only if the study is |
|
|
887 |
# sandboxed |
|
|
888 |
self._lock_non_sandbox() |
|
|
889 |
|
|
|
890 |
# Check that a list is actually passed |
|
|
891 |
if not isinstance(values, list): |
|
|
892 |
raise TypeError('Environmental packages should be a list') |
|
|
893 |
|
|
|
894 |
# Get all the environmental packages |
|
|
895 |
env_pkgs = [pkg[0] |
|
|
896 |
for pkg in qdb.util.get_environmental_packages()] |
|
|
897 |
|
|
|
898 |
# Check that all the passed values are valid environmental packages |
|
|
899 |
missing = set(values).difference(env_pkgs) |
|
|
900 |
if missing: |
|
|
901 |
raise ValueError('Environmetal package(s) not recognized: %s' |
|
|
902 |
% ', '.join(missing)) |
|
|
903 |
|
|
|
904 |
# Delete the previous environmental packages associated with |
|
|
905 |
# the study |
|
|
906 |
sql = """DELETE FROM qiita.study_environmental_package |
|
|
907 |
WHERE study_id=%s""" |
|
|
908 |
qdb.sql_connection.TRN.add(sql, [self._id]) |
|
|
909 |
|
|
|
910 |
# Set the new ones |
|
|
911 |
sql = """INSERT INTO qiita.study_environmental_package |
|
|
912 |
(study_id, environmental_package_name) |
|
|
913 |
VALUES (%s, %s)""" |
|
|
914 |
sql_args = [[self._id, val] for val in values] |
|
|
915 |
qdb.sql_connection.TRN.add(sql, sql_args, many=True) |
|
|
916 |
|
|
|
917 |
qdb.sql_connection.TRN.execute() |
|
|
918 |
|
|
|
919 |
@property |
|
|
920 |
def _portals(self): |
|
|
921 |
"""Portals this study is associated with |
|
|
922 |
|
|
|
923 |
Returns |
|
|
924 |
------- |
|
|
925 |
list of str |
|
|
926 |
Portal names study is associated with |
|
|
927 |
""" |
|
|
928 |
with qdb.sql_connection.TRN: |
|
|
929 |
sql = """SELECT portal |
|
|
930 |
FROM qiita.portal_type |
|
|
931 |
JOIN qiita.study_portal USING (portal_type_id) |
|
|
932 |
WHERE study_id = %s""" |
|
|
933 |
qdb.sql_connection.TRN.add(sql, [self._id]) |
|
|
934 |
return qdb.sql_connection.TRN.execute_fetchflatten() |
|
|
935 |
|
|
|
936 |
@property |
|
|
937 |
def ebi_study_accession(self): |
|
|
938 |
"""The EBI study accession for this study |
|
|
939 |
|
|
|
940 |
Returns |
|
|
941 |
------- |
|
|
942 |
str |
|
|
943 |
The study EBI accession |
|
|
944 |
""" |
|
|
945 |
with qdb.sql_connection.TRN: |
|
|
946 |
sql = """SELECT ebi_study_accession |
|
|
947 |
FROM qiita.{0} |
|
|
948 |
WHERE study_id = %s""".format(self._table) |
|
|
949 |
qdb.sql_connection.TRN.add(sql, [self._id]) |
|
|
950 |
return qdb.sql_connection.TRN.execute_fetchlast() |
|
|
951 |
|
|
|
952 |
@ebi_study_accession.setter |
|
|
953 |
def ebi_study_accession(self, value): |
|
|
954 |
"""Sets the study's EBI study accession |
|
|
955 |
|
|
|
956 |
Parameters |
|
|
957 |
---------- |
|
|
958 |
value : str |
|
|
959 |
The new EBI study accession |
|
|
960 |
|
|
|
961 |
Raises |
|
|
962 |
------ |
|
|
963 |
QiitDBError |
|
|
964 |
If the study already has an EBI study accession |
|
|
965 |
""" |
|
|
966 |
if self.ebi_study_accession is not None: |
|
|
967 |
raise qdb.exceptions.QiitaDBError( |
|
|
968 |
"Study %s already has an EBI study accession" |
|
|
969 |
% self.id) |
|
|
970 |
sql = """UPDATE qiita.{} |
|
|
971 |
SET ebi_study_accession = %s |
|
|
972 |
WHERE study_id = %s""".format(self._table) |
|
|
973 |
qdb.sql_connection.perform_as_transaction(sql, [value, self.id]) |
|
|
974 |
|
|
|
975 |
def _ebi_submission_jobs(self): |
|
|
976 |
"""Helper code to avoid duplication""" |
|
|
977 |
plugin = qdb.software.Software.from_name_and_version( |
|
|
978 |
'Qiita', 'alpha') |
|
|
979 |
cmd = plugin.get_command('submit_to_EBI') |
|
|
980 |
|
|
|
981 |
sql = """SELECT processing_job_id, |
|
|
982 |
pj.command_parameters->>'artifact' as aid, |
|
|
983 |
processing_job_status, can_be_submitted_to_ebi, |
|
|
984 |
array_agg(ebi_run_accession) |
|
|
985 |
FROM qiita.processing_job pj |
|
|
986 |
LEFT JOIN qiita.processing_job_status |
|
|
987 |
USING (processing_job_status_id) |
|
|
988 |
LEFT JOIN qiita.artifact ON ( |
|
|
989 |
artifact_id = ( |
|
|
990 |
pj.command_parameters->>'artifact')::INT) |
|
|
991 |
LEFT JOIN qiita.ebi_run_accession era USING (artifact_id) |
|
|
992 |
LEFT JOIN qiita.artifact_type USING (artifact_type_id) |
|
|
993 |
WHERE pj.command_parameters->>'artifact' IN ( |
|
|
994 |
SELECT artifact_id::text |
|
|
995 |
FROM qiita.study_artifact WHERE study_id = {0}) |
|
|
996 |
AND pj.command_id = {1} |
|
|
997 |
GROUP BY processing_job_id, aid, processing_job_status, |
|
|
998 |
can_be_submitted_to_ebi""".format(self._id, cmd.id) |
|
|
999 |
qdb.sql_connection.TRN.add(sql) |
|
|
1000 |
|
|
|
1001 |
return qdb.sql_connection.TRN.execute_fetchindex() |
|
|
1002 |
|
|
|
1003 |
@property |
|
|
1004 |
def ebi_submission_status(self): |
|
|
1005 |
"""The EBI submission status of this study |
|
|
1006 |
|
|
|
1007 |
Returns |
|
|
1008 |
------- |
|
|
1009 |
str |
|
|
1010 |
The study EBI submission status |
|
|
1011 |
|
|
|
1012 |
Notes |
|
|
1013 |
----- |
|
|
1014 |
There are 4 possible states: 'not submitted', 'submitting', |
|
|
1015 |
'submitted' & 'failed'. We are going to assume 'not submitted' if the |
|
|
1016 |
study doesn't have an accession, 'submitted' if it has an accession, |
|
|
1017 |
'submitting' if there are submit_to_EBI jobs running using the study |
|
|
1018 |
artifacts, & 'failed' if there are artifacts with failed jobs without |
|
|
1019 |
successful ones. |
|
|
1020 |
""" |
|
|
1021 |
status = 'not submitted' |
|
|
1022 |
with qdb.sql_connection.TRN: |
|
|
1023 |
if self.ebi_study_accession: |
|
|
1024 |
status = 'submitted' |
|
|
1025 |
|
|
|
1026 |
jobs = defaultdict(dict) |
|
|
1027 |
for info in self._ebi_submission_jobs(): |
|
|
1028 |
jid, aid, js, cbste, era = info |
|
|
1029 |
if not cbste or era != [None]: |
|
|
1030 |
continue |
|
|
1031 |
jobs[js][aid] = jid |
|
|
1032 |
|
|
|
1033 |
if 'queued' in jobs or 'running' in jobs: |
|
|
1034 |
status = 'submitting' |
|
|
1035 |
elif 'error' in jobs: |
|
|
1036 |
aids_error = [] |
|
|
1037 |
aids_other = [] |
|
|
1038 |
for s, aids in jobs.items(): |
|
|
1039 |
for aid in aids.keys(): |
|
|
1040 |
if s == 'error': |
|
|
1041 |
aids_error.append(aid) |
|
|
1042 |
else: |
|
|
1043 |
aids_other.append(aid) |
|
|
1044 |
difference = set(aids_error) - set(aids_other) |
|
|
1045 |
if difference: |
|
|
1046 |
status = ('Some artifact submissions failed: %s' % |
|
|
1047 |
', '.join(map(str, list(difference)))) |
|
|
1048 |
|
|
|
1049 |
return status |
|
|
1050 |
|
|
|
1051 |
@property |
|
|
1052 |
def tags(self): |
|
|
1053 |
"""Returns the tags of the study |
|
|
1054 |
|
|
|
1055 |
Returns |
|
|
1056 |
------- |
|
|
1057 |
list of str |
|
|
1058 |
The study tags |
|
|
1059 |
""" |
|
|
1060 |
with qdb.sql_connection.TRN: |
|
|
1061 |
sql = """SELECT study_tag |
|
|
1062 |
FROM qiita.study_tags |
|
|
1063 |
LEFT JOIN qiita.per_study_tags USING (study_tag) |
|
|
1064 |
WHERE study_id = {0} |
|
|
1065 |
ORDER BY study_tag""".format(self._id) |
|
|
1066 |
qdb.sql_connection.TRN.add(sql) |
|
|
1067 |
return [t[0] for t in qdb.sql_connection.TRN.execute_fetchindex()] |
|
|
1068 |
|
|
|
1069 |
# --- methods --- |
|
|
1070 |
def artifacts(self, dtype=None, artifact_type=None): |
|
|
1071 |
"""Returns the list of artifacts associated with the study |
|
|
1072 |
|
|
|
1073 |
Parameters |
|
|
1074 |
---------- |
|
|
1075 |
dtype : str, optional |
|
|
1076 |
If given, retrieve only artifacts for given data type. Default, |
|
|
1077 |
return all artifacts associated with the study. |
|
|
1078 |
artifact_type : str, optional |
|
|
1079 |
If given, retrieve only artifacts of given data type. Default, |
|
|
1080 |
return all artifacts associated with the study |
|
|
1081 |
|
|
|
1082 |
Returns |
|
|
1083 |
------- |
|
|
1084 |
list of qiita_db.artifact.Artifact |
|
|
1085 |
""" |
|
|
1086 |
with qdb.sql_connection.TRN: |
|
|
1087 |
sql_args = [self._id] |
|
|
1088 |
sql_where = "" |
|
|
1089 |
if dtype: |
|
|
1090 |
sql_args.append(dtype) |
|
|
1091 |
sql_where = " AND data_type = %s" |
|
|
1092 |
|
|
|
1093 |
if artifact_type: |
|
|
1094 |
sql_args.append(artifact_type) |
|
|
1095 |
sql_where += " AND artifact_type = %s" |
|
|
1096 |
|
|
|
1097 |
sql = """SELECT artifact_id |
|
|
1098 |
FROM qiita.artifact |
|
|
1099 |
JOIN qiita.data_type USING (data_type_id) |
|
|
1100 |
JOIN qiita.study_artifact USING (artifact_id) |
|
|
1101 |
JOIN qiita.artifact_type USING (artifact_type_id) |
|
|
1102 |
WHERE study_id = %s{0} AND visibility_id NOT IN %s |
|
|
1103 |
ORDER BY artifact_id""".format(sql_where) |
|
|
1104 |
sql_args.append(qdb.util.artifact_visibilities_to_skip()) |
|
|
1105 |
|
|
|
1106 |
qdb.sql_connection.TRN.add(sql, sql_args) |
|
|
1107 |
return [qdb.artifact.Artifact(aid) |
|
|
1108 |
for aid in qdb.sql_connection.TRN.execute_fetchflatten()] |
|
|
1109 |
|
|
|
1110 |
def prep_templates(self, data_type=None): |
|
|
1111 |
"""Return list of prep template ids |
|
|
1112 |
|
|
|
1113 |
Parameters |
|
|
1114 |
---------- |
|
|
1115 |
data_type : str, optional |
|
|
1116 |
If given, retrieve only prep templates for given datatype. |
|
|
1117 |
Default None. |
|
|
1118 |
|
|
|
1119 |
Returns |
|
|
1120 |
------- |
|
|
1121 |
list of qiita_db.metadata_template.prep_template.PrepTemplate |
|
|
1122 |
""" |
|
|
1123 |
with qdb.sql_connection.TRN: |
|
|
1124 |
spec_data = "" |
|
|
1125 |
args = [self._id] |
|
|
1126 |
if data_type: |
|
|
1127 |
spec_data = " AND data_type_id = %s" |
|
|
1128 |
args.append(qdb.util.convert_to_id(data_type, "data_type")) |
|
|
1129 |
|
|
|
1130 |
sql = """SELECT prep_template_id |
|
|
1131 |
FROM qiita.study_prep_template |
|
|
1132 |
JOIN qiita.prep_template USING (prep_template_id) |
|
|
1133 |
WHERE study_id = %s{0} |
|
|
1134 |
ORDER BY prep_template_id""".format(spec_data) |
|
|
1135 |
qdb.sql_connection.TRN.add(sql, args) |
|
|
1136 |
return [qdb.metadata_template.prep_template.PrepTemplate(ptid) |
|
|
1137 |
for ptid in qdb.sql_connection.TRN.execute_fetchflatten()] |
|
|
1138 |
|
|
|
1139 |
def analyses(self): |
|
|
1140 |
"""Get all analyses where samples from this study have been used |
|
|
1141 |
|
|
|
1142 |
Returns |
|
|
1143 |
------- |
|
|
1144 |
list of qiita_db.analysis.Analysis |
|
|
1145 |
""" |
|
|
1146 |
with qdb.sql_connection.TRN: |
|
|
1147 |
if self.sample_template is not None: |
|
|
1148 |
sids = self.sample_template.keys() |
|
|
1149 |
if sids: |
|
|
1150 |
sql = """SELECT DISTINCT analysis_id |
|
|
1151 |
FROM qiita.analysis_sample |
|
|
1152 |
WHERE sample_id IN %s |
|
|
1153 |
ORDER BY analysis_id""" |
|
|
1154 |
qdb.sql_connection.TRN.add( |
|
|
1155 |
sql, [tuple(self.sample_template.keys())]) |
|
|
1156 |
|
|
|
1157 |
return [qdb.analysis.Analysis(_id) for _id in |
|
|
1158 |
qdb.sql_connection.TRN.execute_fetchflatten()] |
|
|
1159 |
return [] |
|
|
1160 |
|
|
|
1161 |
def has_access(self, user, no_public=False): |
|
|
1162 |
"""Returns whether the given user has access to the study |
|
|
1163 |
|
|
|
1164 |
Parameters |
|
|
1165 |
---------- |
|
|
1166 |
user : User object |
|
|
1167 |
User we are checking access for |
|
|
1168 |
no_public: bool |
|
|
1169 |
If we should ignore those studies shared with the user. Defaults |
|
|
1170 |
to False |
|
|
1171 |
|
|
|
1172 |
Returns |
|
|
1173 |
------- |
|
|
1174 |
bool |
|
|
1175 |
Whether user has access to study or not |
|
|
1176 |
""" |
|
|
1177 |
with qdb.sql_connection.TRN: |
|
|
1178 |
# return True if the user is one of the admins |
|
|
1179 |
if user.level in {'superuser', 'admin'}: |
|
|
1180 |
return True |
|
|
1181 |
|
|
|
1182 |
# if no_public is False then just check if the study is public |
|
|
1183 |
# and return True |
|
|
1184 |
if not no_public and self.status == 'public': |
|
|
1185 |
return True |
|
|
1186 |
|
|
|
1187 |
# let's check if the study belongs to this user or has been |
|
|
1188 |
# shared with them |
|
|
1189 |
sql = """SELECT EXISTS ( |
|
|
1190 |
SELECT study_id |
|
|
1191 |
FROM qiita.study |
|
|
1192 |
JOIN qiita.study_portal USING (study_id) |
|
|
1193 |
JOIN qiita.portal_type USING (portal_type_id) |
|
|
1194 |
WHERE email = %s AND portal = %s AND study_id = %s |
|
|
1195 |
UNION |
|
|
1196 |
SELECT study_id |
|
|
1197 |
FROM qiita.study_users |
|
|
1198 |
JOIN qiita.study_portal USING (study_id) |
|
|
1199 |
JOIN qiita.portal_type USING (portal_type_id) |
|
|
1200 |
WHERE email = %s AND portal = %s AND study_id = %s |
|
|
1201 |
) |
|
|
1202 |
""" |
|
|
1203 |
qdb.sql_connection.TRN.add( |
|
|
1204 |
sql, [user.email, qiita_config.portal, self.id, |
|
|
1205 |
user.email, qiita_config.portal, self.id]) |
|
|
1206 |
result = qdb.sql_connection.TRN.execute_fetchlast() |
|
|
1207 |
|
|
|
1208 |
return result |
|
|
1209 |
|
|
|
1210 |
def can_edit(self, user): |
|
|
1211 |
"""Returns whether the given user can edit the study |
|
|
1212 |
|
|
|
1213 |
Parameters |
|
|
1214 |
---------- |
|
|
1215 |
user : User object |
|
|
1216 |
User we are checking edit permissions for |
|
|
1217 |
|
|
|
1218 |
Returns |
|
|
1219 |
------- |
|
|
1220 |
bool |
|
|
1221 |
Whether user can edit the study or not |
|
|
1222 |
""" |
|
|
1223 |
# The study is editable only if the user is the owner, is in the shared |
|
|
1224 |
# list or the user is an admin |
|
|
1225 |
return (user.level in {'superuser', 'admin'} or self.owner == user or |
|
|
1226 |
user in self.shared_with) |
|
|
1227 |
|
|
|
1228 |
def share(self, user): |
|
|
1229 |
"""Share the study with another user |
|
|
1230 |
|
|
|
1231 |
Parameters |
|
|
1232 |
---------- |
|
|
1233 |
user: User object |
|
|
1234 |
The user to share the study with |
|
|
1235 |
""" |
|
|
1236 |
# Make sure the study is not already shared with the given user |
|
|
1237 |
if user in self.shared_with: |
|
|
1238 |
return |
|
|
1239 |
# Do not allow the study to be shared with the owner |
|
|
1240 |
if user == self.owner: |
|
|
1241 |
return |
|
|
1242 |
|
|
|
1243 |
sql = """INSERT INTO qiita.study_users (study_id, email) |
|
|
1244 |
VALUES (%s, %s)""" |
|
|
1245 |
qdb.sql_connection.perform_as_transaction(sql, [self._id, user.id]) |
|
|
1246 |
|
|
|
1247 |
def unshare(self, user): |
|
|
1248 |
"""Unshare the study with another user |
|
|
1249 |
|
|
|
1250 |
Parameters |
|
|
1251 |
---------- |
|
|
1252 |
user: User object |
|
|
1253 |
The user to unshare the study with |
|
|
1254 |
""" |
|
|
1255 |
sql = """DELETE FROM qiita.study_users |
|
|
1256 |
WHERE study_id = %s AND email = %s""" |
|
|
1257 |
qdb.sql_connection.perform_as_transaction(sql, [self._id, user.id]) |
|
|
1258 |
|
|
|
1259 |
def update_tags(self, user, tags): |
|
|
1260 |
"""Sets the tags of the study |
|
|
1261 |
|
|
|
1262 |
Parameters |
|
|
1263 |
---------- |
|
|
1264 |
user: User object |
|
|
1265 |
The user reqesting the study tags update |
|
|
1266 |
tags : list of str |
|
|
1267 |
The tags to update within the study |
|
|
1268 |
|
|
|
1269 |
Returns |
|
|
1270 |
------- |
|
|
1271 |
str |
|
|
1272 |
Warnings during insertion |
|
|
1273 |
""" |
|
|
1274 |
message = '' |
|
|
1275 |
# converting to set just to facilitate operations |
|
|
1276 |
system_tags_admin = set(self.get_tags()['admin']) |
|
|
1277 |
user_level = user.level |
|
|
1278 |
current_tags = set(self.tags) |
|
|
1279 |
to_delete = current_tags - set(tags) |
|
|
1280 |
to_add = set(tags) - current_tags |
|
|
1281 |
|
|
|
1282 |
if to_delete or to_add: |
|
|
1283 |
with qdb.sql_connection.TRN: |
|
|
1284 |
if to_delete: |
|
|
1285 |
if user_level != 'admin': |
|
|
1286 |
admin_tags = to_delete & system_tags_admin |
|
|
1287 |
if admin_tags: |
|
|
1288 |
message += 'You cannot remove: %s' % ', '.join( |
|
|
1289 |
admin_tags) |
|
|
1290 |
to_delete = to_delete - admin_tags |
|
|
1291 |
|
|
|
1292 |
if to_delete: |
|
|
1293 |
sql = """DELETE FROM qiita.per_study_tags |
|
|
1294 |
WHERE study_id = %s AND study_tag IN %s""" |
|
|
1295 |
qdb.sql_connection.TRN.add( |
|
|
1296 |
sql, [self._id, tuple(to_delete)]) |
|
|
1297 |
|
|
|
1298 |
if to_add: |
|
|
1299 |
if user_level != 'admin': |
|
|
1300 |
admin_tags = to_add & system_tags_admin |
|
|
1301 |
if admin_tags: |
|
|
1302 |
message += ('Only admins can assign: ' |
|
|
1303 |
'%s' % ', '.join(admin_tags)) |
|
|
1304 |
to_add = to_add - admin_tags |
|
|
1305 |
|
|
|
1306 |
if to_add: |
|
|
1307 |
self.insert_tags(user, to_add) |
|
|
1308 |
|
|
|
1309 |
sql = """INSERT INTO qiita.per_study_tags |
|
|
1310 |
(study_tag, study_id) |
|
|
1311 |
SELECT %s, %s |
|
|
1312 |
WHERE |
|
|
1313 |
NOT EXISTS ( |
|
|
1314 |
SELECT study_tag, study_id |
|
|
1315 |
FROM qiita.per_study_tags |
|
|
1316 |
WHERE study_tag = %s |
|
|
1317 |
AND study_id = %s |
|
|
1318 |
)""" |
|
|
1319 |
sql_args = [[t, self._id, t, self._id] for t in to_add] |
|
|
1320 |
qdb.sql_connection.TRN.add(sql, sql_args, many=True) |
|
|
1321 |
|
|
|
1322 |
qdb.sql_connection.TRN.execute() |
|
|
1323 |
else: |
|
|
1324 |
message = 'No changes in the tags.' |
|
|
1325 |
|
|
|
1326 |
return message |
|
|
1327 |
|
|
|
1328 |
|
|
|
1329 |
class StudyPerson(qdb.base.QiitaObject): |
|
|
1330 |
r"""Object handling information pertaining to people involved in a study |
|
|
1331 |
|
|
|
1332 |
Attributes |
|
|
1333 |
---------- |
|
|
1334 |
name : str |
|
|
1335 |
name of the person |
|
|
1336 |
email : str |
|
|
1337 |
email of the person |
|
|
1338 |
affiliation : str |
|
|
1339 |
institution with which the person is affiliated |
|
|
1340 |
address : str or None |
|
|
1341 |
address of the person |
|
|
1342 |
phone : str or None |
|
|
1343 |
phone number of the person |
|
|
1344 |
""" |
|
|
1345 |
_table = "study_person" |
|
|
1346 |
|
|
|
1347 |
@classmethod |
|
|
1348 |
def iter(cls): |
|
|
1349 |
"""Iterate over all study people in the database |
|
|
1350 |
|
|
|
1351 |
Returns |
|
|
1352 |
------- |
|
|
1353 |
generator |
|
|
1354 |
Yields a `StudyPerson` object for each person in the database, |
|
|
1355 |
in order of ascending study_person_id |
|
|
1356 |
""" |
|
|
1357 |
with qdb.sql_connection.TRN: |
|
|
1358 |
sql = """SELECT study_person_id FROM qiita.{} |
|
|
1359 |
ORDER BY study_person_id""".format(cls._table) |
|
|
1360 |
qdb.sql_connection.TRN.add(sql) |
|
|
1361 |
|
|
|
1362 |
for id_ in qdb.sql_connection.TRN.execute_fetchflatten(): |
|
|
1363 |
yield StudyPerson(id_) |
|
|
1364 |
|
|
|
1365 |
@classmethod |
|
|
1366 |
def exists(cls, name, affiliation): |
|
|
1367 |
"""Checks if a person exists |
|
|
1368 |
|
|
|
1369 |
Parameters |
|
|
1370 |
---------- |
|
|
1371 |
name: str |
|
|
1372 |
Name of the person |
|
|
1373 |
affiliation : str |
|
|
1374 |
institution with which the person is affiliated |
|
|
1375 |
|
|
|
1376 |
Returns |
|
|
1377 |
------- |
|
|
1378 |
bool |
|
|
1379 |
True if person exists else false |
|
|
1380 |
""" |
|
|
1381 |
with qdb.sql_connection.TRN: |
|
|
1382 |
sql = """SELECT EXISTS( |
|
|
1383 |
SELECT * FROM qiita.{0} |
|
|
1384 |
WHERE name = %s |
|
|
1385 |
AND affiliation = %s)""".format(cls._table) |
|
|
1386 |
qdb.sql_connection.TRN.add(sql, [name, affiliation]) |
|
|
1387 |
return qdb.sql_connection.TRN.execute_fetchlast() |
|
|
1388 |
|
|
|
1389 |
@classmethod |
|
|
1390 |
def from_name_and_affiliation(cls, name, affiliation): |
|
|
1391 |
"""Gets a StudyPerson object based on the name and affiliation |
|
|
1392 |
|
|
|
1393 |
Parameters |
|
|
1394 |
---------- |
|
|
1395 |
name: str |
|
|
1396 |
Name of the person |
|
|
1397 |
affiliation : str |
|
|
1398 |
institution with which the person is affiliated |
|
|
1399 |
|
|
|
1400 |
Returns |
|
|
1401 |
------- |
|
|
1402 |
StudyPerson |
|
|
1403 |
The StudyPerson for the name and affiliation |
|
|
1404 |
""" |
|
|
1405 |
with qdb.sql_connection.TRN: |
|
|
1406 |
if not cls.exists(name, affiliation): |
|
|
1407 |
raise qdb.exceptions.QiitaDBLookupError( |
|
|
1408 |
'Study person does not exist') |
|
|
1409 |
|
|
|
1410 |
sql = """SELECT study_person_id FROM qiita.{0} |
|
|
1411 |
WHERE name = %s |
|
|
1412 |
AND affiliation = %s""".format(cls._table) |
|
|
1413 |
qdb.sql_connection.TRN.add(sql, [name, affiliation]) |
|
|
1414 |
return cls(qdb.sql_connection.TRN.execute_fetchlast()) |
|
|
1415 |
|
|
|
1416 |
@classmethod |
|
|
1417 |
def create(cls, name, email, affiliation, address=None, phone=None): |
|
|
1418 |
"""Create a StudyPerson object, checking if person already exists. |
|
|
1419 |
|
|
|
1420 |
Parameters |
|
|
1421 |
---------- |
|
|
1422 |
name : str |
|
|
1423 |
name of person |
|
|
1424 |
email : str |
|
|
1425 |
email of person |
|
|
1426 |
affiliation : str |
|
|
1427 |
institution with which the person is affiliated |
|
|
1428 |
address : str, optional |
|
|
1429 |
address of person |
|
|
1430 |
phone : str, optional |
|
|
1431 |
phone number of person |
|
|
1432 |
|
|
|
1433 |
Returns |
|
|
1434 |
------- |
|
|
1435 |
New StudyPerson object |
|
|
1436 |
|
|
|
1437 |
""" |
|
|
1438 |
with qdb.sql_connection.TRN: |
|
|
1439 |
if cls.exists(name, affiliation): |
|
|
1440 |
sql = """SELECT study_person_id |
|
|
1441 |
FROM qiita.{0} |
|
|
1442 |
WHERE name = %s |
|
|
1443 |
AND affiliation = %s""".format(cls._table) |
|
|
1444 |
args = [name, affiliation] |
|
|
1445 |
else: |
|
|
1446 |
sql = """INSERT INTO qiita.{0} (name, email, affiliation, |
|
|
1447 |
address, phone) |
|
|
1448 |
VALUES (%s, %s, %s, %s, %s) |
|
|
1449 |
RETURNING study_person_id""".format(cls._table) |
|
|
1450 |
args = [name, email, affiliation, address, phone] |
|
|
1451 |
|
|
|
1452 |
qdb.sql_connection.TRN.add(sql, args) |
|
|
1453 |
return cls(qdb.sql_connection.TRN.execute_fetchlast()) |
|
|
1454 |
|
|
|
1455 |
@classmethod |
|
|
1456 |
def delete(cls, id_): |
|
|
1457 |
r"""Deletes the StudyPerson from the database |
|
|
1458 |
|
|
|
1459 |
Parameters |
|
|
1460 |
---------- |
|
|
1461 |
id_ : integer |
|
|
1462 |
The object identifier |
|
|
1463 |
|
|
|
1464 |
Raises |
|
|
1465 |
------ |
|
|
1466 |
QiitaDBError |
|
|
1467 |
If the StudyPerson with the given id is attached to any study |
|
|
1468 |
""" |
|
|
1469 |
with qdb.sql_connection.TRN: |
|
|
1470 |
# checking that the id_ exists |
|
|
1471 |
cls(id_) |
|
|
1472 |
|
|
|
1473 |
# Check if the person is attached to any study |
|
|
1474 |
sql = """SELECT EXISTS( |
|
|
1475 |
SELECT * |
|
|
1476 |
FROM qiita.study |
|
|
1477 |
WHERE lab_person_id = %s OR |
|
|
1478 |
principal_investigator_id = %s)""" |
|
|
1479 |
qdb.sql_connection.TRN.add(sql, [id_, id_]) |
|
|
1480 |
if qdb.sql_connection.TRN.execute_fetchlast(): |
|
|
1481 |
sql = """SELECT study_id |
|
|
1482 |
FROM qiita.study |
|
|
1483 |
WHERE {} = %s""" |
|
|
1484 |
cols = ['lab_person_id', 'principal_investigator_id'] |
|
|
1485 |
rel = {} |
|
|
1486 |
for c in cols: |
|
|
1487 |
qdb.sql_connection.TRN.add(sql.format(c), [id_]) |
|
|
1488 |
rel[c] = qdb.sql_connection.TRN.execute_fetchindex() |
|
|
1489 |
raise qdb.exceptions.QiitaDBError( |
|
|
1490 |
'StudyPerson "%s" cannot be deleted because there are ' |
|
|
1491 |
'studies referencing it: %s' % (id_, str(rel))) |
|
|
1492 |
|
|
|
1493 |
sql = "DELETE FROM qiita.study_person WHERE study_person_id = %s" |
|
|
1494 |
qdb.sql_connection.TRN.add(sql, [id_]) |
|
|
1495 |
qdb.sql_connection.TRN.execute() |
|
|
1496 |
|
|
|
1497 |
# Properties |
|
|
1498 |
@property |
|
|
1499 |
def name(self): |
|
|
1500 |
"""Returns the name of the person |
|
|
1501 |
|
|
|
1502 |
Returns |
|
|
1503 |
------- |
|
|
1504 |
str |
|
|
1505 |
Name of person |
|
|
1506 |
""" |
|
|
1507 |
with qdb.sql_connection.TRN: |
|
|
1508 |
sql = """SELECT name FROM qiita.{0} |
|
|
1509 |
WHERE study_person_id = %s""".format(self._table) |
|
|
1510 |
qdb.sql_connection.TRN.add(sql, [self._id]) |
|
|
1511 |
return qdb.sql_connection.TRN.execute_fetchlast() |
|
|
1512 |
|
|
|
1513 |
@property |
|
|
1514 |
def email(self): |
|
|
1515 |
"""Returns the email of the person |
|
|
1516 |
|
|
|
1517 |
Returns |
|
|
1518 |
------- |
|
|
1519 |
str |
|
|
1520 |
Email of person |
|
|
1521 |
""" |
|
|
1522 |
with qdb.sql_connection.TRN: |
|
|
1523 |
sql = """SELECT email FROM qiita.{0} |
|
|
1524 |
WHERE study_person_id = %s""".format(self._table) |
|
|
1525 |
qdb.sql_connection.TRN.add(sql, [self._id]) |
|
|
1526 |
return qdb.sql_connection.TRN.execute_fetchlast() |
|
|
1527 |
|
|
|
1528 |
@property |
|
|
1529 |
def affiliation(self): |
|
|
1530 |
"""Returns the affiliation of the person |
|
|
1531 |
|
|
|
1532 |
Returns |
|
|
1533 |
------- |
|
|
1534 |
str |
|
|
1535 |
Affiliation of person |
|
|
1536 |
""" |
|
|
1537 |
with qdb.sql_connection.TRN: |
|
|
1538 |
sql = """SELECT affiliation FROM qiita.{0} |
|
|
1539 |
WHERE study_person_id = %s""".format(self._table) |
|
|
1540 |
qdb.sql_connection.TRN.add(sql, [self._id]) |
|
|
1541 |
return qdb.sql_connection.TRN.execute_fetchlast() |
|
|
1542 |
|
|
|
1543 |
@property |
|
|
1544 |
def address(self): |
|
|
1545 |
"""Returns the address of the person |
|
|
1546 |
|
|
|
1547 |
Returns |
|
|
1548 |
------- |
|
|
1549 |
str or None |
|
|
1550 |
address or None if no address in database |
|
|
1551 |
""" |
|
|
1552 |
with qdb.sql_connection.TRN: |
|
|
1553 |
sql = """SELECT address FROM qiita.{0} |
|
|
1554 |
WHERE study_person_id = %s""".format(self._table) |
|
|
1555 |
qdb.sql_connection.TRN.add(sql, [self._id]) |
|
|
1556 |
return qdb.sql_connection.TRN.execute_fetchlast() |
|
|
1557 |
|
|
|
1558 |
@address.setter |
|
|
1559 |
def address(self, value): |
|
|
1560 |
"""Set/update the address of the person |
|
|
1561 |
|
|
|
1562 |
Parameters |
|
|
1563 |
---------- |
|
|
1564 |
value : str |
|
|
1565 |
New address for person |
|
|
1566 |
""" |
|
|
1567 |
sql = """UPDATE qiita.{0} SET address = %s |
|
|
1568 |
WHERE study_person_id = %s""".format(self._table) |
|
|
1569 |
qdb.sql_connection.perform_as_transaction(sql, [value, self._id]) |
|
|
1570 |
|
|
|
1571 |
@property |
|
|
1572 |
def phone(self): |
|
|
1573 |
"""Returns the phone number of the person |
|
|
1574 |
|
|
|
1575 |
Returns |
|
|
1576 |
------- |
|
|
1577 |
str or None |
|
|
1578 |
phone or None if no address in database |
|
|
1579 |
""" |
|
|
1580 |
with qdb.sql_connection.TRN: |
|
|
1581 |
sql = """SELECT phone FROM qiita.{0} |
|
|
1582 |
WHERE study_person_id = %s""".format(self._table) |
|
|
1583 |
qdb.sql_connection.TRN.add(sql, [self._id]) |
|
|
1584 |
return qdb.sql_connection.TRN.execute_fetchlast() |
|
|
1585 |
|
|
|
1586 |
@phone.setter |
|
|
1587 |
def phone(self, value): |
|
|
1588 |
"""Set/update the phone number of the person |
|
|
1589 |
|
|
|
1590 |
Parameters |
|
|
1591 |
---------- |
|
|
1592 |
value : str |
|
|
1593 |
New phone number for person |
|
|
1594 |
""" |
|
|
1595 |
sql = """UPDATE qiita.{0} SET phone = %s |
|
|
1596 |
WHERE study_person_id = %s""".format(self._table) |
|
|
1597 |
qdb.sql_connection.perform_as_transaction(sql, [value, self._id]) |