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.
- general_manager/__init__.py +0 -0
- general_manager/api/graphql.py +732 -0
- general_manager/api/mutation.py +143 -0
- general_manager/api/property.py +20 -0
- general_manager/apps.py +83 -0
- general_manager/auxiliary/__init__.py +2 -0
- general_manager/auxiliary/argsToKwargs.py +25 -0
- general_manager/auxiliary/filterParser.py +97 -0
- general_manager/auxiliary/noneToZero.py +12 -0
- general_manager/cache/cacheDecorator.py +72 -0
- general_manager/cache/cacheTracker.py +33 -0
- general_manager/cache/dependencyIndex.py +300 -0
- general_manager/cache/pathMapping.py +151 -0
- general_manager/cache/signals.py +48 -0
- general_manager/factory/__init__.py +5 -0
- general_manager/factory/factories.py +287 -0
- general_manager/factory/lazy_methods.py +38 -0
- general_manager/interface/__init__.py +3 -0
- general_manager/interface/baseInterface.py +308 -0
- general_manager/interface/calculationInterface.py +406 -0
- general_manager/interface/databaseInterface.py +726 -0
- general_manager/manager/__init__.py +3 -0
- general_manager/manager/generalManager.py +136 -0
- general_manager/manager/groupManager.py +288 -0
- general_manager/manager/input.py +48 -0
- general_manager/manager/meta.py +75 -0
- general_manager/measurement/__init__.py +2 -0
- general_manager/measurement/measurement.py +233 -0
- general_manager/measurement/measurementField.py +152 -0
- general_manager/permission/__init__.py +1 -0
- general_manager/permission/basePermission.py +178 -0
- general_manager/permission/fileBasedPermission.py +0 -0
- general_manager/permission/managerBasedPermission.py +171 -0
- general_manager/permission/permissionChecks.py +53 -0
- general_manager/permission/permissionDataManager.py +55 -0
- general_manager/rule/__init__.py +1 -0
- general_manager/rule/handler.py +122 -0
- general_manager/rule/rule.py +313 -0
- generalmanager-0.0.0.dist-info/METADATA +207 -0
- generalmanager-0.0.0.dist-info/RECORD +43 -0
- generalmanager-0.0.0.dist-info/WHEEL +5 -0
- generalmanager-0.0.0.dist-info/licenses/LICENSE +29 -0
- generalmanager-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,287 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import TYPE_CHECKING, Type, Callable, Union, Any, TypeVar, Literal, cast
|
3
|
+
from factory.declarations import LazyFunction, LazyAttribute, LazyAttributeSequence
|
4
|
+
from factory.faker import Faker
|
5
|
+
import exrex # type: ignore
|
6
|
+
from django.db import models
|
7
|
+
from django.core.validators import RegexValidator
|
8
|
+
from factory.django import DjangoModelFactory
|
9
|
+
from django.utils import timezone
|
10
|
+
import random
|
11
|
+
from decimal import Decimal
|
12
|
+
from general_manager.measurement.measurement import Measurement
|
13
|
+
from general_manager.measurement.measurementField import MeasurementField
|
14
|
+
from datetime import date, datetime, time
|
15
|
+
|
16
|
+
if TYPE_CHECKING:
|
17
|
+
from general_manager.interface.databaseInterface import (
|
18
|
+
DBBasedInterface,
|
19
|
+
)
|
20
|
+
|
21
|
+
modelsModel = TypeVar("modelsModel", bound=models.Model)
|
22
|
+
|
23
|
+
|
24
|
+
class AutoFactory(DjangoModelFactory[modelsModel]):
|
25
|
+
"""
|
26
|
+
A factory class that automatically generates values for model fields,
|
27
|
+
including handling of unique fields and constraints.
|
28
|
+
"""
|
29
|
+
|
30
|
+
interface: Type[DBBasedInterface]
|
31
|
+
_adjustmentMethod: (
|
32
|
+
Callable[..., Union[dict[str, Any], list[dict[str, Any]]]] | None
|
33
|
+
) = None
|
34
|
+
|
35
|
+
@classmethod
|
36
|
+
def _generate( # type: ignore
|
37
|
+
cls, strategy: Literal["build", "create"], params: dict[str, Any]
|
38
|
+
) -> models.Model | list[models.Model]:
|
39
|
+
cls._original_params = params
|
40
|
+
model = getattr(cls._meta, "model")
|
41
|
+
if not issubclass(model, models.Model):
|
42
|
+
raise ValueError("Model must be a type")
|
43
|
+
field_name_list, to_ignore_list = cls.interface.handleCustomFields(model)
|
44
|
+
|
45
|
+
fields = [
|
46
|
+
field
|
47
|
+
for field in model._meta.get_fields()
|
48
|
+
if field.name not in to_ignore_list
|
49
|
+
]
|
50
|
+
special_fields: list[models.Field[Any, Any]] = [
|
51
|
+
getattr(model, field_name) for field_name in field_name_list
|
52
|
+
]
|
53
|
+
pre_declations = getattr(cls._meta, "pre_declarations", [])
|
54
|
+
post_declarations = getattr(cls._meta, "post_declarations", [])
|
55
|
+
declared_fields: set[str] = set(pre_declations) | set(post_declarations)
|
56
|
+
|
57
|
+
field_list: list[models.Field[Any, Any] | models.ForeignObjectRel] = [
|
58
|
+
*fields,
|
59
|
+
*special_fields,
|
60
|
+
]
|
61
|
+
|
62
|
+
for field in field_list:
|
63
|
+
if field.name in [*params, *declared_fields]:
|
64
|
+
continue # Skip fields that are already set
|
65
|
+
if isinstance(field, models.AutoField) or field.auto_created:
|
66
|
+
continue # Skip auto fields
|
67
|
+
params[field.name] = get_field_value(field)
|
68
|
+
|
69
|
+
obj: list[models.Model] | models.Model = super()._generate(strategy, params)
|
70
|
+
if isinstance(obj, list):
|
71
|
+
for item in obj: # type: ignore
|
72
|
+
if not isinstance(item, models.Model):
|
73
|
+
raise ValueError("Model must be a type")
|
74
|
+
cls._handleManyToManyFieldsAfterCreation(item, params)
|
75
|
+
else:
|
76
|
+
cls._handleManyToManyFieldsAfterCreation(obj, params)
|
77
|
+
return obj
|
78
|
+
|
79
|
+
@classmethod
|
80
|
+
def _handleManyToManyFieldsAfterCreation(
|
81
|
+
cls, obj: models.Model, attrs: dict[str, Any]
|
82
|
+
) -> None:
|
83
|
+
for field in obj._meta.many_to_many:
|
84
|
+
if field.name in attrs:
|
85
|
+
m2m_values = attrs[field.name]
|
86
|
+
else:
|
87
|
+
m2m_values = get_m2m_field_value(field)
|
88
|
+
if m2m_values:
|
89
|
+
getattr(obj, field.name).set(m2m_values)
|
90
|
+
|
91
|
+
@classmethod
|
92
|
+
def _adjust_kwargs(cls, **kwargs: dict[str, Any]) -> dict[str, Any]:
|
93
|
+
# Remove ManyToMany fields from kwargs before object creation
|
94
|
+
model: Type[models.Model] = getattr(cls._meta, "model")
|
95
|
+
m2m_fields = {field.name for field in model._meta.many_to_many}
|
96
|
+
for field_name in m2m_fields:
|
97
|
+
kwargs.pop(field_name, None)
|
98
|
+
return kwargs
|
99
|
+
|
100
|
+
@classmethod
|
101
|
+
def _create( # type: ignore
|
102
|
+
cls, model_class: Type[models.Model], *args: list[Any], **kwargs: dict[str, Any]
|
103
|
+
) -> models.Model | list[models.Model]:
|
104
|
+
kwargs = cls._adjust_kwargs(**kwargs)
|
105
|
+
if cls._adjustmentMethod is not None:
|
106
|
+
return cls.__createWithGenerateFunc(strategy=True, params=kwargs)
|
107
|
+
return cls._modelCreation(model_class, **kwargs)
|
108
|
+
|
109
|
+
@classmethod
|
110
|
+
def _build( # type: ignore
|
111
|
+
cls, model_class: Type[models.Model], *args: list[Any], **kwargs: dict[str, Any]
|
112
|
+
) -> models.Model | list[models.Model]:
|
113
|
+
kwargs = cls._adjust_kwargs(**kwargs)
|
114
|
+
if cls._adjustmentMethod is not None:
|
115
|
+
return cls.__createWithGenerateFunc(strategy=False, params=kwargs)
|
116
|
+
return cls._modelBuilding(model_class, **kwargs)
|
117
|
+
|
118
|
+
@classmethod
|
119
|
+
def _modelCreation(
|
120
|
+
cls, model_class: Type[models.Model], **kwargs: dict[str, Any]
|
121
|
+
) -> models.Model:
|
122
|
+
obj = model_class()
|
123
|
+
for field, value in kwargs.items():
|
124
|
+
setattr(obj, field, value)
|
125
|
+
obj.full_clean()
|
126
|
+
obj.save()
|
127
|
+
return obj
|
128
|
+
|
129
|
+
@classmethod
|
130
|
+
def _modelBuilding(
|
131
|
+
cls, model_class: Type[models.Model], **kwargs: dict[str, Any]
|
132
|
+
) -> models.Model:
|
133
|
+
obj = model_class()
|
134
|
+
for field, value in kwargs.items():
|
135
|
+
setattr(obj, field, value)
|
136
|
+
return obj
|
137
|
+
|
138
|
+
@classmethod
|
139
|
+
def __createWithGenerateFunc(
|
140
|
+
cls, strategy: bool, params: dict[str, Any]
|
141
|
+
) -> models.Model | list[models.Model]:
|
142
|
+
model_cls = getattr(cls._meta, "model")
|
143
|
+
if cls._adjustmentMethod is None:
|
144
|
+
raise ValueError("generate_func is not defined")
|
145
|
+
records = cls._adjustmentMethod(**params)
|
146
|
+
if isinstance(records, dict):
|
147
|
+
if strategy:
|
148
|
+
return cls._modelCreation(model_cls, **records)
|
149
|
+
return cls._modelBuilding(model_cls, **records)
|
150
|
+
|
151
|
+
created_objects: list[models.Model] = []
|
152
|
+
for record in records:
|
153
|
+
if strategy:
|
154
|
+
created_objects.append(cls._modelCreation(model_cls, **record))
|
155
|
+
else:
|
156
|
+
created_objects.append(cls._modelBuilding(model_cls, **record))
|
157
|
+
return created_objects
|
158
|
+
|
159
|
+
|
160
|
+
def get_field_value(field: models.Field[Any, Any] | models.ForeignObjectRel) -> object:
|
161
|
+
"""
|
162
|
+
Returns a suitable value for a given Django model field.
|
163
|
+
"""
|
164
|
+
if field.null:
|
165
|
+
if random.choice([True] + 9 * [False]):
|
166
|
+
return None
|
167
|
+
|
168
|
+
if isinstance(field, MeasurementField):
|
169
|
+
base_unit = field.base_unit
|
170
|
+
value = Decimal(str(random.uniform(0, 10_000))[:10])
|
171
|
+
return LazyFunction(lambda: Measurement(value, base_unit))
|
172
|
+
elif isinstance(field, models.CharField):
|
173
|
+
max_length = field.max_length or 100
|
174
|
+
# Check for RegexValidator
|
175
|
+
regex = None
|
176
|
+
for validator in field.validators:
|
177
|
+
if isinstance(validator, RegexValidator):
|
178
|
+
regex = getattr(validator.regex, "pattern", None)
|
179
|
+
break
|
180
|
+
if regex:
|
181
|
+
# Use exrex to generate a string matching the regex
|
182
|
+
return LazyFunction(lambda: exrex.getone(regex)) # type: ignore
|
183
|
+
else:
|
184
|
+
return cast(str, Faker("text", max_nb_chars=max_length))
|
185
|
+
elif isinstance(field, models.TextField):
|
186
|
+
return cast(str, Faker("paragraph"))
|
187
|
+
elif isinstance(field, models.IntegerField):
|
188
|
+
return cast(int, Faker("random_int"))
|
189
|
+
elif isinstance(field, models.DecimalField):
|
190
|
+
max_digits = field.max_digits
|
191
|
+
decimal_places = field.decimal_places
|
192
|
+
left_digits = max_digits - decimal_places
|
193
|
+
return cast(
|
194
|
+
Decimal,
|
195
|
+
Faker(
|
196
|
+
"pydecimal",
|
197
|
+
left_digits=left_digits,
|
198
|
+
right_digits=decimal_places,
|
199
|
+
positive=True,
|
200
|
+
),
|
201
|
+
)
|
202
|
+
elif isinstance(field, models.FloatField):
|
203
|
+
return cast(float, Faker("pyfloat", positive=True))
|
204
|
+
elif isinstance(field, models.DateField):
|
205
|
+
return cast(date, Faker("date_between", start_date="-1y", end_date="today"))
|
206
|
+
elif isinstance(field, models.DateTimeField):
|
207
|
+
return cast(
|
208
|
+
datetime,
|
209
|
+
Faker(
|
210
|
+
"date_time_between",
|
211
|
+
start_date="-1y",
|
212
|
+
end_date="now",
|
213
|
+
tzinfo=timezone.utc,
|
214
|
+
),
|
215
|
+
)
|
216
|
+
elif isinstance(field, models.BooleanField):
|
217
|
+
return cast(bool, Faker("pybool"))
|
218
|
+
elif isinstance(field, models.ForeignKey):
|
219
|
+
# Create or get an instance of the related model
|
220
|
+
if hasattr(field.related_model, "_general_manager_class"):
|
221
|
+
related_factory = field.related_model._general_manager_class.Factory
|
222
|
+
return related_factory()
|
223
|
+
else:
|
224
|
+
# If no factory exists, pick a random existing instance
|
225
|
+
related_instances = list(field.related_model.objects.all())
|
226
|
+
if related_instances:
|
227
|
+
return LazyFunction(lambda: random.choice(related_instances))
|
228
|
+
else:
|
229
|
+
raise ValueError(
|
230
|
+
f"No factory found for {field.related_model.__name__} and no instances found"
|
231
|
+
)
|
232
|
+
elif isinstance(field, models.OneToOneField):
|
233
|
+
# Similar to ForeignKey
|
234
|
+
if hasattr(field.related_model, "_general_manager_class"):
|
235
|
+
related_factory = field.related_model._general_manager_class.Factory
|
236
|
+
return related_factory()
|
237
|
+
else:
|
238
|
+
# If no factory exists, pick a random existing instance
|
239
|
+
related_instances = list(field.related_model.objects.all())
|
240
|
+
if related_instances:
|
241
|
+
return LazyFunction(lambda: random.choice(related_instances))
|
242
|
+
else:
|
243
|
+
raise ValueError(
|
244
|
+
f"No factory found for {field.related_model.__name__} and no instances found"
|
245
|
+
)
|
246
|
+
elif isinstance(field, models.EmailField):
|
247
|
+
return cast(str, Faker("email"))
|
248
|
+
elif isinstance(field, models.URLField):
|
249
|
+
return cast(str, Faker("url"))
|
250
|
+
elif isinstance(field, models.GenericIPAddressField):
|
251
|
+
return cast(str, Faker("ipv4"))
|
252
|
+
elif isinstance(field, models.UUIDField):
|
253
|
+
return cast(str, Faker("uuid4"))
|
254
|
+
elif isinstance(field, models.DurationField):
|
255
|
+
return cast(time, Faker("time_delta"))
|
256
|
+
else:
|
257
|
+
return None # For unsupported field types
|
258
|
+
|
259
|
+
|
260
|
+
def get_m2m_field_value(field: models.ManyToManyField[Any, Any]) -> list[models.Model]:
|
261
|
+
"""
|
262
|
+
Returns a list of instances for a ManyToMany field.
|
263
|
+
"""
|
264
|
+
related_factory = globals().get(f"{field.related_model.__name__}Factory")
|
265
|
+
existing_instances = list(field.related_model.objects.all())
|
266
|
+
|
267
|
+
if related_factory:
|
268
|
+
# Use existing instances if available, otherwise create new ones
|
269
|
+
if existing_instances:
|
270
|
+
max_instances = len(existing_instances)
|
271
|
+
num_instances = random.randint(0, min(max_instances, 15))
|
272
|
+
return random.sample(existing_instances, num_instances)
|
273
|
+
else:
|
274
|
+
# No existing instances, create a few
|
275
|
+
num_to_create = random.randint(1, 3)
|
276
|
+
new_instances = [related_factory() for _ in range(num_to_create)]
|
277
|
+
return new_instances
|
278
|
+
else:
|
279
|
+
# No factory exists, use existing instances
|
280
|
+
if existing_instances:
|
281
|
+
max_instances = len(existing_instances)
|
282
|
+
num_instances = random.randint(0, max_instances)
|
283
|
+
return random.sample(existing_instances, num_instances)
|
284
|
+
else:
|
285
|
+
raise ValueError(
|
286
|
+
f"No factory found for {field.related_model.__name__} and no instances found"
|
287
|
+
)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
from typing import Any
|
2
|
+
from factory.declarations import LazyFunction, LazyAttribute, LazyAttributeSequence
|
3
|
+
import random
|
4
|
+
from general_manager.measurement.measurement import Measurement
|
5
|
+
from datetime import timedelta, date
|
6
|
+
from faker import Faker
|
7
|
+
|
8
|
+
fake = Faker()
|
9
|
+
|
10
|
+
|
11
|
+
def LazyMeasurement(
|
12
|
+
min_value: int | float, max_value: int | float, unit: str
|
13
|
+
) -> LazyFunction:
|
14
|
+
return LazyFunction(
|
15
|
+
lambda: Measurement(str(random.uniform(min_value, max_value))[:10], unit)
|
16
|
+
)
|
17
|
+
|
18
|
+
|
19
|
+
def LazyDeltaDate(avg_delta_days: int, base_attribute: str) -> LazyAttribute:
|
20
|
+
return LazyAttribute(
|
21
|
+
lambda obj: (getattr(obj, base_attribute) or date.today())
|
22
|
+
+ timedelta(days=random.randint(avg_delta_days // 2, avg_delta_days * 3 // 2))
|
23
|
+
)
|
24
|
+
|
25
|
+
|
26
|
+
def LazyProjectName() -> LazyFunction:
|
27
|
+
return LazyFunction(
|
28
|
+
lambda: (
|
29
|
+
f"{fake.word().capitalize()} "
|
30
|
+
f"{fake.word().capitalize()} "
|
31
|
+
f"{fake.random_element(elements=('X', 'Z', 'G'))}"
|
32
|
+
f"-{fake.random_int(min=1, max=1000)}"
|
33
|
+
)
|
34
|
+
)
|
35
|
+
|
36
|
+
|
37
|
+
def LazySapNumber() -> LazyAttributeSequence:
|
38
|
+
return LazyAttributeSequence(lambda obj, n: f"60{n:04d}")
|
@@ -0,0 +1,308 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from abc import ABC, abstractmethod
|
3
|
+
from typing import (
|
4
|
+
Type,
|
5
|
+
Generator,
|
6
|
+
TYPE_CHECKING,
|
7
|
+
Any,
|
8
|
+
TypeVar,
|
9
|
+
Generic,
|
10
|
+
Iterable,
|
11
|
+
ClassVar,
|
12
|
+
Callable,
|
13
|
+
TypedDict,
|
14
|
+
)
|
15
|
+
from datetime import datetime
|
16
|
+
from django.conf import settings
|
17
|
+
from django.db.models import Model
|
18
|
+
from general_manager.auxiliary import args_to_kwargs
|
19
|
+
|
20
|
+
if TYPE_CHECKING:
|
21
|
+
from general_manager.manager.input import Input
|
22
|
+
from general_manager.manager.generalManager import GeneralManager
|
23
|
+
from general_manager.manager.meta import GeneralManagerMeta
|
24
|
+
from general_manager.manager.groupManager import GroupedManager, GroupBucket
|
25
|
+
|
26
|
+
|
27
|
+
GeneralManagerType = TypeVar("GeneralManagerType", bound="GeneralManager")
|
28
|
+
type generalManagerClassName = str
|
29
|
+
type attributes = dict[str, Any]
|
30
|
+
type interfaceBaseClass = Type[InterfaceBase]
|
31
|
+
type newlyCreatedInterfaceClass = Type[InterfaceBase]
|
32
|
+
type relatedClass = Type[Model] | None
|
33
|
+
type newlyCreatedGeneralManagerClass = GeneralManagerMeta
|
34
|
+
|
35
|
+
type classPreCreationMethod = Callable[
|
36
|
+
[generalManagerClassName, attributes, interfaceBaseClass],
|
37
|
+
tuple[attributes, interfaceBaseClass, relatedClass],
|
38
|
+
]
|
39
|
+
|
40
|
+
type classPostCreationMethod = Callable[
|
41
|
+
[newlyCreatedGeneralManagerClass, newlyCreatedInterfaceClass, relatedClass],
|
42
|
+
None,
|
43
|
+
]
|
44
|
+
|
45
|
+
|
46
|
+
class AttributeTypedDict(TypedDict):
|
47
|
+
"""
|
48
|
+
This class is used to define the type of the attributes dictionary.
|
49
|
+
It is used to define the type of the attributes dictionary in the
|
50
|
+
GeneralManager class.
|
51
|
+
"""
|
52
|
+
|
53
|
+
type: type
|
54
|
+
default: Any
|
55
|
+
is_required: bool
|
56
|
+
is_editable: bool
|
57
|
+
|
58
|
+
|
59
|
+
class InterfaceBase(ABC):
|
60
|
+
_parent_class: ClassVar[Type[Any]]
|
61
|
+
_interface_type: ClassVar[str]
|
62
|
+
input_fields: dict[str, Input]
|
63
|
+
|
64
|
+
def __init__(self, *args: Any, **kwargs: Any):
|
65
|
+
self.identification = self.parseInputFieldsToIdentification(*args, **kwargs)
|
66
|
+
self.formatIdentification()
|
67
|
+
|
68
|
+
def parseInputFieldsToIdentification(
|
69
|
+
self,
|
70
|
+
*args: Any,
|
71
|
+
**kwargs: dict[str, Any],
|
72
|
+
) -> dict[str, Any]:
|
73
|
+
identification = {}
|
74
|
+
kwargs = args_to_kwargs(args, self.input_fields.keys(), kwargs)
|
75
|
+
# Prüfe auf fehlende oder unerwartete Argumente
|
76
|
+
extra_args = set(kwargs.keys()) - set(self.input_fields.keys())
|
77
|
+
if extra_args:
|
78
|
+
for extra_arg in extra_args:
|
79
|
+
if extra_arg.replace("_id", "") in self.input_fields.keys():
|
80
|
+
kwargs[extra_arg.replace("_id", "")] = kwargs.pop(extra_arg)
|
81
|
+
else:
|
82
|
+
raise TypeError(f"Unexpected arguments: {', '.join(extra_args)}")
|
83
|
+
|
84
|
+
missing_args = set(self.input_fields.keys()) - set(kwargs.keys())
|
85
|
+
if missing_args:
|
86
|
+
raise TypeError(f"Missing required arguments: {', '.join(missing_args)}")
|
87
|
+
|
88
|
+
# Verarbeite Felder unter Berücksichtigung von Abhängigkeiten
|
89
|
+
processed = set()
|
90
|
+
while len(processed) < len(self.input_fields):
|
91
|
+
progress_made = False
|
92
|
+
for name, input_field in self.input_fields.items():
|
93
|
+
if name in processed:
|
94
|
+
continue
|
95
|
+
depends_on = input_field.depends_on
|
96
|
+
if all(dep in processed for dep in depends_on):
|
97
|
+
value = self.input_fields[name].cast(kwargs[name])
|
98
|
+
self._process_input(name, value, identification)
|
99
|
+
identification[name] = value
|
100
|
+
processed.add(name)
|
101
|
+
progress_made = True
|
102
|
+
if not progress_made:
|
103
|
+
# Zirkuläre Abhängigkeit erkannt
|
104
|
+
unresolved = set(self.input_fields.keys()) - processed
|
105
|
+
raise ValueError(
|
106
|
+
f"Circular dependency detected among inputs: {', '.join(unresolved)}"
|
107
|
+
)
|
108
|
+
return identification
|
109
|
+
|
110
|
+
def formatIdentification(self) -> dict[str, Any]:
|
111
|
+
from general_manager.manager.generalManager import GeneralManager
|
112
|
+
|
113
|
+
for key, value in self.identification.items():
|
114
|
+
if isinstance(value, GeneralManager):
|
115
|
+
self.identification[key] = value.identification
|
116
|
+
elif isinstance(value, (list, tuple)):
|
117
|
+
self.identification[key] = [
|
118
|
+
v.identification if isinstance(v, GeneralManager) else v
|
119
|
+
for v in value
|
120
|
+
]
|
121
|
+
return self.identification
|
122
|
+
|
123
|
+
def _process_input(
|
124
|
+
self, name: str, value: Any, identification: dict[str, Any]
|
125
|
+
) -> None:
|
126
|
+
input_field = self.input_fields[name]
|
127
|
+
if not isinstance(value, input_field.type):
|
128
|
+
raise TypeError(
|
129
|
+
f"Invalid type for {name}: {type(value)}, expected: {input_field.type}"
|
130
|
+
)
|
131
|
+
if settings.DEBUG:
|
132
|
+
# Prüfe mögliche Werte
|
133
|
+
possible_values = input_field.possible_values
|
134
|
+
if possible_values is not None:
|
135
|
+
if callable(possible_values):
|
136
|
+
depends_on = input_field.depends_on
|
137
|
+
dep_values = {
|
138
|
+
dep_name: identification.get(dep_name)
|
139
|
+
for dep_name in depends_on
|
140
|
+
}
|
141
|
+
allowed_values = possible_values(**dep_values)
|
142
|
+
elif isinstance(possible_values, Iterable):
|
143
|
+
allowed_values = possible_values
|
144
|
+
else:
|
145
|
+
raise TypeError(f"Invalid type for possible_values of input {name}")
|
146
|
+
|
147
|
+
if value not in allowed_values:
|
148
|
+
raise ValueError(
|
149
|
+
f"Invalid value for {name}: {value}, allowed: {allowed_values}"
|
150
|
+
)
|
151
|
+
|
152
|
+
@classmethod
|
153
|
+
def create(cls, *args: Any, **kwargs: Any) -> Any:
|
154
|
+
raise NotImplementedError
|
155
|
+
|
156
|
+
def update(self, *args: Any, **kwargs: Any) -> Any:
|
157
|
+
raise NotImplementedError
|
158
|
+
|
159
|
+
def deactivate(self, *args: Any, **kwargs: Any) -> Any:
|
160
|
+
raise NotImplementedError
|
161
|
+
|
162
|
+
@abstractmethod
|
163
|
+
def getData(self, search_date: datetime | None = None) -> Any:
|
164
|
+
raise NotImplementedError
|
165
|
+
|
166
|
+
@classmethod
|
167
|
+
@abstractmethod
|
168
|
+
def getAttributeTypes(cls) -> dict[str, AttributeTypedDict]:
|
169
|
+
raise NotImplementedError
|
170
|
+
|
171
|
+
@classmethod
|
172
|
+
@abstractmethod
|
173
|
+
def getAttributes(cls) -> dict[str, Any]:
|
174
|
+
raise NotImplementedError
|
175
|
+
|
176
|
+
@classmethod
|
177
|
+
@abstractmethod
|
178
|
+
def filter(cls, **kwargs: Any) -> Bucket[Any]:
|
179
|
+
raise NotImplementedError
|
180
|
+
|
181
|
+
@classmethod
|
182
|
+
@abstractmethod
|
183
|
+
def exclude(cls, **kwargs: Any) -> Bucket[Any]:
|
184
|
+
raise NotImplementedError
|
185
|
+
|
186
|
+
@classmethod
|
187
|
+
@abstractmethod
|
188
|
+
def handleInterface(
|
189
|
+
cls,
|
190
|
+
) -> tuple[
|
191
|
+
classPreCreationMethod,
|
192
|
+
classPostCreationMethod,
|
193
|
+
]:
|
194
|
+
"""
|
195
|
+
This method returns a pre and a post GeneralManager creation method
|
196
|
+
and is called inside the GeneralManagerMeta class to initialize the
|
197
|
+
Interface.
|
198
|
+
The pre creation method is called before the GeneralManager instance
|
199
|
+
is created to modify the kwargs.
|
200
|
+
The post creation method is called after the GeneralManager instance
|
201
|
+
is created to modify the instance and add additional data.
|
202
|
+
"""
|
203
|
+
raise NotImplementedError
|
204
|
+
|
205
|
+
@classmethod
|
206
|
+
@abstractmethod
|
207
|
+
def getFieldType(cls, field_name: str) -> type:
|
208
|
+
"""
|
209
|
+
Returns the type of the field with the given name.
|
210
|
+
"""
|
211
|
+
raise NotImplementedError
|
212
|
+
|
213
|
+
|
214
|
+
class Bucket(ABC, Generic[GeneralManagerType]):
|
215
|
+
|
216
|
+
def __init__(self, manager_class: Type[GeneralManagerType]):
|
217
|
+
self._manager_class = manager_class
|
218
|
+
self._data = None
|
219
|
+
self.excludes = {}
|
220
|
+
self.filters = {}
|
221
|
+
|
222
|
+
def __eq__(self, other: object) -> bool:
|
223
|
+
if not isinstance(other, self.__class__):
|
224
|
+
return False
|
225
|
+
return self._data == other._data and self._manager_class == other._manager_class
|
226
|
+
|
227
|
+
def __reduce__(self) -> str | tuple[Any, ...]:
|
228
|
+
return (
|
229
|
+
self.__class__,
|
230
|
+
(None, self._manager_class, self.filters, self.excludes),
|
231
|
+
)
|
232
|
+
|
233
|
+
@abstractmethod
|
234
|
+
def __or__(
|
235
|
+
self, other: Bucket[GeneralManagerType] | GeneralManager[GeneralManagerType]
|
236
|
+
) -> Bucket[GeneralManagerType]:
|
237
|
+
raise NotImplementedError
|
238
|
+
|
239
|
+
@abstractmethod
|
240
|
+
def __iter__(
|
241
|
+
self,
|
242
|
+
) -> Generator[GeneralManagerType | GroupedManager[GeneralManagerType]]:
|
243
|
+
raise NotImplementedError
|
244
|
+
|
245
|
+
@abstractmethod
|
246
|
+
def filter(self, **kwargs: Any) -> Bucket[GeneralManagerType]:
|
247
|
+
raise NotImplementedError
|
248
|
+
|
249
|
+
@abstractmethod
|
250
|
+
def exclude(self, **kwargs: Any) -> Bucket[GeneralManagerType]:
|
251
|
+
raise NotImplementedError
|
252
|
+
|
253
|
+
@abstractmethod
|
254
|
+
def first(self) -> GeneralManagerType | GroupedManager[GeneralManagerType] | None:
|
255
|
+
raise NotImplementedError
|
256
|
+
|
257
|
+
@abstractmethod
|
258
|
+
def last(self) -> GeneralManagerType | GroupedManager[GeneralManagerType] | None:
|
259
|
+
raise NotImplementedError
|
260
|
+
|
261
|
+
@abstractmethod
|
262
|
+
def count(self) -> int:
|
263
|
+
raise NotImplementedError
|
264
|
+
|
265
|
+
@abstractmethod
|
266
|
+
def all(self) -> Bucket[GeneralManagerType]:
|
267
|
+
raise NotImplementedError
|
268
|
+
|
269
|
+
@abstractmethod
|
270
|
+
def get(
|
271
|
+
self, **kwargs: Any
|
272
|
+
) -> GeneralManagerType | GroupedManager[GeneralManagerType]:
|
273
|
+
raise NotImplementedError
|
274
|
+
|
275
|
+
@abstractmethod
|
276
|
+
def __getitem__(
|
277
|
+
self, item: int | slice
|
278
|
+
) -> (
|
279
|
+
GeneralManagerType
|
280
|
+
| GroupedManager[GeneralManagerType]
|
281
|
+
| Bucket[GeneralManagerType]
|
282
|
+
):
|
283
|
+
raise NotImplementedError
|
284
|
+
|
285
|
+
@abstractmethod
|
286
|
+
def __len__(self) -> int:
|
287
|
+
raise NotImplementedError
|
288
|
+
|
289
|
+
@abstractmethod
|
290
|
+
def __contains__(self, item: GeneralManagerType) -> bool:
|
291
|
+
raise NotImplementedError
|
292
|
+
|
293
|
+
@abstractmethod
|
294
|
+
def sort(
|
295
|
+
self,
|
296
|
+
key: tuple[str] | str,
|
297
|
+
reverse: bool = False,
|
298
|
+
) -> Bucket[GeneralManagerType]:
|
299
|
+
raise NotImplementedError
|
300
|
+
|
301
|
+
def group_by(self, *group_by_keys: str) -> GroupBucket[GeneralManagerType]:
|
302
|
+
"""
|
303
|
+
This method groups the data by the given arguments.
|
304
|
+
It returns a GroupBucket with the grouped data.
|
305
|
+
"""
|
306
|
+
from general_manager.manager.groupManager import GroupBucket
|
307
|
+
|
308
|
+
return GroupBucket(self._manager_class, group_by_keys, self)
|