Skip to content

Fix 1293 #2291

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Sep 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions qiita_db/study.py
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,28 @@ def prep_templates(self, data_type=None):
return [qdb.metadata_template.prep_template.PrepTemplate(ptid)
for ptid in qdb.sql_connection.TRN.execute_fetchflatten()]

def analyses(self):
"""Get all analyses where samples from this study have been used

Returns
-------
list of qiita_db.analysis.Analysis
"""
with qdb.sql_connection.TRN:
if self.sample_template is not None:
sids = self.sample_template.keys()
if sids:
sql = """SELECT DISTINCT analysis_id
FROM qiita.analysis_sample
WHERE sample_id IN %s
ORDER BY analysis_id"""
qdb.sql_connection.TRN.add(
sql, [tuple(self.sample_template.keys())])

return [qdb.analysis.Analysis(_id) for _id in
qdb.sql_connection.TRN.execute_fetchflatten()]
return []

def has_access(self, user, no_public=False):
"""Returns whether the given user has access to the study

Expand Down
5 changes: 5 additions & 0 deletions qiita_db/support_files/patches/python_patches/58.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ def correct_redis_data(key, cmd, values_dict, user):
st_cmd = Command.create(qiita_plugin, "update_sample_template",
"Updates the sample template", parameters)

# Create the delete study command
parameters = {'study': ['integer', None]}
Command.create(qiita_plugin, "delete_study",
"Deletes a full study", parameters)

# Create the delete sample template command
parameters = {'study': ['integer', None]}
Command.create(qiita_plugin, "delete_sample_template",
Expand Down
14 changes: 14 additions & 0 deletions qiita_db/test/test_study.py
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,20 @@ def test_retrieve_prep_templates_none(self):
self.assertEqual(new.prep_templates(), [])
qdb.study.Study.delete(new.id)

def test_analyses(self):
new = qdb.study.Study.create(
qdb.user.User('test@foo.bar'),
'NOT Identification of the Microbiomes for Cannabis Soils 13',
self.info)

self.assertEqual(qdb.study.Study(1).analyses(), [
qdb.analysis.Analysis(1), qdb.analysis.Analysis(2),
qdb.analysis.Analysis(3)])

self.assertEqual(qdb.study.Study(2).analyses(), [])

qdb.study.Study.delete(new.id)

def test_environmental_packages(self):
obs = self.study.environmental_packages
exp = ['soil', 'plant-associated']
Expand Down
58 changes: 44 additions & 14 deletions qiita_pet/handlers/api_proxy/studies.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,24 @@
# -----------------------------------------------------------------------------
from __future__ import division
from collections import defaultdict
from json import dumps, loads

from future.utils import viewitems

from qiita_core.exceptions import IncompetentQiitaDeveloperError
from qiita_core.util import execute_as_transaction
from qiita_core.qiita_settings import r_client
from qiita_db.user import User
from qiita_db.study import Study
from qiita_db.metadata_template.prep_template import PrepTemplate
from qiita_db.processing_job import ProcessingJob
from qiita_db.software import Software, Parameters
from qiita_db.util import (supported_filepath_types,
get_files_from_uploads_folders)
from qiita_pet.handlers.api_proxy.util import check_access
from qiita_core.exceptions import IncompetentQiitaDeveloperError


STUDY_KEY_FORMAT = 'study_%s'


def data_types_get_req():
Expand All @@ -35,8 +43,7 @@ def data_types_get_req():
"""
return {'status': 'success',
'message': '',
'data_types': Study.all_data_types()
}
'data_types': Study.all_data_types()}


def study_get_req(study_id, user_id):
Expand Down Expand Up @@ -110,12 +117,35 @@ def study_get_req(study_id, user_id):
study_info['show_raw_download_button'] = any([
True for pt in study.prep_templates() if pt.artifact is not None])

# getting study processing status from redis
processing = False
study_info['level'] = ''
study_info['message'] = ''
job_info = r_client.get(STUDY_KEY_FORMAT % study_id)
if job_info:
job_info = defaultdict(lambda: '', loads(job_info))
job_id = job_info['job_id']
job = ProcessingJob(job_id)
job_status = job.status
processing = job_status not in ('success', 'error')
if processing:
study_info['level'] = 'info'
study_info['message'] = 'This study is currently being processed'
elif job_status == 'error':
study_info['level'] = 'danger'
study_info['message'] = job.log.msg.replace('\n', '</br>')
else:
study_info['level'] = job_info['alert_type']
study_info['message'] = job_info['alert_msg'].replace(
'\n', '</br>')

return {'status': 'success',
'message': '',
'study_info': study_info,
'editable': study.can_edit(User(user_id))}


@execute_as_transaction
def study_delete_req(study_id, user_id):
"""Delete a given study

Expand All @@ -137,17 +167,17 @@ def study_delete_req(study_id, user_id):
if access_error:
return access_error

status = 'success'
try:
Study.delete(int(study_id))
msg = ''
except Exception as e:
status = 'error'
msg = 'Unable to delete study: %s' % str(e)
return {
'status': status,
'message': msg
}
qiita_plugin = Software.from_name_and_version('Qiita', 'alpha')
cmd = qiita_plugin.get_command('delete_study')
params = Parameters.load(cmd, values_dict={'study': study_id})
job = ProcessingJob.create(User(user_id), params)
# Store the job id attaching it to the sample template id
r_client.set(STUDY_KEY_FORMAT % study_id,
dumps({'job_id': job.id}))

job.submit()

return {'status': 'success', 'message': ''}


def study_prep_get_req(study_id, user_id):
Expand Down
39 changes: 4 additions & 35 deletions qiita_pet/handlers/api_proxy/tests/test_studies.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ def test_study_get_req(self):
exp = {
'status': 'success',
'study_info': {
'mixs_compliant': True, 'metadata_complete': True,
'reprocess': False, 'owner': 'test@foo.bar',
'mixs_compliant': True, 'metadata_complete': True, 'level': '',
'reprocess': False, 'owner': 'test@foo.bar', 'message': '',
'emp_person_id': 2, 'number_samples_promised': 27,
'funding': None, 'show_biom_download_button': True,
'publication_pid': ['123456', '7891011'], 'vamps_id': None,
Expand Down Expand Up @@ -341,37 +341,6 @@ def test_study_prep_get_req_no_access(self):
'message': 'User does not have access to study'}
self.assertEqual(obs, exp)

def test_study_delete_req(self):
info = {
"timeseries_type_id": 1,
"metadata_complete": True,
"mixs_compliant": True,
"number_samples_collected": 25,
"number_samples_promised": 28,
"study_alias": "FCM",
"study_description": "DESC",
"study_abstract": "ABS",
"emp_person_id": qdb.study.StudyPerson(2),
"principal_investigator_id": qdb.study.StudyPerson(3),
"lab_person_id": qdb.study.StudyPerson(1)
}

new_study = qdb.study.Study.create(
qdb.user.User('test@foo.bar'), "Some New Study to delete", info)

study_delete_req(new_study.id, 'test@foo.bar')

with self.assertRaises(qdb.exceptions.QiitaDBUnknownIDError):
qdb.study.Study(new_study.id)

def test_study_delete_req_error(self):
obs = study_delete_req(1, 'test@foo.bar')
exp = {'status': 'error',
'message': 'Unable to delete study: Study "Identification of '
'the Microbiomes for Cannabis Soils" cannot be '
'erased because it has a sample template'}
self.assertEqual(obs, exp)

def test_study_delete_req_no_access(self):
obs = study_delete_req(1, 'demo@microbio.me')
exp = {'status': 'error',
Expand Down Expand Up @@ -525,7 +494,7 @@ def test_study_get_tags_request(self):
self.assertEqual(obs, exp)

# check error
obs = study_get_tags_request('shared@foo.bar', 2)
obs = study_get_tags_request('shared@foo.bar', 2000)
exp = {'message': 'Study does not exist', 'status': 'error'}
self.assertEqual(obs, exp)

Expand Down Expand Up @@ -560,7 +529,7 @@ def test_study_tags_patch_request(self):
self.assertEqual(obs, exp)

obs = study_tags_patch_request(
'shared@foo.bar', 2, 'replace', '/tags', ['testA', 'testB'])
'shared@foo.bar', 2000, 'replace', '/tags', ['testA', 'testB'])
exp = {'message': 'Study does not exist', 'status': 'error'}
self.assertEqual(obs, exp)

Expand Down
2 changes: 1 addition & 1 deletion qiita_pet/handlers/study_handlers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def get(self, study_id):
if study_info['status'] != 'success':
raise HTTPError(404, study_info['message'])

if message != '':
if message != '' and level != '':
study_info['level'] = level
study_info['message'] = message

Expand Down
12 changes: 0 additions & 12 deletions qiita_pet/handlers/study_handlers/tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
# The full license is in the file LICENSE, distributed with this software.
# -----------------------------------------------------------------------------
from unittest import main
from json import loads

from qiita_pet.test.tornado_test_base import TestHandlerBase
from qiita_db.handlers.tests.oauthbase import OauthTestingBase
Expand All @@ -27,17 +26,6 @@ class StudyBaseInfoAJAX(TestHandlerBase):
pass


class StudyDeleteAjaxTests(TestHandlerBase):
def test_delete_study(self):
response = self.post('/study/delete/', {'study_id': 1})
self.assertEqual(response.code, 200)
exp = {'status': 'error',
'message': 'Unable to delete study: Study "Identification of '
'the Microbiomes for Cannabis Soils" cannot be '
'erased because it has a sample template'}
self.assertEqual(loads(response.body), exp)


class DataTypesMenuAJAXTests(TestHandlerBase):
def test_get(self):
response = self.get('/study/description/data_type_menu/',
Expand Down
46 changes: 23 additions & 23 deletions qiita_pet/templates/study_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,7 @@
else if(confirm("Are you sure you want to delete {% raw study_title_msg %}?")) {
$.post('{% raw qiita_config.portal_dir %}/study/delete/', { study_id: {{study_info['study_id']}} })
.done(function ( data ) {
if(data.status == "error") {
Copy link
Contributor

Choose a reason for hiding this comment

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

If there's an error with the deletion, the user will just see the page reload, would it reload to the same location? Meaning that the user wouldn't see any change?

Copy link
Member Author

Choose a reason for hiding this comment

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

Excellent question! Well, the errors are now stored in a redis key as this is now gonna run as a job in the queue so the user will see the error on the reload.

bootstrapAlert(data.message.replace("\n", "<br/>"), "danger");
$("#delete-study").modal('hide');
} else {
window.location.replace('{% raw qiita_config.portal_dir %}/study/list/');
}
location.reload();
});
}
}
Expand Down Expand Up @@ -211,6 +206,9 @@
$("#sample-summary-btn").hide();
$("#add-new-preparation-btn").hide();
{% end %}
{% if study_info['message'] != '' %}
bootstrapAlert("{{study_info['message']}}", "{{study_info['level']}}");
{% end %}
});
</script>
<style>
Expand All @@ -223,24 +221,26 @@
{% end %}
{% block content %}
<div class="row">
<div class="col-md-3">
<button class="btn btn-default btn-block" onclick="populate_main_div('{% raw qiita_config.portal_dir %}/study/description/baseinfo/', { study_id: {{study_info['study_id']}} })"><span class="glyphicon glyphicon-info-sign"></span> Study Information</button>
<button class="btn btn-default btn-block" onclick="populate_main_div('{% raw qiita_config.portal_dir %}/study/description/sample_template/', { study_id: {{study_info['study_id']}} })"><span class="glyphicon glyphicon-info-sign"></span> Sample Information</button>
<button class="btn btn-default btn-block" onclick="populate_main_div('{% raw qiita_config.portal_dir %}/study/description/sample_summary/', { study_id: {{study_info['study_id']}} })" id="sample-summary-btn"><span class="glyphicon glyphicon-th-list"></span> Sample Summary</button>
{% if editable %}
<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 %}
{% if study_info['show_biom_download_button'] %}
<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>
{% end %}
{% if study_info['has_access_to_raw_data'] and study_info['show_raw_download_button'] %}
<a class="btn btn-default btn-block" href="{% raw qiita_config.portal_dir %}/download_raw_data/{{study_info['study_id']}}"><span class="glyphicon glyphicon-download-alt"></span> All raw data</a>
{% end %}
<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>
{% if study_info['level'] != 'info' %}
<div class="col-md-3">
<button class="btn btn-default btn-block" onclick="populate_main_div('{% raw qiita_config.portal_dir %}/study/description/baseinfo/', { study_id: {{study_info['study_id']}} })"><span class="glyphicon glyphicon-info-sign"></span> Study Information</button>
<button class="btn btn-default btn-block" onclick="populate_main_div('{% raw qiita_config.portal_dir %}/study/description/sample_template/', { study_id: {{study_info['study_id']}} })"><span class="glyphicon glyphicon-info-sign"></span> Sample Information</button>
<button class="btn btn-default btn-block" onclick="populate_main_div('{% raw qiita_config.portal_dir %}/study/description/sample_summary/', { study_id: {{study_info['study_id']}} })" id="sample-summary-btn"><span class="glyphicon glyphicon-th-list"></span> Sample Summary</button>
{% if editable %}
<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 %}
{% if study_info['show_biom_download_button'] %}
<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>
{% end %}
{% if study_info['has_access_to_raw_data'] and study_info['show_raw_download_button'] %}
<a class="btn btn-default btn-block" href="{% raw qiita_config.portal_dir %}/download_raw_data/{{study_info['study_id']}}"><span class="glyphicon glyphicon-download-alt"></span> All raw data</a>
{% end %}
<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>

<div id="data-types-menu"></div>
</div>
<div id="data-types-menu"></div>
</div>
{% end %}

<div class="col-md-9">
<!-- Study ID and study alias always on top -->
Expand Down
35 changes: 35 additions & 0 deletions qiita_ware/private_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,40 @@ def delete_sample_or_column(job):
job._set_status('success')


def delete_study(job):
"""Deletes a full study

Parameters
----------
job : qiita_db.processing_job.ProcessingJob
The processing job performing the task
"""
with qdb.sql_connection.TRN:
study_id = job.parameters.values['study']
study = qdb.study.Study(study_id)

for a in study.analyses():
artifacts = sorted(
a.artifacts, key=lambda a: a.id, reverse=True)
for artifact in artifacts:
qdb.artifact.Artifact.delete(artifact.id)
qdb.analysis.Analysis.delete(a.id)

artifacts = sorted(
study.artifacts(), key=lambda a: a.id, reverse=True)
for a in artifacts:
qdb.artifact.Artifact.delete(a.id)

for pt in study.prep_templates():
qdb.metadata_template.prep_template.PrepTemplate.delete(pt.id)

qdb.metadata_template.sample_template.SampleTemplate.delete(study_id)

qdb.study.Study.delete(study_id)

job._set_status('success')


def complete_job(job):
"""Deletes a sample or a column from the metadata

Expand Down Expand Up @@ -310,6 +344,7 @@ def complete_job(job):
'delete_sample_template': delete_sample_template,
'update_prep_template': update_prep_template,
'delete_sample_or_column': delete_sample_or_column,
'delete_study': delete_study,
'complete_job': complete_job}


Expand Down
Loading