-
Notifications
You must be signed in to change notification settings - Fork 80
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
Initial qiita_pet commit #31
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
#!/usr/bin/env python | ||
|
||
__author__ = "Joshua Shorenstein" | ||
__copyright__ = "Copyright 2013, The QiiTa-pet Project" | ||
__credits__ = ["Joshua Shorenstein", "Antonio Gonzalez", | ||
"Jose Antonio Navas Molina"] | ||
__license__ = "BSD" | ||
__version__ = "0.2.0-dev" | ||
__maintainer__ = "Joshua Shorenstein" | ||
__email__ = "[email protected]" | ||
__status__ = "Development" | ||
|
||
from tornado.web import authenticated, HTTPError | ||
|
||
from .webserver import BaseHandler | ||
from qiita_core.qiita_settings import DATATYPES, FUNCTIONS | ||
|
||
|
||
class WaitingHandler(BaseHandler): | ||
"""Waiting Page""" | ||
@authenticated | ||
def get(self, analysis): | ||
pass | ||
|
||
@authenticated | ||
#This post function takes care of actual job submission | ||
def post(self, page): | ||
pass | ||
|
||
|
||
class RunningHandler(BaseHandler): | ||
"""Currently running jobs list handler""" | ||
@authenticated | ||
def get(self): | ||
pass | ||
|
||
|
||
class ShowAnalysisHandler(BaseHandler): | ||
"""Completed analysis page""" | ||
@authenticated | ||
def get(self, analysis): | ||
pass | ||
|
||
@authenticated | ||
def post(self, page): | ||
pass | ||
|
||
|
||
class DeleteAnalysisHandler(BaseHandler): | ||
@authenticated | ||
def post(self): | ||
pass | ||
|
||
|
||
#ANALYSES and COMBINED lists are set in settings.py | ||
class QiitaAnalysisHandler(BaseHandler): | ||
#@authenticated | ||
def get(self, page): | ||
if page != "1": | ||
HTTPError("405", "Request page >1 of QiitaAnalysisHandler") | ||
else: | ||
#global variable that is wiped when you start a new analysis | ||
self.render("meta1.html", user=self.get_current_user(), error="", | ||
metadata=["meta1", "meta2", "meta3"], datatypes=DATATYPES) | ||
|
||
@authenticated | ||
def post(self, page): | ||
if page == "1": | ||
HTTPError("405", "Post to page 1 of QiitaAnalysisHandler") | ||
elif page == "2": | ||
raise NotImplementedError("MetaAnalysis Page %s missing!" % page) | ||
else: | ||
raise NotImplementedError("MetaAnalysis Page %s missing!" % page) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
#!/usr/bin/env python | ||
|
||
__author__ = "Joshua Shorenstein" | ||
__copyright__ = "Copyright 2013, The QiiTa-pet Project" | ||
__credits__ = ["Joshua Shorenstein"] | ||
__license__ = "BSD" | ||
__version__ = "0.2.0-dev" | ||
__maintainer__ = "Joshua Shorenstein" | ||
__email__ = "[email protected]" | ||
__status__ = "Development" | ||
|
||
from hashlib import sha512 | ||
|
||
from tornado.escape import url_escape, json_encode | ||
|
||
from .webserver import BaseHandler | ||
from ..qiita_ware.api.user_manager import create_user, check_password | ||
from ..qiita_core.exceptions import QiitaUserError | ||
|
||
|
||
class AuthCreateHandler(BaseHandler): | ||
'''User Creation''' | ||
def get(self): | ||
try: | ||
error_message = self.get_argument("error") | ||
# Tornado can raise an Exception directly, not a defined type | ||
except Exception, e: | ||
error_message = str(e) | ||
self.render("create.html", user=self.get_current_user(), | ||
errormessage=url_escape(error_message)) | ||
|
||
def post(self): | ||
username = self.get_argument("username", "") | ||
passwd = sha512(self.get_argument("password", "")).hexdigest() | ||
try: | ||
create_user(username, passwd) | ||
except QiitaUserError, e: | ||
error_msg = u"?error=" + url_escape(str(e)) | ||
self.redirect(u"/auth/create/" + error_msg) | ||
return | ||
self.redirect(u"/auth/login/?error=User+created") | ||
|
||
|
||
class AuthLoginHandler(BaseHandler): | ||
'''Login Page''' | ||
def get(self): | ||
try: | ||
error_message = self.get_argument("error") | ||
# Tornado can raise an Exception directly, not a defined type | ||
except Exception: | ||
error_message = "" | ||
|
||
self.render("login.html", user=self.get_current_user(), | ||
errormessage=error_message) | ||
|
||
def post(self): | ||
username = self.get_argument("username", "") | ||
passwd = sha512(self.get_argument("password", "")).hexdigest() | ||
auth = check_password(username, passwd) | ||
if auth: | ||
self.set_current_user(username) | ||
self.redirect(self.get_argument("next", u"/")) | ||
else: | ||
error_msg = u"?error=%s" % url_escape("Login incorrect") | ||
self.redirect(u"/auth/login/" + error_msg) | ||
|
||
def set_current_user(self, user): | ||
"""Sets current user or, if already set, clears current user | ||
Input: | ||
user: username to set | ||
""" | ||
if user: | ||
self.set_secure_cookie("user", json_encode(user)) | ||
else: | ||
self.clear_cookie("user") | ||
|
||
|
||
class AuthLogoutHandler(BaseHandler): | ||
'''Logout handler, no page necessary''' | ||
def get(self): | ||
self.clear_cookie("user") | ||
self.redirect("/") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
#!/usr/bin/env python | ||
|
||
__author__ = "Joshua Shorenstein" | ||
__copyright__ = "Copyright 2013, The QiiTa-pet Project" | ||
__credits__ = ["Joshua Shorenstein", "Jose Antonio Navas Molina"] | ||
__license__ = "BSD" | ||
__version__ = "0.2.0-dev" | ||
__maintainer__ = "Joshua Shorenstein" | ||
__email__ = "[email protected]" | ||
__status__ = "Development" | ||
|
||
from redis import Redis | ||
from redis.exceptions import RedisError | ||
|
||
# Set up Redis connection | ||
try: | ||
r_server = Redis() | ||
except RedisError, e: | ||
raise RuntimeError("Unable to connect to the REDIS database: %s" % e) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
#!/usr/bin/env python | ||
|
||
__author__ = "Joshua Shorenstein" | ||
__copyright__ = "Copyright 2013, The QiiTa-pet Project" | ||
__credits__ = ["Joshua Shorenstein", "Jose Antonio Navas Molina"] | ||
__license__ = "BSD" | ||
__version__ = "0.2.0-dev" | ||
__maintainer__ = "Joshua Shorenstein" | ||
__email__ = "[email protected]" | ||
__status__ = "Development" | ||
|
||
# Adapted from | ||
# https://github.com/leporo/tornado-redis/blob/master/demos/websockets | ||
from json import loads, dumps | ||
|
||
from tornadoredis import Client | ||
from tornado.websocket import WebSocketHandler | ||
from tornado.gen import engine, Task | ||
from pyparsing import alphanums, Word, QuotedString, oneOf, Suppress | ||
|
||
from ..qiita_core.search import QiitaSearchCriterion, QiitaSearch | ||
from ..qiita_core.exceptions import IncompetentQiitaDeveloperError | ||
from ..qiita_ware.api.analysis_manager import search_analyses | ||
from ..qiita_ware.api.studies_manager import search_studies | ||
from .connections import r_server | ||
|
||
#all messages are in json format. They must have the following format: | ||
# "job": jobname | ||
# "msg": message to print | ||
# "analysis": what analysis this is from in format datatype:analysis | ||
# "results": list of files created if any | ||
|
||
|
||
class MessageHandler(WebSocketHandler): | ||
def __init__(self, *args, **kwargs): | ||
super(MessageHandler, self).__init__(*args, **kwargs) | ||
self.redis = Client() | ||
self.redis.connect() | ||
|
||
def get_current_user(self): | ||
user = self.get_secure_cookie("user") | ||
if user is None: | ||
return "" | ||
else: | ||
return user.strip("\" '") | ||
|
||
def on_message(self, msg): | ||
msginfo = loads(msg) | ||
#listens for handshake from page | ||
if "user:" in msginfo["msg"]: | ||
self.channel = msginfo["msg"].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 | ||
@engine | ||
def listen(self): | ||
#runs task given, with the yield required to get returned value | ||
#equivalent of callback/wait pairing from tornado.gen | ||
yield 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) | ||
# Try and fight race condition by loading from redis after listen | ||
# started need to use std redis lib because tornadoredis is in | ||
# subscribed state | ||
oldmessages = r_server.lrange(self.channel + ":messages", 0, -1) | ||
if oldmessages is not None: | ||
for message in oldmessages: | ||
self.write_message(message) | ||
|
||
def callback(self, msg): | ||
if msg.kind == "message": | ||
self.write_message(str(msg.body)) | ||
|
||
@engine | ||
def on_close(self): | ||
yield Task(self.redis.unsubscribe, self.channel) | ||
self.redis.disconnect() | ||
|
||
|
||
class SearchASHandler(WebSocketHandler): | ||
|
||
def get_current_user(self): | ||
user = self.get_secure_cookie("user") | ||
if user is None: | ||
return "" | ||
else: | ||
return user.strip("\" '") | ||
|
||
def on_message(self, msg): | ||
""" Parses sent search and sends back results | ||
|
||
Parameters | ||
---------- | ||
msg: json string | ||
contains "query" key with query string and "type" key with return | ||
type, e.g. study, analysis, etc. | ||
|
||
Raises | ||
------ | ||
IncompetentQiitaDeveloperError | ||
If type is not recognised | ||
""" | ||
user = self.get_current_user() | ||
msginfo = loads(msg) | ||
query, searchstr = self.ParseSearchString(msginfo["query"]) | ||
if msginfo["type"] == "analysis": | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My worry about this is to add a new msginfo, you will need to make changes in different parts of the code. What about when defining the possible analysis (datatype:analysis) you can also add the function that will be used for searching and just doing a call to something like |
||
res = search_analyses(user, query) | ||
elif msginfo["type"] == "study": | ||
res = search_studies(user, query) | ||
else: | ||
raise IncompetentQiitaDeveloperError("Unrecognised type: %s" % | ||
msginfo["type"]) | ||
result = [] | ||
for item in res: | ||
result.append((item.name, item.id)) | ||
result = dumps({ | ||
"results": result, | ||
"query": searchstr | ||
}) | ||
self.write_message(result) | ||
|
||
def ParseSearchString(string): | ||
""" Parses query and returns string of what query ended up as and | ||
QiitaSearch object of query | ||
|
||
Parameters | ||
---------- | ||
string: str | ||
query string to parse | ||
|
||
Results | ||
-------- | ||
search: QiitaSearch object | ||
searchstr: str | ||
New search string with unparsed sections removed | ||
""" | ||
search_string = string.lower() | ||
#define sybols to be used | ||
word = Word(alphanums) | ||
quote_word = QuotedString("\"") | ||
query_type = oneOf("includes startswith endswith < > =") | ||
field_type = oneOf("metadata author year") | ||
col = Suppress(":") | ||
#define chunk of grammar | ||
query_search = field_type + col + (word + query_type + | ||
(word | quote_word) | | ||
(word | quote_word)) | ||
search = QiitaSearch() | ||
searchstr = "" | ||
first = 0 | ||
for match, start, end in query_search.scanString(search_string): | ||
operator = None | ||
if first > 0: | ||
operator = search_string[first:start-1].split(" ")[-1] | ||
searchstr = " ".join([searchstr, operator]) | ||
if len(match) < 4: | ||
#pad out to have full information needed | ||
#add field we are looking at, which equals field_type | ||
#implicit includes, so add it into the match | ||
searchstr = " ".join([searchstr, " ".join(match)]) | ||
criterion = QiitaSearchCriterion(match[0], "includes", | ||
match[1]) | ||
else: | ||
criterion = QiitaSearchCriterion(match[0], match[1], match[2]) | ||
searchstr = " ".join([searchstr, " ".join(match)]) | ||
search.add_criterion(criterion) | ||
first = end+1 | ||
return search, searchstr | ||
|
||
def callback(self, msg): | ||
if msg.kind == "message": | ||
self.write_message(str(msg.body)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
#!/usr/bin/env python | ||
|
||
__author__ = "Joshua Shorenstein" | ||
__copyright__ = "Copyright 2013, The QiiTa-pet Project" | ||
__credits__ = ["Joshua Shorenstein"] | ||
__license__ = "BSD" | ||
__version__ = "0.2.0-dev" | ||
__maintainer__ = "Joshua Shorenstein" | ||
__email__ = "[email protected]" | ||
__status__ = "Development" | ||
|
||
from tornado.web import authenticated | ||
|
||
from .webserver import BaseHandler | ||
|
||
|
||
class MultivisHandler(BaseHandler): | ||
def get(self): | ||
self.render("multivis.html", user=self.get_current_user(), error="") | ||
|
||
|
||
class MockupHandler(BaseHandler): | ||
def get(self): | ||
self.render("mockup.html", user=self.get_current_user(), error="") | ||
|
||
|
||
class MinStudyHandler(BaseHandler): | ||
@authenticated | ||
def get(self, study): | ||
self.render("minstudy.html", user=self.get_current_user(), error="", | ||
study=study) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure about this but if not subscribed this shouldn't continue, right?