GeneralManager 0.10.3__py3-none-any.whl → 0.10.4__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/api/graphql.py +31 -38
- general_manager/apps.py +6 -6
- general_manager/bucket/baseBucket.py +5 -8
- general_manager/bucket/calculationBucket.py +8 -9
- general_manager/bucket/databaseBucket.py +18 -31
- general_manager/cache/signals.py +21 -9
- general_manager/factory/autoFactory.py +34 -34
- general_manager/factory/factories.py +73 -43
- general_manager/interface/baseInterface.py +3 -3
- general_manager/interface/databaseBasedInterface.py +63 -43
- general_manager/manager/generalManager.py +52 -42
- general_manager/manager/meta.py +19 -10
- general_manager/measurement/measurementField.py +19 -3
- {generalmanager-0.10.3.dist-info → generalmanager-0.10.4.dist-info}/METADATA +1 -1
- {generalmanager-0.10.3.dist-info → generalmanager-0.10.4.dist-info}/RECORD +18 -18
- {generalmanager-0.10.3.dist-info → generalmanager-0.10.4.dist-info}/WHEEL +0 -0
- {generalmanager-0.10.3.dist-info → generalmanager-0.10.4.dist-info}/licenses/LICENSE +0 -0
- {generalmanager-0.10.3.dist-info → generalmanager-0.10.4.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,8 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import Any, cast
|
2
|
+
from typing import Any, cast, TYPE_CHECKING
|
3
3
|
from factory.declarations import LazyFunction
|
4
4
|
from factory.faker import Faker
|
5
|
-
import exrex
|
5
|
+
import exrex
|
6
6
|
from django.db import models
|
7
7
|
from django.core.validators import RegexValidator
|
8
8
|
import random
|
@@ -11,12 +11,17 @@ from general_manager.measurement.measurement import Measurement
|
|
11
11
|
from general_manager.measurement.measurementField import MeasurementField
|
12
12
|
from datetime import date, datetime, time, timezone
|
13
13
|
|
14
|
+
if TYPE_CHECKING:
|
15
|
+
from general_manager.factory.autoFactory import AutoFactory
|
14
16
|
|
15
|
-
|
17
|
+
|
18
|
+
def getFieldValue(
|
19
|
+
field: models.Field[Any, Any] | models.ForeignObjectRel,
|
20
|
+
) -> object:
|
16
21
|
"""
|
17
|
-
|
22
|
+
Generate a suitable fake or factory value for a given Django model field, for use in tests or data factories.
|
18
23
|
|
19
|
-
|
24
|
+
Returns a value appropriate for the field type, including support for measurement, text, numeric, date/time, boolean, email, URL, IP address, UUID, duration, and character fields (with regex support). For relational fields (OneToOneField and ForeignKey), attempts to use a related model factory or select a random existing instance; raises ValueError if neither is available. Returns None for unsupported field types or with a 10% chance if the field allows null values.
|
20
25
|
"""
|
21
26
|
if field.null:
|
22
27
|
if random.choice([True] + 9 * [False]):
|
@@ -62,78 +67,103 @@ def getFieldValue(field: models.Field[Any, Any] | models.ForeignObjectRel) -> ob
|
|
62
67
|
return cast(date, Faker("date_between", start_date="-1y", end_date="today"))
|
63
68
|
elif isinstance(field, models.BooleanField):
|
64
69
|
return cast(bool, Faker("pybool"))
|
70
|
+
elif isinstance(field, models.EmailField):
|
71
|
+
return cast(str, Faker("email"))
|
72
|
+
elif isinstance(field, models.URLField):
|
73
|
+
return cast(str, Faker("url"))
|
74
|
+
elif isinstance(field, models.GenericIPAddressField):
|
75
|
+
return cast(str, Faker("ipv4"))
|
76
|
+
elif isinstance(field, models.UUIDField):
|
77
|
+
return cast(str, Faker("uuid4"))
|
78
|
+
elif isinstance(field, models.DurationField):
|
79
|
+
return cast(time, Faker("time_delta"))
|
80
|
+
elif isinstance(field, models.CharField):
|
81
|
+
max_length = field.max_length or 100
|
82
|
+
# Check for RegexValidator
|
83
|
+
regex = None
|
84
|
+
for validator in field.validators:
|
85
|
+
if isinstance(validator, RegexValidator):
|
86
|
+
regex = getattr(validator.regex, "pattern", None)
|
87
|
+
break
|
88
|
+
if regex:
|
89
|
+
# Use exrex to generate a string matching the regex
|
90
|
+
return LazyFunction(lambda: exrex.getone(regex))
|
91
|
+
else:
|
92
|
+
return cast(str, Faker("text", max_nb_chars=max_length))
|
65
93
|
elif isinstance(field, models.OneToOneField):
|
66
|
-
|
67
|
-
|
94
|
+
related_model = getRelatedModel(field)
|
95
|
+
if hasattr(related_model, "_general_manager_class"):
|
96
|
+
related_factory = related_model._general_manager_class.Factory # type: ignore
|
68
97
|
return related_factory()
|
69
98
|
else:
|
70
99
|
# If no factory exists, pick a random existing instance
|
71
|
-
related_instances = list(
|
100
|
+
related_instances = list(related_model.objects.all())
|
72
101
|
if related_instances:
|
73
102
|
return LazyFunction(lambda: random.choice(related_instances))
|
74
103
|
else:
|
75
104
|
raise ValueError(
|
76
|
-
f"No factory found for {
|
105
|
+
f"No factory found for {related_model.__name__} and no instances found"
|
77
106
|
)
|
78
107
|
elif isinstance(field, models.ForeignKey):
|
108
|
+
related_model = getRelatedModel(field)
|
79
109
|
# Create or get an instance of the related model
|
80
|
-
if hasattr(
|
110
|
+
if hasattr(related_model, "_general_manager_class"):
|
81
111
|
create_a_new_instance = random.choice([True, True, False])
|
82
112
|
if not create_a_new_instance:
|
83
|
-
existing_instances = list(
|
113
|
+
existing_instances = list(related_model.objects.all())
|
84
114
|
if existing_instances:
|
85
115
|
# Pick a random existing instance
|
86
116
|
return LazyFunction(lambda: random.choice(existing_instances))
|
87
117
|
|
88
|
-
related_factory =
|
118
|
+
related_factory = related_model._general_manager_class.Factory # type: ignore
|
89
119
|
return related_factory()
|
90
120
|
|
91
121
|
else:
|
92
122
|
# If no factory exists, pick a random existing instance
|
93
|
-
related_instances = list(
|
123
|
+
related_instances = list(related_model.objects.all())
|
94
124
|
if related_instances:
|
95
125
|
return LazyFunction(lambda: random.choice(related_instances))
|
96
126
|
else:
|
97
127
|
raise ValueError(
|
98
|
-
f"No factory found for {
|
128
|
+
f"No factory found for {related_model.__name__} and no instances found"
|
99
129
|
)
|
100
|
-
elif isinstance(field, models.EmailField):
|
101
|
-
return cast(str, Faker("email"))
|
102
|
-
elif isinstance(field, models.URLField):
|
103
|
-
return cast(str, Faker("url"))
|
104
|
-
elif isinstance(field, models.GenericIPAddressField):
|
105
|
-
return cast(str, Faker("ipv4"))
|
106
|
-
elif isinstance(field, models.UUIDField):
|
107
|
-
return cast(str, Faker("uuid4"))
|
108
|
-
elif isinstance(field, models.DurationField):
|
109
|
-
return cast(time, Faker("time_delta"))
|
110
|
-
elif isinstance(field, models.CharField):
|
111
|
-
max_length = field.max_length or 100
|
112
|
-
# Check for RegexValidator
|
113
|
-
regex = None
|
114
|
-
for validator in field.validators:
|
115
|
-
if isinstance(validator, RegexValidator):
|
116
|
-
regex = getattr(validator.regex, "pattern", None)
|
117
|
-
break
|
118
|
-
if regex:
|
119
|
-
# Use exrex to generate a string matching the regex
|
120
|
-
return LazyFunction(lambda: exrex.getone(regex)) # type: ignore
|
121
|
-
else:
|
122
|
-
return cast(str, Faker("text", max_nb_chars=max_length))
|
123
130
|
else:
|
124
|
-
return None
|
131
|
+
return None
|
132
|
+
|
133
|
+
|
134
|
+
def getRelatedModel(
|
135
|
+
field: models.ForeignObjectRel | models.Field[Any, Any],
|
136
|
+
) -> type[models.Model]:
|
137
|
+
"""
|
138
|
+
Return the related model class for a given Django relational field.
|
139
|
+
|
140
|
+
Raises:
|
141
|
+
ValueError: If the field does not have a related model defined.
|
142
|
+
"""
|
143
|
+
related_model = field.related_model
|
144
|
+
if related_model is None:
|
145
|
+
raise ValueError(f"Field {field.name} does not have a related model defined.")
|
146
|
+
if related_model == "self":
|
147
|
+
related_model = field.model
|
148
|
+
return related_model # For unsupported field types
|
125
149
|
|
126
150
|
|
127
151
|
def getManyToManyFieldValue(
|
128
152
|
field: models.ManyToManyField,
|
129
153
|
) -> list[models.Model]:
|
130
154
|
"""
|
131
|
-
|
155
|
+
Generate a list of model instances to associate with a ManyToMany field for testing or factory purposes.
|
156
|
+
|
157
|
+
If a related model factory is available, creates new instances as needed. Otherwise, selects from existing instances. Raises a ValueError if neither a factory nor existing instances are available.
|
158
|
+
|
159
|
+
Returns:
|
160
|
+
list[models.Model]: A list of related model instances for the ManyToMany field.
|
132
161
|
"""
|
133
162
|
related_factory = None
|
134
|
-
|
135
|
-
|
136
|
-
|
163
|
+
related_model = getRelatedModel(field)
|
164
|
+
related_instances = list(related_model.objects.all())
|
165
|
+
if hasattr(related_model, "_general_manager_class"):
|
166
|
+
related_factory = related_model._general_manager_class.Factory # type: ignore
|
137
167
|
|
138
168
|
min_required = 0 if field.blank else 1
|
139
169
|
number_of_instances = random.randint(min_required, 10)
|
@@ -158,5 +188,5 @@ def getManyToManyFieldValue(
|
|
158
188
|
return existing_instances
|
159
189
|
else:
|
160
190
|
raise ValueError(
|
161
|
-
f"No factory found for {
|
191
|
+
f"No factory found for {related_model.__name__} and no instances found"
|
162
192
|
)
|
@@ -69,12 +69,12 @@ class InterfaceBase(ABC):
|
|
69
69
|
**kwargs: dict[str, Any],
|
70
70
|
) -> dict[str, Any]:
|
71
71
|
"""
|
72
|
-
|
72
|
+
Parse and validate input arguments into a dictionary of input field values.
|
73
73
|
|
74
|
-
|
74
|
+
Positional and keyword arguments are mapped to input field names, with normalization of argument names (e.g., removing trailing `_id`). Ensures all required fields are present and no unexpected arguments are provided. Processes input fields in dependency order, casting and validating each value. Raises a `TypeError` for missing or unexpected arguments and a `ValueError` if circular dependencies are detected.
|
75
75
|
|
76
76
|
Returns:
|
77
|
-
A dictionary mapping input field names to their validated and cast values.
|
77
|
+
dict[str, Any]: A dictionary mapping input field names to their validated and cast values.
|
78
78
|
"""
|
79
79
|
identification = {}
|
80
80
|
kwargs = args_to_kwargs(args, self.input_fields.keys(), kwargs)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import Type, Any, Callable, TYPE_CHECKING, TypeVar, Generic
|
2
|
+
from typing import Type, Any, Callable, TYPE_CHECKING, TypeVar, Generic, cast
|
3
3
|
from django.db import models
|
4
4
|
from datetime import datetime, timedelta
|
5
5
|
from general_manager.measurement.measurement import Measurement
|
@@ -25,13 +25,13 @@ from general_manager.interface.models import (
|
|
25
25
|
GeneralManagerModel,
|
26
26
|
getFullCleanMethode,
|
27
27
|
)
|
28
|
+
from django.contrib.contenttypes.fields import GenericForeignKey
|
28
29
|
|
29
30
|
if TYPE_CHECKING:
|
30
31
|
from general_manager.rule.rule import Rule
|
31
32
|
|
32
33
|
modelsModel = TypeVar("modelsModel", bound=models.Model)
|
33
34
|
|
34
|
-
|
35
35
|
MODEL_TYPE = TypeVar("MODEL_TYPE", bound=GeneralManagerBasisModel)
|
36
36
|
|
37
37
|
|
@@ -43,12 +43,12 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
43
43
|
self,
|
44
44
|
*args: list[Any],
|
45
45
|
search_date: datetime | None = None,
|
46
|
-
**kwargs:
|
46
|
+
**kwargs: Any,
|
47
47
|
):
|
48
48
|
"""
|
49
|
-
Initialize the interface and load the associated model instance.
|
50
|
-
|
51
|
-
If `search_date` is provided,
|
49
|
+
Initialize the interface and load the associated model instance by primary key.
|
50
|
+
|
51
|
+
If a `search_date` is provided, retrieves the historical record as of that date; otherwise, loads the current record.
|
52
52
|
"""
|
53
53
|
super().__init__(*args, **kwargs)
|
54
54
|
self.pk = self.identification["id"]
|
@@ -135,10 +135,10 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
135
135
|
@classmethod
|
136
136
|
def getAttributeTypes(cls) -> dict[str, AttributeTypedDict]:
|
137
137
|
"""
|
138
|
-
Return a dictionary mapping attribute
|
139
|
-
|
140
|
-
The dictionary includes all model fields, custom fields, foreign keys, many-to-many, and reverse relation fields. For each attribute, the metadata specifies its Python type (translated from Django field types when possible), whether it is required, editable, derived, and its default value. For related models with a general manager class, the type is set to that class.
|
141
|
-
|
138
|
+
Return a dictionary mapping each attribute name of the model to its type information and metadata.
|
139
|
+
|
140
|
+
The returned dictionary includes all standard model fields, custom fields, foreign keys, many-to-many, and reverse relation fields, excluding any GenericForeignKey fields. For each attribute, the metadata specifies its Python type (translated from Django field types when possible), whether it is required, editable, derived, and its default value. For related models with a general manager class, the type is set to that class.
|
141
|
+
|
142
142
|
Returns:
|
143
143
|
dict[str, AttributeTypedDict]: Mapping of attribute names to their type information and metadata.
|
144
144
|
"""
|
@@ -163,7 +163,7 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
163
163
|
fields: dict[str, AttributeTypedDict] = {}
|
164
164
|
field_name_list, to_ignore_list = cls.handleCustomFields(cls._model)
|
165
165
|
for field_name in field_name_list:
|
166
|
-
field
|
166
|
+
field = cast(models.Field, getattr(cls._model, field_name))
|
167
167
|
fields[field_name] = {
|
168
168
|
"type": type(field),
|
169
169
|
"is_derived": False,
|
@@ -174,7 +174,7 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
174
174
|
|
175
175
|
for field_name in cls.__getModelFields():
|
176
176
|
if field_name not in to_ignore_list:
|
177
|
-
field
|
177
|
+
field = cast(models.Field, getattr(cls._model, field_name).field)
|
178
178
|
fields[field_name] = {
|
179
179
|
"type": type(field),
|
180
180
|
"is_derived": False,
|
@@ -185,20 +185,27 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
185
185
|
|
186
186
|
for field_name in cls.__getForeignKeyFields():
|
187
187
|
field = cls._model._meta.get_field(field_name)
|
188
|
+
if isinstance(field, GenericForeignKey):
|
189
|
+
continue
|
188
190
|
related_model = field.related_model
|
191
|
+
if related_model == "self":
|
192
|
+
related_model = cls._model
|
189
193
|
if related_model and hasattr(
|
190
194
|
related_model,
|
191
195
|
"_general_manager_class",
|
192
196
|
):
|
193
|
-
related_model = related_model._general_manager_class
|
197
|
+
related_model = related_model._general_manager_class # type: ignore
|
194
198
|
|
195
199
|
elif related_model is not None:
|
200
|
+
default = None
|
201
|
+
if hasattr(field, "default"):
|
202
|
+
default = field.default # type: ignore
|
196
203
|
fields[field_name] = {
|
197
204
|
"type": related_model,
|
198
205
|
"is_derived": False,
|
199
206
|
"is_required": not field.null,
|
200
207
|
"is_editable": field.editable,
|
201
|
-
"default":
|
208
|
+
"default": default,
|
202
209
|
}
|
203
210
|
|
204
211
|
for field_name, field_call in [
|
@@ -212,11 +219,17 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
212
219
|
raise ValueError("Field name already exists.")
|
213
220
|
field = cls._model._meta.get_field(field_name)
|
214
221
|
related_model = cls._model._meta.get_field(field_name).related_model
|
222
|
+
if related_model == "self":
|
223
|
+
related_model = cls._model
|
224
|
+
if isinstance(field, GenericForeignKey):
|
225
|
+
continue
|
226
|
+
|
215
227
|
if related_model and hasattr(
|
216
228
|
related_model,
|
217
229
|
"_general_manager_class",
|
218
230
|
):
|
219
|
-
related_model = related_model._general_manager_class
|
231
|
+
related_model = related_model._general_manager_class # type: ignore
|
232
|
+
|
220
233
|
if related_model is not None:
|
221
234
|
fields[f"{field_name}_list"] = {
|
222
235
|
"type": related_model,
|
@@ -234,9 +247,12 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
234
247
|
@classmethod
|
235
248
|
def getAttributes(cls) -> dict[str, Callable[[DBBasedInterface], Any]]:
|
236
249
|
"""
|
237
|
-
|
238
|
-
|
239
|
-
The
|
250
|
+
Return a dictionary mapping attribute names to callables that retrieve values from a DBBasedInterface instance.
|
251
|
+
|
252
|
+
The mapping includes accessors for custom fields, standard model fields, foreign keys, many-to-many relations, and reverse relations. For related models with a general manager class, the accessor returns an instance of that class; otherwise, it returns the related object or queryset. Raises a ValueError if a field name conflict is detected.
|
253
|
+
|
254
|
+
Returns:
|
255
|
+
dict: A dictionary where keys are attribute names and values are callables that extract the corresponding value from a DBBasedInterface instance.
|
240
256
|
"""
|
241
257
|
field_values: dict[str, Any] = {}
|
242
258
|
|
@@ -258,7 +274,7 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
258
274
|
related_model,
|
259
275
|
"_general_manager_class",
|
260
276
|
):
|
261
|
-
generalManagerClass = related_model._general_manager_class
|
277
|
+
generalManagerClass = related_model._general_manager_class # type: ignore
|
262
278
|
field_values[f"{field_name}"] = (
|
263
279
|
lambda self, field_name=field_name, manager_class=generalManagerClass: manager_class(
|
264
280
|
getattr(self._instance, field_name).pk
|
@@ -322,13 +338,13 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
322
338
|
@staticmethod
|
323
339
|
def _getCustomFields(model: Type[models.Model] | models.Model) -> list[str]:
|
324
340
|
"""
|
325
|
-
|
326
|
-
|
327
|
-
|
341
|
+
Return a list of custom field names defined directly as class attributes on the given Django model.
|
342
|
+
|
343
|
+
Parameters:
|
328
344
|
model: The Django model class or instance to inspect.
|
329
|
-
|
345
|
+
|
330
346
|
Returns:
|
331
|
-
A list of field names
|
347
|
+
A list of field names for fields declared directly on the model class, excluding those defined via Django's meta system.
|
332
348
|
"""
|
333
349
|
return [
|
334
350
|
field.name
|
@@ -337,11 +353,11 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
337
353
|
]
|
338
354
|
|
339
355
|
@classmethod
|
340
|
-
def __getModelFields(cls):
|
356
|
+
def __getModelFields(cls) -> list[str]:
|
341
357
|
"""
|
342
|
-
|
343
|
-
|
344
|
-
|
358
|
+
Return a list of field names for the model that are neither many-to-many nor related fields.
|
359
|
+
|
360
|
+
Fields representing many-to-many relationships or relations to other models are excluded from the result.
|
345
361
|
"""
|
346
362
|
return [
|
347
363
|
field.name
|
@@ -350,9 +366,9 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
350
366
|
]
|
351
367
|
|
352
368
|
@classmethod
|
353
|
-
def __getForeignKeyFields(cls):
|
369
|
+
def __getForeignKeyFields(cls) -> list[str]:
|
354
370
|
"""
|
355
|
-
|
371
|
+
Return a list of field names for all foreign key and one-to-one relations on the model, excluding generic foreign keys.
|
356
372
|
"""
|
357
373
|
return [
|
358
374
|
field.name
|
@@ -361,11 +377,11 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
361
377
|
]
|
362
378
|
|
363
379
|
@classmethod
|
364
|
-
def __getManyToManyFields(cls):
|
380
|
+
def __getManyToManyFields(cls) -> list[tuple[str, str]]:
|
365
381
|
"""
|
366
|
-
|
367
|
-
|
368
|
-
Each tuple
|
382
|
+
Return a list of tuples representing all many-to-many fields on the model.
|
383
|
+
|
384
|
+
Each tuple contains the field name twice. Fields that are generic foreign keys are excluded.
|
369
385
|
"""
|
370
386
|
return [
|
371
387
|
(field.name, field.name)
|
@@ -374,11 +390,11 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
374
390
|
]
|
375
391
|
|
376
392
|
@classmethod
|
377
|
-
def __getReverseRelations(cls):
|
393
|
+
def __getReverseRelations(cls) -> list[tuple[str, str]]:
|
378
394
|
"""
|
379
|
-
|
380
|
-
|
381
|
-
Each tuple contains the related field's name and its default related accessor name.
|
395
|
+
Return a list of reverse one-to-many relations for the model, excluding generic foreign keys.
|
396
|
+
|
397
|
+
Each tuple contains the related field's name and its default related accessor name (e.g., `fieldname_set`).
|
382
398
|
"""
|
383
399
|
return [
|
384
400
|
(field.name, f"{field.name}_set")
|
@@ -485,11 +501,15 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
485
501
|
@classmethod
|
486
502
|
def getFieldType(cls, field_name: str) -> type:
|
487
503
|
"""
|
488
|
-
|
489
|
-
|
490
|
-
If the field is a relation and its related model
|
504
|
+
Return the type associated with a given model field name.
|
505
|
+
|
506
|
+
If the field is a relation and its related model has a `_general_manager_class` attribute, that class is returned; otherwise, returns the Django field type.
|
491
507
|
"""
|
492
508
|
field = cls._model._meta.get_field(field_name)
|
493
|
-
if
|
494
|
-
|
509
|
+
if (
|
510
|
+
field.is_relation
|
511
|
+
and field.related_model
|
512
|
+
and hasattr(field.related_model, "_general_manager_class")
|
513
|
+
):
|
514
|
+
return field.related_model._general_manager_class # type: ignore
|
495
515
|
return type(field)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import
|
2
|
+
from typing import Type, Any, TYPE_CHECKING, Self
|
3
3
|
from general_manager.manager.meta import GeneralManagerMeta
|
4
4
|
|
5
5
|
from general_manager.api.property import GraphQLProperty
|
@@ -9,17 +9,9 @@ from general_manager.bucket.baseBucket import Bucket
|
|
9
9
|
|
10
10
|
if TYPE_CHECKING:
|
11
11
|
from general_manager.permission.basePermission import BasePermission
|
12
|
-
from general_manager.interface.baseInterface import (
|
13
|
-
InterfaceBase,
|
14
|
-
)
|
15
|
-
GeneralManagerType = TypeVar("GeneralManagerType", bound="GeneralManager")
|
16
|
-
InterfaceType = TypeVar("InterfaceType", bound="InterfaceBase", covariant=True)
|
17
12
|
|
18
13
|
|
19
|
-
class GeneralManager(
|
20
|
-
Generic[GeneralManagerType, InterfaceType], metaclass=GeneralManagerMeta
|
21
|
-
):
|
22
|
-
Interface: Type[InterfaceType]
|
14
|
+
class GeneralManager(metaclass=GeneralManagerMeta):
|
23
15
|
Permission: Type[BasePermission]
|
24
16
|
_attributes: dict[str, Any]
|
25
17
|
|
@@ -49,18 +41,15 @@ class GeneralManager(
|
|
49
41
|
|
50
42
|
def __or__(
|
51
43
|
self,
|
52
|
-
other:
|
53
|
-
|
54
|
-
| Bucket[GeneralManagerType]
|
55
|
-
),
|
56
|
-
) -> Bucket[GeneralManagerType]:
|
44
|
+
other: Self | Bucket[Self],
|
45
|
+
) -> Bucket[Self]:
|
57
46
|
"""
|
58
|
-
|
47
|
+
Returns a Bucket containing the union of this manager and another manager of the same class or a Bucket.
|
59
48
|
|
60
|
-
If
|
49
|
+
If `other` is a Bucket, the result is the union of the Bucket and this manager. If `other` is a manager of the same class, the result is a Bucket containing both managers. Raises a TypeError if `other` is not a supported type.
|
61
50
|
|
62
51
|
Returns:
|
63
|
-
Bucket[
|
52
|
+
Bucket[Self]: A Bucket containing the combined managers.
|
64
53
|
"""
|
65
54
|
if isinstance(other, Bucket):
|
66
55
|
return other | self
|
@@ -90,20 +79,20 @@ class GeneralManager(
|
|
90
79
|
creator_id: int | None = None,
|
91
80
|
history_comment: str | None = None,
|
92
81
|
ignore_permission: bool = False,
|
93
|
-
**kwargs:
|
94
|
-
) ->
|
82
|
+
**kwargs: Any,
|
83
|
+
) -> Self:
|
95
84
|
"""
|
96
|
-
|
85
|
+
Create a new managed object via the underlying interface and return a manager instance representing it.
|
97
86
|
|
98
|
-
Performs a permission check
|
87
|
+
Performs a permission check unless `ignore_permission` is True. All additional keyword arguments are passed to the interface's `create` method.
|
99
88
|
|
100
89
|
Parameters:
|
101
|
-
creator_id (int | None): Optional
|
102
|
-
history_comment (str | None): Optional comment for audit or history
|
103
|
-
ignore_permission (bool): If True,
|
90
|
+
creator_id (int | None): Optional ID of the user creating the object.
|
91
|
+
history_comment (str | None): Optional comment for audit or history purposes.
|
92
|
+
ignore_permission (bool): If True, bypasses the permission check.
|
104
93
|
|
105
94
|
Returns:
|
106
|
-
|
95
|
+
Self: Manager instance for the newly created object.
|
107
96
|
"""
|
108
97
|
if not ignore_permission:
|
109
98
|
cls.Permission.checkCreatePermission(kwargs, cls, creator_id)
|
@@ -118,19 +107,19 @@ class GeneralManager(
|
|
118
107
|
creator_id: int | None = None,
|
119
108
|
history_comment: str | None = None,
|
120
109
|
ignore_permission: bool = False,
|
121
|
-
**kwargs:
|
122
|
-
) ->
|
110
|
+
**kwargs: Any,
|
111
|
+
) -> Self:
|
123
112
|
"""
|
124
|
-
Update the
|
113
|
+
Update the managed object with new data and return a new manager instance reflecting the changes.
|
125
114
|
|
126
115
|
Parameters:
|
127
|
-
creator_id (int | None):
|
116
|
+
creator_id (int | None): Identifier of the user performing the update, if applicable.
|
128
117
|
history_comment (str | None): Optional comment describing the update.
|
129
|
-
ignore_permission (bool): If True,
|
130
|
-
**kwargs:
|
118
|
+
ignore_permission (bool): If True, bypasses permission checks.
|
119
|
+
**kwargs: Fields and values to update on the managed object.
|
131
120
|
|
132
121
|
Returns:
|
133
|
-
|
122
|
+
Self: A new manager instance representing the updated object.
|
134
123
|
"""
|
135
124
|
if not ignore_permission:
|
136
125
|
self.Permission.checkUpdatePermission(kwargs, self, creator_id)
|
@@ -147,17 +136,17 @@ class GeneralManager(
|
|
147
136
|
creator_id: int | None = None,
|
148
137
|
history_comment: str | None = None,
|
149
138
|
ignore_permission: bool = False,
|
150
|
-
) ->
|
139
|
+
) -> Self:
|
151
140
|
"""
|
152
|
-
|
141
|
+
Deactivate the managed object and return a new manager instance representing its deactivated state.
|
153
142
|
|
154
143
|
Parameters:
|
155
|
-
creator_id (int | None): Optional
|
156
|
-
history_comment (str | None): Optional comment
|
157
|
-
ignore_permission (bool): If True,
|
144
|
+
creator_id (int | None): Optional ID of the user performing the deactivation.
|
145
|
+
history_comment (str | None): Optional comment explaining the deactivation.
|
146
|
+
ignore_permission (bool): If True, bypasses permission checks.
|
158
147
|
|
159
148
|
Returns:
|
160
|
-
|
149
|
+
Self: A new manager instance for the deactivated object.
|
161
150
|
"""
|
162
151
|
if not ignore_permission:
|
163
152
|
self.Permission.checkDeletePermission(self, creator_id)
|
@@ -167,21 +156,42 @@ class GeneralManager(
|
|
167
156
|
return self.__class__(**self.identification)
|
168
157
|
|
169
158
|
@classmethod
|
170
|
-
def filter(cls, **kwargs: Any) -> Bucket[
|
159
|
+
def filter(cls, **kwargs: Any) -> Bucket[Self]:
|
160
|
+
"""
|
161
|
+
Return a bucket of managed objects matching the specified filter criteria.
|
162
|
+
|
163
|
+
Parameters:
|
164
|
+
kwargs: Field lookups used to filter the managed objects.
|
165
|
+
|
166
|
+
Returns:
|
167
|
+
Bucket[Self]: A collection of manager instances matching the filter conditions.
|
168
|
+
"""
|
171
169
|
DependencyTracker.track(
|
172
170
|
cls.__name__, "filter", f"{cls.__parse_identification(kwargs)}"
|
173
171
|
)
|
174
172
|
return cls.Interface.filter(**kwargs)
|
175
173
|
|
176
174
|
@classmethod
|
177
|
-
def exclude(cls, **kwargs: Any) -> Bucket[
|
175
|
+
def exclude(cls, **kwargs: Any) -> Bucket[Self]:
|
176
|
+
"""
|
177
|
+
Return a bucket of managed objects excluding those that match the specified criteria.
|
178
|
+
|
179
|
+
Parameters:
|
180
|
+
kwargs: Field-value pairs used to determine which objects to exclude.
|
181
|
+
|
182
|
+
Returns:
|
183
|
+
Bucket[Self]: A collection of managed objects not matching the exclusion criteria.
|
184
|
+
"""
|
178
185
|
DependencyTracker.track(
|
179
186
|
cls.__name__, "exclude", f"{cls.__parse_identification(kwargs)}"
|
180
187
|
)
|
181
188
|
return cls.Interface.exclude(**kwargs)
|
182
189
|
|
183
190
|
@classmethod
|
184
|
-
def all(cls) -> Bucket[
|
191
|
+
def all(cls) -> Bucket[Self]:
|
192
|
+
"""
|
193
|
+
Return a bucket containing all managed objects of this class.
|
194
|
+
"""
|
185
195
|
return cls.Interface.filter()
|
186
196
|
|
187
197
|
@staticmethod
|