summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleš Smodiš <aless@guru.si>2015-08-25 14:20:42 +0200
committerAleš Smodiš <aless@guru.si>2015-08-25 14:20:42 +0200
commit88a5cd83b47a9dfb5a832936095c7b99ce0d8179 (patch)
treea01f0696da1d31201f42242cf61cbc664df65401
parent816d11b6e238e389f84430196bed19d66f49d751 (diff)
Implemented methods to fetch a list of available problems and the problem description.
JavaScript no longer parses pythonic problem descriptions, instead they are loaded by server and JSONized.
-rw-r--r--client/__init__.py22
-rw-r--r--server/__init__.py30
-rw-r--r--server/problems.py28
-rw-r--r--server/prolog_session.py8
-rw-r--r--server/user_session.py28
-rw-r--r--wsgi_server.py128
6 files changed, 188 insertions, 56 deletions
diff --git a/client/__init__.py b/client/__init__.py
index 8781a96..1ac18cd 100644
--- a/client/__init__.py
+++ b/client/__init__.py
@@ -9,18 +9,28 @@ class CodeqManager(multiprocessing.managers.BaseManager):
CodeqManager.register('PrologSession')
CodeqManager.register('UserSession')
-CodeqManager.register('get_session_by_id')
-CodeqManager.register('get_or_create_session')
-CodeqManager.register('authenticate_and_create_session')
+#CodeqManager.register('get_session_by_id')
+#CodeqManager.register('get_or_create_session')
+#CodeqManager.register('authenticate_and_create_session')
+#CodeqManager.register('list_problems_in_groups')
+CodeqManager.register('Codeq')
m = CodeqManager(address=('localhost', 16231), authkey=b'c0d3q3y')
m.connect()
+codeq = m.Codeq()
def get_session_by_id(sid):
- return m.get_session_by_id(sid)
+# return m.get_session_by_id(sid)
+ return codeq.get_session_by_id(sid)
def get_or_create_session(uid, username, sid=None):
- return m.get_or_create_session(uid, username, sid)
+# return m.get_or_create_session(uid, username, sid)
+ return codeq.get_or_create_session(uid, username, sid)
def authenticate_and_create_session(username, password):
- return m.authenticate_and_create_session(username, password)
+# return m.authenticate_and_create_session(username, password)
+ return codeq.authenticate_and_create_session(username, password)
+
+def list_problems_in_groups(language):
+# return m.list_problems_in_groups(language)
+ return codeq.list_problems_in_groups(language)
diff --git a/server/__init__.py b/server/__init__.py
index f2c73b7..9f2469d 100644
--- a/server/__init__.py
+++ b/server/__init__.py
@@ -3,8 +3,28 @@
import multiprocessing.managers
from . import user_session
from . import prolog_session
+import server.problems
-__all__ = ['user_session']
+__all__ = ['user_session', 'prolog_session', 'problems', 'start']
+
+class Codeq(object):
+ _method_to_typeid_ = {
+ 'get_session_by_id': 'UserSession',
+ 'get_or_create_session': 'UserSession',
+ 'authenticate_and_create_session': 'UserSession'
+ }
+
+ def get_session_by_id(self, sid):
+ return user_session.get_session_by_id(sid)
+
+ def get_or_create_session(self, uid, username, sid=None):
+ return user_session.get_or_create_session(uid, username, sid)
+
+ def authenticate_and_create_session(self, username, password):
+ return user_session.authenticate_and_create_session(username, password)
+
+ def list_problems_in_groups(self, language):
+ return server.problems.list_problems_in_groups(language)
class CodeqManager(multiprocessing.managers.BaseManager):
pass
@@ -14,9 +34,11 @@ class UserSessionProxy(multiprocessing.managers.BaseProxy):
CodeqManager.register('PrologSession')
CodeqManager.register('UserSession', proxytype=UserSessionProxy)
-CodeqManager.register('get_session_by_id', callable=user_session.get_session_by_id, proxytype=UserSessionProxy)
-CodeqManager.register('get_or_create_session', callable=user_session.get_or_create_session, proxytype=UserSessionProxy)
-CodeqManager.register('authenticate_and_create_session', callable=user_session.authenticate_and_create_session, proxytype=UserSessionProxy)
+#CodeqManager.register('get_session_by_id', callable=user_session.get_session_by_id, proxytype=UserSessionProxy)
+#CodeqManager.register('get_or_create_session', callable=user_session.get_or_create_session, proxytype=UserSessionProxy)
+#CodeqManager.register('authenticate_and_create_session', callable=user_session.authenticate_and_create_session, proxytype=UserSessionProxy)
+#CodeqManager.register('list_problems_in_groups', callable=server.problems.list_problems_in_groups)
+CodeqManager.register('Codeq', callable=Codeq, method_to_typeid=Codeq._method_to_typeid_)
def start():
m = CodeqManager(address=('localhost', 16231), authkey=b'c0d3q3y')
diff --git a/server/problems.py b/server/problems.py
index 7fb2606..bce72fe 100644
--- a/server/problems.py
+++ b/server/problems.py
@@ -3,6 +3,7 @@
import sys
import importlib.machinery
import threading
+from db import get_connection, return_connection
#sys.path.append('/home/aless/job/codeq/source/codeq-problems/')
_path_prefix = '/home/aless/job/codeq/source/codeq-problems/'
@@ -45,7 +46,6 @@ def load_problems(language, tuples, tail_module):
modules = []
for problem_group, problem in tuples:
mod = '{0}.problems.{1}.{2}.{3}'.format(language, problem_group, problem, tail_module)
- print('importing {}'.format(mod))
modules.append(load_module(mod))
return modules
@@ -81,3 +81,29 @@ def solutions_for_problems(language, tuples):
if f:
facts.add(f)
return '\n'.join(facts) + '\n' + '\n'.join(solutions)
+
+def list_problems_in_groups(language):
+ conn = get_connection()
+ try:
+ cur = conn.cursor()
+ try:
+ cur.arraysize = 1000
+ cur.execute("select g.identifier, g.name, p.identifier, p.name from problem p inner join problem_group g on g.id = p.problem_group_id where p.language_id = (select id from language where identifier = %s) order by g.identifier, p.identifier", (language,))
+ result = []
+ previous_group_name = ''
+ current_sublist = None
+ row = cur.fetchone()
+ while row:
+ current_group_name = row[0]
+ if previous_group_name != current_group_name:
+ current_sublist = []
+ result.append({'identifier': current_group_name, 'name': row[1], 'problems': current_sublist})
+ previous_group_name = current_group_name
+ current_sublist.append({'identifier': row[2], 'name': row[3]})
+ row = cur.fetchone()
+ return result
+ finally:
+ cur.close()
+ finally:
+ conn.commit()
+ return_connection(conn)
diff --git a/server/prolog_session.py b/server/prolog_session.py
index e00afd8..c21d929 100644
--- a/server/prolog_session.py
+++ b/server/prolog_session.py
@@ -86,7 +86,7 @@ class PrologSession(object):
prolog.engine.stop(self._engine_id)
self._engine_id = None
- def run_for_user(self, user_id, language, problem_group, problem, program, query):
+ def run_for_user(self, user_id, problem_id, program, query):
"""A "shorthand" method to start a Prolog session, load correct solutions of all user's solved
problems and the given program, and ask a query.
"""
@@ -94,10 +94,12 @@ class PrologSession(object):
try:
cur = conn.cursor()
try:
- cur.execute('select l.id, p.id from problem p inner join problem_group g on g.id = p.problem_group_id inner join language l on l.id = p.language_id where l.identifier = %s and g.identifier = %s and p.identifier = %s', (language, problem_group, problem))
+ cur.execute('select l.id, l.identifier, g.identifier, p.identifier from problem p inner join problem_group g on g.id = p.problem_group_id inner join language l on l.id = p.language_id where p.id = %s', (problem_id,))
row = cur.fetchone()
language_id = row[0]
- problem_id = row[1]
+ language = row[1]
+ problem_group = row[2]
+ problem = row[3]
cur.execute('select g.identifier, p.identifier from solution s inner join problem p on p.id = s.problem_id inner join problem_group g on g.id = p.problem_group_id where s.codeq_user_id = %s and s.done = True and s.problem_id != %s and p.language_id = %s', (user_id, problem_id, language_id))
solved_problems = cur.fetchall()
finally:
diff --git a/server/user_session.py b/server/user_session.py
index b12d6f1..bb4179d 100644
--- a/server/user_session.py
+++ b/server/user_session.py
@@ -6,6 +6,7 @@ import hashlib
import base64
import random
from . import prolog_session
+from . import problems
import db
from errors.session import NoSuchSession, AuthenticationFailed
@@ -51,6 +52,33 @@ class UserSession(object):
self.prolog_session = prolog_session.PrologSession() # lazy init
return self.prolog_session
+ def get_problem_data(self, language, problem_group, problem):
+ mod = problems.load_problem(language, problem_group, problem, 'en')
+ conn = db.get_connection()
+ try:
+ cur = conn.cursor()
+ try:
+ cur.execute("select l.id, l.name, g.id, g.name, p.id, p.name from problem p inner join language l on l.id = p.language_id inner join problem_group g on g.id = p.problem_group_id where l.identifier = %s and g.identifier = %s and p.identifier = %s", (language, problem_group, problem))
+ row = cur.fetchone()
+ problem_id = row[4]
+ result = {
+ 'language': {'id': row[0], 'identifier': language, 'name': row[1]},
+ 'problem_group': {'id': row[2], 'identifier': problem_group, 'name': row[3]},
+ 'problem': {'id': problem_id, 'identifier': problem, 'name': row[5], 'slug': mod.slug, 'description': mod.description, 'hint': mod.hint}
+ }
+ cur.execute("select content from solution where problem_id = %s and codeq_user_id = %s", (problem_id, self.uid))
+ row = cur.fetchone()
+ if row:
+ result['solution'] = row[0]
+ else:
+ result['solution'] = ''
+ return result
+ finally:
+ cur.close()
+ finally:
+ conn.commit()
+ db.return_connection(conn)
+
def __del__(self):
# no locking needed if GC is removing us, as there cannot be any concurrent access by definition
if hasattr(self, 'prolog_session') and (self.prolog_session is not None):
diff --git a/wsgi_server.py b/wsgi_server.py
index 0f70b40..9b3a51e 100644
--- a/wsgi_server.py
+++ b/wsgi_server.py
@@ -12,7 +12,15 @@ api = application = falcon.API()
def get_json_payload(req, session_is_optional=False):
- if not ((req.content_type == 'application/json') or (req.content_type == 'text/json')):
+ content_type_parts = req.content_type.split(';')
+ content_type = content_type_parts[0].strip()
+ encoding = 'utf-8'
+ if len(content_type_parts) > 1:
+ for part in content_type_parts[1:]:
+ subparts = part.strip().split('=')
+ if (len(subparts) == 2) and subparts[0] == 'charset':
+ encoding = subparts[1]
+ if not ((content_type == 'application/json') or (content_type == 'text/json')):
raise falcon.HTTPUnsupportedMediaType('Unsupported content-type: {0}'.format(req.content_type))
length = req.get_header('Content-Length')
if length is None:
@@ -28,95 +36,121 @@ def get_json_payload(req, session_is_optional=False):
except Exception as e:
raise falcon.HTTPBadRequest('Error reading request', 'Error while reading the request body: {0}'.format(e.message))
try:
- js = json.loads(txt, encoding="utf-8")
+ js = json.loads(txt.decode(encoding=encoding, errors='replace'))
except ValueError as e:
raise falcon.HTTPBadRequest('Error parsing JSON payload', 'Error while parsing the request as JSON: {0}'.format(e.message))
sid = js.get('sid')
if sid is None:
if session_is_optional:
- return js
+ return js, None
raise falcon.HTTPBadRequest('No Session', 'Request is missing a session-ID')
del js['sid']
try:
session = client.get_session_by_id(sid)
except NoSuchSession:
if session_is_optional:
- return js
+ return js, None
raise falcon.HTTPBadRequest('Session Expired', 'This user session has expired. Please log-in again.')
- req.context['session'] = session
- return js
+ return js, session
-class Login(object):
+class CodeqService(object):
+ """Base class for all CodeQ services.
+ It only support the POST method and JSON data.
+ Handles JSON decoding and encoding, and session retrieval which can be optional.
+ """
+ session_is_optional = False
+
def on_post(self, req, resp):
+ js, session = get_json_payload(req, self.session_is_optional)
+ resp.body = json.dumps(self.process(js, session))
resp.cache_control = 'private, no-cache, no-store'
- js = get_json_payload(req, True)
+ resp.content_type = 'application/json'
+ resp.status = falcon.HTTP_200
+
+ def process(self, js, session):
+ raise falcon.HTTPNotFound()
+
+class ProblemList(CodeqService):
+ session_is_optional = True
+
+ def process(self, js, session):
+ language = js.get('language')
+ if language is None:
+ return {'code': 1, 'message': 'Language was not provided'}
+ else:
+ return {'code': 0, 'message': 'ok', 'problems': client.list_problems_in_groups(language)}
+
+class Login(CodeqService):
+ session_is_optional = True
+
+ def process(self, js, old_session):
username = js.get('username')
password = js.get('password')
if username is None:
- response = {'code': 1, 'message': 'Username was not provided'}
+ return {'code': 1, 'message': 'Username was not provided'}
elif password is None:
- response = {'code': 2, 'message': 'Password was not provided'}
+ return {'code': 2, 'message': 'Password was not provided'}
else:
try:
session = client.authenticate_and_create_session(username, password)
except AuthenticationFailed:
- response = {'code': 3, 'message': 'Username or password do not match'}
+ return {'code': 3, 'message': 'Username or password do not match'}
else:
- response = {'code': 0, 'message': 'OK', 'sid':session.get_sid()}
- old_session = req.context.get('session')
if old_session:
old_session.destroy()
- resp.body = json.dumps(response)
- resp.content_type = 'application/json'
- resp.status = falcon.HTTP_200
+ return {'code': 0, 'message': 'OK', 'sid':session.get_sid()}
-class Activity(object):
+class Activity(CodeqService):
def on_get(self, req, resp):
resp.body = '{}'
resp.status = falcon.HTTP_200
-class Query(object):
- def on_post(self, req, resp):
- resp.cache_control = 'private, no-cache, no-store'
- js = get_json_payload(req)
- session = req.context['session']
+class Query(CodeqService):
+ def process(self, js, session):
step = js.get('step')
if step is None:
- response = {'code': 1, 'message': '"step" is not set'}
+ return {'code': 1, 'message': '"step" is not set'}
else:
prolog = session.get_prolog()
if step == 'run':
program = js.get('program')
query = js.get('query')
- language = js.get('language')
- problem_group = js.get('problem_group')
- problem = js.get('problem')
+ problem_id = js.get('problem_id')
if program is None:
- response = {'code': 2, 'message': 'No program specified'}
+ return {'code': 2, 'message': 'No program specified'}
elif query is None:
- response = {'code': 3, 'message': 'No query specified'}
- elif language is None:
- response = {'code': 4, 'message': 'Language identifier not given'}
- elif problem_group is None:
- response = {'code': 5, 'message': 'Problem group identifier not given'}
- elif problem is None:
- response = {'code': 6, 'message': 'Problem identifier not given'}
+ return {'code': 3, 'message': 'No query specified'}
+ elif problem_id is None:
+ return {'code': 4, 'message': 'Problem ID not given'}
else:
- messages, status, have_more = prolog.run_for_user(session.get_uid(), language, problem_group, problem, program, query)
- response = {'code': 0, 'message': 'ok', 'terminal': {'messages': messages, 'status': status, 'have_more': have_more}}
+ messages, status, have_more = prolog.run_for_user(session.get_uid(), problem_id, program, query)
+ return {'code': 0, 'message': 'ok', 'terminal': {'messages': messages, 'status': status, 'have_more': have_more}}
elif step == 'next':
messages, status, have_more = prolog.step()
- response = {'code': 0, 'message': 'ok', 'terminal': {'messages': messages, 'status': status, 'have_more': have_more}}
+ return {'code': 0, 'message': 'ok', 'terminal': {'messages': messages, 'status': status, 'have_more': have_more}}
elif step == 'end':
messages, status, have_more = prolog.end()
- response = {'code': 0, 'message': 'ok', 'terminal': {'messages': messages, 'status': status, 'have_more': have_more}}
+ return {'code': 0, 'message': 'ok', 'terminal': {'messages': messages, 'status': status, 'have_more': have_more}}
else:
- response = {'code': 7, 'message': 'Unknown prolog step: {0}'.format(step)}
+ return {'code': 5, 'message': 'Unknown prolog step: {0}'.format(step)}
- resp.body = json.dumps(response)
- resp.content_type = 'application/json'
- resp.status = falcon.HTTP_200
+class GetProblem(CodeqService):
+ def process(self, js, session):
+ language = js.get('language')
+ problem_group = js.get('problem_group')
+ problem = js.get('problem')
+ if language is None:
+ return {'code': 1, 'message': 'Language identifier not given'}
+ elif problem_group is None:
+ return {'code': 2, 'message': 'Problem group identifier not given'}
+ elif problem is None:
+ return {'code': 3, 'message': 'Problem identifier not given'}
+ else:
+ return {'code': 0, 'message': 'ok', 'data': session.get_problem_data(language, problem_group, problem)}
+
+problem_list = ProblemList()
+api.add_route('/list_problems', problem_list)
login = Login()
api.add_route('/login', login)
@@ -126,3 +160,13 @@ api.add_route('/activity', activity)
query = Query()
api.add_route('/query', query)
+
+get_problem = GetProblem()
+api.add_route('/get_problem', get_problem)
+
+if __name__ == '__main__':
+ import logging
+ from waitress import serve
+ logger = logging.getLogger('waitress')
+ logger.setLevel(logging.DEBUG)
+ serve(application, host='0.0.0.0', port=8082)