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,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,3 @@
1
+ # from .baseInterface import InterfaceBase, Bucket
2
+ # from .databaseInterface import DatabaseInterface, ReadOnlyInterface, DatabaseBucket
3
+ # from .calculationInterface import CalculationInterface, CalculationBucket
@@ -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)