GeneralManager 0.4.4__tar.gz → 0.4.6__tar.gz

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 (75) hide show
  1. {generalmanager-0.4.4 → generalmanager-0.4.6}/GeneralManager.egg-info/PKG-INFO +1 -1
  2. {generalmanager-0.4.4 → generalmanager-0.4.6}/GeneralManager.egg-info/SOURCES.txt +3 -0
  3. {generalmanager-0.4.4 → generalmanager-0.4.6}/PKG-INFO +1 -1
  4. {generalmanager-0.4.4 → generalmanager-0.4.6}/pyproject.toml +1 -1
  5. generalmanager-0.4.6/src/general_manager/factory/autoFactory.py +224 -0
  6. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/factory/factories.py +4 -146
  7. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/interface/databaseInterface.py +8 -12
  8. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/manager/generalManager.py +22 -6
  9. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/measurement/measurement.py +49 -2
  10. generalmanager-0.4.6/tests/test_autoFactory.py +247 -0
  11. generalmanager-0.4.6/tests/test_generalManager.py +278 -0
  12. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_measurement.py +112 -0
  13. {generalmanager-0.4.4 → generalmanager-0.4.6}/GeneralManager.egg-info/dependency_links.txt +0 -0
  14. {generalmanager-0.4.4 → generalmanager-0.4.6}/GeneralManager.egg-info/requires.txt +0 -0
  15. {generalmanager-0.4.4 → generalmanager-0.4.6}/GeneralManager.egg-info/top_level.txt +0 -0
  16. {generalmanager-0.4.4 → generalmanager-0.4.6}/LICENSE +0 -0
  17. {generalmanager-0.4.4 → generalmanager-0.4.6}/README.md +0 -0
  18. {generalmanager-0.4.4 → generalmanager-0.4.6}/setup.cfg +0 -0
  19. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/__init__.py +0 -0
  20. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/api/graphql.py +0 -0
  21. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/api/mutation.py +0 -0
  22. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/api/property.py +0 -0
  23. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/apps.py +0 -0
  24. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/auxiliary/__init__.py +0 -0
  25. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/auxiliary/argsToKwargs.py +0 -0
  26. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/auxiliary/filterParser.py +0 -0
  27. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/auxiliary/jsonEncoder.py +0 -0
  28. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/auxiliary/makeCacheKey.py +0 -0
  29. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/auxiliary/noneToZero.py +0 -0
  30. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/auxiliary/pathMapping.py +0 -0
  31. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/cache/cacheDecorator.py +0 -0
  32. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/cache/cacheTracker.py +0 -0
  33. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/cache/dependencyIndex.py +0 -0
  34. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/cache/modelDependencyCollector.py +0 -0
  35. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/cache/signals.py +0 -0
  36. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/factory/__init__.py +0 -0
  37. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/factory/factoryMethods.py +0 -0
  38. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/interface/__init__.py +0 -0
  39. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/interface/baseInterface.py +0 -0
  40. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/interface/calculationInterface.py +0 -0
  41. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/manager/__init__.py +0 -0
  42. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/manager/groupManager.py +0 -0
  43. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/manager/input.py +0 -0
  44. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/manager/meta.py +0 -0
  45. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/measurement/__init__.py +0 -0
  46. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/measurement/measurementField.py +0 -0
  47. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/permission/__init__.py +0 -0
  48. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/permission/basePermission.py +0 -0
  49. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/permission/fileBasedPermission.py +0 -0
  50. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/permission/managerBasedPermission.py +0 -0
  51. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/permission/permissionChecks.py +0 -0
  52. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/permission/permissionDataManager.py +0 -0
  53. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/rule/__init__.py +0 -0
  54. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/rule/handler.py +0 -0
  55. {generalmanager-0.4.4 → generalmanager-0.4.6}/src/general_manager/rule/rule.py +0 -0
  56. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_argsToKwargs.py +0 -0
  57. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_basePermission.py +0 -0
  58. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_cacheDecorator.py +0 -0
  59. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_cacheTracker.py +0 -0
  60. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_dependencyIndex.py +0 -0
  61. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_factories.py +0 -0
  62. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_factoryMethods.py +0 -0
  63. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_filterParser.py +0 -0
  64. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_graph_ql.py +0 -0
  65. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_input.py +0 -0
  66. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_jsonEncoder.py +0 -0
  67. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_makeCacheKey.py +0 -0
  68. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_managerBasedPermission.py +0 -0
  69. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_measurement_field.py +0 -0
  70. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_modelDependencyCollector.py +0 -0
  71. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_noneToZero.py +0 -0
  72. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_rule_handler.py +0 -0
  73. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_rules.py +0 -0
  74. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_settings.py +0 -0
  75. {generalmanager-0.4.4 → generalmanager-0.4.6}/tests/test_signals.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.4.4
3
+ Version: 0.4.6
4
4
  Summary: Kurzbeschreibung deines Pakets
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License-Expression: MIT
@@ -24,6 +24,7 @@ src/general_manager/cache/dependencyIndex.py
24
24
  src/general_manager/cache/modelDependencyCollector.py
25
25
  src/general_manager/cache/signals.py
26
26
  src/general_manager/factory/__init__.py
27
+ src/general_manager/factory/autoFactory.py
27
28
  src/general_manager/factory/factories.py
28
29
  src/general_manager/factory/factoryMethods.py
29
30
  src/general_manager/interface/__init__.py
@@ -48,6 +49,7 @@ src/general_manager/rule/__init__.py
48
49
  src/general_manager/rule/handler.py
49
50
  src/general_manager/rule/rule.py
50
51
  tests/test_argsToKwargs.py
52
+ tests/test_autoFactory.py
51
53
  tests/test_basePermission.py
52
54
  tests/test_cacheDecorator.py
53
55
  tests/test_cacheTracker.py
@@ -55,6 +57,7 @@ tests/test_dependencyIndex.py
55
57
  tests/test_factories.py
56
58
  tests/test_factoryMethods.py
57
59
  tests/test_filterParser.py
60
+ tests/test_generalManager.py
58
61
  tests/test_graph_ql.py
59
62
  tests/test_input.py
60
63
  tests/test_jsonEncoder.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.4.4
3
+ Version: 0.4.6
4
4
  Summary: Kurzbeschreibung deines Pakets
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License-Expression: MIT
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "GeneralManager"
10
- version = "0.4.4"
10
+ version = "0.4.6"
11
11
  description = "Kurzbeschreibung deines Pakets"
12
12
  readme = "README.md"
13
13
  authors = [
@@ -0,0 +1,224 @@
1
+ from __future__ import annotations
2
+ from typing import TYPE_CHECKING, Type, Callable, Union, Any, TypeVar, Literal
3
+ from django.db import models
4
+ from factory.django import DjangoModelFactory
5
+ from general_manager.factory.factories import getFieldValue, getManyToManyFieldValue
6
+
7
+
8
+ if TYPE_CHECKING:
9
+ from general_manager.interface.databaseInterface import (
10
+ DBBasedInterface,
11
+ )
12
+
13
+ modelsModel = TypeVar("modelsModel", bound=models.Model)
14
+
15
+
16
+ class AutoFactory(DjangoModelFactory[modelsModel]):
17
+ """
18
+ A factory class that automatically generates values for model fields,
19
+ including handling of unique fields and constraints.
20
+ """
21
+
22
+ interface: Type[DBBasedInterface]
23
+ _adjustmentMethod: (
24
+ Callable[..., Union[dict[str, Any], list[dict[str, Any]]]] | None
25
+ ) = None
26
+
27
+ @classmethod
28
+ def _generate(
29
+ cls, strategy: Literal["build", "create"], params: dict[str, Any]
30
+ ) -> models.Model | list[models.Model]:
31
+ """
32
+ Generates and populates a Django model instance or list of instances with automatic field value assignment.
33
+
34
+ Automatically assigns values to unset model fields, including handling custom and special fields, and processes many-to-many relationships after instance creation or building. Raises a ValueError if the model is not a subclass of Django's Model.
35
+
36
+ Args:
37
+ strategy: Specifies whether to build (unsaved) or create (saved) the instance(s).
38
+ params: Field values to use for instance generation; missing fields are auto-filled.
39
+
40
+ Returns:
41
+ A single model instance or a list of model instances, depending on the input parameters and strategy.
42
+ """
43
+ cls._original_params = params
44
+ model = cls._meta.model
45
+ if not issubclass(model, models.Model):
46
+ raise ValueError("Model must be a type")
47
+ field_name_list, to_ignore_list = cls.interface.handleCustomFields(model)
48
+
49
+ fields = [
50
+ field
51
+ for field in model._meta.get_fields()
52
+ if field.name not in to_ignore_list
53
+ ]
54
+ special_fields: list[models.Field[Any, Any]] = [
55
+ getattr(model, field_name) for field_name in field_name_list
56
+ ]
57
+ pre_declarations = getattr(cls._meta, "pre_declarations", [])
58
+ post_declarations = getattr(cls._meta, "post_declarations", [])
59
+ declared_fields: set[str] = set(pre_declarations) | set(post_declarations)
60
+
61
+ field_list: list[models.Field[Any, Any] | models.ForeignObjectRel] = [
62
+ *fields,
63
+ *special_fields,
64
+ ]
65
+
66
+ for field in field_list:
67
+ if field.name in [*params, *declared_fields]:
68
+ continue # Skip fields that are already set
69
+ if isinstance(field, models.AutoField) or field.auto_created:
70
+ continue # Skip auto fields
71
+ params[field.name] = getFieldValue(field)
72
+
73
+ obj: list[models.Model] | models.Model = super()._generate(strategy, params)
74
+ if isinstance(obj, list):
75
+ for item in obj: # type: ignore
76
+ if not isinstance(item, models.Model):
77
+ raise ValueError("Model must be a type")
78
+ cls._handleManyToManyFieldsAfterCreation(item, params)
79
+ else:
80
+ cls._handleManyToManyFieldsAfterCreation(obj, params)
81
+ return obj
82
+
83
+ @classmethod
84
+ def _handleManyToManyFieldsAfterCreation(
85
+ cls, obj: models.Model, attrs: dict[str, Any]
86
+ ) -> None:
87
+ """
88
+ Sets many-to-many field values on a Django model instance after creation.
89
+
90
+ For each many-to-many field, assigns related objects from the provided attributes if available; otherwise, generates values using `getManyToManyFieldValue`. The related objects are set using the Django ORM's `set` method.
91
+ """
92
+ for field in obj._meta.many_to_many:
93
+ if field.name in attrs:
94
+ m2m_values = attrs[field.name]
95
+ else:
96
+ m2m_values = getManyToManyFieldValue(field)
97
+ if m2m_values:
98
+ getattr(obj, field.name).set(m2m_values)
99
+
100
+ @classmethod
101
+ def _adjust_kwargs(cls, **kwargs: dict[str, Any]) -> dict[str, Any]:
102
+ # Remove ManyToMany fields from kwargs before object creation
103
+ """
104
+ Removes many-to-many field entries from keyword arguments before model instance creation.
105
+
106
+ Returns:
107
+ The keyword arguments dictionary with many-to-many fields removed.
108
+ """
109
+ model: Type[models.Model] = cls._meta.model
110
+ m2m_fields = {field.name for field in model._meta.many_to_many}
111
+ for field_name in m2m_fields:
112
+ kwargs.pop(field_name, None)
113
+ return kwargs
114
+
115
+ @classmethod
116
+ def _create(
117
+ cls, model_class: Type[models.Model], *args: list[Any], **kwargs: dict[str, Any]
118
+ ) -> models.Model | list[models.Model]:
119
+ """
120
+ Creates and saves a Django model instance or instances, applying optional adjustment logic.
121
+
122
+ If an adjustment method is defined, it is used to generate or modify field values before creation. Otherwise, the model is instantiated and saved with the provided attributes.
123
+
124
+ Args:
125
+ model_class: The Django model class to instantiate.
126
+
127
+ Returns:
128
+ A saved model instance or a list of instances.
129
+ """
130
+ kwargs = cls._adjust_kwargs(**kwargs)
131
+ if cls._adjustmentMethod is not None:
132
+ return cls.__createWithGenerateFunc(use_creation_method=True, params=kwargs)
133
+ return cls._modelCreation(model_class, **kwargs)
134
+
135
+ @classmethod
136
+ def _build(
137
+ cls, model_class: Type[models.Model], *args: list[Any], **kwargs: dict[str, Any]
138
+ ) -> models.Model | list[models.Model]:
139
+ """
140
+ Constructs an unsaved instance or list of instances of the given Django model class.
141
+
142
+ If an adjustment method is defined, it is used to generate or modify field values before building the instance(s). Many-to-many fields are removed from the keyword arguments prior to instantiation.
143
+ """
144
+ kwargs = cls._adjust_kwargs(**kwargs)
145
+ if cls._adjustmentMethod is not None:
146
+ return cls.__createWithGenerateFunc(
147
+ use_creation_method=False, params=kwargs
148
+ )
149
+ return cls._modelBuilding(model_class, **kwargs)
150
+
151
+ @classmethod
152
+ def _modelCreation(
153
+ cls, model_class: Type[models.Model], **kwargs: dict[str, Any]
154
+ ) -> models.Model:
155
+ """
156
+ Creates and saves a Django model instance with the provided field values.
157
+
158
+ Initializes the model, assigns attributes from keyword arguments, validates the instance using `full_clean()`, and saves it to the database.
159
+
160
+ Returns:
161
+ The saved Django model instance.
162
+ """
163
+ obj = model_class()
164
+ for field, value in kwargs.items():
165
+ setattr(obj, field, value)
166
+ obj.full_clean()
167
+ obj.save()
168
+ return obj
169
+
170
+ @classmethod
171
+ def _modelBuilding(
172
+ cls, model_class: Type[models.Model], **kwargs: dict[str, Any]
173
+ ) -> models.Model:
174
+ """
175
+ Constructs an unsaved instance of the specified Django model with provided field values.
176
+
177
+ Args:
178
+ model_class: The Django model class to instantiate.
179
+ **kwargs: Field values to set on the model instance.
180
+
181
+ Returns:
182
+ An unsaved Django model instance with attributes set from kwargs.
183
+ """
184
+ obj = model_class()
185
+ for field, value in kwargs.items():
186
+ setattr(obj, field, value)
187
+ return obj
188
+
189
+ @classmethod
190
+ def __createWithGenerateFunc(
191
+ cls, use_creation_method: bool, params: dict[str, Any]
192
+ ) -> models.Model | list[models.Model]:
193
+ """
194
+ Generates one or more model instances using the adjustment method for field values.
195
+
196
+ If the adjustment method returns a single dictionary, creates or builds a single model instance.
197
+ If it returns a list of dictionaries, creates or builds multiple instances accordingly.
198
+
199
+ Args:
200
+ use_creation_method: If True, saves instances to the database; otherwise, builds unsaved instances.
201
+ params: Parameters to pass to the adjustment method for generating field values.
202
+
203
+ Returns:
204
+ A single model instance or a list of model instances, depending on the adjustment method's output.
205
+
206
+ Raises:
207
+ ValueError: If the adjustment method is not defined.
208
+ """
209
+ model_cls = cls._meta.model
210
+ if cls._adjustmentMethod is None:
211
+ raise ValueError("generate_func is not defined")
212
+ records = cls._adjustmentMethod(**params)
213
+ if isinstance(records, dict):
214
+ if use_creation_method:
215
+ return cls._modelCreation(model_cls, **records)
216
+ return cls._modelBuilding(model_cls, **records)
217
+
218
+ created_objects: list[models.Model] = []
219
+ for record in records:
220
+ if use_creation_method:
221
+ created_objects.append(cls._modelCreation(model_cls, **record))
222
+ else:
223
+ created_objects.append(cls._modelBuilding(model_cls, **record))
224
+ return created_objects
@@ -1,164 +1,22 @@
1
1
  from __future__ import annotations
2
- from typing import TYPE_CHECKING, Type, Callable, Union, Any, TypeVar, Literal, cast
2
+ from typing import Any, cast
3
3
  from factory.declarations import LazyFunction
4
4
  from factory.faker import Faker
5
5
  import exrex # type: ignore
6
6
  from django.db import models
7
7
  from django.core.validators import RegexValidator
8
- from factory.django import DjangoModelFactory
9
8
  import random
10
9
  from decimal import Decimal
11
10
  from general_manager.measurement.measurement import Measurement
12
11
  from general_manager.measurement.measurementField import MeasurementField
13
12
  from datetime import date, datetime, time, timezone
14
13
 
15
- if TYPE_CHECKING:
16
- from general_manager.interface.databaseInterface import (
17
- DBBasedInterface,
18
- )
19
-
20
- modelsModel = TypeVar("modelsModel", bound=models.Model)
21
-
22
-
23
- class AutoFactory(DjangoModelFactory[modelsModel]):
24
- """
25
- A factory class that automatically generates values for model fields,
26
- including handling of unique fields and constraints.
27
- """
28
-
29
- interface: Type[DBBasedInterface]
30
- _adjustmentMethod: (
31
- Callable[..., Union[dict[str, Any], list[dict[str, Any]]]] | None
32
- ) = None
33
-
34
- @classmethod
35
- def _generate(
36
- cls, strategy: Literal["build", "create"], params: dict[str, Any]
37
- ) -> models.Model | list[models.Model]:
38
- cls._original_params = params
39
- model = getattr(cls._meta, "model")
40
- if not issubclass(model, models.Model):
41
- raise ValueError("Model must be a type")
42
- field_name_list, to_ignore_list = cls.interface.handleCustomFields(model)
43
-
44
- fields = [
45
- field
46
- for field in model._meta.get_fields()
47
- if field.name not in to_ignore_list
48
- ]
49
- special_fields: list[models.Field[Any, Any]] = [
50
- getattr(model, field_name) for field_name in field_name_list
51
- ]
52
- pre_declations = getattr(cls._meta, "pre_declarations", [])
53
- post_declarations = getattr(cls._meta, "post_declarations", [])
54
- declared_fields: set[str] = set(pre_declations) | set(post_declarations)
55
-
56
- field_list: list[models.Field[Any, Any] | models.ForeignObjectRel] = [
57
- *fields,
58
- *special_fields,
59
- ]
60
-
61
- for field in field_list:
62
- if field.name in [*params, *declared_fields]:
63
- continue # Skip fields that are already set
64
- if isinstance(field, models.AutoField) or field.auto_created:
65
- continue # Skip auto fields
66
- params[field.name] = getFieldValue(field)
67
-
68
- obj: list[models.Model] | models.Model = super()._generate(strategy, params)
69
- if isinstance(obj, list):
70
- for item in obj: # type: ignore
71
- if not isinstance(item, models.Model):
72
- raise ValueError("Model must be a type")
73
- cls._handleManyToManyFieldsAfterCreation(item, params)
74
- else:
75
- cls._handleManyToManyFieldsAfterCreation(obj, params)
76
- return obj
77
-
78
- @classmethod
79
- def _handleManyToManyFieldsAfterCreation(
80
- cls, obj: models.Model, attrs: dict[str, Any]
81
- ) -> None:
82
- for field in obj._meta.many_to_many:
83
- if field.name in attrs:
84
- m2m_values = attrs[field.name]
85
- else:
86
- m2m_values = getManyToManyFieldValue(field)
87
- if m2m_values:
88
- getattr(obj, field.name).set(m2m_values)
89
-
90
- @classmethod
91
- def _adjust_kwargs(cls, **kwargs: dict[str, Any]) -> dict[str, Any]:
92
- # Remove ManyToMany fields from kwargs before object creation
93
- model: Type[models.Model] = getattr(cls._meta, "model")
94
- m2m_fields = {field.name for field in model._meta.many_to_many}
95
- for field_name in m2m_fields:
96
- kwargs.pop(field_name, None)
97
- return kwargs
98
-
99
- @classmethod
100
- def _create(
101
- cls, model_class: Type[models.Model], *args: list[Any], **kwargs: dict[str, Any]
102
- ) -> models.Model | list[models.Model]:
103
- kwargs = cls._adjust_kwargs(**kwargs)
104
- if cls._adjustmentMethod is not None:
105
- return cls.__createWithGenerateFunc(strategy=True, params=kwargs)
106
- return cls._modelCreation(model_class, **kwargs)
107
-
108
- @classmethod
109
- def _build(
110
- cls, model_class: Type[models.Model], *args: list[Any], **kwargs: dict[str, Any]
111
- ) -> models.Model | list[models.Model]:
112
- kwargs = cls._adjust_kwargs(**kwargs)
113
- if cls._adjustmentMethod is not None:
114
- return cls.__createWithGenerateFunc(strategy=False, params=kwargs)
115
- return cls._modelBuilding(model_class, **kwargs)
116
-
117
- @classmethod
118
- def _modelCreation(
119
- cls, model_class: Type[models.Model], **kwargs: dict[str, Any]
120
- ) -> models.Model:
121
- obj = model_class()
122
- for field, value in kwargs.items():
123
- setattr(obj, field, value)
124
- obj.full_clean()
125
- obj.save()
126
- return obj
127
-
128
- @classmethod
129
- def _modelBuilding(
130
- cls, model_class: Type[models.Model], **kwargs: dict[str, Any]
131
- ) -> models.Model:
132
- obj = model_class()
133
- for field, value in kwargs.items():
134
- setattr(obj, field, value)
135
- return obj
136
-
137
- @classmethod
138
- def __createWithGenerateFunc(
139
- cls, strategy: bool, params: dict[str, Any]
140
- ) -> models.Model | list[models.Model]:
141
- model_cls = getattr(cls._meta, "model")
142
- if cls._adjustmentMethod is None:
143
- raise ValueError("generate_func is not defined")
144
- records = cls._adjustmentMethod(**params)
145
- if isinstance(records, dict):
146
- if strategy:
147
- return cls._modelCreation(model_cls, **records)
148
- return cls._modelBuilding(model_cls, **records)
149
-
150
- created_objects: list[models.Model] = []
151
- for record in records:
152
- if strategy:
153
- created_objects.append(cls._modelCreation(model_cls, **record))
154
- else:
155
- created_objects.append(cls._modelBuilding(model_cls, **record))
156
- return created_objects
157
-
158
14
 
159
15
  def getFieldValue(field: models.Field[Any, Any] | models.ForeignObjectRel) -> object:
160
16
  """
161
- Returns a suitable value for a given Django model field.
17
+ Generates an appropriate value for a given Django model field for use in testing or data factories.
18
+
19
+ If the field allows null values, there is a 10% chance of returning None. Handles a wide range of Django field types, including measurement, text, numeric, date/time, boolean, relational, and specialized fields, returning a suitable fake or factory-generated value for each. For relational fields (OneToOneField and ForeignKey), attempts to use a factory if available or selects a random existing instance; raises ValueError if neither is possible. Returns None for unsupported field types.
162
20
  """
163
21
  if field.null:
164
22
  if random.choice([True] + 9 * [False]):
@@ -18,7 +18,7 @@ from simple_history.models import HistoricalRecords # type: ignore
18
18
  from general_manager.measurement.measurement import Measurement
19
19
  from general_manager.measurement.measurementField import MeasurementField
20
20
  from decimal import Decimal
21
- from general_manager.factory.factories import AutoFactory
21
+ from general_manager.factory.autoFactory import AutoFactory
22
22
  from django.core.exceptions import ValidationError
23
23
  from general_manager.interface.baseInterface import (
24
24
  InterfaceBase,
@@ -646,13 +646,13 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
646
646
  self, basis: dict[str, list[Any]], **kwargs: Any
647
647
  ) -> dict[str, list[Any]]:
648
648
  """
649
- Merges filter definitions by combining existing filter criteria with additional keyword arguments.
649
+ Combines existing filter definitions with additional keyword arguments.
650
650
 
651
651
  Args:
652
- basis: A dictionary mapping filter keys to lists of values.
652
+ basis: Dictionary mapping filter keys to lists of values. Additional keyword arguments are merged into this dictionary.
653
653
 
654
654
  Returns:
655
- A dictionary where each key maps to a list of all values from both the original basis and the new keyword arguments.
655
+ A dictionary where each key maps to a list containing all values from both the original basis and the new keyword arguments.
656
656
  """
657
657
  kwarg_filter: dict[str, list[Any]] = {}
658
658
  for key, value in basis.items():
@@ -665,9 +665,9 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
665
665
 
666
666
  def filter(self, **kwargs: Any) -> DatabaseBucket[GeneralManagerType]:
667
667
  """
668
- Returns a new bucket containing manager instances matching the given filter criteria.
668
+ Returns a new bucket with manager instances matching the specified filter criteria.
669
669
 
670
- Additional filter keyword arguments are merged with existing filters to refine the queryset.
670
+ Additional filter keyword arguments are merged with any existing filters to further restrict the queryset.
671
671
  """
672
672
  merged_filter = self.__mergeFilterDefinitions(self.filters, **kwargs)
673
673
  return self.__class__(
@@ -679,13 +679,9 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
679
679
 
680
680
  def exclude(self, **kwargs: Any) -> DatabaseBucket[GeneralManagerType]:
681
681
  """
682
- Returns a new bucket excluding items matching the given filter criteria.
682
+ Returns a new DatabaseBucket excluding items that match the specified criteria.
683
683
 
684
- Args:
685
- **kwargs: Field lookups to exclude from the queryset.
686
-
687
- Returns:
688
- A DatabaseBucket containing items not matching the specified filters.
684
+ Keyword arguments specify field lookups to exclude from the queryset. The resulting bucket contains only items that do not match these filters.
689
685
  """
690
686
  merged_exclude = self.__mergeFilterDefinitions(self.excludes, **kwargs)
691
687
  return self.__class__(
@@ -128,15 +128,31 @@ class GeneralManager(Generic[GeneralManagerType], metaclass=GeneralManagerMeta):
128
128
 
129
129
  @staticmethod
130
130
  def __parse_identification(kwargs: dict[str, Any]) -> dict[str, Any] | None:
131
+ """
132
+ Converts a dictionary of keyword arguments by replacing any GeneralManager instances with their identifications.
133
+
134
+ For each key-value pair, if the value is a GeneralManager, it is replaced with its identification. Lists and tuples are processed recursively, replacing contained GeneralManager instances with their identifications. Returns a new dictionary with the processed values, or None if the result is empty.
135
+
136
+ Args:
137
+ kwargs: Dictionary of keyword arguments to process.
138
+
139
+ Returns:
140
+ A new dictionary with GeneralManager instances replaced by their identifications, or None if the dictionary is empty.
141
+ """
142
+ output = {}
131
143
  for key, value in kwargs.items():
132
144
  if isinstance(value, GeneralManager):
133
- kwargs[key] = value.identification
145
+ output[key] = value.identification
134
146
  elif isinstance(value, list):
135
- kwargs[key] = [
136
- v.identification for v in value if isinstance(v, GeneralManager)
147
+ output[key] = [
148
+ v.identification if isinstance(v, GeneralManager) else v
149
+ for v in value
137
150
  ]
138
151
  elif isinstance(value, tuple):
139
- kwargs[key] = tuple(
140
- v.identification for v in value if isinstance(v, GeneralManager)
152
+ output[key] = tuple(
153
+ v.identification if isinstance(v, GeneralManager) else v
154
+ for v in value
141
155
  )
142
- return kwargs if kwargs else None
156
+ else:
157
+ output[key] = value
158
+ return output if output else None
@@ -56,7 +56,22 @@ class Measurement:
56
56
 
57
57
  @classmethod
58
58
  def from_string(cls, value: str) -> Measurement:
59
- value, unit = value.split(" ")
59
+ """
60
+ Creates a Measurement instance from a string in the format 'value unit'.
61
+
62
+ Args:
63
+ value: A string containing a numeric value and a unit separated by a space (e.g., '10.5 kg').
64
+
65
+ Returns:
66
+ A Measurement instance representing the parsed value and unit.
67
+
68
+ Raises:
69
+ ValueError: If the input string does not contain exactly two parts separated by a space.
70
+ """
71
+ splitted = value.split(" ")
72
+ if len(splitted) != 2:
73
+ raise ValueError("String must be in the format 'value unit'.")
74
+ value, unit = splitted
60
75
  return cls(value, unit)
61
76
 
62
77
  @staticmethod
@@ -202,12 +217,31 @@ class Measurement:
202
217
  return f"Measurement({self.quantity.magnitude}, '{self.quantity.units}')"
203
218
 
204
219
  def _compare(self, other: Any, operation: Callable[..., bool]) -> bool:
220
+ """
221
+ Compares this Measurement with another using the specified comparison operation.
222
+
223
+ If `other` is a string, it is parsed into a Measurement. Raises a TypeError if
224
+ `other` is not a Measurement instance. Converts `other` to this instance's unit
225
+ before applying the comparison. Raises a ValueError if the measurements have
226
+ incompatible dimensions.
227
+
228
+ Args:
229
+ other: The object to compare with, either a Measurement or a string in the format "value unit".
230
+ operation: A callable that takes two magnitudes and returns a boolean result.
231
+
232
+ Returns:
233
+ The result of the comparison operation.
234
+
235
+ Raises:
236
+ TypeError: If `other` is not a Measurement instance or a valid string representation.
237
+ ValueError: If the measurements have different dimensions and cannot be compared.
238
+ """
205
239
  if isinstance(other, str):
206
240
  other = Measurement.from_string(other)
207
241
 
208
242
  # Überprüfen, ob `other` ein Measurement-Objekt ist
209
243
  if not isinstance(other, Measurement):
210
- return NotImplemented
244
+ raise TypeError("Comparison is only allowed between Measurement instances.")
211
245
  try:
212
246
  # Convert `other` to the same units as `self`
213
247
  other_converted: pint.Quantity = other.quantity.to(self.quantity.units) # type: ignore
@@ -238,4 +272,17 @@ class Measurement:
238
272
  return self._compare(other, gt)
239
273
 
240
274
  def __ge__(self, other: Any) -> bool:
275
+ """
276
+ Returns True if this measurement is greater than or equal to another measurement.
277
+
278
+ Comparison is performed after converting the other operand to the same unit. Raises a TypeError if the other object is not a Measurement instance or a compatible string, or a ValueError if the units are not compatible.
279
+ """
241
280
  return self._compare(other, ge)
281
+
282
+ def __hash__(self) -> int:
283
+ """
284
+ Returns a hash value based on the magnitude and unit of the measurement.
285
+
286
+ This enables Measurement instances to be used in hash-based collections such as sets and dictionaries.
287
+ """
288
+ return hash((self.quantity.magnitude, str(self.quantity.units)))