Skip to content

Commit 3b9290e

Browse files
committed
Merge pull request #1593 from squirrelo/admin-actions
Admin actions
2 parents c62daae + 8e24a0a commit 3b9290e

File tree

13 files changed

+490
-153
lines changed

13 files changed

+490
-153
lines changed

qiita_db/test/test_util.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,11 @@ def test_scrub_data_single_quote(self):
697697
"""Correctly removes single quotes from the string"""
698698
self.assertEqual(qdb.util.scrub_data("'quotes'"), "quotes")
699699

700+
def test_get_visibilities(self):
701+
obs = qdb.util.get_visibilities()
702+
exp = ['awaiting_approval', 'sandbox', 'private', 'public']
703+
self.assertEqual(obs, exp)
704+
700705
def test_infer_status(self):
701706
obs = qdb.util.infer_status([])
702707
self.assertEqual(obs, 'sandbox')

qiita_db/util.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
convert_from_id
2626
convert_to_id
2727
get_environmental_packages
28+
get_visibilities
2829
purge_filepaths
2930
move_filepaths_to_upload_folder
3031
move_upload_files_to_trash
@@ -979,6 +980,19 @@ def get_environmental_packages():
979980
return qdb.sql_connection.TRN.execute_fetchindex()
980981

981982

983+
def get_visibilities():
984+
"""Get the list of available visibilities for artifacts
985+
986+
Returns
987+
-------
988+
list of str
989+
The available visibilities
990+
"""
991+
with qdb.sql_connection.TRN:
992+
qdb.sql_connection.TRN.add("SELECT visibility FROM qiita.visibility")
993+
return qdb.sql_connection.TRN.execute_fetchflatten()
994+
995+
982996
def get_timeseries_types():
983997
"""Get the list of available timeseries types
984998

qiita_pet/handlers/api_proxy/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
ena_ontology_get_req, prep_template_samples_get_req)
2525
from .studies import (
2626
data_types_get_req, study_get_req, study_prep_get_req, study_delete_req)
27-
from .artifact import artifact_graph_get_req
27+
from .artifact import (artifact_graph_get_req, artifact_get_req,
28+
artifact_status_put_req, artifact_delete_req)
2829

2930
__all__ = ['prep_template_summary_get_req', 'sample_template_post_req',
3031
'sample_template_put_req', 'data_types_get_req',
@@ -33,7 +34,8 @@
3334
'prep_template_summary_get_req', 'prep_template_post_req',
3435
'prep_template_put_req', 'prep_template_delete_req',
3536
'prep_template_graph_get_req', 'prep_template_filepaths_get_req',
36-
'artifact_get_graph', 'prep_template_get_req', 'study_delete_req',
37+
'artifact_get_req', 'artifact_status_put_req',
38+
'artifact_delete_req', 'prep_template_get_req', 'study_delete_req',
3739
'study_prep_get_req', 'sample_template_get_req',
3840
'artifact_graph_get_req', 'ena_ontology_get_req',
3941
'sample_template_meta_cats_get_req',

qiita_pet/handlers/api_proxy/artifact.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
# The full license is in the file LICENSE, distributed with this software.
77
# -----------------------------------------------------------------------------
88
from qiita_db.artifact import Artifact
9+
from qiita_db.user import User
10+
from qiita_db.util import get_visibilities
11+
from qiita_db.exceptions import QiitaDBArtifactDeletionError
12+
from qiita_core.qiita_settings import qiita_config
913
from qiita_pet.handlers.api_proxy.util import check_access
1014

1115

@@ -53,3 +57,124 @@ def artifact_graph_get_req(artifact_id, direction, user_id):
5357
'node_labels': node_labels,
5458
'status': 'success',
5559
'message': ''}
60+
61+
62+
def artifact_get_req(artifact_id, user_id):
63+
"""Get information about the artifact
64+
65+
Parameters
66+
----------
67+
artifact_id : int
68+
Artifact being acted on
69+
user_id : str
70+
The user requesting the action
71+
72+
Returns
73+
-------
74+
dict
75+
information about the artifact
76+
"""
77+
pd = Artifact(int(artifact_id))
78+
access_error = check_access(pd.study.id, user_id)
79+
if access_error:
80+
return access_error
81+
82+
can_submit_to_ebi = pd.can_be_submitted_to_ebi
83+
ebi_run_accessions = pd.ebi_run_accessions if can_submit_to_ebi else None
84+
85+
can_submit_to_vamps = pd.can_be_submitted_to_vamps
86+
is_submitted_to_vamps = pd.is_submitted_to_vamps if can_submit_to_vamps \
87+
else False
88+
89+
return {
90+
'timestamp': pd.timestamp,
91+
'processing_parameters': pd.processing_parameters,
92+
'visibility': pd.visibility,
93+
'artifact_type': pd.artifact_type,
94+
'data_type': pd.data_type,
95+
'can_be_submitted_to_ebi': can_submit_to_ebi,
96+
'can_be_submitted_to_vamps': can_submit_to_vamps,
97+
'is_submitted_to_vamps': is_submitted_to_vamps,
98+
'filepaths': pd.filepaths,
99+
'parents': [a.id for a in pd.parents],
100+
'prep_templates': [p.id for p in pd.prep_templates],
101+
'ebi_run_accessions': ebi_run_accessions,
102+
'study': pd.study.id}
103+
104+
105+
def artifact_delete_req(artifact_id, user_id):
106+
"""Deletes the artifact
107+
108+
Parameters
109+
----------
110+
artifact_id : int
111+
Artifact being acted on
112+
user_id : str
113+
The user requesting the action
114+
115+
Returns
116+
-------
117+
dict
118+
Status of action, in the form {'status': status, 'message': msg}
119+
status: status of the action, either success or error
120+
message: Human readable message for status
121+
"""
122+
pd = Artifact(int(artifact_id))
123+
access_error = check_access(pd.study.id, user_id)
124+
if access_error:
125+
return access_error
126+
try:
127+
Artifact.delete(int(artifact_id))
128+
except QiitaDBArtifactDeletionError as e:
129+
return {'status': 'error',
130+
'message': str(e)}
131+
return {'status': 'success',
132+
'message': ''}
133+
134+
135+
def artifact_status_put_req(artifact_id, user_id, visibility):
136+
"""Set the status of the artifact given
137+
138+
Parameters
139+
----------
140+
artifact_id : int
141+
Artifact being acted on
142+
user_id : str
143+
The user requesting the action
144+
visibility : {'sandbox', 'awaiting_approval', 'private', 'public'}
145+
What to change the visibility to
146+
147+
Returns
148+
-------
149+
dict
150+
Status of action, in the form {'status': status, 'message': msg}
151+
status: status of the action, either success or error
152+
message: Human readable message for status
153+
"""
154+
if visibility not in get_visibilities():
155+
return {'status': 'error',
156+
'message': 'Unknown visiblity value: %s' % visibility}
157+
158+
pd = Artifact(int(artifact_id))
159+
access_error = check_access(pd.study.id, user_id)
160+
if access_error:
161+
return access_error
162+
user = User(str(user_id))
163+
status = 'success'
164+
msg = 'Artifact visibility changed to %s' % visibility
165+
# Set the approval to private if needs approval and admin
166+
if visibility == 'private':
167+
if not qiita_config.require_approval:
168+
pd.visibility = 'private'
169+
# Set the approval to private if approval not required
170+
elif user.level == 'admin':
171+
pd.visibility = 'private'
172+
# Trying to set approval without admin privileges
173+
else:
174+
status = 'error'
175+
msg = 'User does not have permissions to approve change'
176+
else:
177+
pd.visibility = visibility
178+
179+
return {'status': status,
180+
'message': msg}

qiita_pet/handlers/api_proxy/tests/test_artifact.py

Lines changed: 101 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,103 @@
66
# The full license is in the file LICENSE, distributed with this software.
77
# -----------------------------------------------------------------------------
88
from unittest import TestCase, main
9+
from datetime import datetime
10+
from os.path import join
911

10-
from qiita_pet.handlers.api_proxy.artifact import artifact_graph_get_req
12+
from qiita_core.util import qiita_test_checker
13+
from qiita_core.qiita_settings import qiita_config
14+
from qiita_db.artifact import Artifact
15+
from qiita_db.exceptions import QiitaDBUnknownIDError
16+
from qiita_pet.handlers.api_proxy.artifact import (
17+
artifact_get_req, artifact_status_put_req, artifact_graph_get_req,
18+
artifact_delete_req)
1119

1220

21+
@qiita_test_checker()
1322
class TestArtifactAPI(TestCase):
23+
def tearDown(self):
24+
Artifact(1).visibility = 'private'
25+
26+
def test_artifact_get_req(self):
27+
obs = artifact_get_req(1, 'test@foo.bar')
28+
exp = {'is_submitted_to_vamps': False,
29+
'data_type': '18S',
30+
'can_be_submitted_to_vamps': False,
31+
'can_be_submitted_to_ebi': False,
32+
'timestamp': datetime(2012, 10, 1, 9, 30, 27),
33+
'prep_templates': [1],
34+
'visibility': 'private',
35+
'study': 1,
36+
'processing_parameters': None,
37+
'ebi_run_accessions': None,
38+
'parents': [],
39+
'filepaths': [
40+
(1, join(qiita_config.base_data_dir,
41+
'raw_data/1_s_G1_L001_sequences.fastq.gz'),
42+
'raw_forward_seqs'),
43+
(2, join(qiita_config.base_data_dir,
44+
'raw_data/1_s_G1_L001_sequences_barcodes.'
45+
'fastq.gz'),
46+
'raw_barcodes')],
47+
'artifact_type': 'FASTQ'}
48+
self.assertEqual(obs, exp)
49+
50+
def test_artifact_get_req_no_access(self):
51+
obs = artifact_get_req(1, 'demo@microbio.me')
52+
exp = {'status': 'error',
53+
'message': 'User does not have access to study'}
54+
self.assertEqual(obs, exp)
55+
56+
def test_artifact_delete_req(self):
57+
obs = artifact_delete_req(3, 'test@foo.bar')
58+
exp = {'status': 'success', 'message': ''}
59+
self.assertEqual(obs, exp)
60+
61+
with self.assertRaises(QiitaDBUnknownIDError):
62+
Artifact(3)
63+
64+
def test_artifact_delete_req_error(self):
65+
obs = artifact_delete_req(1, 'test@foo.bar')
66+
exp = {'status': 'error',
67+
'message': 'Cannot delete artifact 1: it has children: 2, 3'}
68+
self.assertEqual(obs, exp)
69+
70+
def test_artifact_delete_req_no_access(self):
71+
obs = artifact_delete_req(3, 'demo@microbio.me')
72+
exp = {'status': 'error',
73+
'message': 'User does not have access to study'}
74+
self.assertEqual(obs, exp)
75+
76+
def test_artifact_status_put_req(self):
77+
obs = artifact_status_put_req(1, 'test@foo.bar', 'sandbox')
78+
exp = {'status': 'success',
79+
'message': 'Artifact visibility changed to sandbox'}
80+
self.assertEqual(obs, exp)
81+
82+
def test_artifact_status_put_req_private(self):
83+
obs = artifact_status_put_req(1, 'admin@foo.bar', 'private')
84+
exp = {'status': 'success',
85+
'message': 'Artifact visibility changed to private'}
86+
self.assertEqual(obs, exp)
87+
88+
def test_artifact_status_put_req_private_bad_permissions(self):
89+
obs = artifact_status_put_req(1, 'test@foo.bar', 'private')
90+
exp = {'status': 'error',
91+
'message': 'User does not have permissions to approve change'}
92+
self.assertEqual(obs, exp)
93+
94+
def test_artifact_status_put_req_no_access(self):
95+
obs = artifact_status_put_req(1, 'demo@microbio.me', 'sandbox')
96+
exp = {'status': 'error',
97+
'message': 'User does not have access to study'}
98+
self.assertEqual(obs, exp)
99+
100+
def test_artifact_status_put_req_unknown_status(self):
101+
obs = artifact_status_put_req(1, 'test@foo.bar', 'BADSTAT')
102+
exp = {'status': 'error',
103+
'message': 'Unknown visiblity value: BADSTAT'}
104+
self.assertEqual(obs, exp)
105+
14106
def test_artifact_graph_get_req_ancestors(self):
15107
obs = artifact_graph_get_req(1, 'ancestors', 'test@foo.bar')
16108
exp = {'status': 'success',
@@ -23,25 +115,24 @@ def test_artifact_graph_get_req_descendants(self):
23115
obs = artifact_graph_get_req(1, 'descendants', 'test@foo.bar')
24116
exp = {'status': 'success',
25117
'message': '',
26-
'edge_list': [(1, 3), (1, 2), (2, 4)],
27118
'node_labels': [(1, 'Raw data 1 - FASTQ'),
28119
(3, 'Demultiplexed 2 - Demultiplexed'),
29120
(2, 'Demultiplexed 1 - Demultiplexed'),
30-
(4, 'BIOM - BIOM')]}
31-
self.assertEqual(obs, exp)
32-
33-
def test_artifact_graph_get_req_bad(self):
34-
obs = artifact_graph_get_req(1, 'UNKNOWN', 'test@foo.bar')
35-
exp = {'status': 'error',
36-
'message': 'Unknown directon UNKNOWN'}
121+
(4, 'BIOM - BIOM')],
122+
'edge_list': [(1, 3), (1, 2), (2, 4)]}
37123
self.assertEqual(obs, exp)
38124

39125
def test_artifact_graph_get_req_no_access(self):
40-
obs = artifact_graph_get_req(1, 'descendants', 'demo@microbio.me')
126+
obs = artifact_graph_get_req(1, 'ancestors', 'demo@microbio.me')
41127
exp = {'status': 'error',
42128
'message': 'User does not have access to study'}
43129
self.assertEqual(obs, exp)
44130

131+
def test_artifact_graph_get_req_bad_direction(self):
132+
obs = artifact_graph_get_req(1, 'WRONG', 'test@foo.bar')
133+
exp = {'status': 'error', 'message': 'Unknown directon WRONG'}
134+
self.assertEqual(obs, exp)
135+
45136

46137
if __name__ == "__main__":
47138
main()

0 commit comments

Comments
 (0)