GeneralManager 0.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. general_manager/__init__.py +0 -0
  2. general_manager/api/graphql.py +732 -0
  3. general_manager/api/mutation.py +143 -0
  4. general_manager/api/property.py +20 -0
  5. general_manager/apps.py +83 -0
  6. general_manager/auxiliary/__init__.py +2 -0
  7. general_manager/auxiliary/argsToKwargs.py +25 -0
  8. general_manager/auxiliary/filterParser.py +97 -0
  9. general_manager/auxiliary/noneToZero.py +12 -0
  10. general_manager/cache/cacheDecorator.py +72 -0
  11. general_manager/cache/cacheTracker.py +33 -0
  12. general_manager/cache/dependencyIndex.py +300 -0
  13. general_manager/cache/pathMapping.py +151 -0
  14. general_manager/cache/signals.py +48 -0
  15. general_manager/factory/__init__.py +5 -0
  16. general_manager/factory/factories.py +287 -0
  17. general_manager/factory/lazy_methods.py +38 -0
  18. general_manager/interface/__init__.py +3 -0
  19. general_manager/interface/baseInterface.py +308 -0
  20. general_manager/interface/calculationInterface.py +406 -0
  21. general_manager/interface/databaseInterface.py +726 -0
  22. general_manager/manager/__init__.py +3 -0
  23. general_manager/manager/generalManager.py +136 -0
  24. general_manager/manager/groupManager.py +288 -0
  25. general_manager/manager/input.py +48 -0
  26. general_manager/manager/meta.py +75 -0
  27. general_manager/measurement/__init__.py +2 -0
  28. general_manager/measurement/measurement.py +233 -0
  29. general_manager/measurement/measurementField.py +152 -0
  30. general_manager/permission/__init__.py +1 -0
  31. general_manager/permission/basePermission.py +178 -0
  32. general_manager/permission/fileBasedPermission.py +0 -0
  33. general_manager/permission/managerBasedPermission.py +171 -0
  34. general_manager/permission/permissionChecks.py +53 -0
  35. general_manager/permission/permissionDataManager.py +55 -0
  36. general_manager/rule/__init__.py +1 -0
  37. general_manager/rule/handler.py +122 -0
  38. general_manager/rule/rule.py +313 -0
  39. generalmanager-0.0.0.dist-info/METADATA +207 -0
  40. generalmanager-0.0.0.dist-info/RECORD +43 -0
  41. generalmanager-0.0.0.dist-info/WHEEL +5 -0
  42. generalmanager-0.0.0.dist-info/licenses/LICENSE +29 -0
  43. generalmanager-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,3 @@
1
+ from .generalManager import GeneralManager
2
+ from .input import Input
3
+ from ..api.property import graphQlProperty
@@ -0,0 +1,136 @@
1
+ from __future__ import annotations
2
+ from typing import Generic, Type, Any, TYPE_CHECKING
3
+ from general_manager.manager.meta import GeneralManagerMeta
4
+ from general_manager.interface.baseInterface import (
5
+ InterfaceBase,
6
+ Bucket,
7
+ GeneralManagerType,
8
+ )
9
+ from general_manager.api.property import GraphQLProperty
10
+ from general_manager.cache.cacheTracker import addDependency
11
+ from general_manager.cache.signals import dataChange
12
+
13
+ if TYPE_CHECKING:
14
+ from general_manager.permission.basePermission import BasePermission
15
+
16
+
17
+ class GeneralManager(Generic[GeneralManagerType], metaclass=GeneralManagerMeta):
18
+ Interface: Type[InterfaceBase]
19
+ _attributes: dict[str, Any]
20
+
21
+ def __init__(self, *args: Any, **kwargs: Any):
22
+ self._interface = self.Interface(*args, **kwargs)
23
+ self.__id: dict[str, Any] = self._interface.identification
24
+ addDependency(self.__class__.__name__, "identification", f"{self.__id}")
25
+
26
+ def __str__(self):
27
+ return f"{self.__class__.__name__}(**{self.__id})"
28
+
29
+ def __repr__(self):
30
+ return f"{self.__class__.__name__}(**{self.__id})"
31
+
32
+ def __reduce__(self) -> str | tuple[Any, ...]:
33
+ return (self.__class__, tuple(self.__id.values()))
34
+
35
+ def __or__(
36
+ self, other: GeneralManager[GeneralManagerType] | Bucket[GeneralManagerType]
37
+ ) -> Bucket[GeneralManagerType]:
38
+ if isinstance(other, Bucket):
39
+ return other | self
40
+ elif isinstance(other, GeneralManager) and other.__class__ == self.__class__:
41
+ return self.filter(id__in=[self.__id, other.__id])
42
+ else:
43
+ raise TypeError(f"Unsupported type for union: {type(other)}")
44
+
45
+ @property
46
+ def identification(self):
47
+ return self.__id
48
+
49
+ def __iter__(self):
50
+ for key, value in self._attributes.items():
51
+ if callable(value):
52
+ yield key, value(self._interface)
53
+ continue
54
+ yield key, value
55
+ for name, value in self.__class__.__dict__.items():
56
+ if isinstance(value, (GraphQLProperty, property)):
57
+ yield name, getattr(self, name)
58
+
59
+ @classmethod
60
+ @dataChange
61
+ def create(
62
+ cls,
63
+ creator_id: int,
64
+ history_comment: str | None = None,
65
+ ignore_permission: bool = False,
66
+ **kwargs: dict[str, Any],
67
+ ) -> GeneralManager[GeneralManagerType]:
68
+ Permission: Type[BasePermission] | None = getattr(cls, "Permission", None)
69
+ if Permission is not None and not ignore_permission:
70
+ Permission.checkCreatePermission(kwargs, cls, creator_id)
71
+ identification = cls.Interface.create(
72
+ creator_id=creator_id, history_comment=history_comment, **kwargs
73
+ )
74
+ return cls(identification)
75
+
76
+ @dataChange
77
+ def update(
78
+ self,
79
+ creator_id: int,
80
+ history_comment: str | None = None,
81
+ ignore_permission: bool = False,
82
+ **kwargs: dict[str, Any],
83
+ ) -> GeneralManager[GeneralManagerType]:
84
+ Permission: Type[BasePermission] | None = getattr(self, "Permission", None)
85
+ if Permission is not None and not ignore_permission:
86
+ Permission.checkUpdatePermission(kwargs, self, creator_id)
87
+ self._interface.update(
88
+ creator_id=creator_id,
89
+ history_comment=history_comment,
90
+ **kwargs,
91
+ )
92
+ return self.__class__(**self.identification)
93
+
94
+ @dataChange
95
+ def deactivate(
96
+ self,
97
+ creator_id: int,
98
+ history_comment: str | None = None,
99
+ ignore_permission: bool = False,
100
+ ) -> GeneralManager[GeneralManagerType]:
101
+ Permission: Type[BasePermission] | None = getattr(self, "Permission", None)
102
+ if Permission is not None and not ignore_permission:
103
+ Permission.checkDeletePermission(self, creator_id)
104
+ self._interface.deactivate(
105
+ creator_id=creator_id, history_comment=history_comment
106
+ )
107
+ return self.__class__(**self.identification)
108
+
109
+ @classmethod
110
+ def filter(cls, **kwargs: Any) -> Bucket[GeneralManagerType]:
111
+ addDependency(cls.__name__, "filter", f"{cls.__parse_identification(kwargs)}")
112
+ return cls.Interface.filter(**kwargs)
113
+
114
+ @classmethod
115
+ def exclude(cls, **kwargs: Any) -> Bucket[GeneralManagerType]:
116
+ addDependency(cls.__name__, "exclude", f"{cls.__parse_identification(kwargs)}")
117
+ return cls.Interface.exclude(**kwargs)
118
+
119
+ @classmethod
120
+ def all(cls) -> Bucket[GeneralManagerType]:
121
+ return cls.Interface.filter()
122
+
123
+ @staticmethod
124
+ def __parse_identification(kwargs: dict[str, Any]) -> dict[str, Any] | None:
125
+ for key, value in kwargs.items():
126
+ if isinstance(value, GeneralManager):
127
+ kwargs[key] = value.identification
128
+ elif isinstance(value, list):
129
+ kwargs[key] = [
130
+ v.identification for v in value if isinstance(v, GeneralManager)
131
+ ]
132
+ elif isinstance(value, tuple):
133
+ kwargs[key] = tuple(
134
+ v.identification for v in value if isinstance(v, GeneralManager)
135
+ )
136
+ return kwargs if kwargs else None
@@ -0,0 +1,288 @@
1
+ from __future__ import annotations
2
+ from typing import (
3
+ Type,
4
+ Generator,
5
+ Any,
6
+ Generic,
7
+ get_args,
8
+ cast,
9
+ )
10
+ import json
11
+ from datetime import datetime, date, time
12
+ from general_manager.api.graphql import GraphQLProperty
13
+ from general_manager.measurement import Measurement
14
+ from general_manager.manager.generalManager import GeneralManager
15
+ from general_manager.interface.baseInterface import (
16
+ Bucket,
17
+ GeneralManagerType,
18
+ )
19
+
20
+
21
+ class GroupBucket(Bucket[GeneralManagerType]):
22
+
23
+ def __init__(
24
+ self,
25
+ manager_class: Type[GeneralManagerType],
26
+ group_by_keys: tuple[str, ...],
27
+ data: Bucket[GeneralManagerType],
28
+ ):
29
+ super().__init__(manager_class)
30
+ self.__checkGroupByArguments(group_by_keys)
31
+ self._group_by_keys = group_by_keys
32
+ self._data = self.__buildGroupedManager(data)
33
+ self._basis_data = data
34
+
35
+ def __checkGroupByArguments(self, group_by_keys: tuple[str, ...]) -> None:
36
+ """
37
+ This method checks if the given arguments are valid for the groupBy method.
38
+ It raises a TypeError if the arguments are not valid.
39
+ """
40
+ if not all(isinstance(arg, str) for arg in group_by_keys):
41
+ raise TypeError("groupBy() argument must be a string")
42
+ if not all(
43
+ arg in self._manager_class.Interface.getAttributes().keys()
44
+ for arg in group_by_keys
45
+ ):
46
+ raise TypeError(
47
+ f"groupBy() argument must be a valid attribute of {self._manager_class.__name__}"
48
+ )
49
+
50
+ def __buildGroupedManager(
51
+ self,
52
+ data: Bucket[GeneralManagerType],
53
+ ) -> list[GroupedManager[GeneralManagerType]]:
54
+ """
55
+ This method builds the grouped manager.
56
+ It returns a GroupBucket with the grouped data.
57
+ """
58
+ group_by_values = set()
59
+ for entry in data:
60
+ group_by_value = {}
61
+ for arg in self._group_by_keys:
62
+ group_by_value[arg] = getattr(entry, arg)
63
+ group_by_values.add(json.dumps(group_by_value))
64
+
65
+ groups = []
66
+ for group_by_value in group_by_values:
67
+ group_by_value = json.loads(group_by_value)
68
+ grouped_manager_objects = data.filter(**group_by_value)
69
+ groups.append(
70
+ GroupedManager(
71
+ self._manager_class, group_by_value, grouped_manager_objects
72
+ )
73
+ )
74
+ return groups
75
+
76
+ def __or__(self, other: object) -> GroupBucket[GeneralManagerType]:
77
+ if not isinstance(other, self.__class__):
78
+ raise ValueError("Cannot combine different bucket types")
79
+ if self._manager_class != other._manager_class:
80
+ raise ValueError("Cannot combine different manager classes")
81
+ return GroupBucket(
82
+ self._manager_class,
83
+ self._group_by_keys,
84
+ self._basis_data | other._basis_data,
85
+ )
86
+
87
+ def __iter__(self) -> Generator[GroupedManager[GeneralManagerType]]:
88
+ for grouped_manager in self._data:
89
+ yield grouped_manager
90
+
91
+ def filter(self, **kwargs: Any) -> GroupBucket[GeneralManagerType]:
92
+ new_basis_data = self._basis_data.filter(**kwargs)
93
+ return GroupBucket(
94
+ self._manager_class,
95
+ self._group_by_keys,
96
+ new_basis_data,
97
+ )
98
+
99
+ def exclude(self, **kwargs: Any) -> GroupBucket[GeneralManagerType]:
100
+ new_basis_data = self._basis_data.exclude(**kwargs)
101
+ return GroupBucket(
102
+ self._manager_class,
103
+ self._group_by_keys,
104
+ new_basis_data,
105
+ )
106
+
107
+ def first(self) -> GroupedManager[GeneralManagerType] | None:
108
+ try:
109
+ return next(iter(self))
110
+ except StopIteration:
111
+ return None
112
+
113
+ def last(self) -> GroupedManager[GeneralManagerType] | None:
114
+ items = list(self)
115
+ if items:
116
+ return items[-1]
117
+ return None
118
+
119
+ def count(self) -> int:
120
+ return sum(1 for _ in self)
121
+
122
+ def all(self) -> Bucket[GeneralManagerType]:
123
+ return self
124
+
125
+ def get(self, **kwargs: Any) -> GroupedManager[GeneralManagerType]:
126
+ first_value = self.filter(**kwargs).first()
127
+ if first_value is None:
128
+ raise ValueError(
129
+ f"Cannot find {self._manager_class.__name__} with {kwargs}"
130
+ )
131
+ return first_value
132
+
133
+ def __getitem__(
134
+ self, item: int | slice
135
+ ) -> GroupedManager[GeneralManagerType] | GroupBucket[GeneralManagerType]:
136
+ if isinstance(item, int):
137
+ return self._data[item]
138
+ elif isinstance(item, slice):
139
+ new_data = self._data[item]
140
+ new_base_data = None
141
+ for manager in new_data:
142
+ if new_base_data is None:
143
+ new_base_data = manager._data
144
+ else:
145
+ new_base_data = new_base_data | manager._data
146
+ if new_base_data is None:
147
+ raise ValueError("Cannot slice an empty GroupBucket")
148
+ return GroupBucket(self._manager_class, self._group_by_keys, new_base_data)
149
+ raise TypeError(f"Invalid argument type: {type(item)}. Expected int or slice.")
150
+
151
+ def __len__(self) -> int:
152
+ return self.count()
153
+
154
+ def __contains__(self, item: GeneralManagerType) -> bool:
155
+ return item in self._basis_data
156
+
157
+ def sort(
158
+ self,
159
+ key: tuple[str] | str,
160
+ reverse: bool = False,
161
+ ) -> Bucket[GeneralManagerType]:
162
+ if isinstance(key, str):
163
+ key = (key,)
164
+ if reverse:
165
+ sorted_data = self._data.sort(
166
+ key=lambda x: tuple([-getattr(x, k) for k in key])
167
+ )
168
+ else:
169
+ sorted_data = self._data.sort(
170
+ key=lambda x: tuple([getattr(x, k) for k in key])
171
+ )
172
+
173
+ new_bucket = GroupBucket(
174
+ self._manager_class, self._group_by_keys, self._basis_data
175
+ )
176
+ new_bucket._data = sorted_data
177
+ return new_bucket
178
+
179
+ def group_by(self, *group_by_keys: str) -> GroupBucket[GeneralManagerType]:
180
+ """
181
+ This method groups the data by the given arguments.
182
+ It returns a GroupBucket with the grouped data.
183
+ """
184
+ return GroupBucket(
185
+ self._manager_class, tuple([*self._group_by_keys, *group_by_keys]), self
186
+ )
187
+
188
+
189
+ class GroupedManager(Generic[GeneralManagerType]):
190
+ """
191
+ This class is used to group the data of a GeneralManager.
192
+ It is used to create a new GeneralManager with the grouped data.
193
+ """
194
+
195
+ def __init__(
196
+ self,
197
+ manager_class: Type[GeneralManagerType],
198
+ group_by_value: dict[str, Any],
199
+ data: Bucket[GeneralManagerType],
200
+ ):
201
+ self._manager_class = manager_class
202
+ self._group_by_value = group_by_value
203
+ self._data = data
204
+ self._grouped_data: dict[str, Any] = {}
205
+
206
+ def __eq__(self, other: object) -> bool:
207
+ if not isinstance(other, self.__class__):
208
+ return False
209
+ return (
210
+ self._data == other._data
211
+ and self._manager_class == other._manager_class
212
+ and self._group_by_value == other._group_by_value
213
+ )
214
+
215
+ def __hash__(self) -> int:
216
+ return hash(
217
+ (self._manager_class, frozenset(self._group_by_value.items()), self._data)
218
+ )
219
+
220
+ def __repr__(self) -> str:
221
+ return f"{self.__class__.__name__}({self._manager_class}, {self._group_by_value}, {self._data})"
222
+
223
+ def __iter__(self):
224
+ for attribute in self._manager_class.Interface.getAttributes().keys():
225
+ yield attribute, getattr(self, attribute)
226
+ for attribute, attr_value in self._manager_class.__dict__.items():
227
+ if isinstance(attr_value, GraphQLProperty):
228
+ yield attribute, getattr(self, attribute)
229
+
230
+ def __getattr__(self, item: str) -> Any:
231
+ if item in self._group_by_value:
232
+ return self._group_by_value[item]
233
+ if item not in self._grouped_data.keys():
234
+ self._grouped_data[item] = self.combineValue(item)
235
+ return self._grouped_data[item]
236
+
237
+ def combineValue(self, item: str) -> Any:
238
+ if item == "id":
239
+ return None
240
+
241
+ data_type = (
242
+ self._manager_class.Interface.getAttributeTypes().get(item, {}).get("type")
243
+ )
244
+ if data_type is None and item in self._manager_class.__dict__:
245
+ attr_value = self._manager_class.__dict__[item]
246
+ if isinstance(attr_value, GraphQLProperty):
247
+ type_hints = get_args(attr_value.graphql_type_hint)
248
+ data_type = (
249
+ type_hints[0]
250
+ if type_hints
251
+ else cast(type, attr_value.graphql_type_hint)
252
+ )
253
+ if data_type is None:
254
+ raise AttributeError(f"{self.__class__.__name__} has no attribute {item}")
255
+
256
+ total_data = []
257
+ for entry in self._data:
258
+ total_data.append(getattr(entry, item))
259
+
260
+ new_data = None
261
+ if all([i is None for i in total_data]):
262
+ return new_data
263
+ total_data = [i for i in total_data if i is not None]
264
+
265
+ if issubclass(data_type, (Bucket, GeneralManager)):
266
+ for entry in total_data:
267
+ if new_data is None:
268
+ new_data = entry
269
+ else:
270
+ new_data = entry | new_data
271
+ elif issubclass(data_type, list):
272
+ new_data = []
273
+ for entry in total_data:
274
+ new_data.extend(entry)
275
+ elif issubclass(data_type, dict):
276
+ new_data = {}
277
+ for entry in total_data:
278
+ new_data.update(entry)
279
+ elif issubclass(data_type, str):
280
+ new_data = " ".join(total_data)
281
+ elif issubclass(data_type, (int, float, Measurement)):
282
+ new_data = sum(total_data)
283
+ elif issubclass(data_type, (datetime, date, time)):
284
+ new_data = max(total_data)
285
+ elif issubclass(data_type, bool):
286
+ new_data = any(total_data)
287
+
288
+ return new_data
@@ -0,0 +1,48 @@
1
+ from __future__ import annotations
2
+ from typing import Iterable, Optional, Callable, List, TypeVar, Generic, Any
3
+ import inspect
4
+
5
+ from general_manager.manager.generalManager import GeneralManager
6
+ from datetime import date, datetime
7
+ from general_manager.measurement import Measurement
8
+
9
+
10
+ INPUT_TYPE = TypeVar("INPUT_TYPE", bound=type)
11
+
12
+
13
+ class Input(Generic[INPUT_TYPE]):
14
+ def __init__(
15
+ self,
16
+ type: INPUT_TYPE,
17
+ possible_values: Optional[Callable | Iterable] = None,
18
+ depends_on: Optional[List[str]] = None,
19
+ ):
20
+ self.type = type
21
+ self.possible_values = possible_values
22
+ self.is_manager = issubclass(type, GeneralManager)
23
+
24
+ if depends_on is not None:
25
+ # Verwende die angegebenen Abhängigkeiten
26
+ self.depends_on = depends_on
27
+ elif callable(possible_values):
28
+ # Ermittele Abhängigkeiten automatisch aus den Parametern der Funktion
29
+ sig = inspect.signature(possible_values)
30
+ self.depends_on = list(sig.parameters.keys())
31
+ else:
32
+ # Keine Abhängigkeiten
33
+ self.depends_on = []
34
+
35
+ def cast(self, value: Any) -> Any:
36
+ if isinstance(value, self.type):
37
+ return value
38
+ if issubclass(self.type, GeneralManager):
39
+ if isinstance(value, dict):
40
+ return self.type(**value) # type: ignore
41
+ return self.type(id=value) # type: ignore
42
+ if self.type == date:
43
+ return date.fromisoformat(value)
44
+ if self.type == datetime:
45
+ return datetime.fromisoformat(value)
46
+ if self.type == Measurement and isinstance(value, str):
47
+ return Measurement.from_string(value)
48
+ return self.type(value)
@@ -0,0 +1,75 @@
1
+ from __future__ import annotations
2
+ from general_manager.interface.baseInterface import (
3
+ InterfaceBase,
4
+ )
5
+ from django.conf import settings
6
+ from typing import Any, Type, TYPE_CHECKING, Generic, TypeVar
7
+
8
+ if TYPE_CHECKING:
9
+ from general_manager.interface.databaseInterface import ReadOnlyInterface
10
+ from general_manager.manager.generalManager import GeneralManager
11
+
12
+ GeneralManagerType = TypeVar("GeneralManagerType", bound="GeneralManager")
13
+
14
+
15
+ class GeneralManagerMeta(type):
16
+ all_classes: list[Type[GeneralManager]] = []
17
+ read_only_classes: list[Type[ReadOnlyInterface]] = []
18
+ pending_graphql_interfaces: list[Type[GeneralManager]] = []
19
+ pending_attribute_initialization: list[Type[GeneralManager]] = []
20
+ Interface: type[InterfaceBase]
21
+
22
+ def __new__(mcs, name: str, bases: tuple[type, ...], attrs: dict[str, Any]) -> type:
23
+ def createNewGeneralManagerClass(
24
+ mcs, name: str, bases: tuple[type, ...], attrs: dict[str, Any]
25
+ ) -> Type[GeneralManager]:
26
+ return super().__new__(mcs, name, bases, attrs)
27
+
28
+ if "Interface" in attrs:
29
+ interface = attrs.pop("Interface")
30
+ if not issubclass(interface, InterfaceBase):
31
+ raise TypeError(
32
+ f"Interface must be a subclass of {InterfaceBase.__name__}"
33
+ )
34
+ preCreation, postCreation = interface.handleInterface()
35
+ attrs, interface_cls, model = preCreation(name, attrs, interface)
36
+ new_class = createNewGeneralManagerClass(mcs, name, bases, attrs)
37
+ postCreation(new_class, interface_cls, model)
38
+ mcs.pending_attribute_initialization.append(new_class)
39
+ mcs.all_classes.append(new_class)
40
+
41
+ else:
42
+ new_class = createNewGeneralManagerClass(mcs, name, bases, attrs)
43
+
44
+ if getattr(settings, "AUTOCREATE_GRAPHQL", False):
45
+ mcs.pending_graphql_interfaces.append(new_class)
46
+
47
+ return new_class
48
+
49
+ @staticmethod
50
+ def createAtPropertiesForAttributes(
51
+ attributes: dict[str, Any], new_class: Type[GeneralManager]
52
+ ):
53
+
54
+ def desciptorMethod(attr_name: str, new_class: type):
55
+ class Descriptor(Generic[GeneralManagerType]):
56
+ def __init__(self, attr_name: str, new_class: Type[GeneralManager]):
57
+ self.attr_name = attr_name
58
+ self.new_class = new_class
59
+
60
+ def __get__(
61
+ self,
62
+ instance: GeneralManager[GeneralManagerType] | None,
63
+ owner: type | None = None,
64
+ ):
65
+ if instance is None:
66
+ return self.new_class.Interface.getFieldType(self.attr_name)
67
+ attribute = instance._attributes[attr_name]
68
+ if callable(attribute):
69
+ return attribute(instance._interface)
70
+ return attribute
71
+
72
+ return Descriptor(attr_name, new_class)
73
+
74
+ for attr_name in attributes.keys():
75
+ setattr(new_class, attr_name, desciptorMethod(attr_name, new_class))
@@ -0,0 +1,2 @@
1
+ from .measurement import Measurement
2
+ from .measurementField import MeasurementField