Skip to content

Commit 2369a5e

Browse files
committed
erge branch 'master' of https://github.com/biocore/qiita into listing-tags
2 parents bba8f03 + 208bac3 commit 2369a5e

File tree

8 files changed

+257
-10
lines changed

8 files changed

+257
-10
lines changed

qiita_db/test/test_meta_util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ def test_validate_filepath_access_by_user(self):
9898
self.assertTrue(qdb.meta_util.validate_filepath_access_by_user(
9999
admin, i[0]))
100100

101-
# returning to origina sharing
101+
# returning to original sharing
102102
qdb.study.Study(1).share(user)
103103
qdb.analysis.Analysis(1).share(user)
104104
qdb.study.Study.delete(study.id)

qiita_db/util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -896,12 +896,12 @@ def filepath_id_to_rel_path(filepath_id):
896896
LEFT JOIN qiita.artifact_filepath USING (filepath_id)
897897
WHERE filepath_id = %s"""
898898
qdb.sql_connection.TRN.add(sql, [filepath_id])
899+
# It should be only one row
899900
mp, fp, sd, a_id = qdb.sql_connection.TRN.execute_fetchindex()[0]
900901
if sd:
901902
result = join(mp, str(a_id), fp)
902903
else:
903904
result = join(mp, fp)
904-
# It should be only one row
905905
return result
906906

907907

qiita_pet/handlers/download.py

Lines changed: 92 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
from tornado.web import authenticated
1+
from tornado.web import authenticated, HTTPError
22

3-
from os.path import basename
3+
from os.path import basename, getsize, join
4+
from os import walk
5+
from datetime import datetime
46

57
from .base_handlers import BaseHandler
6-
from qiita_pet.exceptions import QiitaPetAuthorizationError
7-
from qiita_db.util import filepath_id_to_rel_path
8+
from qiita_pet.handlers.api_proxy import study_get_req
9+
from qiita_db.study import Study
10+
from qiita_db.util import filepath_id_to_rel_path, get_db_files_base_dir
811
from qiita_db.meta_util import validate_filepath_access_by_user
912
from qiita_core.util import execute_as_transaction
1013

@@ -16,8 +19,9 @@ def get(self, filepath_id):
1619
fid = int(filepath_id)
1720

1821
if not validate_filepath_access_by_user(self.current_user, fid):
19-
raise QiitaPetAuthorizationError(
20-
self.current_user, 'filepath id %s' % str(fid))
22+
raise HTTPError(
23+
403, "%s doesn't have access to "
24+
"filepath_id: %s" % (self.current_user.email, str(fid)))
2125

2226
relpath = filepath_id_to_rel_path(fid)
2327
fname = basename(relpath)
@@ -37,3 +41,85 @@ def get(self, filepath_id):
3741
'attachment; filename=%s' % fname)
3842

3943
self.finish()
44+
45+
46+
class DownloadStudyBIOMSHandler(BaseHandler):
47+
@authenticated
48+
@execute_as_transaction
49+
def get(self, study_id):
50+
study_id = int(study_id)
51+
# Check access to study
52+
study_info = study_get_req(study_id, self.current_user.id)
53+
54+
if study_info['status'] != 'success':
55+
raise HTTPError(405, "%s: %s, %s" % (study_info['message'],
56+
self.current_user.email,
57+
str(study_id)))
58+
59+
study = Study(study_id)
60+
user = self.current_user
61+
basedir = get_db_files_base_dir()
62+
basedir_len = len(basedir) + 1
63+
# loop over artifacts and retrieve those that we have access to
64+
to_download = []
65+
vfabu = validate_filepath_access_by_user
66+
for a in study.artifacts():
67+
if a.artifact_type == 'BIOM':
68+
to_add = True
69+
for i, (fid, path, data_type) in enumerate(a.filepaths):
70+
# validate access only of the first artifact filepath,
71+
# the rest have the same permissions
72+
if (i == 0 and not vfabu(user, fid)):
73+
to_add = False
74+
break
75+
# ignore if tgz as they could create problems and the
76+
# raw data is in the folder
77+
if data_type == 'tgz':
78+
continue
79+
if data_type == 'directory':
80+
# If we have a directory, we actually need to list
81+
# all the files from the directory so NGINX can
82+
# actually download all of them
83+
for dp, _, fps in walk(path):
84+
for fname in fps:
85+
fullpath = join(dp, fname)
86+
spath = fullpath
87+
if fullpath.startswith(basedir):
88+
spath = fullpath[basedir_len:]
89+
to_download.append((fullpath, spath, spath))
90+
elif path.startswith(basedir):
91+
spath = path[basedir_len:]
92+
to_download.append((path, spath, spath))
93+
else:
94+
# We are not aware of any case that can trigger this
95+
# situation, but we wanted to be overly cautious
96+
# There is no test for this line cause we don't know
97+
# how to trigger it
98+
to_download.append((path, path, path))
99+
100+
if to_add:
101+
for pt in a.prep_templates:
102+
qmf = pt.qiime_map_fp
103+
if qmf is not None:
104+
sqmf = qmf
105+
if qmf.startswith(basedir):
106+
sqmf = qmf[basedir_len:]
107+
to_download.append(
108+
(qmf, sqmf, 'mapping_files/%s_mapping_file.txt'
109+
% a.id))
110+
111+
# If we don't have nginx, write a file that indicates this
112+
all_files = '\n'.join(["- %s /protected/%s %s" % (getsize(fp), sfp, n)
113+
for fp, sfp, n in to_download])
114+
self.write("%s\n" % all_files)
115+
116+
zip_fn = 'study_%d_%s.zip' % (
117+
study_id, datetime.now().strftime('%m%d%y-%H%M%S'))
118+
119+
self.set_header('Content-Description', 'File Transfer')
120+
self.set_header('Expires', '0')
121+
self.set_header('Cache-Control', 'no-cache')
122+
self.set_header('X-Archive-Files', 'zip')
123+
self.set_header('Content-Disposition',
124+
'attachment; filename=%s' % zip_fn)
125+
self.finish()

qiita_pet/nginx_example.conf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ http {
1818
internal;
1919

2020
# CHANGE ME: This should match the BASE_DATA_DIR in your qiita
21-
# config. E.g.,
21+
# config. E.g.,
2222
# alias /Users/username/qiita/qiita_db/support_files/test_data/;
2323
alias ;
2424
}
@@ -30,6 +30,7 @@ http {
3030
proxy_set_header Host $host;
3131
proxy_set_header X-Real-IP $remote_addr;
3232
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
33+
proxy_set_header Accept-Encoding identity;
3334
}
3435
}
3536
}

qiita_pet/support_files/doc/source/faq.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,17 @@ A few more instructions: for the example above the workflow should be:
6565
perform closed OTU picking against the latest version of Greengenes and can
6666
be quite time consuming depending on the number of samples and the depth
6767
of sequencing.
68+
69+
.. _issues_unzip:
70+
71+
How to solve unzip errors?
72+
--------------------------
73+
74+
When downloading large zip files within Qiita there is a change that you will get
75+
an error like: **"start of central directory not found; zipfile corrupt"**. This issue
76+
arises from using old versions of zip and you need to have unzip >= 6.0.0. To check
77+
you unzip version you can run: `unzip -v`.
78+
79+
To update your unzip for most operating systems you can simply use your regular package
80+
admin program. However, for Mac we suggest using
81+
`this version of unzip <ftp://ftp.microbio.me/pub/qiita/unzip>`__.

qiita_pet/templates/study_base.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,8 @@
239239
<a class="btn btn-default btn-block" href="{% raw qiita_config.portal_dir %}/study/upload/{{study_info['study_id']}}"><span class="glyphicon glyphicon-upload"></span> Upload Files</a>
240240
<button class="btn btn-default btn-block" onclick="populate_main_div('{% raw qiita_config.portal_dir %}/study/new_prep_template/', { study_id: {{study_info['study_id']}} })" id="add-new-preparation-btn"><span class="glyphicon glyphicon-plus-sign"></span> Add New Preparation</button>
241241
{% end %}
242+
<a class="btn btn-default btn-block" href="{% raw qiita_config.portal_dir %}/download_study_bioms/{{study_info['study_id']}}"><span class="glyphicon glyphicon-download-alt"></span> All QIIME maps and BIOMs</a>
243+
<div style="text-align: center;"><small><a href="{% raw qiita_config.portal_dir %}/static/doc/html/faq.html#how-to-solve-unzip-errors">Issues opening the downloaded zip?</a></small></div>
242244

243245
<div id="data-types-menu"></div>
244246
</div>

qiita_pet/test/test_download.py

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# -----------------------------------------------------------------------------
2+
# Copyright (c) 2014--, The Qiita Development Team.
3+
#
4+
# Distributed under the terms of the BSD 3-clause License.
5+
#
6+
# The full license is in the file LICENSE, distributed with this software.
7+
# -----------------------------------------------------------------------------
8+
9+
from unittest import main
10+
from mock import Mock
11+
from os.path import exists, isdir, join
12+
from os import remove, makedirs
13+
from shutil import rmtree
14+
from tempfile import mkdtemp
15+
16+
from biom.util import biom_open
17+
from biom import example_table as et
18+
19+
from qiita_pet.test.tornado_test_base import TestHandlerBase
20+
from qiita_pet.handlers.base_handlers import BaseHandler
21+
from qiita_db.user import User
22+
from qiita_db.artifact import Artifact
23+
from qiita_db.software import Parameters, Command
24+
25+
26+
class TestDownloadHandler(TestHandlerBase):
27+
28+
def setUp(self):
29+
super(TestDownloadHandler, self).setUp()
30+
31+
def tearDown(self):
32+
super(TestDownloadHandler, self).tearDown()
33+
34+
def test_download(self):
35+
# check success
36+
response = self.get('/download/1')
37+
self.assertEqual(response.code, 200)
38+
self.assertEqual(response.body, (
39+
"This installation of Qiita was not equipped with nginx, so it "
40+
"is incapable of serving files. The file you attempted to "
41+
"download is located at raw_data/1_s_G1_L001_sequences.fastq.gz"))
42+
43+
# failure
44+
response = self.get('/download/1000')
45+
self.assertEqual(response.code, 403)
46+
47+
48+
class TestDownloadStudyBIOMSHandler(TestHandlerBase):
49+
50+
def setUp(self):
51+
super(TestDownloadStudyBIOMSHandler, self).setUp()
52+
self._clean_up_files = []
53+
54+
def tearDown(self):
55+
super(TestDownloadStudyBIOMSHandler, self).tearDown()
56+
for fp in self._clean_up_files:
57+
if exists(fp):
58+
if isdir(fp):
59+
rmtree(fp)
60+
else:
61+
remove(fp)
62+
63+
def test_download_study(self):
64+
tmp_dir = mkdtemp()
65+
self._clean_up_files.append(tmp_dir)
66+
67+
biom_fp = join(tmp_dir, 'otu_table.biom')
68+
smr_dir = join(tmp_dir, 'sortmerna_picked_otus')
69+
log_dir = join(smr_dir, 'seqs_otus.log')
70+
tgz = join(tmp_dir, 'sortmerna_picked_otus.tgz')
71+
72+
with biom_open(biom_fp, 'w') as f:
73+
et.to_hdf5(f, "test")
74+
makedirs(smr_dir)
75+
with open(log_dir, 'w') as f:
76+
f.write('\n')
77+
with open(tgz, 'w') as f:
78+
f.write('\n')
79+
80+
self._clean_up_files.append(tmp_dir)
81+
82+
files_biom = [(biom_fp, 'biom'), (smr_dir, 'directory'), (tgz, 'tgz')]
83+
84+
params = Parameters.from_default_params(
85+
Command(3).default_parameter_sets.next(), {'input_data': 1})
86+
a = Artifact.create(files_biom, "BIOM", parents=[Artifact(2)],
87+
processing_parameters=params)
88+
for _, fp, _ in a.filepaths:
89+
self._clean_up_files.append(fp)
90+
91+
response = self.get('/download_study_bioms/1')
92+
self.assertEqual(response.code, 200)
93+
exp = (
94+
'- 1256812 /protected/processed_data/1_study_1001_closed_'
95+
'reference_otu_table.biom processed_data/1_study_1001_closed_'
96+
'reference_otu_table.biom\n'
97+
'- 36615 /protected/templates/1_prep_1_qiime_[0-9]*-'
98+
'[0-9]*.txt mapping_files/4_mapping_file.txt\n'
99+
'- 1256812 /protected/processed_data/'
100+
'1_study_1001_closed_reference_otu_table.biom processed_data/'
101+
'1_study_1001_closed_reference_otu_table.biom\n'
102+
'- 36615 /protected/templates/1_prep_1_qiime_[0-9]*-'
103+
'[0-9]*.txt mapping_files/5_mapping_file.txt\n'
104+
'- 1256812 /protected/processed_data/'
105+
'1_study_1001_closed_reference_otu_table_Silva.biom processed_data'
106+
'/1_study_1001_closed_reference_otu_table_Silva.biom\n'
107+
'- 36615 /protected/templates/1_prep_1_qiime_[0-9]*-'
108+
'[0-9]*.txt mapping_files/6_mapping_file.txt\n'
109+
'- 36615 /protected/templates/1_prep_2_qiime_[0-9]*-'
110+
'[0-9]*.txt mapping_files/7_mapping_file.txt\n'
111+
'- 39752 /protected/BIOM/{0}/otu_table.biom '
112+
'BIOM/{0}/otu_table.biom\n'
113+
'- 1 /protected/BIOM/{0}/sortmerna_picked_otus/seqs_otus.log '
114+
'BIOM/{0}/sortmerna_picked_otus/seqs_otus.log\n'
115+
'- 36615 /protected/templates/1_prep_1_qiime_[0-9]*-[0-9]*.'
116+
'txt mapping_files/{0}_mapping_file.txt\n'.format(a.id))
117+
self.assertRegexpMatches(response.body, exp)
118+
119+
response = self.get('/download_study_bioms/200')
120+
self.assertEqual(response.code, 405)
121+
122+
# changing user so we can test the failures
123+
BaseHandler.get_current_user = Mock(
124+
return_value=User("demo@microbio.me"))
125+
response = self.get('/download_study_bioms/1')
126+
self.assertEqual(response.code, 405)
127+
128+
a.visibility = 'public'
129+
response = self.get('/download_study_bioms/1')
130+
self.assertEqual(response.code, 200)
131+
exp = (
132+
'- 39752 /protected/BIOM/{0}/otu_table.biom '
133+
'BIOM/{0}/otu_table.biom\n'
134+
'- 1 /protected/BIOM/{0}/sortmerna_picked_otus/seqs_otus.log '
135+
'BIOM/{0}/sortmerna_picked_otus/seqs_otus.log\n'
136+
'- 36615 /protected/templates/1_prep_1_qiime_[0-9]*-[0-9]*.'
137+
'txt mapping_files/{0}_mapping_file.txt\n'.format(a.id))
138+
self.assertRegexpMatches(response.body, exp)
139+
140+
141+
if __name__ == '__main__':
142+
main()

qiita_pet/webserver.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@
3838
from qiita_pet.handlers.logger_handlers import LogEntryViewerHandler
3939
from qiita_pet.handlers.upload import UploadFileHandler, StudyUploadFileHandler
4040
from qiita_pet.handlers.stats import StatsHandler
41-
from qiita_pet.handlers.download import DownloadHandler
41+
from qiita_pet.handlers.download import (
42+
DownloadHandler, DownloadStudyBIOMSHandler)
4243
from qiita_pet.handlers.prep_template import PrepTemplateHandler
4344
from qiita_pet.handlers.ontology import OntologyHandler
4445
from qiita_db.handlers.processing_job import (
@@ -146,6 +147,7 @@ def __init__(self):
146147
(r"/check_study/", CreateStudyAJAX),
147148
(r"/stats/", StatsHandler),
148149
(r"/download/(.*)", DownloadHandler),
150+
(r"/download_study_bioms/(.*)", DownloadStudyBIOMSHandler),
149151
(r"/vamps/(.*)", VAMPSHandler),
150152
# Plugin handlers - the order matters here so do not change
151153
# qiita_db/jobs/(.*) should go after any of the

0 commit comments

Comments
 (0)