Skip to content
Closed
2 changes: 1 addition & 1 deletion qiita_db/test/test_meta_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def test_validate_filepath_access_by_user(self):
self.assertTrue(qdb.meta_util.validate_filepath_access_by_user(
admin, i[0]))

# returning to origina sharing
# returning to original sharing
qdb.study.Study(1).share(user)
qdb.analysis.Analysis(1).share(user)
qdb.study.Study.delete(study.id)
Expand Down
2 changes: 1 addition & 1 deletion qiita_db/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -896,12 +896,12 @@ def filepath_id_to_rel_path(filepath_id):
LEFT JOIN qiita.artifact_filepath USING (filepath_id)
WHERE filepath_id = %s"""
qdb.sql_connection.TRN.add(sql, [filepath_id])
# It should be only one row
mp, fp, sd, a_id = qdb.sql_connection.TRN.execute_fetchindex()[0]
if sd:
result = join(mp, str(a_id), fp)
else:
result = join(mp, fp)
# It should be only one row
return result


Expand Down
90 changes: 84 additions & 6 deletions qiita_pet/handlers/download.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from tornado.web import authenticated
from tornado.web import authenticated, HTTPError

from os.path import basename
from os.path import basename, getsize, join
from os import walk
from datetime import datetime

from .base_handlers import BaseHandler
from qiita_pet.exceptions import QiitaPetAuthorizationError
from qiita_db.util import filepath_id_to_rel_path
from qiita_pet.handlers.api_proxy import study_get_req
from qiita_db.study import Study
from qiita_db.util import filepath_id_to_rel_path, get_db_files_base_dir
from qiita_db.meta_util import validate_filepath_access_by_user
from qiita_core.util import execute_as_transaction

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

if not validate_filepath_access_by_user(self.current_user, fid):
raise QiitaPetAuthorizationError(
self.current_user, 'filepath id %s' % str(fid))
raise HTTPError(
404, "%s doesn't have access to "
"filepath_id: %s" % (self.current_user.email, str(fid)))

relpath = filepath_id_to_rel_path(fid)
fname = basename(relpath)
Expand All @@ -37,3 +41,77 @@ def get(self, filepath_id):
'attachment; filename=%s' % fname)

self.finish()


class DownloadStudyBIOMSHandler(BaseHandler):
@authenticated
@execute_as_transaction
def get(self, study_id):
study_id = int(study_id)
# Check access to study
study_info = study_get_req(study_id, self.current_user.id)

if study_info['status'] != 'success':
raise HTTPError(405, "%s: %s, %s" % (study_info['message'],
self.current_user.email,
str(study_id)))

study = Study(study_id)
user = self.current_user
basedir = get_db_files_base_dir()
basedir_len = len(basedir) + 1
# loop over artifacts and retrieve those that we have access to
to_download = []
vfabu = validate_filepath_access_by_user
for a in study.artifacts():
if a.artifact_type == 'BIOM':
to_add = True
for i, (fid, path, data_type) in enumerate(a.filepaths):
# validate access only of the first artifact filepath,
# the rest have the same permissions
if (i == 0 and not vfabu(user, fid)):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a tests for this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

to_add = False
break
if data_type == 'directory':
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... and one for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

# If we have a directory, we actually need to list
# all the files from the directory so NGINX can
# actually download all of them
for dp, _, fps in walk(path):
for fname in fps:
fullpath = join(dp, fname)
spath = fullpath
if fullpath.startswith(basedir):
spath = fullpath[basedir_len:]
to_download.append((fullpath, spath, spath))
elif path.startswith(basedir):
spath = path[basedir_len:]
to_download.append((path, spath, spath))
else:
to_download.append((path, path, path))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... and this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how to add a test, added a comment explaining why


if to_add:
for pt in a.prep_templates:
qmf = pt.qiime_map_fp
if qmf is not None:
sqmf = qmf
if qmf.startswith(basedir):
sqmf = qmf[basedir_len:]
to_download.append(
(qmf, sqmf, 'mapping_files/%s_mapping_file.txt'
% a.id))

# If we don't have nginx, write a file that indicates this
all_files = '\n'.join(["- %s /protected/%s %s" % (getsize(fp), sfp, n)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I liked having the message "This installation of Qiita was not equipped with nginx, ... ", is it possible to add it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According the the mod_zip documentation you cannot have any extra lines, so I don't think it is possible to add that information.

for fp, sfp, n in to_download])
self.write("%s\n" % all_files)

zip_fn = 'study_%d_%s.zip' % (
study_id, datetime.now().strftime('%m%d%y-%H%M%S'))

self.set_header('Content-Description', 'File Transfer')
self.set_header('Expires', '0')
self.set_header('Cache-Control', 'no-cache')
self.set_header('X-Archive-Files', 'zip')
self.set_header('Content-Disposition',
'attachment; filename=%s' % zip_fn)
self.finish()
1 change: 1 addition & 0 deletions qiita_pet/templates/study_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@
<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>
<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>
{% end %}
<a class="btn btn-default btn-block" href="/download_study_bioms/{{study_info['study_id']}}"><span class="glyphicon glyphicon-download-alt"></span> All QIIME maps and BIOMs</a>

<div id="data-types-menu"></div>
</div>
Expand Down
81 changes: 81 additions & 0 deletions qiita_pet/test/test_download.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# -----------------------------------------------------------------------------
# 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 unittest import main
from mock import Mock

from qiita_pet.test.tornado_test_base import TestHandlerBase
from qiita_pet.handlers.base_handlers import BaseHandler
from qiita_db.user import User


class TestDownloadHandler(TestHandlerBase):

def setUp(self):
super(TestDownloadHandler, self).setUp()

def tearDown(self):
super(TestDownloadHandler, self).tearDown()

def test_download(self):
# check success
response = self.get('/download/1')
self.assertEqual(response.code, 200)
self.assertEqual(response.body, (
"This installation of Qiita was not equipped with nginx, so it "
"is incapable of serving files. The file you attempted to "
"download is located at raw_data/1_s_G1_L001_sequences.fastq.gz"))

# failure
response = self.get('/download/1000')
self.assertEqual(response.code, 404)


class TestDownloadStudyBIOMSHandler(TestHandlerBase):

def setUp(self):
super(TestDownloadStudyBIOMSHandler, self).setUp()

def tearDown(self):
super(TestDownloadStudyBIOMSHandler, self).tearDown()

def test_download_study(self):
response = self.get('/download_study_bioms/1')
self.assertEqual(response.code, 200)
exp = (
'- 1256812 /protected/processed_data/1_study_1001_closed_'
'reference_otu_table.biom processed_data/1_study_1001_closed_'
'reference_otu_table.biom\n'
'- 36615 /protected/templates/1_prep_1_qiime_[0-9]*-[0-9]*.txt '
'mapping_files/4_mapping_file.txt\n'
'- 1256812 /protected/processed_data/1_study_1001_closed_reference'
'_otu_table.biom processed_data/1_study_1001_closed_'
'reference_otu_table.biom\n'
'- 36615 /protected/templates/1_prep_1_qiime_[0-9]*-[0-9]*.txt '
'mapping_files/5_mapping_file.txt\n'
'- 1256812 /protected/processed_data/1_study_1001_closed_reference'
'_otu_table_Silva.biom processed_data/1_study_1001_closed_'
'reference_otu_table_Silva.biom\n'
'- 36615 /protected/templates/1_prep_1_qiime_[0-9]*-[0-9]*.txt '
'mapping_files/6_mapping_file.txt\n'
'- 36615 /protected/templates/1_prep_2_qiime_[0-9]*-[0-9]*.txt '
'mapping_files/7_mapping_file.txt\n')
self.assertRegexpMatches(response.body, exp)

response = self.get('/download_study_bioms/200')
self.assertEqual(response.code, 405)

# changing user so we can test the failures
BaseHandler.get_current_user = Mock(
return_value=User("demo@microbio.me"))
response = self.get('/download_study_bioms/1')
self.assertEqual(response.code, 405)


if __name__ == '__main__':
main()
4 changes: 3 additions & 1 deletion qiita_pet/webserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
from qiita_pet.handlers.logger_handlers import LogEntryViewerHandler
from qiita_pet.handlers.upload import UploadFileHandler, StudyUploadFileHandler
from qiita_pet.handlers.stats import StatsHandler
from qiita_pet.handlers.download import DownloadHandler
from qiita_pet.handlers.download import (
DownloadHandler, DownloadStudyBIOMSHandler)
from qiita_pet.handlers.prep_template import PrepTemplateHandler
from qiita_pet.handlers.ontology import OntologyHandler
from qiita_db.handlers.processing_job import (
Expand Down Expand Up @@ -144,6 +145,7 @@ def __init__(self):
(r"/check_study/", CreateStudyAJAX),
(r"/stats/", StatsHandler),
(r"/download/(.*)", DownloadHandler),
(r"/download_study_bioms/(.*)", DownloadStudyBIOMSHandler),
(r"/vamps/(.*)", VAMPSHandler),
# Plugin handlers - the order matters here so do not change
# qiita_db/jobs/(.*) should go after any of the
Expand Down