Skip to content

Commit

Permalink
Merge 97d93c4 into a748c94
Browse files Browse the repository at this point in the history
  • Loading branch information
squirrelo committed Jun 19, 2014
2 parents a748c94 + 97d93c4 commit 31b4a85
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 59 deletions.
5 changes: 1 addition & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,8 @@ install:
- pip install .
script:
- qiita_env make_test_env
- nosetests --with-doctest
- nosetests --with-doctest --with-coverage --cover-package=coveralls
- pep8 qiita_db qiita_core qiita_pet setup.py
# we need to run the test suite from setup.py for coveralls to grab the info
- coverage run setup.py test
- coverage report -m
services:
- redis-server
after_success:
Expand Down
58 changes: 58 additions & 0 deletions qiita_pet/handlers/analysis_handlers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
r"""
Qitta analysis handlers for the Tornado webserver.
"""
# -----------------------------------------------------------------------------
# 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 __future__ import division

from tornado.web import authenticated
from collections import defaultdict

Expand All @@ -7,6 +20,7 @@
from qiita_db.study import Study
from qiita_db.data import ProcessedData
from qiita_db.metadata_template import SampleTemplate
from qiita_db.job import Job
# login code modified from https://gist.github.com/guillaumevincent/4771570


Expand Down Expand Up @@ -78,3 +92,47 @@ def post(self):
samples = [(processed_data[data_type], sid) for sid in
sample_ids]
analysis.add_samples(samples)


class AnalysisWaitHandler(BaseHandler):
@authenticated
def get(self, analysis_id):
analysis = Analysis(analysis_id)
commands = []
for job in analysis.jobs:
jobject = Job(job)
commands.append("%s:%s" % (jobject.datatype, jobject.command[0]))

self.render("analysis_waiting.html", user=self.get_current_user(),
aid=analysis_id, aname=analysis.name,
commands=commands)

@authenticated
def post(self, analysis_id):
command_args = self.get_arguments("commands")
split = [x.split("#") for x in command_args]
analysis = Analysis(analysis_id)

commands = []
for data_type, command in split:
job = Job.create(data_type, command, {}, analysis)
commands.append("%s:%s" % (data_type, command))

self.render("analysis_waiting.html", user=self.get_current_user(),
aid=analysis_id, aname=analysis.name,
commands=commands)
# fire off analysis run here


class AnalysisResultsHandler(BaseHandler):
@authenticated
def get(self, aid):
analysis = Analysis(aid)
jobres = defaultdict(list)
for job in analysis.jobs:
jobject = Job(job)
jobres[jobject.datatype].append((jobject.command[0],
jobject.results))

self.render("analysis_results.html", user=self.get_current_user(),
jobres=jobres, aname=analysis.name)
40 changes: 13 additions & 27 deletions qiita_pet/handlers/websocket_handlers.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
# adapted from
# https://github.com/leporo/tornado-redis/blob/master/demos/websockets
from time import sleep

from redis import Redis
from tornadoredis import Client
from tornado.websocket import WebSocketHandler
import tornado.gen
from tornado.gen import engine, Task
from json import loads

# all messages are in json format. They must have the following format:
# 'job': jobname
# 'analysis': analysis_id
# 'msg': message to print
# 'analysis': what analysis this is from in format datatype:analysis
# 'results': list of files created if any
# 'command': what command this is from in format datatype#command


class MessageHandler(WebSocketHandler):
def __init__(self, *args, **kwargs):
super(MessageHandler, self).__init__(*args, **kwargs)
self.r_server = Redis()
self.redis = Client()
self.redis.connect()
# self.redis = Client()
# self.redis.connect()

def get_current_user(self):
user = self.get_secure_cookie("user")
Expand All @@ -32,33 +29,22 @@ def on_message(self, msg):
msginfo = loads(msg)
# listens for handshake from page
if "user:" in msginfo['msg']:
self.channel = msginfo['msg'].split(':')[1]
self.aid = msginfo['msg'].split()[0]
self.channel = msginfo['msg'].split()[1].split(':')[1]
# need to split the rest off to new func so it can be asynchronous
self.listen()

# decorator turns the function into an asynchronous generator object
@tornado.gen.engine
@engine
def listen(self):
# runs task given, with the yield required to get returned value
# equivalent of callback/wait pairing from tornado.gen
yield tornado.gen.Task(self.redis.subscribe, self.channel)
if not self.redis.subscribed:
self.write_message('ERROR IN SUBSCRIPTION')
# listen from tornadoredis makes the listen object asynchronous
# if using standard redis lib, it blocks while listening
self.redis.listen(self.callback)
# fight race condition by loading from redis after listen started
# need to use std redis lib because tornadoredis is in subscribed state
oldmessages = self.r_server.lrange(self.channel + ':messages', 0, -1)
if oldmessages is not None:
for message in oldmessages:
self.write_message(message)
sleep(5)
self.write_message({"analysis": self.aid, "msg": "allcomplete"})

def callback(self, msg):
if msg.kind == 'message':
self.write_message(str(msg.body))

@tornado.gen.engine
@engine
def on_close(self):
yield tornado.gen.Task(self.redis.unsubscribe, self.channel)
yield Task(self.redis.unsubscribe, self.channel)
self.redis.disconnect()
1 change: 1 addition & 0 deletions qiita_pet/static/css/style.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#template-content{
padding: 20px;
height: 100%;
}

37 changes: 37 additions & 0 deletions qiita_pet/templates/analysis_results.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{% extends sitebase.html%}

{%block head%}


{%end%}

{%block content%}
<div class="container">
<div class="row">
<h1>Analysis {{aname}}</h1>
</div>
<div class="row">
<div class="col-md-2">
<div class="panel-group" id="accordion">
{% for data_type, jobs in jobres.items() %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><a data-toggle="collapse" data-parent="#accordion" href="#{{data_type}}">{{data_type}}</a></h3>
</div>
<div id="{{data_type}}" class="panel-collapse collapse">
{% for job, result in jobs%}
<div class="panel-body">
<a href="/results/{{result}}" target="resframe">{{job}}</a>
</div>
{% end %}
</div>
</div>
{% end %}
</div>
</div>
<div class="col-md-10">
<iframe id="resframe" name="resframe" width="100%" height="900" frameBorder=0></iframe>
</div>
</div>
</div>
{%end%}
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@
noerror = true;

websocket.onopen = function() {
websocket.send(JSON.stringify({'msg': 'user:{{user}}'}));
console.log(JSON.stringify({'msg': 'user:{{user}}'}));
websocket.send(JSON.stringify({'msg': '{{aid}} user:{{user}}'}));
console.log(JSON.stringify({'msg':'{{aid}} user:{{user}}'}));
};
websocket.onmessage = function(evt) {
console.log(evt.data);
message = JSON.parse(evt.data);
if(message.job == '{{job}}') {
if(message.analysis == '{{aid}}') {
if(noerror && message.msg == 'allcomplete') {
window.location.replace("/completed/{{job}}");
window.location.replace("/analysis/results/{{aid}}");
}
list = document.getElementById(message.analysis);
if(message.msg.indexOf("ERROR") != -1) {
Expand All @@ -30,11 +30,7 @@
else {
list.style.color="Blue";
}
list.innerHTML = message.msg + '<br />';
for(var i=0; i<message.results.length; i++) {
list.innerHTML += '<a class="link" target="_blank" href="../' +
message.results[i] + '">Plot ' + (i+1) + '</a><br />';
}
list.innerHTML = message.msg;
}
};
websocket.onerror = function(evt) { };
Expand All @@ -52,24 +48,12 @@
{% end %}

{% block content %}
<table width='100%'>
<tr>
<td width=180 valign='top'>
1) Choose studies<br />
2) Select analyses<br />
3) Set analysis options<br />
4) Review analysis<br />
<b>5) Running analysis</b><br />
6) Analysis results<br />
</td>
<td>
<h1>Analysis {{job}}</h1>
{% for analysis in analyses %}
<h1>Analysis {{aname}}</h1>
{% for command in commands%}
<p>
{{analysis}}: <span id='{{analysis}}'>Queued</span>
{{command}}: <span id='{{command}}'>Queued</span>
</p>
{% end %}
</td></tr></table>
<div id='error'></div>

{% end %}
3 changes: 1 addition & 2 deletions qiita_pet/templates/select_commands.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ <h1>Select Commands</h1>
{% end %}
</ul>

<form role="form" action="/analysis/wait/" method="post">
<input type="hidden" name="analysis-id" value="{{aid}}">
<form role="form" action="/analysis/wait/{{aid}}" method="post">
<div class="tab-content" style="height:300px">
{% for data_type in data_types %}
<div class="tab-pane" id="{{ data_type }}">
Expand Down
2 changes: 1 addition & 1 deletion qiita_pet/templates/sitebase.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

{% block head %}{% end %}
</head>
<body style="padding-top: 65px;">
<body style="padding-top: 65px; height: 100%">
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
Expand Down
7 changes: 6 additions & 1 deletion qiita_pet/webserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
from qiita_pet.handlers.auth_handlers import (
AuthCreateHandler, AuthLoginHandler, AuthLogoutHandler, AuthVerifyHandler)
from qiita_pet.handlers.analysis_handlers import (
CreateAnalysisHandler, SelectStudiesHandler, SelectCommandsHandler)
CreateAnalysisHandler, SelectStudiesHandler, SelectCommandsHandler,
AnalysisWaitHandler, AnalysisResultsHandler)
from qiita_pet.handlers.websocket_handlers import MessageHandler

define("port", default=8888, help="run on the given port", type=int)

Expand All @@ -43,6 +45,9 @@ def __init__(self):
(r"/analysis/1", CreateAnalysisHandler),
(r"/analysis/2", SelectStudiesHandler),
(r"/analysis/3", SelectCommandsHandler),
(r"/analysis/wait/(.*)", AnalysisWaitHandler),
(r"/analysis/results/(.*)", AnalysisResultsHandler),
(r"/consumer/", MessageHandler),
(r"/mockup/", MockupHandler),
# 404 PAGE MUST BE LAST IN THIS LIST!
(r".*", NoPageHandler)
Expand Down

0 comments on commit 31b4a85

Please sign in to comment.