Skip to content

Improving analysis listing #2170

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
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
4 changes: 3 additions & 1 deletion qiita_pet/handlers/analysis_handlers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
AnalysisGraphHandler, AnalysisJobsHandler)
from .listing_handlers import (ListAnalysesHandler, AnalysisSummaryAJAX,
SelectedSamplesHandler)
from .sharing_handlers import ShareAnalysisAJAX

__all__ = ['CreateAnalysisHandler', 'AnalysisDescriptionHandler',
'AnalysisGraphHandler', 'AnalysisJobsHandler',
'ListAnalysesHandler', 'AnalysisSummaryAJAX',
'SelectedSamplesHandler', 'check_analysis_access']
'SelectedSamplesHandler', 'check_analysis_access',
'ShareAnalysisAJAX']
68 changes: 68 additions & 0 deletions qiita_pet/handlers/analysis_handlers/sharing_handlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# -----------------------------------------------------------------------------
# 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 json import dumps

from tornado.web import authenticated, HTTPError
from tornado.gen import coroutine, Task

from qiita_core.qiita_settings import qiita_config
from qiita_core.util import execute_as_transaction
from qiita_pet.handlers.base_handlers import BaseHandler
from qiita_pet.handlers.util import get_shared_links
from qiita_db.user import User
from qiita_db.analysis import Analysis
from qiita_db.util import add_message


class ShareAnalysisAJAX(BaseHandler):
@execute_as_transaction
def _get_shared_for_study(self, analysis, callback):
shared_links = get_shared_links(analysis)
users = [u.email for u in analysis.shared_with]
callback((users, shared_links))

@execute_as_transaction
def _share(self, analysis, user, callback):
user = User(user)
add_message('Analysis <a href="%s/analysis/results/%d">\'%s\'</a> '
'has been shared with you.' %
(qiita_config.portal_dir, analysis.id, analysis.name),
[user])
callback(analysis.share(user))

@execute_as_transaction
def _unshare(self, analysis, user, callback):
user = User(user)
add_message('Analysis \'%s\' has been unshared with you.' %
analysis.name, [user])
callback(analysis.unshare(user))

@authenticated
@coroutine
@execute_as_transaction
def get(self):
analysis_id = int(self.get_argument('id'))
analysis = Analysis(analysis_id)
if self.current_user != analysis.owner and \
self.current_user not in analysis.shared_with:
raise HTTPError(403, 'User %s does not have permissions to share '
'analysis %s' % (
self.current_user.id, analysis.id))

selected = self.get_argument('selected', None)
deselected = self.get_argument('deselected', None)

if selected is not None:
yield Task(self._share, analysis, selected)
if deselected is not None:
yield Task(self._unshare, analysis, deselected)

users, links = yield Task(self._get_shared_for_study, analysis)

self.write(dumps({'users': users, 'links': links}))
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# -----------------------------------------------------------------------------
# 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 json import loads

from qiita_db.analysis import Analysis
from qiita_db.user import User
from qiita_pet.test.tornado_test_base import TestHandlerBase


class TestShareStudyAjax(TestHandlerBase):

def test_get(self):
a = Analysis(1)
u = User('shared@foo.bar')
self.assertEqual(a.shared_with, [u])

# deselecting
args = {'deselected': u.id, 'id': a.id}
response = self.get('/analysis/sharing/', args)
self.assertEqual(response.code, 200)
exp = {'users': [], 'links': ''}
Copy link
Contributor

Choose a reason for hiding this comment

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

When analyses are unshared the output data should be either empty or include a "completed" message. We were doing this for the portion of the REST API that @wasade and I worked on. Seemed clean and you don't really need an empty list of users or links. What do you think?

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 think that's a fair point and perhaps worth it. Note that this will imply that we will need to have 2 calls for the same functionality as right now the call actually also changes the display so the new code will have to be: delete/add user, request current values.

Now, based on (1) this code/mechanism is based on Artifact sharing, which means that we will need to change both to be complete and (2) that what I did is simply return these deleted lines from this branch + add some tests; I also believe that this is out of the scope of this PR and opening an issue for further discussion is the best path forward. See #2171.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree, sounds good!

self.assertEqual(loads(response.body), exp)
self.assertEqual(a.shared_with, [])

# Make sure unshared message added to the system
self.assertEqual("Analysis 'SomeAnalysis' has been unshared with you.",
u.messages()[0][1])

# selecting
args = {'selected': u.id, 'id': a.id}
response = self.get('/analysis/sharing/', args)
self.assertEqual(response.code, 200)
exp = {
'users': ['shared@foo.bar'],
'links':
('<a target="_blank" href="mailto:shared@foo.bar">Shared</a>')}
self.assertEqual(loads(response.body), exp)
self.assertEqual(a.shared_with, [u])

# Make sure shared message added to the system
self.assertEqual('Analysis <a href="/analysis/results/1">\'SomeAnalys'
'is\'</a> has been shared with you.',
u.messages()[0][1])

def test_get_no_access(self):
args = {'selected': 'demo@microbio.me', 'id': 2}
response = self.get('/analysis/sharing/', args)
self.assertEqual(response.code, 403)


if __name__ == '__main__':
main()
3 changes: 2 additions & 1 deletion qiita_pet/static/js/qiita.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ function draw_processing_graph(nodes, edges, target, artifactFunc, jobFunc) {
edges: edges
};
var options = {
clickToUse: true,
nodes: {
shape: 'dot',
font: {
Expand All @@ -172,7 +173,7 @@ function draw_processing_graph(nodes, edges, target, artifactFunc, jobFunc) {
zoomView: true,
selectConnectedEdges: true,
navigationButtons: true,
keyboard: true
keyboard: false
},
groups: {
jobs: {
Expand Down
3 changes: 1 addition & 2 deletions qiita_pet/static/js/sharing.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ function init_sharing(portal) {

function modify_sharing(id) {
var shared_list;
$('#shares-select').attr('data-current-id', id);
$.get($('#shares-select').attr('data-share-url'), {id: id})
.done(function(data) {
var users_links = JSON.parse(data);
Expand All @@ -51,4 +50,4 @@ function update_share(params) {
links = users_links.links;
$("#shared_html_"+share_id).html(links);
});
}
}
51 changes: 46 additions & 5 deletions qiita_pet/templates/analysis_description.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{% extends sitebase.html %}
{% block head %}
<script type="text/javascript">
<link rel="stylesheet" href="{% raw qiita_config.portal_dir %}/static/vendor/css/select2.min.css" type="text/css">
<script type="text/javascript" src="{% raw qiita_config.portal_dir %}/static/vendor/js/select2.min.js"></script>
<script type="text/javascript" src="{% raw qiita_config.portal_dir %}/static/js/sharing.js"></script>

<script type="text/javascript">
/**
*
* This function is needed by the artifact subsection of the page. Since the
Expand Down Expand Up @@ -219,12 +222,16 @@
{% if alert_msg %}
bootstrapAlert("{{alert_msg}}", "{{alert_type}}");
{% end %}

// starting share
init_sharing("{% raw qiita_config.portal_dir %}");
update_share();
});
</script>
<style>
.graph {
width:100%;
height:300px;
width: 80%;
height: 400px;
border: 1px solid #ccc;
}
</style>
Expand All @@ -233,8 +240,12 @@

<div class="row">
<div class="col">
<h2>{{analysis_name}} - ID {{analysis_id}}</h2>
<h2>
{{analysis_name}} - ID {{analysis_id}}
<a class="btn btn-info glyphicon glyphicon-share" data-toggle="modal" data-target="#share-analysis-modal-view" onclick="modify_sharing({{analysis_id}});"></a>
</h2>
<h3>{{analysis_description}}</h3>
Shared with: <span id="shared_html_{{analysis_id}}"></span>
</div>
</div>
<div class="row" id='analysis-content-div'>
Expand All @@ -245,7 +256,11 @@ <h4><a class="btn btn-info" id="show-hide-network-btn" onclick="toggle_network_g
</div>
</div>
<div class='row' id="analysis-graph-vue">
<analysis-graph></analysis-graph>
<div class='col-md-1'>
</div>
<div class='col-md-11'>
<analysis-graph></analysis-graph>
</div>
</div>
<div class='row'>
<div class='col-md-12' style='width:90%' id='analysis-job-div'>
Expand All @@ -258,4 +273,30 @@ <h4><a class="btn btn-info" id="show-hide-network-btn" onclick="toggle_network_g
</div>
<div class="row" id='processing-content-div'></div>


<!-- Modal used to share the analysis -->
<div class="modal fade" id="share-analysis-modal-view" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="myModalLabel">Modify Sharing Settings</h4>
</div>
<div class="modal-body">
<div>
<div class="form-group">
<label for="shares-select">Add/Remove Users</label>
<select multiple class="analysis" id="shares-select" data-share-url="{% raw qiita_config.portal_dir %}/analysis/sharing/" data-current-id={{analysis_id}} style="width:50%"></select>
<br>
<br>
Adding or removing email addresses automatically updates who the analysis is shared with. Once you click the `X` or give mouse focus to the analysis page you'll see your new sharing settings.
</div>
</div>
</div>
<div class="modal-footer">
</div>
</div>
</div>
</div>

{% end %}
39 changes: 26 additions & 13 deletions qiita_pet/templates/artifact_ajax/artifact_summary.html
Original file line number Diff line number Diff line change
Expand Up @@ -127,31 +127,44 @@ <h4>
<div class='row'>
<div class='col-md-12'>
{% if processing_jobs %}
<b>Jobs using this set of files:</b></br>
{% end %}
{% for j_id, c_name, j_status, j_step, j_error in processing_jobs %}
Job <b>{{j_id}}</b> ({{c_name}}). Status: <i>{{j_status}}</i>.
{% if j_step %}
Step: <i>{{j_step}}</i>
{% end %}
{% if j_error %}
Error message: <i>{{j_error}}</i>
{% end %}
</br>
<br/>
<button class="btn btn-info btn-sm" data-toggle="collapse" data-target="#jobs-description">Jobs using this data</button>
<div id="jobs-description" class="collapse">
{% for j_id, c_name, j_status, j_step, j_error in processing_jobs %}
Job <b>{{j_id}}</b> ({{c_name}}). Status: <i>{{j_status}}</i>.
{% if j_step %}
Step: <i>{{j_step}}</i>
{% end %}
{% if j_error %}
Error message: <i>{{j_error}}</i>
{% end %}
</br>
{% end %}
<br/>
</div>
{% end %}
</div>
</div>
<div class='row'>
<div class='col-md-12' id='artifact-summary-content'>
{% if summary is not None %}
<br/>
<a class="btn btn-info btn-sm" href="/artifact/html_summary/{{summary}}" target="_blank">Open summary in a new window</a>
<br/>
<iframe width="100%" height="900" src="/artifact/html_summary/{{summary}}" frameBorder=0></iframe>
{% elif job is not None %}
Job <b>{{ job[0] }}</b>: <i>{{ job[1] }}</i> {{ job[2] }}
{% else %}
Currently, no summary exists. </br>
<button class="btn btn-info btn-sm" onclick="generate_html_summary({{artifact_id}});">Generate summary</button></br>
{% for j_id, j_error in errored_jobs %}
Job <b>{{ j_id }}</b>: <i>{% raw j_error.replace('\n', '</br>') %}</i></br>
{% if errored_jobs %}
<br/>
<button class="btn btn-info btn-sm" data-toggle="collapse" data-target="#jobs-error-description">Errored jobs</button>
<div id="jobs-error-description" class="collapse">
{% for j_id, j_error in errored_jobs %}
Job <b>{{ j_id }}</b>: <i>{% raw j_error.replace('\n', '</br>') %}</i></br>
{% end %}
</div>
{% end %}
{% end %}
</div>
Expand Down
13 changes: 11 additions & 2 deletions qiita_pet/templates/artifact_ajax/processing_artifact.html
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@
}
$("<div>").appendTo("#cmd-opts-div").attr('id', 'opt-vals-div').attr('name', 'opt-vals-div');

sel.change(function(event){
sel.change(function(){
var v = $("#params-sel").val();
$("#opt-vals-div").empty();
if (v !== "") {
Expand All @@ -356,6 +356,14 @@
$("#add-cmd-btn-div").hide();
}
});

sel.show(function(){
// select first option if only 2 options ("Choose parameter set", "unique value")
if ($("#params-sel option").length == 2) {
$("#params-sel")[0].selectedIndex = 1;
$("#params-sel").trigger("change");
}
});
});
}

Expand Down Expand Up @@ -422,6 +430,7 @@
var data = {nodes: nodes, edges: edges};

var options = {
clickToUse: true,
nodes: {
shape: 'dot',
font: {
Expand All @@ -447,7 +456,7 @@
zoomView: true,
selectConnectedEdges: false,
navigationButtons: true,
keyboard: true
keyboard: false
},
groups: {
jobs: {
Expand Down
Loading