Skip to content

Commit

Permalink
Merge pull request #1310 from squirrelo/portals-pet
Browse files Browse the repository at this point in the history
Portals pet - Jose & Yoshiki
  • Loading branch information
josenavas committed Jul 4, 2015
2 parents 7704a5e + 52b77a0 commit bb2dadd
Show file tree
Hide file tree
Showing 7 changed files with 333 additions and 8 deletions.
2 changes: 1 addition & 1 deletion qiita_db/environment_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def make_environment(load_ontologies, download_reference, add_demo_user):
sql = """INSERT INTO qiita.analysis_portal
(analysis_id, portal_type_id)
VALUES (%s, %s)"""
conn.execute_many(sql, args)
conn.executemany(sql, args)

print('Demo user successfully created')

Expand Down
14 changes: 10 additions & 4 deletions qiita_db/portal.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ def __init__(self, portal):

@staticmethod
def list_portals():
"""Returns list of portals available in system
"""Returns list of non-default portals available in system
Returns
-------
list of str
List of portal names for the system
Notes
-----
This does not return the QIITA portal in the list, as it is a required
Expand Down Expand Up @@ -237,15 +239,17 @@ def add_studies(self, studies):
sql, [self._id, tuple(studies)])]

if len(duplicates) > 0:
warnings.warn("The following studies area already part of %s: %s" %
warnings.warn("The following studies are already part of %s: %s" %
(self.portal, ', '.join(map(str, duplicates))),
QiitaDBWarning)

# Add cleaned list to the portal
clean_studies = set(studies).difference(duplicates)
sql = """INSERT INTO qiita.study_portal (study_id, portal_type_id)
VALUES (%s, %s)"""
conn_handler.executemany(sql, [(s, self._id) for s in clean_studies])
if len(clean_studies) != 0:
conn_handler.executemany(
sql, [(s, self._id) for s in clean_studies])

def remove_studies(self, studies):
"""Removes studies from given portal
Expand Down Expand Up @@ -357,7 +361,9 @@ def add_analyses(self, analyses):
(analysis_id, portal_type_id)
VALUES (%s, %s)"""
clean_analyses = set(analyses).difference(duplicates)
conn_handler.executemany(sql, [(a, self._id) for a in clean_analyses])
if len(clean_analyses) != 0:
conn_handler.executemany(
sql, [(a, self._id) for a in clean_analyses])

def remove_analyses(self, analyses):
"""Removes analyses from given portal
Expand Down
90 changes: 90 additions & 0 deletions qiita_pet/handlers/portal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# -----------------------------------------------------------------------------
# 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.
# -----------------------------------------------------------------------------
import warnings
from json import dumps
from copy import deepcopy

from tornado.web import authenticated, HTTPError

from qiita_db.study import Study
from qiita_db.portal import Portal
from .base_handlers import BaseHandler


class PortalEditBase(BaseHandler):
study_cols = ['study_id', 'study_title', 'study_alias']

def check_admin(self):
if self.current_user.level != "admin":
raise HTTPError(403, "%s does not have access to portal editing!" %
self.current_user.id)

def get_info(self, portal="QIITA"):
# Add the portals and, optionally, checkbox to the information
studies = Portal(portal).get_studies()
if not studies:
return []

study_info = Study.get_info(studies, info_cols=self.study_cols)
info = []
for s in study_info:
# Make sure in correct order
hold = dict(s)
hold['portals'] = ', '.join(sorted(Study(s['study_id'])._portals))
info.append(hold)
return info


class StudyPortalHandler(PortalEditBase):
@authenticated
def get(self):
self.check_admin()
info = self.get_info()
portals = Portal.list_portals()
headers = deepcopy(self.study_cols)
headers.insert(0, "portals")
self.render('portals_edit.html', headers=headers, info=info,
portals=portals, submit_url="/admin/portals/studies/")

@authenticated
def post(self):
self.check_admin()
portal = self.get_argument('portal')
studies = map(int, self.get_arguments('selected'))
action = self.get_argument('action')

portal = Portal(portal)
with warnings.catch_warnings(record=True) as warns:
if action == "Add":
portal.add_studies(studies)
elif action == "Remove":
portal.remove_studies(studies)
else:
raise HTTPError(400, "Unknown action: %s" % action)

msg = '; '.join([str(w.message) for w in warns])
self.write(action + " completed successfully<br/>" + msg)


class StudyPortalAJAXHandler(PortalEditBase):
@authenticated
def get(self):
self.check_admin()
portal = self.get_argument('view-portal')
echo = self.get_argument('sEcho')
info = self.get_info(portal=portal)
# build the table json
results = {
"sEcho": echo,
"iTotalRecords": len(info),
"iTotalDisplayRecords": len(info),
"aaData": info
}

# return the json in compact form to save transmit size
self.write(dumps(results, separators=(',', ':')))
52 changes: 51 additions & 1 deletion qiita_pet/static/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,54 @@

.dropdown:hover .dropdown-menu {
display: block;
}
}

.topfloat{
position: fixed;
padding-top: 5px;
height:55px;
top:50px;
z-index:6;
background:white;
width:100%;
}

/* Make the navbar collapse at medium size, not small.
Fixes really ugly middle ground where menu stacking occurs
adapted from http://stackoverflow.com/a/23298184 */
@media (max-width: 991px) {
.navbar-header {
float: none;
}
.navbar-toggle {
display: block;
}
.navbar-collapse {
border-top: 1px solid transparent;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.1);
}
.navbar-collapse.collapse {
display: none!important;
}
.navbar-nav {
float: none!important;
margin: 7.5px -15px;
}
.navbar-nav>li {
float: none;
}
.navbar-nav>li>a {
padding-top: 10px;
padding-bottom: 10px;
}
.navbar-text {
float: none;
margin: 15px 0;
}
/* since 3.1.0 */ .navbar-collapse.collapse.in {
display: block!important;
}
.collapsing {
overflow: hidden!important;
}
}
164 changes: 164 additions & 0 deletions qiita_pet/templates/portals_edit.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
{% extends sitebase.html%}

{%block head%}
<link rel="stylesheet" href="/static/vendor/css/jquery.dataTables.css" type="text/css">
<style type="text/css">
.navlist li
{
display: inline;
padding-right: 20px;
}
.portal-select {
width: 15em;
}
.alert {
position: fixed;
top: 55px;
left:40%;
z-index:100;
white-space: nowrap;
}
</style>
<script src="/static/vendor/js/jquery.dataTables.min.js"></script>
<script type="text/javascript">
function check_submit(action) {
//disable submit buttons
$("#add-button").prop('disabled', true);
$("#remove-button").prop('disabled', true);
$("#messages").html("");
var errors = "";
if($("#portal").val() === '') {
//No selected portal, so add error message if needed and then don't submit
if(!$('#portal-error').length) { errors += "Please select a portal<br/>"; }
}
var boxes = oTable.$(".selected:checked", {"page": "all"});
//serialize the checked boxes
var data = "";
for(i=0;i<boxes.length;i++) {
data += "&selected="+boxes[i].value;
}
if(data.length === 0) {
//No checked rows, so add error message if needed and then don't submit
if(!$('#checkbox-error').length) { errors += "Please select at least one row<br/>"; }
}
if(errors.length > 0) {
$("#add-button").prop('disabled', false);
$("#remove-button").prop('disabled', false);
bootstrapAlert(errors, "danger", 1900);
return false;
}
$('#action').val(action);
data = $('#portal-form').serialize() + data;
$.post('{{submit_url}}', data, function(data,textStatus,jqXHR){
$("#add-button").prop('disabled', false);
$("#remove-button").prop('disabled', false);
var echo = "?sEcho=" + Math.floor(Math.random()*1001) + "&view-portal=" + $("#view-portal").val();
$('#info-table').DataTable().ajax.url("/admin/portals/studiesAJAX/" + echo).load();
bootstrapAlert(data, "success", 1900);
$("#portal").val('');
});
}

function render_checkbox(data, type, full) {
return "<input type='checkbox' class='selected' onclick='checkbox_change()' value='" + full['study_id'] + "' />";
}

function checkbox_change() {
$("#checkbox-error").remove();
}

function checkbox_action(action) {
var boxes = oTable.$(':checkbox', {"filter":"applied", "page": "all"});
if(action == 'check') { boxes.prop('checked',true); }
else if(action == 'uncheck') { boxes.prop('checked',false); }
else if(action='invert') { boxes.each( function() {
$(this).is(':checked') ? $(this).prop('checked',false) : $(this).prop('checked',true);
});}
return false;
}

$(document).ready(function() {
oTable = $('#info-table').dataTable({
"deferRender": true,
"iDisplayLength": 50,
"oLanguage": {
"sZeroRecords": "No studies belong to selected portal"
},
"columns": [
{"className": 'select', "data": null},
{% for h in headers %}
{"data": "{{h}}"},
{% end %}
],
'aoColumnDefs': [
{ 'mRender': render_checkbox, 'mData': 2, 'aTargets': [ 0 ] }
],
"ajax": {
"url": "/admin/portals/studiesAJAX/?sEcho=" + Math.floor(Math.random()*1001) + "&view-portal=QIITA",
"aoColumns": [
{"sSortDataType": "dom-checkbox", "bSearchable": false, "bSortable": false},
{% for h in headers %}
{"data": "{{h}}"},
{% end %}
],
"error": function(jqXHR, textStatus, ex) {
$("#add-button").prop('disabled', true);
$("#remove-button").prop('disabled', true);
$("#errors").append("<li id='comm-error'>Internal Server Error, please try again later</li>");
}
}
});

$('#portal').change(function(){
$("#portal-error").remove();
});

$('#view-portal').change(function() {
var echo = "?sEcho=" + Math.floor(Math.random()*1001) + "&view-portal=" + $("#view-portal").val();
$('#info-table').DataTable().ajax.url("/admin/portals/studiesAJAX/" + echo).load();
});
});
</script>
{%end%}

{%block content%}
<div class="row topfloat">
<div class='col-lg-12'>
<span style="float:right"><b>View from portal:</b>
<select name="view-portal" id="view-portal" class="portal-select">
<option value="QIITA">QIITA</option>
{% for portal in portals %}
<option value="{{portal}}">{{portal}}</option>
{% end %}
</select>
</span>
<form role="form" id="portal-form">
<select name="portal" id="portal" class="portal-select">
<option value="">Select Portal...</option>
{% for portal in portals %}
<option value="{{portal}}">{{portal}}</option>
{% end %}
</select>
<input type="hidden" name="action" id="action" value="">
<input type="button" id="add-button" value="Add" class="btn btn-sm btn-success" onclick="check_submit('Add')"> <input type="button" id="remove-button" value="Remove" class="btn btn-sm btn-danger" onclick="check_submit('Remove')">
</form>
</div>
</div>
<div class='row' style="margin-top:50px">
<div class='col-lg-12'>
<input type=button onclick="checkbox_action('check')" class="btn btn-xs btn-info" value="Select All"> | <input type=button onclick="checkbox_action('uncheck')" class="btn btn-xs btn-info" value="Select None"> | <input type=button onclick="checkbox_action('inverse')" class="btn btn-xs btn-info" value="Select Inverse"><br/>
<table id="info-table" class="table">
<thead>
<tr>
<td>Select</td>
{% for head in headers %}
<td>{{head}}</td>
{% end %}
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
{% end %}
4 changes: 4 additions & 0 deletions qiita_pet/templates/sitebase.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{% from moi import r_client %}
{% from qiita_core.qiita_settings import qiita_config %}
{% from qiita_pet import allowed_min_browser_versions %}
{% from qiita_pet.portal import portal_styling %}
{% set maintenance = r_client.get('maintenance') %}
Expand Down Expand Up @@ -145,6 +146,9 @@
<ul class="dropdown-menu">
<li><a href="/admin/error/">View Errors</a></li>
<li><a href="/admin/approval/">View Studies awaiting approval</a></li>
{% if qiita_config.portal == "QIITA" %}
<li><a href="/admin/portals/studies/">Edit study portal connections</a></li>
{% end %}
</ul>
</li>
{% elif user_level == 'dev' %}
Expand Down
Loading

0 comments on commit bb2dadd

Please sign in to comment.