__author__ = 'jonathan'

from lib.rome.core.utils import merge_dicts
from lib.rome.core.utils import current_milli_time
from lib.rome.core.lazy_reference import LazyReference

import logging

import uuid

class SessionException(Exception):
    pass

class SessionControlledExecution():

    def __init__(self, session):
        self.session = session

    def __enter__(self):
        pass

    def __exit__(self, type, value, traceback):
        if traceback:
            print(traceback)
        else:
            self.session.flush()

class Session(object):

    max_duration = 300

    def __init__(self):
        self.session_id = uuid.uuid1()
        self.session_objects_add = []
        self.session_objects_delete = []
        self.session_timeout = current_milli_time() + Session.max_duration

    def add(self, *objs):
        self.session_objects_add += objs

    def delete(self, *objs):
        self.session_objects_delete += objs

    def query(self, *entities, **kwargs):
        from lib.rome.core.orm.query import Query
        return Query(*entities, **merge_dicts(kwargs, {"session": self}))

    def begin(self, *args, **kwargs):
        return SessionControlledExecution(session=self)

    def flush(self, *args, **kwargs):
        logging.info("flushing session %s" % (self.session_id))
        if self.commit_request():
            logging.info("committing session %s" % (self.session_id))
            self.commit()

    def can_be_used(self, obj):
        if getattr(obj, "session", None) is None:
            return True
        else:
            if obj.session["session_id"] == self.session_id:
                return True
            if current_milli_time >= obj.session["session_timeout"]:
                return True
        logging.error("session %s cannot use object %s" % (self.session_id, obj))
        return False

    def commit_request(self):
        processed_objects = []
        success = True
        for obj in self.session_objects_add + self.session_objects_delete:
            if obj.id is not None:
                recent_version = LazyReference(obj.__tablename__, obj.id, self.session_id, None).load()
                if self.can_be_used(recent_version):
                    session_object = {"session_id": str(self.session_id), "session_timeout": self.session_timeout}
                    recent_version.update({"session": session_object})
                    recent_version.save(force=True, session=self, no_nested_save=True, increase_version=False)
                    processed_objects += [recent_version]
                else:
                    success = False
        if not success:
            logging.error("session %s encountered a conflict, aborting commit" % (self.session_id))
            for obj in processed_objects:
                obj.session = None
                obj.save(force=True, no_nested_save=True)
        return success

    def commit(self):
        logging.info("session %s will start commit" % (self.session_id))
        for obj in self.session_objects_add:
            obj.update({"session": None}, skip_session=True)
            obj.save(force=True)
        for obj in self.session_objects_delete:
            obj.update({"session": None}, skip_session=True)
            obj.soft_delete(force=True)
        logging.info("session %s committed" % (self.session_id))
        self.session_objects_add = []
        self.session_objects_delete = []

@@ -167,16 +167,55 @@
 eference
+%0Afrom redlock import Redlock as Redlock
 %0A%0Aimport
@@ -834,16 +834,133 @@
 duration
+%0A        self.dlm = Redlock(%5B%7B%22host%22: %22localhost%22, %22port%22: 6379, %22db%22: 0%7D, %5D, retry_count=10)%0A        self.locks = %5B%5D
 %0A%0A    de
@@ -2080,24 +2080,54 @@
 cess = True%0A
+        tablenames_index = %7B%7D%0A
         for 
@@ -2192,32 +2192,89 @@
 ete:%0A           
+ tablenames_index%5Bobj.__tablename__%5D = True%0A            #
  if obj.id is no
@@ -2285,36 +2285,38 @@
 ne:%0A            
+#
     
+ 
 recent_version =
@@ -2394,24 +2394,26 @@
 %0A           
+ #
      if self
@@ -2445,32 +2445,34 @@
 on):%0A           
+ #
          session
@@ -2562,32 +2562,34 @@
 out%7D%0A           
+ #
          recent_
@@ -2635,32 +2635,34 @@
 ct%7D)%0A           
+ #
          recent_
@@ -2757,24 +2757,25 @@
         
+#
         
 processe
@@ -2758,32 +2758,33 @@
        #        
+ 
 processed_object
@@ -2812,24 +2812,26 @@
 %0A           
+ #
      else:%0A 
@@ -2832,32 +2832,34 @@
 lse:%0A           
+ #
          success
@@ -2867,16 +2867,251 @@
 = False%0A
+        for tablename in tablenames_index:%0A            lockname = %22lock-%25s%22 %25 (tablename)%0A            lock = self.dlm.lock(lockname, 1000)%0A            if lock is False:%0A                success = False%0A            self.locks += %5Block%5D%0A%0A
         
@@ -3303,16 +3303,16 @@
  = None%0A
-
         
@@ -3353,32 +3353,106 @@
 sted_save=True)%0A
+            for lock in self.locks:%0A                self.dlm.unlock(lock)%0A
         return s

fix a bug;