[879b32]: / qiita_pet / handlers / api_proxy / artifact.py

Download this file

353 lines (299 with data), 12.0 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# -----------------------------------------------------------------------------
# Copyright (c) 2014--, The Qiita Development Team.
#
# Distributed under the terms of the BSD 3-clause License.
#
# The full license is in the file LICENSE, distributed with this software.
# -----------------------------------------------------------------------------
from os.path import join
from functools import partial
from json import dumps
from itertools import chain
from qiita_core.util import execute_as_transaction
from qiita_core.qiita_settings import qiita_config, r_client
from qiita_pet.handlers.api_proxy.util import check_access, check_fp
from qiita_db.artifact import Artifact
from qiita_db.user import User
from qiita_db.metadata_template.prep_template import PrepTemplate
from qiita_db.util import (
get_mountpoint, get_visibilities, get_artifacts_information)
from qiita_db.software import Command, Parameters, Software
from qiita_db.processing_job import ProcessingJob
from qiita_db.exceptions import QiitaDBError
from qiita_db.logger import LogEntry
PREP_TEMPLATE_KEY_FORMAT = 'prep_template_%s'
def artifact_get_req(user_id, artifact_id):
"""Returns all base information about an artifact
Parameters
----------
user_id : str
user making the request
artifact_id : int or str coercable to int
Atrtifact to get information for
Returns
-------
dict of objects
A dictionary containing the artifact information
{'status': status,
'message': message,
'artifact': {info key: val, ...}}
"""
artifact_id = int(artifact_id)
artifact = Artifact(artifact_id)
access_error = check_access(artifact.study.id, user_id)
if access_error:
return access_error
can_submit_ebi = artifact.can_be_submitted_to_ebi
ebi_run_accessions = (artifact.ebi_run_accessions
if can_submit_ebi else None)
can_submit_vamps = artifact.can_be_submitted_to_vamps
is_submitted_vamps = (artifact.is_submitted_to_vamps
if can_submit_vamps else False)
return {'id': artifact_id,
'timestamp': artifact.timestamp,
'processing_parameters': artifact.processing_parameters,
'visibility': artifact.visibility,
'type': artifact.artifact_type,
'data_type': artifact.data_type,
'filepaths': artifact.filepaths,
'parents': [a.id for a in artifact.parents],
'study': artifact.study.id if artifact.study else None,
'can_submit_ebi': can_submit_ebi,
'ebi_run_accessions': ebi_run_accessions,
'can_submit_vamps': can_submit_vamps,
'is_submitted_vamps': is_submitted_vamps}
@execute_as_transaction
def artifact_get_prep_req(user_id, artifact_ids):
"""Returns all prep info sample ids for the given artifact_ids
Parameters
----------
user_id : str
user making the request
artifact_ids : list of int
list of artifact ids
Returns
-------
dict of objects
A dictionary containing the artifact information
{'status': status,
'message': message,
'data': {artifact_id: [prep info sample ids]}
"""
samples = {}
for aid in sorted(artifact_ids):
artifact = Artifact(aid)
access_error = check_access(artifact.study.id, user_id)
if access_error:
return access_error
samples[aid] = list(chain(
*[sorted(pt.keys()) for pt in Artifact(aid).prep_templates]))
return {'status': 'success', 'msg': '', 'data': samples}
@execute_as_transaction
def artifact_get_info(user_id, artifact_ids, only_biom=True):
"""Returns all artifact info for the given artifact_ids
Parameters
----------
user_id : str
user making the request
artifact_ids : list of int
list of artifact ids
only_biom : bool
If true only the biom artifacts are retrieved
Returns
-------
dict of objects
A dictionary containing the artifact information
{'status': status,
'message': message,
'data': {artifact_id: {biom_info}}
"""
artifact_info = {}
artifact_info = get_artifacts_information(artifact_ids, only_biom)
return {'status': 'success', 'msg': '', 'data': artifact_info}
@execute_as_transaction
def artifact_post_req(user_id, filepaths, artifact_type, name,
prep_template_id, artifact_id=None):
"""Creates the initial artifact for the prep template
Parameters
----------
user_id : str
User adding the atrifact
filepaths : dict of str
Comma-separated list of files to attach to the artifact,
keyed by file type
artifact_type : str
The type of the artifact
name : str
Name to give the artifact
prep_template_id : int or str castable to int
Prep template to attach the artifact to
artifact_id : int or str castable to int, optional
The id of the imported artifact
Returns
-------
dict of objects
A dictionary containing the new artifact ID
{'status': status,
'message': message,
'artifact': id}
"""
prep_template_id = int(prep_template_id)
prep = PrepTemplate(prep_template_id)
study_id = prep.study_id
# First check if the user has access to the study
access_error = check_access(study_id, user_id)
if access_error:
return access_error
user = User(user_id)
if artifact_id:
# if the artifact id has been provided, import the artifact
qiita_plugin = Software.from_name_and_version('Qiita', 'alpha')
cmd = qiita_plugin.get_command('copy_artifact')
params = Parameters.load(cmd, values_dict={'artifact': artifact_id,
'prep_template': prep.id})
job = ProcessingJob.create(user, params, True)
else:
uploads_path = get_mountpoint('uploads')[0][1]
path_builder = partial(join, uploads_path, str(study_id))
cleaned_filepaths = {}
for ftype, file_list in filepaths.items():
# JavaScript sends us this list as a comma-separated list
for fp in file_list.split(','):
# JavaScript will send this value as an empty string if the
# list of files was empty. In such case, the split will
# generate a single element containing the empty string. Check
# for that case here and, if fp is not the empty string,
# proceed to check if the file exists
if fp:
# Check if filepath being passed exists for study
full_fp = path_builder(fp)
exists = check_fp(study_id, full_fp)
if exists['status'] != 'success':
return {'status': 'error',
'message': 'File does not exist: %s' % fp}
if ftype not in cleaned_filepaths:
cleaned_filepaths[ftype] = []
cleaned_filepaths[ftype].append(full_fp)
# This should never happen, but it doesn't hurt to actually have
# a explicit check, in case there is something odd with the JS
if not cleaned_filepaths:
return {'status': 'error',
'message': "Can't create artifact, no files provided."}
# This try/except will catch the case when the plugins are not
# activated so there is no Validate for the given artifact_type
try:
command = Command.get_validator(artifact_type)
except QiitaDBError as e:
return {'status': 'error', 'message': str(e)}
job = ProcessingJob.create(
user,
Parameters.load(command, values_dict={
'template': prep_template_id,
'files': dumps(cleaned_filepaths),
'artifact_type': artifact_type,
'name': name,
'analysis': None,
}), True)
# Submit the job
job.submit()
r_client.set(PREP_TEMPLATE_KEY_FORMAT % prep.id,
dumps({'job_id': job.id, 'is_qiita_job': True}))
return {'status': 'success', 'message': ''}
def artifact_types_get_req():
"""Gets artifact types and descriptions available
Returns
-------
dict of objects
{'status': status,
'message': message,
'types': [[str, str], ...]}
types holds type and description of the artifact type, in the form
[[artifact_type, description], ...]
"""
return {'status': 'success',
'message': '',
'types': Artifact.types()}
def artifact_graph_get_req(artifact_id, direction, user_id):
"""Creates graphs of ancestor or descendant artifacts from given one
Parameters
----------
artifact_id : int
Artifact ID to get graph for
direction : {'ancestors', 'descendants'}
What direction to get the graph in
Returns
-------
dict of lists of tuples
A dictionary containing the edge list representation of the graph,
and the node labels. Formatted as:
{'status': status,
'message': message,
'edge_list': [(0, 1), (0, 2)...],
'node_labels': [(0, 'label0'), (1, 'label1'), ...]}
Notes
-----
Nodes are identified by the corresponding Artifact ID.
"""
access_error = check_access(Artifact(artifact_id).study.id, user_id)
if access_error:
return access_error
if direction == 'descendants':
G = Artifact(int(artifact_id)).descendants
elif direction == 'ancestors':
G = Artifact(int(artifact_id)).ancestors
else:
return {
'status': 'error',
'message': 'Unknown directon %s' % direction
}
node_labels = [(n.id, ' - '.join([n.name, n.artifact_type]))
for n in G.nodes()]
return {'edge_list': [(n.id, m.id) for n, m in G.edges()],
'node_labels': node_labels,
'status': 'success',
'message': ''}
def artifact_status_put_req(artifact_id, user_id, visibility):
"""Set the status of the artifact given
Parameters
----------
artifact_id : int
Artifact being acted on
user_id : str
The user requesting the action
visibility : {'sandbox', 'awaiting_approval', 'private', 'public'}
What to change the visibility to
Returns
-------
dict
Status of action, in the form {'status': status, 'message': msg}
status: status of the action, either success or error
message: Human readable message for status
"""
if visibility not in get_visibilities():
return {'status': 'error',
'message': 'Unknown visibility value: %s' % visibility}
pd = Artifact(int(artifact_id))
sid = pd.study.id
access_error = check_access(sid, user_id)
if access_error:
return access_error
user = User(str(user_id))
status = 'success'
msg = 'Artifact visibility changed to %s' % visibility
# Set the approval to private if needs approval and admin
if visibility == 'private':
if not qiita_config.require_approval:
pd.visibility = 'private'
# Set the approval to private if approval not required
elif user.level == 'admin':
pd.visibility = 'private'
# Trying to set approval without admin privileges
else:
status = 'error'
msg = 'User does not have permissions to approve change'
else:
pd.visibility = visibility
LogEntry.create('Warning', '%s changed artifact %s (study %d) to %s' % (
user_id, artifact_id, sid, visibility))
return {'status': status,
'message': msg}