From a4f639242f9f6221a486e0e91adeb75ba6096f45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Smodi=C5=A1?= Date: Mon, 24 Aug 2015 19:09:41 +0200 Subject: Split the development into daemon and wsgi_server. Implemented basic infrastructure for daemon (Prolog), and partial support for services in wsgi_server. --- server/prolog_session.py | 117 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 server/prolog_session.py (limited to 'server/prolog_session.py') diff --git a/server/prolog_session.py b/server/prolog_session.py new file mode 100644 index 0000000..e00afd8 --- /dev/null +++ b/server/prolog_session.py @@ -0,0 +1,117 @@ +# coding=utf-8 + +import operator +import threading +import prolog.engine +import db +from . import problems + +__all__ = ['PrologSession'] + +def format_prolog_output(reply, output): + messages = [text for text in map(operator.itemgetter(1), output)] + # When an engine is destroyed, a nested data object has the actual query result. + event = reply['event'] + if event == 'destroy': + reply = reply['data'] + event = reply['event'] + + if event == 'success': + messages.append(prolog.engine.pretty_vars(reply['data'][0])) + return messages, 'ok', True and reply['more'] + if event == 'failure': + messages.append('false') + return messages, 'ok', False + if event == 'error': + # Remove potential module name (engine ID) from the error message. + messages.append('error: ' + reply['data'].replace("'{}':".format(reply['id']), '')) + return messages, 'error', False + + return messages, 'ok', False # TODO: is it possible to reach this return statement? + + +class PrologSession(object): + """Abstracts a Prolog session. + Only public methods are available to the outside world due to the use of multiprocessing managers. + Therefore prefix any private methods with an underscore (_). + No properties are accessible; use getters and setters instead. + Values are passed by value instead of by reference (deep copy!). + """ + def __init__(self): + self._access_lock = threading.Lock() + self._engine_id = None + + def run(self, code): + with self._access_lock: + if self._engine_id is not None: + prolog.engine.stop(self._engine_id) + self._engine_id = None + reply, output = prolog.engine.create(code=code) + if reply.get('event') != 'create': + raise Exception('System error: could not create a prolog engine') + self._engine_id = reply['id'] + messages = [text for text in map(operator.itemgetter(1), output)] + status = 'error' if 'error' in map(operator.itemgetter(0), output) else 'ok' + return messages, status, False + + def query(self, query): + with self._access_lock: + if self._engine_id is None: + return ['Prolog is not running'], 'error', False + try: + return format_prolog_output(*prolog.engine.ask(self._engine_id, query)) + except Exception as e: + return [str(e)], 'error', False + + def step(self): + with self._access_lock: + if self._engine_id is None: + return ['Prolog is not running'], 'error', False + try: + return format_prolog_output(*prolog.engine.next(self._engine_id)) + except Exception as e: + return [str(e)], 'error', False + + def end(self): + """Stops the Prolog engine.""" + with self._access_lock: + if self._engine_id is not None: + prolog.engine.stop(self._engine_id) + self._engine_id = None + return [], 'ok', False + + def __del__(self): + # no locking needed if GC is removing us, as there cannot be any concurrent access by definition + if hasattr(self, '_engine_id') and (self._engine_id is not None): + prolog.engine.stop(self._engine_id) + self._engine_id = None + + def run_for_user(self, user_id, language, problem_group, problem, 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. + """ + conn = db.get_connection() + 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)) + row = cur.fetchone() + language_id = row[0] + problem_id = row[1] + 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: + cur.close() + finally: + conn.commit() + db.return_connection(conn) + + other_solutions = problems.solutions_for_problems(language, solved_problems) + problem_module = problems.load_problem(language, problem_group, problem, 'common') + problem_facts = problems.get_facts(language, problem_module) or '' + code = other_solutions + problem_facts + program + messages, status, have_more = self.run(code) + if status == 'ok': + more_messages, status, have_more = self.query(query) + messages.extend(more_messages) + return messages, status, have_more -- cgit v1.2.1