Skip to content

Commit 8492ecb

Browse files
committed
Merge pull request #1768 from squirrelo/share-analyses
Share analyses
2 parents fc91318 + 5ad25f0 commit 8492ecb

File tree

13 files changed

+221
-67
lines changed

13 files changed

+221
-67
lines changed

qiita_db/analysis.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -683,13 +683,11 @@ def share(self, user):
683683
user: User object
684684
The user to share the analysis with
685685
"""
686-
with qdb.sql_connection.TRN:
687-
self._lock_check()
688-
689-
# Make sure the analysis is not already shared with the given user
690-
if user.id in self.shared_with:
691-
return
686+
# Make sure the analysis is not already shared with the given user
687+
if user.id == self.owner or user.id in self.shared_with:
688+
return
692689

690+
with qdb.sql_connection.TRN:
693691
sql = """INSERT INTO qiita.analysis_users (analysis_id, email)
694692
VALUES (%s, %s)"""
695693
qdb.sql_connection.TRN.add(sql, [self._id, user.id])
@@ -704,8 +702,6 @@ def unshare(self, user):
704702
The user to unshare the analysis with
705703
"""
706704
with qdb.sql_connection.TRN:
707-
self._lock_check()
708-
709705
sql = """DELETE FROM qiita.analysis_users
710706
WHERE analysis_id = %s AND email = %s"""
711707
qdb.sql_connection.TRN.add(sql, [self._id, user.id])

qiita_db/support_files/populate_test_db.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ INSERT INTO qiita.job (data_type_id, job_status_id, command_id, options, input_f
427427
INSERT INTO qiita.job_results_filepath (job_id, filepath_id) VALUES (1, 13), (2, 14);
428428

429429
-- Insert Analysis
430-
INSERT INTO qiita.analysis (email, name, description, analysis_status_id, pmid) VALUES ('test@foo.bar', 'SomeAnalysis', 'A test analysis', 1, '121112'), ('test@foo.bar', 'SomeSecondAnalysis', 'Another test analysis', 1, '22221112');
430+
INSERT INTO qiita.analysis (email, name, description, analysis_status_id, pmid) VALUES ('test@foo.bar', 'SomeAnalysis', 'A test analysis', 1, '121112'), ('admin@foo.bar', 'SomeSecondAnalysis', 'Another test analysis', 1, '22221112');
431431
INSERT INTO qiita.analysis_portal (analysis_id, portal_type_id) VALUES (1, 1), (2, 1);
432432
-- Insert Analysis Workflow
433433
INSERT INTO qiita.analysis_workflow (analysis_id, step) VALUES (1, 3), (2, 3);

qiita_db/test/test_user.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ def test_get_shared_studies(self):
261261
def test_get_private_analyses(self):
262262
user = qdb.user.User('test@foo.bar')
263263
qiita_config.portal = "QIITA"
264-
exp = {qdb.analysis.Analysis(1), qdb.analysis.Analysis(2)}
264+
exp = {qdb.analysis.Analysis(1)}
265265
self.assertEqual(user.private_analyses, exp)
266266

267267
qiita_config.portal = "EMP"

qiita_pet/handlers/analysis_handlers.py

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,22 @@
1717
from functools import partial
1818

1919
from tornado.web import authenticated, HTTPError, StaticFileHandler
20+
from tornado.gen import coroutine, Task
2021
from moi import ctx_default, r_client
2122
from moi.job import submit
2223
from moi.group import get_id_from_user, create_info
2324

2425
from qiita_pet.util import is_localhost
2526
from qiita_pet.handlers.base_handlers import BaseHandler
26-
from qiita_pet.handlers.util import download_link_or_path
27+
from qiita_pet.handlers.util import (
28+
download_link_or_path, get_shared_links)
2729
from qiita_pet.exceptions import QiitaPetAuthorizationError
2830
from qiita_ware.dispatchable import run_analysis
2931
from qiita_db.analysis import Analysis
3032
from qiita_db.artifact import Artifact
3133
from qiita_db.job import Command
32-
from qiita_db.util import (get_db_files_base_dir,
34+
from qiita_db.user import User
35+
from qiita_db.util import (get_db_files_base_dir, add_message,
3336
check_access_to_analysis_result,
3437
filepath_ids_to_rel_paths, get_filepath_id)
3538
from qiita_db.exceptions import QiitaDBUnknownIDError
@@ -175,10 +178,13 @@ def get(self, analysis_id):
175178
data_type = proc_data.data_type
176179
dropped[data_type].append((proc_data.study.title, len(samples),
177180
', '.join(samples)))
181+
share_access = (self.current_user.id in analysis.shared_with or
182+
self.current_user.id == analysis.owner)
178183

179184
self.render("analysis_results.html", analysis_id=analysis_id,
180185
jobres=jobres, aname=analysis.name, dropped=dropped,
181-
basefolder=get_db_files_base_dir())
186+
basefolder=get_db_files_base_dir(),
187+
share_access=share_access)
182188

183189
@authenticated
184190
@execute_as_transaction
@@ -354,3 +360,49 @@ class AnalysisSummaryAJAX(BaseHandler):
354360
def get(self):
355361
info = self.current_user.default_analysis.summary_data()
356362
self.write(dumps(info))
363+
364+
365+
class ShareAnalysisAJAX(BaseHandler):
366+
@execute_as_transaction
367+
def _get_shared_for_study(self, analysis, callback):
368+
shared_links = get_shared_links(analysis)
369+
users = [u.email for u in analysis.shared_with]
370+
callback((users, shared_links))
371+
372+
@execute_as_transaction
373+
def _share(self, analysis, user, callback):
374+
user = User(user)
375+
add_message('Analysis <a href="/analysis/results/%d">\'%s\'</a> '
376+
'has been shared with you.' %
377+
(analysis.id, analysis.name), [user])
378+
callback(analysis.share(user))
379+
380+
@execute_as_transaction
381+
def _unshare(self, analysis, user, callback):
382+
user = User(user)
383+
add_message('Analysis \'%s\' has been unshared from you.' %
384+
analysis.name, [user])
385+
callback(analysis.unshare(user))
386+
387+
@authenticated
388+
@coroutine
389+
@execute_as_transaction
390+
def get(self):
391+
analysis_id = int(self.get_argument('id'))
392+
analysis = Analysis(analysis_id)
393+
if self.current_user != analysis.owner:
394+
raise HTTPError(403, 'User %s does not have permissions to share '
395+
'analysis %s' % (
396+
self.current_user.id, analysis.id))
397+
398+
selected = self.get_argument('selected', None)
399+
deselected = self.get_argument('deselected', None)
400+
401+
if selected is not None:
402+
yield Task(self._share, analysis, selected)
403+
if deselected is not None:
404+
yield Task(self._unshare, analysis, deselected)
405+
406+
users, links = yield Task(self._get_shared_for_study, analysis)
407+
408+
self.write(dumps({'users': users, 'links': links}))

qiita_pet/handlers/study_handlers/listing_handlers.py

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,28 +23,13 @@
2323
from qiita_db.logger import LogEntry
2424
from qiita_db.exceptions import QiitaDBIncompatibleDatatypeError
2525
from qiita_db.reference import Reference
26-
from qiita_db.util import get_pubmed_ids_from_dois
26+
from qiita_db.util import get_pubmed_ids_from_dois, add_message
2727
from qiita_core.exceptions import IncompetentQiitaDeveloperError
2828
from qiita_core.util import execute_as_transaction
2929
from qiita_pet.handlers.base_handlers import BaseHandler
3030
from qiita_pet.handlers.util import (
31-
study_person_linkifier, doi_linkifier, pubmed_linkifier, check_access)
32-
33-
34-
@execute_as_transaction
35-
def _get_shared_links_for_study(study):
36-
shared = []
37-
for person in study.shared_with:
38-
name = person.info['name']
39-
email = person.email
40-
# Name is optional, so default to email if non existant
41-
if name:
42-
shared.append(study_person_linkifier(
43-
(email, name)))
44-
else:
45-
shared.append(study_person_linkifier(
46-
(email, email)))
47-
return ", ".join(shared)
31+
study_person_linkifier, doi_linkifier, pubmed_linkifier, check_access,
32+
get_shared_links)
4833

4934

5035
@execute_as_transaction
@@ -83,7 +68,7 @@ def _build_single_study_info(study, info, study_proc, proc_samples):
8368
info['pmid'] = ""
8469
if info["number_samples_collected"] is None:
8570
info["number_samples_collected"] = 0
86-
info["shared"] = _get_shared_links_for_study(study)
71+
info["shared"] = get_shared_links(study)
8772
# raw data is any artifact that is not Demultiplexed or BIOM
8873

8974
info["num_raw_data"] = len([a for a in study.artifacts()
@@ -258,25 +243,30 @@ def get(self):
258243
class ShareStudyAJAX(BaseHandler):
259244
@execute_as_transaction
260245
def _get_shared_for_study(self, study, callback):
261-
shared_links = _get_shared_links_for_study(study)
246+
shared_links = get_shared_links(study)
262247
users = [u.email for u in study.shared_with]
263248
callback((users, shared_links))
264249

265250
@execute_as_transaction
266251
def _share(self, study, user, callback):
267252
user = User(user)
253+
add_message('Study <a href="/study/description/%d">\'%s\'</a> '
254+
'has been shared with you.' %
255+
(study.id, study.title), [user])
268256
callback(study.share(user))
269257

270258
@execute_as_transaction
271259
def _unshare(self, study, user, callback):
272260
user = User(user)
261+
add_message('Study \'%s\' has been unshared from you.' %
262+
study.title, [user])
273263
callback(study.unshare(user))
274264

275265
@authenticated
276266
@coroutine
277267
@execute_as_transaction
278268
def get(self):
279-
study_id = int(self.get_argument('study_id'))
269+
study_id = int(self.get_argument('id'))
280270
study = Study(study_id)
281271
check_access(self.current_user, study, no_public=True,
282272
raise_error=True)

qiita_pet/handlers/study_handlers/tests/test_listing_handlers.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818
from qiita_db.user import User
1919
from qiita_pet.test.tornado_test_base import TestHandlerBase
2020
from qiita_pet.handlers.study_handlers.listing_handlers import (
21-
_get_shared_links_for_study, _build_study_info, _build_single_study_info,
22-
_build_single_proc_data_info)
21+
_build_study_info, _build_single_study_info, _build_single_proc_data_info)
2322
from qiita_pet.handlers.base_handlers import BaseHandler
2423

2524

@@ -135,11 +134,6 @@ def setUp(self):
135134
}
136135
self.exp = [self.single_exp]
137136

138-
def test_get_shared_links_for_study(self):
139-
obs = _get_shared_links_for_study(Study(1))
140-
exp = '<a target="_blank" href="mailto:shared@foo.bar">Shared</a>'
141-
self.assertEqual(obs, exp)
142-
143137
def test_build_single_study_info(self):
144138
study_proc = {1: {'18S': [4, 5, 6]}}
145139
proc_samples = {4: self.proc_data_exp[0]['samples'],
@@ -237,18 +231,23 @@ class TestShareStudyAjax(TestHandlerBase):
237231
def test_get_deselected(self):
238232
s = Study(1)
239233
u = User('shared@foo.bar')
240-
args = {'deselected': u.id, 'study_id': s.id}
234+
args = {'deselected': u.id, 'id': s.id}
241235
self.assertEqual(s.shared_with, [u])
242236
response = self.get('/study/sharing/', args)
243237
self.assertEqual(response.code, 200)
244238
exp = {'users': [], 'links': ''}
245239
self.assertEqual(loads(response.body), exp)
246240
self.assertEqual(s.shared_with, [])
247241

242+
# Make sure unshared message added to the system
243+
self.assertEqual('Study \'Identification of the Microbiomes for '
244+
'Cannabis Soils\' has been unshared from you.',
245+
u.messages()[0][1])
246+
248247
def test_get_selected(self):
249248
s = Study(1)
250249
u = User('admin@foo.bar')
251-
args = {'selected': u.id, 'study_id': s.id}
250+
args = {'selected': u.id, 'id': s.id}
252251
response = self.get('/study/sharing/', args)
253252
self.assertEqual(response.code, 200)
254253
exp = {
@@ -259,6 +258,12 @@ def test_get_selected(self):
259258
self.assertEqual(loads(response.body), exp)
260259
self.assertEqual(s.shared_with, [User('shared@foo.bar'), u])
261260

261+
# Make sure shared message added to the system
262+
self.assertEqual('Study <a href="/study/description/1">'
263+
'\'Identification of the Microbiomes for Cannabis '
264+
'Soils\'</a> has been shared with you.',
265+
u.messages()[0][1])
266+
262267
def test_get_no_access(self):
263268
# Create a new study belonging to the 'shared' user, so 'test' doesn't
264269
# have access
@@ -275,7 +280,7 @@ def test_get_no_access(self):
275280
s = Study.create(u, 'test_study', efo=[1], info=info)
276281
self.assertEqual(s.shared_with, [])
277282

278-
args = {'selected': 'test@foo.bar', 'study_id': s.id}
283+
args = {'selected': 'test@foo.bar', 'id': s.id}
279284
response = self.get('/study/sharing/', args)
280285
self.assertEqual(response.code, 403)
281286
self.assertEqual(s.shared_with, [])

qiita_pet/handlers/util.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,32 @@ def to_int(value):
8989
except ValueError:
9090
raise HTTPError(400, "%s cannot be converted to an integer" % value)
9191
return res
92+
93+
94+
@execute_as_transaction
95+
def get_shared_links(obj):
96+
"""Creates email links for the users obj is shared with
97+
98+
Parameters
99+
----------
100+
obj : QiitaObject
101+
A qiita object with a 'shared_with' property that returns a list of
102+
User objects
103+
104+
Returns
105+
-------
106+
str
107+
Email links for each person obj is shared with
108+
"""
109+
shared = []
110+
for person in obj.shared_with:
111+
name = person.info['name']
112+
email = person.email
113+
# Name is optional, so default to email if non existant
114+
if name:
115+
shared.append(study_person_linkifier(
116+
(email, name)))
117+
else:
118+
shared.append(study_person_linkifier(
119+
(email, email)))
120+
return ", ".join(shared)

qiita_pet/static/js/sharing.js

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
var current_study = null;
2-
31
$(document).ready(function () {
42
$('#shares-select').select2({
53
ajax: {
@@ -18,18 +16,18 @@ $(document).ready(function () {
1816
});
1917

2018
$('#shares-select').on("select2:select", function (e) {
21-
update_share({selected: e.params.data.text});
19+
update_share(e.target.classList[0], {selected: e.params.data.text});
2220
});
2321

2422
$('#shares-select').on("select2:unselect", function (e) {
25-
update_share({deselected: e.params.data.text});
23+
update_share(e.target.classList[0], {deselected: e.params.data.text});
2624
});
2725
});
2826

29-
function modify_sharing(study_id) {
30-
var shared_list;
31-
current_study = study_id;
32-
$.get('/study/sharing/', {study_id: study_id})
27+
function modify_sharing(share_type, id) {
28+
var shared_list;
29+
$('#shares-select').attr('data-current-id', id);
30+
$.get('/' + share_type + '/sharing/', {id: id})
3331
.done(function(data) {
3432
var users_links = JSON.parse(data);
3533
var users = users_links.users;
@@ -43,13 +41,14 @@ $.get('/study/sharing/', {study_id: study_id})
4341
});
4442
}
4543

46-
function update_share(params) {
44+
function update_share(share_type, params) {
45+
share_id = $('#shares-select').attr('data-current-id');
4746
data = params || {};
48-
data.study_id = current_study;
49-
$.get('/study/sharing/', data)
47+
data.id = share_id;
48+
$.get('/' + share_type + '/sharing/', data)
5049
.done(function(data) {
5150
users_links = JSON.parse(data);
5251
links = users_links.links;
53-
$("#shared_html_"+current_study).html(links);
52+
$("#shared_html_"+share_id).html(links);
5453
});
5554
}

0 commit comments

Comments
 (0)