From 6c2ec1c970348b42c043d37d0f7a020fa18ecc37 Mon Sep 17 00:00:00 2001 From: Alex Volshtein Date: Sun, 18 Feb 2018 23:19:24 +0300 Subject: [PATCH] Accomplishing todos --- main.py | 57 ++++++++++++++++++++++++++++++++++++++++--------- orm/document.py | 56 ++++++++++++++++++++++++++---------------------- orm/fields.py | 19 +++++++++++------ 3 files changed, 90 insertions(+), 42 deletions(-) diff --git a/main.py b/main.py index e23f5e5..a630964 100644 --- a/main.py +++ b/main.py @@ -12,7 +12,7 @@ 'password': 'password' }, firebase={ - 'cred': 'cred.json', + 'cred': '', 'database': 'https://fr.firebaseio.com/', 'bucket': 'fr.appspot.com', @@ -51,12 +51,14 @@ def presentation(cls, document): class Incident(Document): _container = '/incidents' - incident_id = StringField(id=True, presentational=True) + incident_id = StringField(id=True, presentation=True) confirmed = BooleanField(default=False) - owner = StringField(presentational=True) - reliability = IntField(default=0, presentational=True) - confidence = IntField(default=0, presentational=True) - created = DateField(presentational=True) + action = StringField() + firstResponder = StringField(presentation=True) + owner = StringField(presentation=True) + reliability = IntField(default=0, presentation=True) + confidence = IntField(default=0, presentation=True) + created = DateField(presentation=True) @staticmethod def on_save(document): @@ -95,12 +97,47 @@ def presentation(cls, document): 'id': document.incident_id, 'rel': document.reliability * document.confidence, 'created': document.created.isoformat(), - 'owner': document.owner.replace(' ', '-') + 'owner': document.owner.replace(' ', '-'), + 'firstResponder': document.firstResponder } if __name__ == "__main__": + # TODO: Add unit tests + + import datetime + from enum import Enum + + class Type(Enum): + CREATE = 1 + DELETE = 2 + UPDATE_PRESENTATION = 3 + UPDATE_CACHE = 4 + UPDATE_FULL = 5 + + action = Type.UPDATE_PRESENTATION + for i in range(0, 20): - with Incident().get(i) as incident: - incident.reliability = 3 - incident.confirmed = not incident.confirmed + if action == Type.CREATE: + Incident( + incident_id=i, + owner='123', + reliability=i % 3, + confidence=i * 2, + confirmed=not bool(i % 2), + created=datetime.datetime.utcnow() + ).save() + elif action == Type.DELETE: + Incident.get(i).delete() + elif action == Type.UPDATE_PRESENTATION: + with Incident.get(i) as incident: + incident.reliability = i / 4 + elif action == Type.UPDATE_CACHE: + with Incident.get(i) as incident: + incident.confirmed = not incident.confirmed + elif action == Type.UPDATE_FULL: + with Incident.get(i) as incident: + incident.firstResponder = 'AAB' + incident.action = 'cleared' + + diff --git a/orm/document.py b/orm/document.py index ef985bc..8f2444d 100644 --- a/orm/document.py +++ b/orm/document.py @@ -28,19 +28,31 @@ def __new__(mcs, name, bases, attrs): if isinstance(item, BaseField) ] - presentational = set() + presentation_fields = set() for fieldname in fieldnames: field = attrs[fieldname] fields[fieldname] = field - if field.presentational: - presentational.add(fieldname) + if field.presentation: + presentation_fields.add(fieldname) + + if field.is_id: + id_field = copy.copy(field) + id_field._presentation = False + id_field._id = False + fields['id'] = id_field del attrs[fieldname] attrs['__fields__'] = fields - attrs['_presentational'] = presentational + + # If we have "presentation" in class name, then all of its fields are presentational + # Should think of a better way + if "presentation" not in name.lower(): + attrs['_presentation_fields'] = presentation_fields + else: + attrs['_presentation_fields'] = set(fieldnames) return super().__new__(mcs, name, bases, attrs) @@ -79,15 +91,16 @@ def __init__(self, ignore_non_existing=False, override=False, **kwargs): self._fetched = override self._changed = set() - def get(self, id): - # TODO: Make this method static - + @classmethod + def get(cls, id): if id is None: raise AttributeError("Document must have id field!") - if self._redis.exists(id): - object.__setattr__(self, '_id', id) - return self._fetch() + document = cls(id=id) + + if document._redis.exists(id): + object.__setattr__(document, '_id', id) + return document._fetch() raise LookupError('No such document with id: {}'.format(id)) @@ -146,8 +159,9 @@ def save(self, force=False): if any(( force, - self._presentational and self._changed & self._presentational, - not self._presentational and self._changed + self._presentation_fields and self._changed & self._presentation_fields, + not self._presentation_fields and self._changed, + not exists )): self._firebase.update(self._path, self._id, presentation) @@ -170,17 +184,14 @@ def presentation(cls, document): return { key: value for key, value in document - if key in cls._presentational + if key in cls._presentation_fields } on_save = None - on_presentation_save = None # NOTE: Inactive yet - on_delete = None class DocumentView(dict): - # TODO: Add __changed__ field def __init__(self, document: Document): super().__init__() @@ -189,6 +200,9 @@ def __init__(self, document: Document): for key, item in document._fields.items() }) + self.__changed__ = frozenset(document._changed) + self.__presentation_changed__ = frozenset(document._changed & document._presentation_fields) + def __getattr__(self, item): if item in self: return self[item] @@ -196,10 +210,7 @@ def __getattr__(self, item): raise AttributeError("No such attribute: {}!".format(item)) -class PresentationDocument(Document, metaclass=abc.ABCMeta): - # TODO: Create another metaclass - # TODO: Make fields presentational by default for PresentationDocument - +class PresentationDocument(Document): def __init__(self, ignore_non_existing=False, override=False, **kwargs): super().__init__(ignore_non_existing, override, **kwargs) @@ -223,8 +234,3 @@ def delete(self): if self._redis.exists(self._id): self._redis.delete(self._id) self._firebase.delete(self._path, self._view_id) - - @classmethod - @abstractmethod - def presentation(cls, document: DocumentView): - pass diff --git a/orm/fields.py b/orm/fields.py index 639f395..a8ed7b8 100644 --- a/orm/fields.py +++ b/orm/fields.py @@ -13,9 +13,10 @@ def __new__(mcs, name, bases, namespace, **kwargs): class BaseField(metaclass=FieldMeta): __field_type__ = None - def __init__(self, id=False, default=None, presentational=False): + def __init__(self, id=False, default=None, presentation=False): + # TODO: add immutable option, make ids immutable by default self._id = id - self._presentational = presentational + self._presentation = presentation if default is None or isinstance(default, self.__field_type__): self._value = default @@ -27,12 +28,12 @@ def is_id(self): return self._id @property - def presentational(self): - return self._presentational + def presentation(self): + return self._presentation - @presentational.setter - def presentational(self, value): - self._presentational = bool(value) + @presentation.setter + def presentation(self, value): + self._presentation = bool(value) @property def value(self): @@ -53,6 +54,10 @@ def value(self, value): self._value = value + @value.deleter + def value(self): + self._value = None + class AnyField(BaseField): __field_type__ = object