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
73 changes: 72 additions & 1 deletion qiita_db/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from .base import QiitaStatusObject
from .data import ProcessedData
from .study import Study
from .exceptions import QiitaDBStatusError, QiitaDBError
from .exceptions import QiitaDBStatusError, QiitaDBError, QiitaDBUnknownIDError
from .util import (convert_to_id, get_work_base_dir,
get_mountpoint, insert_filepaths)

Expand Down Expand Up @@ -66,9 +66,13 @@ class Analysis(QiitaStatusObject):
unshare
build_files
summary_data
exists
create
delete
"""

_table = "analysis"
_analysis_id_column = 'analysis_id'

def _lock_check(self, conn_handler):
"""Raises QiitaDBStatusError if analysis is not in_progress"""
Expand Down Expand Up @@ -165,6 +169,73 @@ def create(cls, owner, name, description, parent=None, from_default=False):
a_id = conn_handler.execute_queue(queue)[0]
return cls(a_id)

@classmethod
def delete(cls, _id):
Copy link
Contributor

Choose a reason for hiding this comment

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

@antgonza there is still another table in which the analysis can be present: analysis_chain. Right now we are not using that table for anything, but we can easily forget about it on deletion once we start using it. There are 2 possible solutions:

  • Open an issue, so we don't loose track of it
  • We add that table here. This implies that you also need to add a check: a study cannot be deleted if it has 'child' analyses.

I'm fine with adding an issue, since currently we are not using that and a lot of other changes will be needed once that gets in place.

Copy link
Member Author

Choose a reason for hiding this comment

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

I happy adding the check and the delete of those rows ... shouldn't be a problem.

Copy link
Member Author

Choose a reason for hiding this comment

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

Decided to leave it as an issue #1176 so the solution is complete when implemented.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks @antgonza just one minor thing: can you add a comment with a reference to the issue? Like # TODO: issue #1176, so we have a pointer in the code and we don't forget about it.

"""Deletes an analysis

Parameters
----------
_id : int
The analysis id

Raises
------
QiitaDBUnknownIDError
If the analysis id doesn't exist
"""
# check if the analysis exist
if not cls.exists(_id):
raise QiitaDBUnknownIDError(_id, "analysis")

queue = "delete_analysis_%d" % _id
conn_handler = SQLConnectionHandler()
conn_handler.create_queue(queue)

sql = ("DELETE FROM qiita.analysis_filepath WHERE "
"{0} = {1}".format(cls._analysis_id_column, _id))
conn_handler.add_to_queue(queue, sql)

sql = ("DELETE FROM qiita.analysis_workflow WHERE "
"{0} = {1}".format(cls._analysis_id_column, _id))
conn_handler.add_to_queue(queue, sql)

sql = ("DELETE FROM qiita.analysis_sample WHERE "
"{0} = {1}".format(cls._analysis_id_column, _id))
conn_handler.add_to_queue(queue, sql)

sql = ("DELETE FROM qiita.collection_analysis WHERE "
"{0} = {1}".format(cls._analysis_id_column, _id))
conn_handler.add_to_queue(queue, sql)

# TODO: issue #1176

sql = ("DELETE FROM qiita.{0} WHERE "
"{1} = {2}".format(cls._table, cls._analysis_id_column, _id))
conn_handler.add_to_queue(queue, sql)

conn_handler.execute_queue(queue)

@classmethod
def exists(cls, analysis_id):
r"""Checks if the given analysis _id exists

Parameters
----------
analysis_id : int
The id of the analysis we are searching for

Returns
-------
bool
True if exists, false otherwise.
"""
conn_handler = SQLConnectionHandler()

return conn_handler.execute_fetchone(
"SELECT EXISTS(SELECT * FROM qiita.{0} WHERE "
"{1}=%s)".format(cls._table, cls._analysis_id_column),
(analysis_id, ))[0]

# ---- Properties ----
@property
def owner(self):
Expand Down
18 changes: 17 additions & 1 deletion qiita_db/test/test_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
from qiita_db.analysis import Analysis, Collection
from qiita_db.job import Job
from qiita_db.user import User
from qiita_db.exceptions import QiitaDBStatusError, QiitaDBError
from qiita_db.exceptions import (QiitaDBStatusError, QiitaDBError,
QiitaDBUnknownIDError)
from qiita_db.util import get_mountpoint, get_count
from qiita_db.study import Study, StudyPerson
from qiita_db.data import ProcessedData
Expand Down Expand Up @@ -145,6 +146,21 @@ def test_create_from_default(self):
[new_id, 1, '1.SKM4.640180']]
self.assertEqual(obs, exp)

def test_exists(self):
self.assertTrue(Analysis.exists(1))
new_id = get_count("qiita.analysis") + 1
self.assertFalse(Analysis.exists(new_id))

def test_delete(self):
# successful delete
total_analyses = get_count("qiita.analysis")
Analysis.delete(1)
self.assertEqual(total_analyses - 1, get_count("qiita.analysis"))

# no possible to delete
with self.assertRaises(QiitaDBUnknownIDError):
Analysis.delete(total_analyses + 1)

def test_retrieve_owner(self):
self.assertEqual(self.analysis.owner, "test@foo.bar")

Expand Down
38 changes: 36 additions & 2 deletions qiita_pet/handlers/analysis_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
filepath_ids_to_rel_paths)
from qiita_db.exceptions import QiitaDBUnknownIDError
from qiita_db.study import Study
from qiita_db.logger import LogEntry

SELECT_SAMPLES = 2
SELECT_COMMANDS = 3
Expand Down Expand Up @@ -153,21 +154,54 @@ def get(self, analysis_id):
dropped[data_type].append((Study(study).title, len(samples),
', '.join(samples)))

self.render("analysis_results.html",
self.render("analysis_results.html", analysis_id=analysis_id,
jobres=jobres, aname=analysis.name, dropped=dropped,
basefolder=get_db_files_base_dir())

@authenticated
def post(self, analysis_id):
analysis_id = int(analysis_id.split("/")[0])
analysis_id_sent = int(self.get_argument('analysis_id'))
action = self.get_argument('action')

if analysis_id != analysis_id_sent or action != 'delete_analysis':
raise QiitaPetAuthorizationError(
self.current_user.id,
'analysis/results/%d-delete' % analysis_id)

analysis = Analysis(analysis_id)
analysis_name = analysis.name
check_analysis_access(self.current_user, analysis)

try:
Analysis.delete(analysis_id)
msg = ("Analysis <b><i>%s</i></b> has been deleted." % (
analysis_name))
level = "success"
except Exception as e:
e = str(e)
msg = ("Couldn't remove <b><i>%s</i></b> analysis: %s" % (
analysis_name, e))
level = "danger"
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you log the error?

LogEntry.create('Runtime', "Couldn't remove analysis ID %d: %s" %
(analysis_id, e))

self.redirect(u"/analysis/show/?level=%s&message=%s" % (level, msg))


class ShowAnalysesHandler(BaseHandler):
"""Shows the user's analyses"""
@authenticated
def get(self):
message = self.get_argument('message', '')
level = self.get_argument('level', '')
user = self.current_user

analyses = [Analysis(a) for a in
user.shared_analyses | user.private_analyses]

self.render("show_analyses.html", analyses=analyses)
self.render("show_analyses.html", analyses=analyses, message=message,
level=level)


class ResultsHandler(StaticFileHandler, BaseHandler):
Expand Down
34 changes: 33 additions & 1 deletion qiita_pet/templates/analysis_results.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,16 @@


<div class="row">
<h1>&nbsp;Analysis: <u>{{aname}}</u></h1>
<table border="0">
<tr>
<td><h1>&nbsp;Analysis: <u>{{aname}}</u></h1></td>
<td width="20px"></td>
<td>
<h1><a class="btn btn-danger glyphicon glyphicon-trash" onclick="delete_analysis();"></a></h1>
</td>
</tr>
</table>
</h1>
</div>


Expand Down Expand Up @@ -74,4 +83,27 @@ <h3 style="color:red">ERROR</h3>
<iframe id="resframe" name="resframe" width="100%" height="900" frameBorder=0></iframe>
</div>
</div>


<script>

function delete_analysis() {
if (confirm('Are you sure you want to delete analysis: {{aname}}?')) {
var form = $("<form>")
.attr("action", window.location.href)
.attr("method", "post")
.append($("<input>")
.attr("type", "hidden")
.attr("name", "analysis_id")
.attr("value", {{analysis_id}}))
.append($("<input>")
.attr("type", "hidden")
.attr("name", "action")
.attr("value", "delete_analysis"));
$("body").append(form);
form.submit();
}
}

</script>
{%end%}