Skip to content
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
5 changes: 3 additions & 2 deletions qiita_pet/handlers/api_proxy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
prep_template_put_req, prep_template_delete_req, prep_template_get_req,
prep_template_graph_get_req, prep_template_filepaths_get_req,
ena_ontology_get_req, prep_template_samples_get_req)
from .studies import data_types_get_req, study_get_req, study_prep_get_req
from .studies import (
data_types_get_req, study_get_req, study_prep_get_req, study_delete_req)
from .artifact import artifact_graph_get_req

__all__ = ['prep_template_summary_get_req', 'sample_template_post_req',
Expand All @@ -32,7 +33,7 @@
'prep_template_summary_get_req', 'prep_template_post_req',
'prep_template_put_req', 'prep_template_delete_req',
'prep_template_graph_get_req', 'prep_template_filepaths_get_req',
'artifact_get_graph', 'prep_template_get_req',
'artifact_get_graph', 'prep_template_get_req', 'study_delete_req',
'study_prep_get_req', 'sample_template_get_req',
'artifact_graph_get_req', 'ena_ontology_get_req',
'sample_template_meta_cats_get_req',
Expand Down
34 changes: 34 additions & 0 deletions qiita_pet/handlers/api_proxy/studies.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,40 @@ def study_get_req(study_id, user_id):
}


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

Parameters
----------
study_id : int
Study id to delete
user_id : str
User requesting the deletion

Returns
-------
dict
Status of deletion, in the format
{status: status,
message: message}
"""
access_error = check_access(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
}


def study_prep_get_req(study_id, user_id):
"""Gives a summary of each prep template attached to the study

Expand Down
49 changes: 48 additions & 1 deletion qiita_pet/handlers/api_proxy/tests/test_studies.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@
from unittest import TestCase, main
from datetime import datetime

from qiita_core.util import qiita_test_checker
import qiita_db as qdb
from qiita_pet.handlers.api_proxy.studies import (
data_types_get_req, study_get_req, study_prep_get_req)
data_types_get_req, study_get_req, study_prep_get_req, study_delete_req)


@qiita_test_checker
class TestStudyAPI(TestCase):
def test_data_types_get_req(self):
obs = data_types_get_req()
Expand Down Expand Up @@ -101,5 +104,49 @@ 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):
Copy link
Contributor

Choose a reason for hiding this comment

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

This is modifying the DB - shouldn't the class be decorated with the safeguard that we put in place?

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

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", [1],
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',
'message': 'User does not have access to study'}
self.assertEqual(obs, exp)

def test_study_delete_req_no_exists(self):
obs = study_delete_req(4, 'test@foo.bar')
exp = {'status': 'error',
'message': 'Study does not exist'}
self.assertEqual(obs, exp)

if __name__ == '__main__':
main()
4 changes: 2 additions & 2 deletions qiita_pet/handlers/study_handlers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
PreprocessingSummaryHandler)
from .ebi_handlers import EBISubmitHandler
from .vamps_handlers import VAMPSHandler
from .base import StudyIndexHandler, StudyBaseInfoAJAX
from .base import StudyIndexHandler, StudyBaseInfoAJAX, StudyDeleteAjax
from .prep_template import PrepTemplateGraphAJAX, PrepTemplateAJAX
from .artifact import ArtifactGraphAJAX
from .sample_template import SampleTemplateAJAX, SampleAJAX
Expand All @@ -24,4 +24,4 @@
'MetadataSummaryHandler', 'VAMPSHandler', 'SearchStudiesAJAX',
'PrepTemplateGraphAJAX', 'ArtifactGraphAJAX',
'StudyIndexHandler', 'StudyBaseInfoAJAX', 'SampleTemplateAJAX',
'PrepTemplateAJAX', 'SampleAJAX']
'PrepTemplateAJAX', 'SampleAJAX', 'StudyDeleteAjax']
8 changes: 7 additions & 1 deletion qiita_pet/handlers/study_handlers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from qiita_pet.handlers.util import to_int, doi_linkifier
from qiita_pet.handlers.base_handlers import BaseHandler
from qiita_pet.handlers.api_proxy import (
study_prep_get_req, data_types_get_req, study_get_req)
study_prep_get_req, data_types_get_req, study_get_req, study_delete_req)


class StudyIndexHandler(BaseHandler):
Expand Down Expand Up @@ -51,3 +51,9 @@ def get(self):
self.render('study_ajax/base_info.html',
study_info=study_info, publications=study_doi, pi=pi,
contact=contact)


class StudyDeleteAjax(BaseHandler):
def post(self):
study_id = self.get_argument('study_id')
self.write(study_delete_req(int(study_id), self.current_user.id))
61 changes: 53 additions & 8 deletions qiita_pet/templates/study_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
var nodes = [];
// format edge list data
for(i=0;i<data.edge_list.length;i++) {
edges.push({from: data.edge_list[i][0], to: data.edge_list[i][1],});
edges.push({from: data.edge_list[i][0], to: data.edge_list[i][1]});
}
//format node depth data
for(i=0;i<data.node_labels.length;i++) {
Expand All @@ -35,12 +35,12 @@
draw_graph(nodes, edges, "study-prep-graph");
// Copy existing formatted prep info to header position
$("#study-prep-subheader").html("<h2>" + $("#prep-header-" + pid).html() + '</h2><a href="#" onclick="togglegraphs(); return false;" id="graph-toggle-link">Hide files</a>');
});
$.get( "/study/description/prep_template/", { prep_id: pid, study_id: {{study_info['study_id']}}} )
});
$.get( "/study/description/prep_template/", { prep_id: pid, study_id: {{study_info['study_id']}} } )
.done(function( data ) {
$("#study-main").html(data);
});
}
});
}

function draw_graph(nodes, edges, target) {
var container = document.getElementById(target);
Expand Down Expand Up @@ -112,11 +112,37 @@
$(document).ready(function() {
fill_base({{study_info['study_id']}});
});

function validate_delete_study_text() {
if ($("#study-alias").val() == "{{study_info['study_alias']}}") {
$('#delete-study-button').prop('disabled', false);
} else {
$('#delete-study-button').prop('disabled', true);
}
}

function delete_study() {
if ($("#study-alias").val() != "{{study_info['study_alias']}}") {
alert("The entered study alias doesn't match the study");
return false;
}
if (confirm("Are you sure you want to delete {{study_info['study_title']}}?")) {
$.post('/study/delete/', {study_id: {{study_info['study_id']}}})
.done(function ( data ) {
if(data["status"] == "error") {
bootstrapAlert(data.message.replace("\n", "<br/>"), "danger", 4000);
} else {
window.location.replace = '/'
}
});
}
}
</script>
<style>
.graph {
width:100%;
height:500px;
height:300px;
border: 1px solid #ccc;
}
</style>
{% end %}
Expand All @@ -128,8 +154,8 @@ <h3><a href="#" onclick="fill_base({{study_info['study_id']}})">Study Informatio
<a href="#" onclick="fill_sample_summary({{study_info['study_id']}}); return false;">Sample Summary</a><br />
{% if editable %}
<a href="/study/upload/{{study_info['study_id']}}">Upload Files</a><br />
<a href="#">Edit Study Information</a><br />
<a href="#">Delete Study</a>
<a href="/study/edit/{{study_info['study_id']}}">Edit Study Information</a><br />
<a href="#" data-toggle="modal" data-target="#delete-study">Delete Study</a>
{% end %}

<h3>Data Types</h3>
Expand Down Expand Up @@ -177,4 +203,23 @@ <h3>{{study_info['study_alias']}}</h3>
<div class="row"><div class="col-md-12" id="study-main"></div></div>
</div>
</div>

<div class="modal fade delete-study" tabindex="-1" role="dialog" id="delete-study">
<div class="modal-dialog modal-md">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>Deleting:<br/></h3><h4>{{study_info['study_title']}}</h4>
</div>
<div class="modal-body">
You will only be able to delete a study that has no data associated with it.
</div>
<div class="modal-footer">
To continue you need to write the alias name of the study:
<input type="text" name="study-alias" id="study-alias" onkeyup="validate_delete_study_text();">
<button class="btn btn-danger glyphicon glyphicon-trash" onclick="delete_study();" id="delete-study-button" disabled></button>
</div>
</div>
</div>
</div>
{% end %}
9 changes: 9 additions & 0 deletions qiita_pet/test/test_study_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,15 @@ def test_post_error(self):
class TestDelete(TestHandlerBase):
database = True

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)

def test_delete_sample_template(self):
response = self.post('/study/description/sample_template/',
{'study_id': 1,
Expand Down
3 changes: 2 additions & 1 deletion qiita_pet/webserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
StudyEditHandler, ListStudiesHandler, SearchStudiesAJAX, EBISubmitHandler,
CreateStudyAJAX, ShareStudyAJAX, StudyApprovalList, ArtifactGraphAJAX,
PreprocessingSummaryHandler, VAMPSHandler, PrepTemplateGraphAJAX,
PrepTemplateAJAX, SampleAJAX)
PrepTemplateAJAX, SampleAJAX, StudyDeleteAjax)
from qiita_pet.handlers.websocket_handlers import (
MessageHandler, SelectedSocketHandler, SelectSamplesHandler)
from qiita_pet.handlers.logger_handlers import LogEntryViewerHandler
Expand Down Expand Up @@ -113,6 +113,7 @@ def __init__(self):
(r"/study/description/prep_template/", PrepTemplateAJAX),
(r"/study/description/baseinfo/", StudyBaseInfoAJAX),
(r"/study/description/(.*)", StudyIndexHandler),
(r"/study/delete/", StudyDeleteAjax),
(r"/study/upload/(.*)", StudyUploadFileHandler),
(r"/upload/", UploadFileHandler),
(r"/check_study/", CreateStudyAJAX),
Expand Down