diff options
-rw-r--r-- | server/prolog_session.py | 7 | ||||
-rw-r--r-- | server/user_session.py | 34 |
2 files changed, 39 insertions, 2 deletions
diff --git a/server/prolog_session.py b/server/prolog_session.py index c21d929..039f40a 100644 --- a/server/prolog_session.py +++ b/server/prolog_session.py @@ -37,9 +37,11 @@ class PrologSession(object): No properties are accessible; use getters and setters instead. Values are passed by value instead of by reference (deep copy!). """ - def __init__(self): + def __init__(self, user_session): + self._user_session = user_session # the owning session self._access_lock = threading.Lock() self._engine_id = None + self._problem_id = -1 def run(self, code): with self._access_lock: @@ -78,6 +80,7 @@ class PrologSession(object): if self._engine_id is not None: prolog.engine.stop(self._engine_id) self._engine_id = None + self._problem_id = -1 return [], 'ok', False def __del__(self): @@ -116,4 +119,6 @@ class PrologSession(object): if status == 'ok': more_messages, status, have_more = self.query(query) messages.extend(more_messages) + self._problem_id = problem_id + self._user_session.update_solution(problem_id, [], program) return messages, status, have_more diff --git a/server/user_session.py b/server/user_session.py index bb4179d..d8cdf1d 100644 --- a/server/user_session.py +++ b/server/user_session.py @@ -9,6 +9,7 @@ from . import prolog_session from . import problems import db from errors.session import NoSuchSession, AuthenticationFailed +import psycopg2.extras __all__ = ['get_session_by_id', 'get_or_create_session', 'UserSession'] @@ -49,7 +50,7 @@ class UserSession(object): def get_prolog(self): with self._access_lock: if self.prolog_session is None: - self.prolog_session = prolog_session.PrologSession() # lazy init + self.prolog_session = prolog_session.PrologSession(self) # lazy init return self.prolog_session def get_problem_data(self, language, problem_group, problem): @@ -79,6 +80,37 @@ class UserSession(object): conn.commit() db.return_connection(conn) + def update_solution(self, problem_id, trace, solution): + if (trace is None) and (solution is None): + return + conn = db.get_connection() + try: + cur = conn.cursor() + try: + # TODO: convert to upsert with postgresql 9.5 to eliminate the small window where it's possible for more than one concurrent insert to execute + cur.execute('select id, trace, solution content from solution where codeq_user_id = %s and problem_id = %s for update', (self.uid, problem_id)) + row = cur.fetchone() + if row: + if row[1]: + new_trace = row[1] + if trace: + new_trace.extend(trace) + else: + new_trace = trace + new_solution = row[2] if solution is None else solution + cur.execute('update solution set content = %s, trace = %s where id = %s', (new_solution, psycopg2.extras.Json(new_trace), row[0])) + else: + # this is the first entry + cur.execute('insert into solution (done, content, problem_id, codeq_user_id, trace) values (%s, %s, %s, %s, %s)', (False, solution, problem_id, self.uid, psycopg2.extras.Json(trace))) + finally: + cur.close() + conn.commit() + except: + conn.rollback() + raise + finally: + 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): |