Added events history

This commit is contained in:
2020-01-23 17:03:36 +01:00
parent 188e3c1ea2
commit fed0735eb9
6 changed files with 199 additions and 55 deletions
+6 -1
View File
@@ -96,7 +96,7 @@ class Sheerka(Concept):
if self.sdp.first_time:
self.sdp.set_key(self.USER_CONCEPTS_KEYS, 1000)
event = Event("Initializing Sheerka.")
event = Event("Initializing Sheerka.", user=self.name)
self.sdp.save_event(event)
exec_context = ExecutionContext(self.key, event, self)
@@ -547,6 +547,11 @@ class Sheerka(Concept):
return sorted(res, key=lambda i: int(i.id))
def history(self, page=10, start=0):
"""Gets the history of all commands"""
return self.sdp.load_events(page, start)
def test(self):
return f"I have access to Sheerka !"
+23
View File
@@ -42,3 +42,26 @@ class SheerkaDump:
self.sheerka.log.info(f"body : {c.body}")
self.sheerka.log.info(f"digest : {c.get_digest()}")
first = False
def dump_history(self, page=20, start=0):
count = 0
resolved_page = page if page > 0 else 50
page_count = 0
while count < page if page > 0 else True:
history = self.sheerka.history(resolved_page, start + page_count * resolved_page)
try:
h = next(history)
except StopIteration:
break
while True:
try:
if h.user != self.sheerka.name:
self.sheerka.log.info(h)
count += 1
h = next(history)
except StopIteration:
break
page_count += 1
+1
View File
@@ -64,6 +64,7 @@ class PythonEvaluator(OneReturnValueEvaluator):
"desc": context.sheerka.dump_handler.dump_desc,
"concepts": context.sheerka.dump_handler.dump_concepts,
"definitions": context.sheerka.dump_handler.dump_definitions,
"history": context.sheerka.dump_handler.dump_history,
}
if context.obj:
context.log(self.verbose_log,
+68 -21
View File
@@ -25,13 +25,17 @@ class Event(object):
Class that represents something that modifies the state of the system
"""
def __init__(self, message="", user="", date=datetime.now()):
def __init__(self, message="", user="", date=datetime.now(), parents=None):
self.version = 1
self.user = user
self.date = date
self.message = message
self.parents = parents
self._digest = None
def __str__(self):
return f"{self.date.strftime('%d/%m/%Y %H:%M:%S')} {self.message}"
def get_digest(self):
"""
Returns the digest of the event
@@ -48,7 +52,8 @@ class Event(object):
if not isinstance(self.message, str):
raise NotImplementedError
self._digest = hashlib.sha256(f"Event:{self.user}{self.date}{self.message}".encode("utf-8")).hexdigest()
to_hash = f"Event:{self.user}{self.date}{self.message}{self.parents}".encode("utf-8")
self._digest = hashlib.sha256(to_hash).hexdigest()
return self._digest
def to_dict(self):
@@ -58,6 +63,7 @@ class Event(object):
self.user = as_dict["user"]
self.date = datetime.fromisoformat(as_dict["date"])
self.message = as_dict["message"]
self.parents = as_dict["parents"]
class ObjToUpdate:
@@ -280,6 +286,7 @@ class SheerkaDataProvider:
ObjectsFolder = "objects"
CacheFolder = "cache"
HeadFile = "HEAD"
LastEventFile = "LAST_EVENT"
KeysFile = "keys"
REF_PREFIX = "##REF##:"
@@ -365,7 +372,7 @@ class SheerkaDataProvider:
if is_ref and not isinstance(obj, dict):
raise SheerkaDataProviderError("is_ref can only be used with dictionaries", obj)
snapshot = self.get_snapshot()
snapshot = self.get_snapshot(SheerkaDataProvider.HeadFile)
state = self.load_state(snapshot)
self.log.debug(f"Adding obj '{obj}' in entry '{entry}' (allow_multiple={allow_multiple}, use_ref={use_ref})")
@@ -400,7 +407,7 @@ class SheerkaDataProvider:
state.update(entry, obj)
new_snapshot = self.save_state(state)
self.set_snapshot(new_snapshot)
self.set_snapshot(SheerkaDataProvider.HeadFile, new_snapshot)
return entry, key
def add_with_auto_key(self, event_digest: str, entry, obj):
@@ -419,7 +426,7 @@ class SheerkaDataProvider:
def add_unique(self, event_digest: str, entry, obj):
"""Add an entry and make sure it's unique"""
snapshot = self.get_snapshot()
snapshot = self.get_snapshot(SheerkaDataProvider.HeadFile)
state = self.load_state(snapshot)
state.parents = [] if snapshot is None else [snapshot]
@@ -434,7 +441,7 @@ class SheerkaDataProvider:
state.data[entry].add(obj)
new_snapshot = self.save_state(state)
self.set_snapshot(new_snapshot)
self.set_snapshot(SheerkaDataProvider.HeadFile, new_snapshot)
return (None if already_exist else entry), None
def set(self, event_digest, entry, obj, use_ref=False, is_ref=False):
@@ -455,7 +462,7 @@ class SheerkaDataProvider:
if is_ref and not isinstance(obj, dict):
raise SheerkaDataProviderError("is_ref can only be used with dictionaries", obj)
snapshot = self.get_snapshot()
snapshot = self.get_snapshot(SheerkaDataProvider.HeadFile)
state = self.load_state(snapshot)
state.parents = [] if snapshot is None else [snapshot]
@@ -472,7 +479,7 @@ class SheerkaDataProvider:
state.data[entry] = obj if key is None else {key: obj}
new_snapshot = self.save_state(state)
self.set_snapshot(new_snapshot)
self.set_snapshot(SheerkaDataProvider.HeadFile, new_snapshot)
return entry, key
def modify(self, event_digest, entry, key, obj):
@@ -489,7 +496,7 @@ class SheerkaDataProvider:
if key is None:
raise SheerkaDataProviderError("Key is mandatory.", None)
snapshot = self.get_snapshot()
snapshot = self.get_snapshot(SheerkaDataProvider.HeadFile)
state = self.load_state(snapshot)
if entry not in state.data:
@@ -517,7 +524,7 @@ class SheerkaDataProvider:
state.modify(entry, key, obj, obj_key)
new_snapshot = self.save_state(state)
self.set_snapshot(new_snapshot)
self.set_snapshot(SheerkaDataProvider.HeadFile, new_snapshot)
return entry, obj_key
def list(self, entry, filter=None):
@@ -527,7 +534,7 @@ class SheerkaDataProvider:
:param filter: filter to use
:return: list of elements
"""
snapshot = self.get_snapshot()
snapshot = self.get_snapshot(SheerkaDataProvider.HeadFile)
state = self.load_state(snapshot)
if entry not in state.data:
return []
@@ -562,7 +569,7 @@ class SheerkaDataProvider:
:return: new sha256 of the state
TODO: Remove by key
"""
snapshot = self.get_snapshot()
snapshot = self.get_snapshot(SheerkaDataProvider.HeadFile)
state = self.load_state(snapshot)
if entry not in state.data:
@@ -574,7 +581,7 @@ class SheerkaDataProvider:
state.remove(entry, filter)
new_snapshot = self.save_state(state)
self.set_snapshot(new_snapshot)
self.set_snapshot(SheerkaDataProvider.HeadFile, new_snapshot)
return new_snapshot
def get(self, entry, key=None, load_origin=True):
@@ -582,9 +589,10 @@ class SheerkaDataProvider:
Retrieve an element by its key
:param entry:
:param key:
:param load_origin: if True, adds the origin (parent digest) to the object
:return:
"""
snapshot = self.get_snapshot()
snapshot = self.get_snapshot(SheerkaDataProvider.HeadFile)
state = self.load_state(snapshot)
if entry not in state.data:
@@ -604,9 +612,10 @@ class SheerkaDataProvider:
Retrieve an element by its key. Return None if the element does not exist
:param entry:
:param key:
:param load_origin: if True, adds the origin (parent digest) to the object
:return:
"""
snapshot = self.get_snapshot()
snapshot = self.get_snapshot(SheerkaDataProvider.HeadFile)
state = self.load_state(snapshot)
if entry not in state.data:
@@ -629,7 +638,7 @@ class SheerkaDataProvider:
:param digest: digest of the object, when several entries share the same key
:return:
"""
snapshot = self.get_snapshot()
snapshot = self.get_snapshot(SheerkaDataProvider.HeadFile)
state = self.load_state(snapshot)
exist = entry in state.data
@@ -659,25 +668,63 @@ class SheerkaDataProvider:
:return: digest of the event
"""
digest = event.get_digest()
parent = self.get_snapshot(SheerkaDataProvider.LastEventFile)
event.parents = [parent] if parent else None
target_path = self.io.get_obj_path(SheerkaDataProvider.EventFolder, digest)
if self.io.exists(target_path):
return digest
self.io.write_binary(target_path, self.serializer.serialize(event, None).read())
self.set_snapshot(SheerkaDataProvider.LastEventFile, digest)
return digest
def load_event(self, digest):
def load_event(self, digest=None):
"""
return an event, given its digest
:param digest:
:return:
"""
digest = digest or self.get_snapshot(SheerkaDataProvider.LastEventFile)
if digest is None:
return None
target_path = self.io.get_obj_path(SheerkaDataProvider.EventFolder, digest)
with self.io.open(target_path, "rb") as f:
return self.serializer.deserialize(f, None)
def load_events(self, page_size, start=0):
"""
Load multiple events in the same command
:param start:
:param page_size:
:return:
"""
digest = None
if start:
for i in range(start):
event = self.load_event(digest)
if event is None or event.parents is None:
return
digest = event.parents[0]
count = 0
while count < page_size:
event = self.load_event(digest)
if event is None:
return
yield event
if event.parents is None:
return
digest = event.parents[0]
count += 1
def save_result(self, execution_context):
"""
Save the execution context associated with an event
@@ -833,16 +880,16 @@ class SheerkaDataProvider:
digest, cache_path = self.get_cache_params(category, key)
return self.io.exists(cache_path)
def get_snapshot(self):
head_file = self.io.path_join(SheerkaDataProvider.HeadFile)
def get_snapshot(self, file):
head_file = self.io.path_join(file)
if not self.io.exists(head_file):
return None
return self.io.read_text(head_file)
# with open(head_file, "r") as f:
# return f.read()
def set_snapshot(self, digest):
head_file = self.io.path_join(SheerkaDataProvider.HeadFile)
def set_snapshot(self, file, digest):
head_file = self.io.path_join(file)
return self.io.write_text(head_file, digest)
# with open(head_file, "w") as f:
# return f.write(digest)
+3 -3
View File
@@ -29,13 +29,13 @@ def json_default_converter(o):
if isinstance(o, Enum):
return o.name
raise Exception("Cannot serialize " + o.__class__.__name__)
raise Exception(f"Cannot serialize object '{o}', class='{o.__class__.__name__}'")
# In debug mode, just
#
# with open("json_encoding_error.txt", "a") as f:
# f.write(o.__class__.__name__ + "\n")
@dataclass()
class SerializerContext:
user_name: str = None