GeneralManager 0.14.1__py3-none-any.whl → 0.15.1__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 (62) hide show
  1. general_manager/__init__.py +49 -0
  2. general_manager/api/__init__.py +36 -0
  3. general_manager/api/graphql.py +92 -43
  4. general_manager/api/mutation.py +35 -10
  5. general_manager/api/property.py +26 -3
  6. general_manager/apps.py +23 -16
  7. general_manager/bucket/__init__.py +32 -0
  8. general_manager/bucket/baseBucket.py +76 -64
  9. general_manager/bucket/calculationBucket.py +188 -108
  10. general_manager/bucket/databaseBucket.py +130 -49
  11. general_manager/bucket/groupBucket.py +113 -60
  12. general_manager/cache/__init__.py +38 -0
  13. general_manager/cache/cacheDecorator.py +29 -17
  14. general_manager/cache/cacheTracker.py +34 -15
  15. general_manager/cache/dependencyIndex.py +117 -33
  16. general_manager/cache/modelDependencyCollector.py +17 -8
  17. general_manager/cache/signals.py +17 -6
  18. general_manager/factory/__init__.py +34 -5
  19. general_manager/factory/autoFactory.py +57 -60
  20. general_manager/factory/factories.py +39 -14
  21. general_manager/factory/factoryMethods.py +38 -1
  22. general_manager/interface/__init__.py +36 -0
  23. general_manager/interface/baseInterface.py +71 -27
  24. general_manager/interface/calculationInterface.py +18 -10
  25. general_manager/interface/databaseBasedInterface.py +102 -71
  26. general_manager/interface/databaseInterface.py +66 -20
  27. general_manager/interface/models.py +10 -4
  28. general_manager/interface/readOnlyInterface.py +44 -30
  29. general_manager/manager/__init__.py +36 -3
  30. general_manager/manager/generalManager.py +73 -47
  31. general_manager/manager/groupManager.py +72 -17
  32. general_manager/manager/input.py +23 -15
  33. general_manager/manager/meta.py +53 -53
  34. general_manager/measurement/__init__.py +37 -2
  35. general_manager/measurement/measurement.py +135 -58
  36. general_manager/measurement/measurementField.py +161 -61
  37. general_manager/permission/__init__.py +32 -1
  38. general_manager/permission/basePermission.py +29 -12
  39. general_manager/permission/managerBasedPermission.py +32 -26
  40. general_manager/permission/mutationPermission.py +32 -3
  41. general_manager/permission/permissionChecks.py +9 -1
  42. general_manager/permission/permissionDataManager.py +49 -15
  43. general_manager/permission/utils.py +14 -3
  44. general_manager/rule/__init__.py +27 -1
  45. general_manager/rule/handler.py +90 -5
  46. general_manager/rule/rule.py +40 -27
  47. general_manager/utils/__init__.py +44 -2
  48. general_manager/utils/argsToKwargs.py +17 -9
  49. general_manager/utils/filterParser.py +29 -30
  50. general_manager/utils/formatString.py +2 -0
  51. general_manager/utils/jsonEncoder.py +14 -1
  52. general_manager/utils/makeCacheKey.py +18 -12
  53. general_manager/utils/noneToZero.py +8 -6
  54. general_manager/utils/pathMapping.py +92 -29
  55. general_manager/utils/public_api.py +49 -0
  56. general_manager/utils/testing.py +135 -69
  57. {generalmanager-0.14.1.dist-info → generalmanager-0.15.1.dist-info}/METADATA +10 -2
  58. generalmanager-0.15.1.dist-info/RECORD +62 -0
  59. generalmanager-0.14.1.dist-info/RECORD +0 -58
  60. {generalmanager-0.14.1.dist-info → generalmanager-0.15.1.dist-info}/WHEEL +0 -0
  61. {generalmanager-0.14.1.dist-info → generalmanager-0.15.1.dist-info}/licenses/LICENSE +0 -0
  62. {generalmanager-0.14.1.dist-info → generalmanager-0.15.1.dist-info}/top_level.txt +0 -0
general_manager/apps.py CHANGED
@@ -1,17 +1,17 @@
1
1
  from __future__ import annotations
2
2
  from django.apps import AppConfig
3
- import graphene
3
+ import graphene # type: ignore[import]
4
4
  import os
5
5
  from django.conf import settings
6
6
  from django.urls import path
7
- from graphene_django.views import GraphQLView
7
+ from graphene_django.views import GraphQLView # type: ignore[import]
8
8
  from importlib import import_module
9
9
  from general_manager.manager.generalManager import GeneralManager
10
10
  from general_manager.manager.meta import GeneralManagerMeta
11
11
  from general_manager.manager.input import Input
12
12
  from general_manager.api.property import graphQlProperty
13
13
  from general_manager.api.graphql import GraphQL
14
- from typing import TYPE_CHECKING, Type, Any, cast
14
+ from typing import TYPE_CHECKING, Type, cast
15
15
  from django.core.checks import register
16
16
  import logging
17
17
  from django.core.management.base import BaseCommand
@@ -27,7 +27,7 @@ class GeneralmanagerConfig(AppConfig):
27
27
  default_auto_field = "django.db.models.BigAutoField"
28
28
  name = "general_manager"
29
29
 
30
- def ready(self):
30
+ def ready(self) -> None:
31
31
  """
32
32
  Performs initialization tasks for the general_manager app when Django starts.
33
33
 
@@ -44,10 +44,10 @@ class GeneralmanagerConfig(AppConfig):
44
44
  @staticmethod
45
45
  def handleReadOnlyInterface(
46
46
  read_only_classes: list[Type[GeneralManager]],
47
- ):
47
+ ) -> None:
48
48
  """
49
49
  Configures synchronization and schema validation for the given read-only GeneralManager classes.
50
-
50
+
51
51
  For each provided class, ensures that its data is synchronized before any Django management command executes, and registers a system check to verify that the associated schema remains up to date.
52
52
  """
53
53
  GeneralmanagerConfig.patchReadOnlyInterfaceSync(read_only_classes)
@@ -69,17 +69,20 @@ class GeneralmanagerConfig(AppConfig):
69
69
  @staticmethod
70
70
  def patchReadOnlyInterfaceSync(
71
71
  general_manager_classes: list[Type[GeneralManager]],
72
- ):
72
+ ) -> None:
73
73
  """
74
74
  Monkey-patches Django's management command runner to synchronize all provided read-only interfaces before executing any management command, except during autoreload subprocesses of 'runserver'.
75
-
75
+
76
76
  For each class in `general_manager_classes`, the associated read-only interface's `syncData` method is called prior to command execution, ensuring data consistency before management operations.
77
77
  """
78
78
  from general_manager.interface.readOnlyInterface import ReadOnlyInterface
79
79
 
80
80
  original_run_from_argv = BaseCommand.run_from_argv
81
81
 
82
- def run_from_argv_with_sync(self, argv):
82
+ def run_from_argv_with_sync(
83
+ self: BaseCommand,
84
+ argv: list[str],
85
+ ) -> None:
83
86
  # Ensure syncData is only called at real run of runserver
84
87
  """
85
88
  Executes a Django management command, synchronizing all registered read-only interfaces before execution unless running in an autoreload subprocess of 'runserver'.
@@ -102,15 +105,16 @@ class GeneralmanagerConfig(AppConfig):
102
105
 
103
106
  logger.debug("finished syncing ReadOnlyInterface data.")
104
107
 
105
- return original_run_from_argv(self, argv)
108
+ result = original_run_from_argv(self, argv)
109
+ return result
106
110
 
107
- BaseCommand.run_from_argv = run_from_argv_with_sync
111
+ setattr(BaseCommand, "run_from_argv", run_from_argv_with_sync)
108
112
 
109
113
  @staticmethod
110
114
  def initializeGeneralManagerClasses(
111
115
  pending_attribute_initialization: list[Type[GeneralManager]],
112
116
  all_classes: list[Type[GeneralManager]],
113
- ):
117
+ ) -> None:
114
118
  """
115
119
  Initializes attributes and establishes dynamic relationships for GeneralManager classes.
116
120
 
@@ -150,7 +154,7 @@ class GeneralmanagerConfig(AppConfig):
150
154
  @staticmethod
151
155
  def handleGraphQL(
152
156
  pending_graphql_interfaces: list[Type[GeneralManager]],
153
- ):
157
+ ) -> None:
154
158
  """
155
159
  Creates GraphQL interfaces and mutations for the provided general manager classes, builds the GraphQL schema, and registers the GraphQL endpoint in the Django URL configuration.
156
160
  """
@@ -166,7 +170,10 @@ class GeneralmanagerConfig(AppConfig):
166
170
  mutation_class = type(
167
171
  "Mutation",
168
172
  (graphene.ObjectType,),
169
- {name: mutation.Field() for name, mutation in GraphQL._mutations.items()},
173
+ {
174
+ name: mutation.Field()
175
+ for name, mutation in GraphQL._mutations.items()
176
+ },
170
177
  )
171
178
  GraphQL._mutation_class = mutation_class
172
179
  schema = graphene.Schema(
@@ -179,7 +186,7 @@ class GeneralmanagerConfig(AppConfig):
179
186
  GeneralmanagerConfig.addGraphqlUrl(schema)
180
187
 
181
188
  @staticmethod
182
- def addGraphqlUrl(schema):
189
+ def addGraphqlUrl(schema: graphene.Schema) -> None:
183
190
  """
184
191
  Adds a GraphQL endpoint to the Django URL configuration using the provided schema.
185
192
 
@@ -203,7 +210,7 @@ class GeneralmanagerConfig(AppConfig):
203
210
  )
204
211
 
205
212
  @staticmethod
206
- def checkPermissionClass(general_manager_class: Type[GeneralManager]):
213
+ def checkPermissionClass(general_manager_class: Type[GeneralManager]) -> None:
207
214
  """
208
215
  Checks if the class has a Permission attribute and if it is a subclass of BasePermission.
209
216
  If so, it sets the Permission attribute on the class.
@@ -0,0 +1,32 @@
1
+ """Bucket utilities for GeneralManager."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from general_manager.utils.public_api import build_module_dir, resolve_export
8
+
9
+ __all__ = ["Bucket", "DatabaseBucket", "CalculationBucket", "GroupBucket"]
10
+
11
+ _MODULE_MAP = {
12
+ "Bucket": ("general_manager.bucket.baseBucket", "Bucket"),
13
+ "DatabaseBucket": ("general_manager.bucket.databaseBucket", "DatabaseBucket"),
14
+ "CalculationBucket": (
15
+ "general_manager.bucket.calculationBucket",
16
+ "CalculationBucket",
17
+ ),
18
+ "GroupBucket": ("general_manager.bucket.groupBucket", "GroupBucket"),
19
+ }
20
+
21
+
22
+ def __getattr__(name: str) -> Any:
23
+ return resolve_export(
24
+ name,
25
+ module_all=__all__,
26
+ module_map=_MODULE_MAP,
27
+ module_globals=globals(),
28
+ )
29
+
30
+
31
+ def __dir__() -> list[str]:
32
+ return build_module_dir(module_all=__all__, module_globals=globals())
@@ -1,3 +1,5 @@
1
+ """Abstract bucket primitives for managing GeneralManager collections."""
2
+
1
3
  from __future__ import annotations
2
4
  from abc import ABC, abstractmethod
3
5
  from typing import (
@@ -19,25 +21,32 @@ if TYPE_CHECKING:
19
21
 
20
22
 
21
23
  class Bucket(ABC, Generic[GeneralManagerType]):
24
+ """Abstract interface for lazily evaluated GeneralManager collections."""
22
25
 
23
- def __init__(self, manager_class: Type[GeneralManagerType]):
26
+ def __init__(self, manager_class: Type[GeneralManagerType]) -> None:
24
27
  """
25
- Initializes the Bucket with a specified manager class.
28
+ Create a bucket bound to a specific manager class.
29
+
30
+ Parameters:
31
+ manager_class (type[GeneralManagerType]): GeneralManager subclass whose instances this bucket represents.
26
32
 
27
- Args:
28
- manager_class: The class of manager objects this bucket will manage.
33
+ Returns:
34
+ None
29
35
  """
30
36
  self._manager_class = manager_class
31
- self._data = None
32
- self.excludes = {}
33
- self.filters = {}
37
+ self._data: Any = None
38
+ self.excludes: dict[str, Any] = {}
39
+ self.filters: dict[str, Any] = {}
34
40
 
35
41
  def __eq__(self, other: object) -> bool:
36
42
  """
37
- Checks if this Bucket is equal to another by comparing class, data, and manager class.
43
+ Compare two buckets for equality.
44
+
45
+ Parameters:
46
+ other (object): Object tested for equality with this bucket.
38
47
 
39
48
  Returns:
40
- True if both objects are of the same class and have equal internal data and manager class; otherwise, False.
49
+ bool: True when the buckets share the same class, manager class, and data payload.
41
50
  """
42
51
  if not isinstance(other, self.__class__):
43
52
  return False
@@ -45,10 +54,10 @@ class Bucket(ABC, Generic[GeneralManagerType]):
45
54
 
46
55
  def __reduce__(self) -> str | tuple[Any, ...]:
47
56
  """
48
- Prepares the object for pickling by returning the class and initialization arguments.
57
+ Provide pickling support by returning the constructor and arguments.
49
58
 
50
59
  Returns:
51
- A tuple containing the class and a tuple of arguments needed to reconstruct the object during unpickling.
60
+ tuple[Any, ...]: Data allowing the bucket to be reconstructed during unpickling.
52
61
  """
53
62
  return (
54
63
  self.__class__,
@@ -61,13 +70,13 @@ class Bucket(ABC, Generic[GeneralManagerType]):
61
70
  other: Bucket[GeneralManagerType] | GeneralManagerType,
62
71
  ) -> Bucket[GeneralManagerType]:
63
72
  """
64
- Return a new bucket representing the union of this bucket and another bucket or a single manager instance.
65
-
73
+ Return a bucket containing the union of this bucket and another input.
74
+
66
75
  Parameters:
67
- other: Another bucket or a single manager instance to include in the union.
68
-
76
+ other (Bucket[GeneralManagerType] | GeneralManagerType): Bucket or single manager instance to merge.
77
+
69
78
  Returns:
70
- A new bucket containing all unique items from both this bucket and the provided argument.
79
+ Bucket[GeneralManagerType]: New bucket with the combined contents.
71
80
  """
72
81
  raise NotImplementedError
73
82
 
@@ -76,74 +85,76 @@ class Bucket(ABC, Generic[GeneralManagerType]):
76
85
  self,
77
86
  ) -> Generator[GeneralManagerType | GroupManager[GeneralManagerType], None, None]:
78
87
  """
79
- Returns an iterator over the items in the bucket.
88
+ Iterate over items in the bucket.
80
89
 
81
90
  Yields:
82
- Instances of the managed type or group manager contained in the bucket.
91
+ GeneralManagerType | GroupManager[GeneralManagerType]: Items stored in the bucket.
83
92
  """
84
93
  raise NotImplementedError
85
94
 
86
95
  @abstractmethod
87
96
  def filter(self, **kwargs: Any) -> Bucket[GeneralManagerType]:
88
97
  """
89
- Returns a new bucket containing only items that match the specified filter criteria.
98
+ Return a bucket reduced to items matching the provided filters.
90
99
 
91
- Args:
92
- **kwargs: Field-value pairs used to filter items in the bucket.
100
+ Parameters:
101
+ **kwargs: Field lookups applied to the underlying query.
93
102
 
94
103
  Returns:
95
- A new Bucket instance with items matching the given criteria.
104
+ Bucket[GeneralManagerType]: Filtered bucket instance.
96
105
  """
97
106
  raise NotImplementedError
98
107
 
99
108
  @abstractmethod
100
109
  def exclude(self, **kwargs: Any) -> Bucket[GeneralManagerType]:
101
110
  """
102
- Returns a new Bucket excluding items that match the specified criteria.
111
+ Return a bucket that excludes items matching the provided filters.
103
112
 
104
- Args:
105
- **kwargs: Field-value pairs specifying the exclusion criteria.
113
+ Parameters:
114
+ **kwargs: Field lookups specifying records to remove from the result.
106
115
 
107
116
  Returns:
108
- A new Bucket instance with items matching the criteria excluded.
117
+ Bucket[GeneralManagerType]: Bucket with the specified records excluded.
109
118
  """
110
119
  raise NotImplementedError
111
120
 
112
121
  @abstractmethod
113
122
  def first(self) -> GeneralManagerType | GroupManager[GeneralManagerType] | None:
114
123
  """
115
- Returns the first item in the bucket, or None if the bucket is empty.
124
+ Return the first item contained in the bucket.
116
125
 
117
126
  Returns:
118
- The first GeneralManager or GroupManager instance, or None if no items exist.
127
+ GeneralManagerType | GroupManager[GeneralManagerType] | None: First entry if present, otherwise None.
119
128
  """
120
129
  raise NotImplementedError
121
130
 
122
131
  @abstractmethod
123
132
  def last(self) -> GeneralManagerType | GroupManager[GeneralManagerType] | None:
124
133
  """
125
- Returns the last item in the bucket, or None if the bucket is empty.
134
+ Return the last item contained in the bucket.
126
135
 
127
136
  Returns:
128
- The last GeneralManager or GroupManager instance, or None if no items exist.
137
+ GeneralManagerType | GroupManager[GeneralManagerType] | None: Last entry if present, otherwise None.
129
138
  """
130
139
  raise NotImplementedError
131
140
 
132
141
  @abstractmethod
133
142
  def count(self) -> int:
134
143
  """
135
- Returns the number of items in the bucket.
144
+ Return the number of items represented by the bucket.
136
145
 
137
- Subclasses must implement this method to provide the count of contained elements.
146
+ Returns:
147
+ int: Count of items.
138
148
  """
139
149
  raise NotImplementedError
140
150
 
141
151
  @abstractmethod
142
152
  def all(self) -> Bucket[GeneralManagerType]:
143
153
  """
144
- Returns a bucket containing all items managed by this instance.
154
+ Return a bucket encompassing every item managed by this instance.
145
155
 
146
- Subclasses must implement this method to provide access to the complete collection without filters or exclusions applied.
156
+ Returns:
157
+ Bucket[GeneralManagerType]: Bucket without filters or exclusions.
147
158
  """
148
159
  raise NotImplementedError
149
160
 
@@ -152,16 +163,13 @@ class Bucket(ABC, Generic[GeneralManagerType]):
152
163
  self, **kwargs: Any
153
164
  ) -> GeneralManagerType | GroupManager[GeneralManagerType]:
154
165
  """
155
- Retrieves a single item matching the specified criteria.
166
+ Retrieve a single item matching the provided criteria.
156
167
 
157
- Args:
158
- **kwargs: Field-value pairs used to identify the item.
168
+ Parameters:
169
+ **kwargs: Field lookups identifying the target record.
159
170
 
160
171
  Returns:
161
- The matching GeneralManager or GroupManager instance.
162
-
163
- Raises:
164
- NotImplementedError: If the method is not implemented by a subclass.
172
+ GeneralManagerType | GroupManager[GeneralManagerType]: Matching item.
165
173
  """
166
174
  raise NotImplementedError
167
175
 
@@ -174,25 +182,23 @@ class Bucket(ABC, Generic[GeneralManagerType]):
174
182
  | Bucket[GeneralManagerType]
175
183
  ):
176
184
  """
177
- Retrieves an item or a slice from the bucket.
185
+ Retrieve an item or slice from the bucket.
178
186
 
179
- Args:
180
- item: An integer index to retrieve a single element, or a slice to retrieve a subset.
187
+ Parameters:
188
+ item (int | slice): Index or slice specifying the desired record(s).
181
189
 
182
190
  Returns:
183
- A single manager instance if an integer is provided, or a new Bucket containing the sliced elements if a slice is provided.
184
-
185
- Raises:
186
- NotImplementedError: This method must be implemented by subclasses.
191
+ GeneralManagerType | GroupManager[GeneralManagerType] | Bucket[GeneralManagerType]: Resulting item or bucket slice.
187
192
  """
188
193
  raise NotImplementedError
189
194
 
190
195
  @abstractmethod
191
196
  def __len__(self) -> int:
192
197
  """
193
- Returns the number of items in the bucket.
198
+ Return the number of items contained in the bucket.
194
199
 
195
- Subclasses must implement this method to provide the count of contained elements.
200
+ Returns:
201
+ int: Count of elements.
196
202
  """
197
203
  raise NotImplementedError
198
204
 
@@ -201,11 +207,11 @@ class Bucket(ABC, Generic[GeneralManagerType]):
201
207
  """
202
208
  Checks whether the specified item is present in the bucket.
203
209
 
204
- Args:
205
- item: The manager instance to check for membership.
210
+ Parameters:
211
+ item (GeneralManagerType): Manager instance evaluated for membership.
206
212
 
207
213
  Returns:
208
- True if the item is contained in the bucket, otherwise False.
214
+ bool: True if the bucket contains the provided instance.
209
215
  """
210
216
  raise NotImplementedError
211
217
 
@@ -216,26 +222,26 @@ class Bucket(ABC, Generic[GeneralManagerType]):
216
222
  reverse: bool = False,
217
223
  ) -> Bucket[GeneralManagerType]:
218
224
  """
219
- Returns a new Bucket with items sorted by the specified key or keys.
225
+ Return a sorted bucket.
220
226
 
221
- Args:
222
- key: A string or tuple of strings specifying the attribute(s) to sort by.
223
- reverse: If True, sorts in descending order. Defaults to False.
227
+ Parameters:
228
+ key (str | tuple[str, ...]): Attribute name(s) used for sorting.
229
+ reverse (bool): Whether to sort in descending order.
224
230
 
225
231
  Returns:
226
- A new Bucket instance with items sorted according to the given key(s).
232
+ Bucket[GeneralManagerType]: Sorted bucket instance.
227
233
  """
228
234
  raise NotImplementedError
229
235
 
230
236
  def group_by(self, *group_by_keys: str) -> GroupBucket[GeneralManagerType]:
231
237
  """
232
- Return a GroupBucket that groups the items in this bucket by the specified attribute keys.
233
-
238
+ Materialise a grouped view of the bucket.
239
+
234
240
  Parameters:
235
- *group_by_keys (str): Attribute names to group the items by.
236
-
241
+ *group_by_keys (str): Attribute names used to form groups.
242
+
237
243
  Returns:
238
- GroupBucket[GeneralManagerType]: A bucket containing items grouped by the given keys.
244
+ GroupBucket[GeneralManagerType]: Bucket grouping items by the provided keys.
239
245
  """
240
246
  from general_manager.bucket.groupBucket import GroupBucket
241
247
 
@@ -243,7 +249,13 @@ class Bucket(ABC, Generic[GeneralManagerType]):
243
249
 
244
250
  def none(self) -> Bucket[GeneralManagerType]:
245
251
  """
246
- Raise NotImplementedError to indicate that subclasses must implement a method returning an empty bucket.
252
+ Return an empty bucket instance.
253
+
254
+ Returns:
255
+ Bucket[GeneralManagerType]: Empty bucket.
256
+
257
+ Raises:
258
+ NotImplementedError: Always raised by the base implementation; subclasses must provide a concrete version.
247
259
  """
248
260
  raise NotImplementedError(
249
261
  "The 'none' method is not implemented in the base Bucket class. "