python_arango_ogm.db.pao_model

  1from __future__ import annotations
  2
  3import copy
  4import sys
  5from abc import ABC, abstractmethod
  6from enum import StrEnum, auto
  7from functools import partialmethod
  8from typing import Any, Dict, Sequence, Type, Union
  9
 10from python_arango_ogm.db.pao_edges import PAOEdgeDef
 11from python_arango_ogm.db.pao_fields import Field, FloatField
 12from python_arango_ogm.utils import str_util
 13from python_arango_ogm.db.pao_db_base import PAODBBase
 14
 15class LevelEnum(StrEnum):
 16    """
 17    Level Enum, used to specify when schema validation is applied
 18    """
 19
 20    NONE = auto()
 21    NEW = auto()
 22    MODERATE = auto()
 23    STRICT = auto()
 24
 25class PAOModel(ABC):
 26    LEVEL = LevelEnum.STRICT
 27    ADDITIONAL_PROPERTIES = False
 28    SCHEMA_NAME = None
 29    db:PAODBBase = None
 30
 31    def __init__(self):
 32        super().__init__()
 33
 34    @classmethod
 35    def is_field(cls, attribute_name: str) -> bool:
 36        attr = getattr(cls, attribute_name)
 37        return ((not attribute_name.startswith('_')) and
 38                (issubclass(type(attr), Field) or issubclass(type(attr), PAOEdgeDef)))
 39
 40    @classmethod
 41    def all(cls, sort_fields:Dict[str, str], marshall=True) -> str:
 42        records = cls.db.get_by_attributes(cls.collection_name(), sort_key_dict=sort_fields)
 43        return cls.marshall_rows(records) if marshall else records
 44
 45    @classmethod
 46    def insert(cls, attributes:Dict[str, Any]):
 47        # TODO: See if we have timestamp fields (created_at, updated_at) and add accordingly
 48        doc = cls.add_timestamps(attributes, created=True, updated=True)
 49        return cls.db.insert_doc(cls.collection_name(), doc)
 50    @classmethod
 51    def upsert(cls, attributes:Dict[str, Any], insert_attrs:Dict[str, Any], update_attrs:Dict[str, Any]):
 52        insert_doc = cls.add_timestamps(insert_attrs, created=True, updated=True)
 53        update_doc = cls.add_timestamps(update_attrs, updated=True)
 54        return cls.db.upsert_doc(cls.collection_name(), attributes, insert_doc, update_doc)
 55
 56    @classmethod
 57    def find_by_key(cls, key, marshall:bool=True) -> Union[Dict[str, Any], Type[PAOModel]]:
 58        """ Find a single record by given key and return """
 59        record = cls.db.find_by_key(cls.collection_name(), key)
 60        return cls.marshall_row(record) if marshall and record else record
 61
 62    @classmethod
 63    def find_by_attributes(cls, attributes:Dict[str, Any], marshall:bool=True):
 64        """ Find a single record by given attributes and return """
 65        records = cls.db.find_by_attributes(cls.collection_name(), {'_key': key})
 66        return cls.marshall_rows(records) if marshall else records
 67
 68    @classmethod
 69    def get_by_attributes(cls, attributes:Dict[str, Any], sort_keys: Dict[str, str], marshall:bool=True):
 70        """ Find records by given attributes, sorting by sort_keys and return """
 71        cls.db.get_by_attributes(cls.collection_name(), {'_key': key})
 72
 73    @classmethod
 74    def remove_by_key(cls, key):
 75        cls.db.remove_by_key(cls.collection_name(), key)
 76
 77    @classmethod
 78    def get_fields(cls) -> Dict[str, Union[Field, PAOEdgeDef]]:
 79        model_fields = {f:getattr(cls, f) for f in dir(cls) if cls.is_field(f)}
 80        return model_fields
 81
 82    @classmethod
 83    def get_edge_defs(cls) -> Dict[str, PAOEdgeDef]:
 84        return {e:getattr(cls, e) for e in dir(cls) if isinstance(getattr(cls, e), PAOEdgeDef)}
 85
 86    def get_edges(self, edge_def:PAOEdgeDef) -> Sequence[PAOEdgeDef]:
 87        if not self._key:
 88            raise AttributeError("_key field is not present (this model instance hasn't been obtained from a marshalling method).")
 89
 90        return edge_def.associated_edges({"key": self._key})
 91
 92    def insert_edge(self, edge_def:PAOEdgeDef, to: Union[str, PAOModel]) -> Sequence[PAOEdgeDef]:
 93        if not self._key:
 94            raise AttributeError("_key field is not present (this model instance hasn't been obtained from a marshalling method).")
 95
 96        return edge_def.insert_edge({"from": self._key, "to": to})
 97
 98    @classmethod
 99    def collection_name(cls) -> str:
100        if cls.SCHEMA_NAME:
101            coll_name = cls.SCHEMA_NAME
102        else:
103            coll_name = str_util.snake_text(cls.__name__.split('Model')[0])
104        return coll_name
105
106    @classmethod
107    def add_timestamps(cls, field_dict:Dict[str, Any], created:bool=False, updated:bool=False) -> Dict[str, Any]:
108        fields = cls.get_fields()
109        doc = copy.copy(field_dict)
110        if created and 'created_at' in fields:
111            doc['created_at'] = '`DATE_NOW()`'
112        if updated and 'updated_at' in fields:
113            doc['updated_at'] = '`DATE_NOW()`'
114        return doc
115
116    @classmethod
117    def marshall_row(cls, field_dict:Dict[str, Any]) -> Type[PAOModel]:
118        model = PAOModel()
119        for k, v in field_dict.items():
120            setattr(model, k, v)
121
122        # edge_defs = cls.get_edge_defs()
123        # for edge_def in edge_defs.values():
124        #     to_model = edge_def.model_class_to()
125        #     def edge_accessor(self):
126        #         self.get_edges(self.key, edge)
127        #
128        #     # edge_accessor = partialmethod(get_edges, edge_def=edge_def)
129        #
130        #     setattr(model, f"{to_model.collection_name()}_edges", edge_accessor)
131        return model
132
133    @classmethod
134    def marshall_rows(cls, rows:Sequence[Dict[str, Any]]) -> Sequence[Type[PAOModel]]:
135        return [cls.marshall_row(row) for row in rows]
class LevelEnum(enum.StrEnum):
16class LevelEnum(StrEnum):
17    """
18    Level Enum, used to specify when schema validation is applied
19    """
20
21    NONE = auto()
22    NEW = auto()
23    MODERATE = auto()
24    STRICT = auto()

Level Enum, used to specify when schema validation is applied

NONE = <LevelEnum.NONE: 'none'>
NEW = <LevelEnum.NEW: 'new'>
MODERATE = <LevelEnum.MODERATE: 'moderate'>
STRICT = <LevelEnum.STRICT: 'strict'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class PAOModel(abc.ABC):
 26class PAOModel(ABC):
 27    LEVEL = LevelEnum.STRICT
 28    ADDITIONAL_PROPERTIES = False
 29    SCHEMA_NAME = None
 30    db:PAODBBase = None
 31
 32    def __init__(self):
 33        super().__init__()
 34
 35    @classmethod
 36    def is_field(cls, attribute_name: str) -> bool:
 37        attr = getattr(cls, attribute_name)
 38        return ((not attribute_name.startswith('_')) and
 39                (issubclass(type(attr), Field) or issubclass(type(attr), PAOEdgeDef)))
 40
 41    @classmethod
 42    def all(cls, sort_fields:Dict[str, str], marshall=True) -> str:
 43        records = cls.db.get_by_attributes(cls.collection_name(), sort_key_dict=sort_fields)
 44        return cls.marshall_rows(records) if marshall else records
 45
 46    @classmethod
 47    def insert(cls, attributes:Dict[str, Any]):
 48        # TODO: See if we have timestamp fields (created_at, updated_at) and add accordingly
 49        doc = cls.add_timestamps(attributes, created=True, updated=True)
 50        return cls.db.insert_doc(cls.collection_name(), doc)
 51    @classmethod
 52    def upsert(cls, attributes:Dict[str, Any], insert_attrs:Dict[str, Any], update_attrs:Dict[str, Any]):
 53        insert_doc = cls.add_timestamps(insert_attrs, created=True, updated=True)
 54        update_doc = cls.add_timestamps(update_attrs, updated=True)
 55        return cls.db.upsert_doc(cls.collection_name(), attributes, insert_doc, update_doc)
 56
 57    @classmethod
 58    def find_by_key(cls, key, marshall:bool=True) -> Union[Dict[str, Any], Type[PAOModel]]:
 59        """ Find a single record by given key and return """
 60        record = cls.db.find_by_key(cls.collection_name(), key)
 61        return cls.marshall_row(record) if marshall and record else record
 62
 63    @classmethod
 64    def find_by_attributes(cls, attributes:Dict[str, Any], marshall:bool=True):
 65        """ Find a single record by given attributes and return """
 66        records = cls.db.find_by_attributes(cls.collection_name(), {'_key': key})
 67        return cls.marshall_rows(records) if marshall else records
 68
 69    @classmethod
 70    def get_by_attributes(cls, attributes:Dict[str, Any], sort_keys: Dict[str, str], marshall:bool=True):
 71        """ Find records by given attributes, sorting by sort_keys and return """
 72        cls.db.get_by_attributes(cls.collection_name(), {'_key': key})
 73
 74    @classmethod
 75    def remove_by_key(cls, key):
 76        cls.db.remove_by_key(cls.collection_name(), key)
 77
 78    @classmethod
 79    def get_fields(cls) -> Dict[str, Union[Field, PAOEdgeDef]]:
 80        model_fields = {f:getattr(cls, f) for f in dir(cls) if cls.is_field(f)}
 81        return model_fields
 82
 83    @classmethod
 84    def get_edge_defs(cls) -> Dict[str, PAOEdgeDef]:
 85        return {e:getattr(cls, e) for e in dir(cls) if isinstance(getattr(cls, e), PAOEdgeDef)}
 86
 87    def get_edges(self, edge_def:PAOEdgeDef) -> Sequence[PAOEdgeDef]:
 88        if not self._key:
 89            raise AttributeError("_key field is not present (this model instance hasn't been obtained from a marshalling method).")
 90
 91        return edge_def.associated_edges({"key": self._key})
 92
 93    def insert_edge(self, edge_def:PAOEdgeDef, to: Union[str, PAOModel]) -> Sequence[PAOEdgeDef]:
 94        if not self._key:
 95            raise AttributeError("_key field is not present (this model instance hasn't been obtained from a marshalling method).")
 96
 97        return edge_def.insert_edge({"from": self._key, "to": to})
 98
 99    @classmethod
100    def collection_name(cls) -> str:
101        if cls.SCHEMA_NAME:
102            coll_name = cls.SCHEMA_NAME
103        else:
104            coll_name = str_util.snake_text(cls.__name__.split('Model')[0])
105        return coll_name
106
107    @classmethod
108    def add_timestamps(cls, field_dict:Dict[str, Any], created:bool=False, updated:bool=False) -> Dict[str, Any]:
109        fields = cls.get_fields()
110        doc = copy.copy(field_dict)
111        if created and 'created_at' in fields:
112            doc['created_at'] = '`DATE_NOW()`'
113        if updated and 'updated_at' in fields:
114            doc['updated_at'] = '`DATE_NOW()`'
115        return doc
116
117    @classmethod
118    def marshall_row(cls, field_dict:Dict[str, Any]) -> Type[PAOModel]:
119        model = PAOModel()
120        for k, v in field_dict.items():
121            setattr(model, k, v)
122
123        # edge_defs = cls.get_edge_defs()
124        # for edge_def in edge_defs.values():
125        #     to_model = edge_def.model_class_to()
126        #     def edge_accessor(self):
127        #         self.get_edges(self.key, edge)
128        #
129        #     # edge_accessor = partialmethod(get_edges, edge_def=edge_def)
130        #
131        #     setattr(model, f"{to_model.collection_name()}_edges", edge_accessor)
132        return model
133
134    @classmethod
135    def marshall_rows(cls, rows:Sequence[Dict[str, Any]]) -> Sequence[Type[PAOModel]]:
136        return [cls.marshall_row(row) for row in rows]

Helper class that provides a standard way to create an ABC using inheritance.

LEVEL = <LevelEnum.STRICT: 'strict'>
ADDITIONAL_PROPERTIES = False
SCHEMA_NAME = None
@classmethod
def is_field(cls, attribute_name: str) -> bool:
35    @classmethod
36    def is_field(cls, attribute_name: str) -> bool:
37        attr = getattr(cls, attribute_name)
38        return ((not attribute_name.startswith('_')) and
39                (issubclass(type(attr), Field) or issubclass(type(attr), PAOEdgeDef)))
@classmethod
def all(cls, sort_fields: Dict[str, str], marshall=True) -> str:
41    @classmethod
42    def all(cls, sort_fields:Dict[str, str], marshall=True) -> str:
43        records = cls.db.get_by_attributes(cls.collection_name(), sort_key_dict=sort_fields)
44        return cls.marshall_rows(records) if marshall else records
@classmethod
def insert(cls, attributes: Dict[str, Any]):
46    @classmethod
47    def insert(cls, attributes:Dict[str, Any]):
48        # TODO: See if we have timestamp fields (created_at, updated_at) and add accordingly
49        doc = cls.add_timestamps(attributes, created=True, updated=True)
50        return cls.db.insert_doc(cls.collection_name(), doc)
@classmethod
def upsert( cls, attributes: Dict[str, Any], insert_attrs: Dict[str, Any], update_attrs: Dict[str, Any]):
51    @classmethod
52    def upsert(cls, attributes:Dict[str, Any], insert_attrs:Dict[str, Any], update_attrs:Dict[str, Any]):
53        insert_doc = cls.add_timestamps(insert_attrs, created=True, updated=True)
54        update_doc = cls.add_timestamps(update_attrs, updated=True)
55        return cls.db.upsert_doc(cls.collection_name(), attributes, insert_doc, update_doc)
@classmethod
def find_by_key( cls, key, marshall: bool = True) -> Union[Dict[str, Any], Type[PAOModel]]:
57    @classmethod
58    def find_by_key(cls, key, marshall:bool=True) -> Union[Dict[str, Any], Type[PAOModel]]:
59        """ Find a single record by given key and return """
60        record = cls.db.find_by_key(cls.collection_name(), key)
61        return cls.marshall_row(record) if marshall and record else record

Find a single record by given key and return

@classmethod
def find_by_attributes(cls, attributes: Dict[str, Any], marshall: bool = True):
63    @classmethod
64    def find_by_attributes(cls, attributes:Dict[str, Any], marshall:bool=True):
65        """ Find a single record by given attributes and return """
66        records = cls.db.find_by_attributes(cls.collection_name(), {'_key': key})
67        return cls.marshall_rows(records) if marshall else records

Find a single record by given attributes and return

@classmethod
def get_by_attributes( cls, attributes: Dict[str, Any], sort_keys: Dict[str, str], marshall: bool = True):
69    @classmethod
70    def get_by_attributes(cls, attributes:Dict[str, Any], sort_keys: Dict[str, str], marshall:bool=True):
71        """ Find records by given attributes, sorting by sort_keys and return """
72        cls.db.get_by_attributes(cls.collection_name(), {'_key': key})

Find records by given attributes, sorting by sort_keys and return

@classmethod
def remove_by_key(cls, key):
74    @classmethod
75    def remove_by_key(cls, key):
76        cls.db.remove_by_key(cls.collection_name(), key)
@classmethod
def get_fields( cls) -> Dict[str, Union[python_arango_ogm.db.pao_fields.Field, python_arango_ogm.db.pao_edges.PAOEdgeDef]]:
78    @classmethod
79    def get_fields(cls) -> Dict[str, Union[Field, PAOEdgeDef]]:
80        model_fields = {f:getattr(cls, f) for f in dir(cls) if cls.is_field(f)}
81        return model_fields
@classmethod
def get_edge_defs(cls) -> Dict[str, python_arango_ogm.db.pao_edges.PAOEdgeDef]:
83    @classmethod
84    def get_edge_defs(cls) -> Dict[str, PAOEdgeDef]:
85        return {e:getattr(cls, e) for e in dir(cls) if isinstance(getattr(cls, e), PAOEdgeDef)}
def get_edges( self, edge_def: python_arango_ogm.db.pao_edges.PAOEdgeDef) -> Sequence[python_arango_ogm.db.pao_edges.PAOEdgeDef]:
87    def get_edges(self, edge_def:PAOEdgeDef) -> Sequence[PAOEdgeDef]:
88        if not self._key:
89            raise AttributeError("_key field is not present (this model instance hasn't been obtained from a marshalling method).")
90
91        return edge_def.associated_edges({"key": self._key})
def insert_edge( self, edge_def: python_arango_ogm.db.pao_edges.PAOEdgeDef, to: Union[str, PAOModel]) -> Sequence[python_arango_ogm.db.pao_edges.PAOEdgeDef]:
93    def insert_edge(self, edge_def:PAOEdgeDef, to: Union[str, PAOModel]) -> Sequence[PAOEdgeDef]:
94        if not self._key:
95            raise AttributeError("_key field is not present (this model instance hasn't been obtained from a marshalling method).")
96
97        return edge_def.insert_edge({"from": self._key, "to": to})
@classmethod
def collection_name(cls) -> str:
 99    @classmethod
100    def collection_name(cls) -> str:
101        if cls.SCHEMA_NAME:
102            coll_name = cls.SCHEMA_NAME
103        else:
104            coll_name = str_util.snake_text(cls.__name__.split('Model')[0])
105        return coll_name
@classmethod
def add_timestamps( cls, field_dict: Dict[str, Any], created: bool = False, updated: bool = False) -> Dict[str, Any]:
107    @classmethod
108    def add_timestamps(cls, field_dict:Dict[str, Any], created:bool=False, updated:bool=False) -> Dict[str, Any]:
109        fields = cls.get_fields()
110        doc = copy.copy(field_dict)
111        if created and 'created_at' in fields:
112            doc['created_at'] = '`DATE_NOW()`'
113        if updated and 'updated_at' in fields:
114            doc['updated_at'] = '`DATE_NOW()`'
115        return doc
@classmethod
def marshall_row( cls, field_dict: Dict[str, Any]) -> Type[PAOModel]:
117    @classmethod
118    def marshall_row(cls, field_dict:Dict[str, Any]) -> Type[PAOModel]:
119        model = PAOModel()
120        for k, v in field_dict.items():
121            setattr(model, k, v)
122
123        # edge_defs = cls.get_edge_defs()
124        # for edge_def in edge_defs.values():
125        #     to_model = edge_def.model_class_to()
126        #     def edge_accessor(self):
127        #         self.get_edges(self.key, edge)
128        #
129        #     # edge_accessor = partialmethod(get_edges, edge_def=edge_def)
130        #
131        #     setattr(model, f"{to_model.collection_name()}_edges", edge_accessor)
132        return model
@classmethod
def marshall_rows( cls, rows: Sequence[Dict[str, Any]]) -> Sequence[Type[PAOModel]]:
134    @classmethod
135    def marshall_rows(cls, rows:Sequence[Dict[str, Any]]) -> Sequence[Type[PAOModel]]:
136        return [cls.marshall_row(row) for row in rows]