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.
- general_manager/__init__.py +49 -0
- general_manager/api/__init__.py +36 -0
- general_manager/api/graphql.py +92 -43
- general_manager/api/mutation.py +35 -10
- general_manager/api/property.py +26 -3
- general_manager/apps.py +23 -16
- general_manager/bucket/__init__.py +32 -0
- general_manager/bucket/baseBucket.py +76 -64
- general_manager/bucket/calculationBucket.py +188 -108
- general_manager/bucket/databaseBucket.py +130 -49
- general_manager/bucket/groupBucket.py +113 -60
- general_manager/cache/__init__.py +38 -0
- general_manager/cache/cacheDecorator.py +29 -17
- general_manager/cache/cacheTracker.py +34 -15
- general_manager/cache/dependencyIndex.py +117 -33
- general_manager/cache/modelDependencyCollector.py +17 -8
- general_manager/cache/signals.py +17 -6
- general_manager/factory/__init__.py +34 -5
- general_manager/factory/autoFactory.py +57 -60
- general_manager/factory/factories.py +39 -14
- general_manager/factory/factoryMethods.py +38 -1
- general_manager/interface/__init__.py +36 -0
- general_manager/interface/baseInterface.py +71 -27
- general_manager/interface/calculationInterface.py +18 -10
- general_manager/interface/databaseBasedInterface.py +102 -71
- general_manager/interface/databaseInterface.py +66 -20
- general_manager/interface/models.py +10 -4
- general_manager/interface/readOnlyInterface.py +44 -30
- general_manager/manager/__init__.py +36 -3
- general_manager/manager/generalManager.py +73 -47
- general_manager/manager/groupManager.py +72 -17
- general_manager/manager/input.py +23 -15
- general_manager/manager/meta.py +53 -53
- general_manager/measurement/__init__.py +37 -2
- general_manager/measurement/measurement.py +135 -58
- general_manager/measurement/measurementField.py +161 -61
- general_manager/permission/__init__.py +32 -1
- general_manager/permission/basePermission.py +29 -12
- general_manager/permission/managerBasedPermission.py +32 -26
- general_manager/permission/mutationPermission.py +32 -3
- general_manager/permission/permissionChecks.py +9 -1
- general_manager/permission/permissionDataManager.py +49 -15
- general_manager/permission/utils.py +14 -3
- general_manager/rule/__init__.py +27 -1
- general_manager/rule/handler.py +90 -5
- general_manager/rule/rule.py +40 -27
- general_manager/utils/__init__.py +44 -2
- general_manager/utils/argsToKwargs.py +17 -9
- general_manager/utils/filterParser.py +29 -30
- general_manager/utils/formatString.py +2 -0
- general_manager/utils/jsonEncoder.py +14 -1
- general_manager/utils/makeCacheKey.py +18 -12
- general_manager/utils/noneToZero.py +8 -6
- general_manager/utils/pathMapping.py +92 -29
- general_manager/utils/public_api.py +49 -0
- general_manager/utils/testing.py +135 -69
- {generalmanager-0.14.1.dist-info → generalmanager-0.15.1.dist-info}/METADATA +10 -2
- generalmanager-0.15.1.dist-info/RECORD +62 -0
- generalmanager-0.14.1.dist-info/RECORD +0 -58
- {generalmanager-0.14.1.dist-info → generalmanager-0.15.1.dist-info}/WHEEL +0 -0
- {generalmanager-0.14.1.dist-info → generalmanager-0.15.1.dist-info}/licenses/LICENSE +0 -0
- {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,
|
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(
|
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
|
-
|
108
|
+
result = original_run_from_argv(self, argv)
|
109
|
+
return result
|
106
110
|
|
107
|
-
BaseCommand
|
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
|
-
{
|
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
|
-
|
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
|
-
|
28
|
-
|
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
|
-
|
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
|
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
|
-
|
57
|
+
Provide pickling support by returning the constructor and arguments.
|
49
58
|
|
50
59
|
Returns:
|
51
|
-
|
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
|
65
|
-
|
73
|
+
Return a bucket containing the union of this bucket and another input.
|
74
|
+
|
66
75
|
Parameters:
|
67
|
-
other:
|
68
|
-
|
76
|
+
other (Bucket[GeneralManagerType] | GeneralManagerType): Bucket or single manager instance to merge.
|
77
|
+
|
69
78
|
Returns:
|
70
|
-
|
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
|
-
|
88
|
+
Iterate over items in the bucket.
|
80
89
|
|
81
90
|
Yields:
|
82
|
-
|
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
|
-
|
98
|
+
Return a bucket reduced to items matching the provided filters.
|
90
99
|
|
91
|
-
|
92
|
-
**kwargs: Field
|
100
|
+
Parameters:
|
101
|
+
**kwargs: Field lookups applied to the underlying query.
|
93
102
|
|
94
103
|
Returns:
|
95
|
-
|
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
|
-
|
111
|
+
Return a bucket that excludes items matching the provided filters.
|
103
112
|
|
104
|
-
|
105
|
-
**kwargs: Field
|
113
|
+
Parameters:
|
114
|
+
**kwargs: Field lookups specifying records to remove from the result.
|
106
115
|
|
107
116
|
Returns:
|
108
|
-
|
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
|
-
|
124
|
+
Return the first item contained in the bucket.
|
116
125
|
|
117
126
|
Returns:
|
118
|
-
|
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
|
-
|
134
|
+
Return the last item contained in the bucket.
|
126
135
|
|
127
136
|
Returns:
|
128
|
-
|
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
|
-
|
144
|
+
Return the number of items represented by the bucket.
|
136
145
|
|
137
|
-
|
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
|
-
|
154
|
+
Return a bucket encompassing every item managed by this instance.
|
145
155
|
|
146
|
-
|
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
|
-
|
166
|
+
Retrieve a single item matching the provided criteria.
|
156
167
|
|
157
|
-
|
158
|
-
**kwargs: Field
|
168
|
+
Parameters:
|
169
|
+
**kwargs: Field lookups identifying the target record.
|
159
170
|
|
160
171
|
Returns:
|
161
|
-
|
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
|
-
|
185
|
+
Retrieve an item or slice from the bucket.
|
178
186
|
|
179
|
-
|
180
|
-
item
|
187
|
+
Parameters:
|
188
|
+
item (int | slice): Index or slice specifying the desired record(s).
|
181
189
|
|
182
190
|
Returns:
|
183
|
-
|
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
|
-
|
198
|
+
Return the number of items contained in the bucket.
|
194
199
|
|
195
|
-
|
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
|
-
|
205
|
-
|
210
|
+
Parameters:
|
211
|
+
item (GeneralManagerType): Manager instance evaluated for membership.
|
206
212
|
|
207
213
|
Returns:
|
208
|
-
|
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
|
-
|
225
|
+
Return a sorted bucket.
|
220
226
|
|
221
|
-
|
222
|
-
key
|
223
|
-
reverse:
|
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
|
-
|
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
|
-
|
233
|
-
|
238
|
+
Materialise a grouped view of the bucket.
|
239
|
+
|
234
240
|
Parameters:
|
235
|
-
*group_by_keys (str): Attribute names to
|
236
|
-
|
241
|
+
*group_by_keys (str): Attribute names used to form groups.
|
242
|
+
|
237
243
|
Returns:
|
238
|
-
GroupBucket[GeneralManagerType]:
|
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
|
-
|
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. "
|