GeneralManager 0.9.1__py3-none-any.whl → 0.10.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/api/mutation.py +4 -4
- general_manager/apps.py +31 -8
- general_manager/bucket/calculationBucket.py +11 -6
- general_manager/cache/cacheDecorator.py +1 -1
- general_manager/interface/baseInterface.py +1 -1
- general_manager/interface/databaseBasedInterface.py +7 -7
- general_manager/interface/databaseInterface.py +4 -1
- general_manager/manager/generalManager.py +20 -22
- general_manager/manager/meta.py +7 -5
- general_manager/measurement/measurement.py +67 -61
- general_manager/{auxiliary → utils}/makeCacheKey.py +4 -4
- general_manager/utils/testing.py +114 -6
- {generalmanager-0.9.1.dist-info → generalmanager-0.10.1.dist-info}/METADATA +1 -1
- {generalmanager-0.9.1.dist-info → generalmanager-0.10.1.dist-info}/RECORD +24 -24
- /general_manager/{auxiliary → utils}/__init__.py +0 -0
- /general_manager/{auxiliary → utils}/argsToKwargs.py +0 -0
- /general_manager/{auxiliary → utils}/filterParser.py +0 -0
- /general_manager/{auxiliary → utils}/formatString.py +0 -0
- /general_manager/{auxiliary → utils}/jsonEncoder.py +0 -0
- /general_manager/{auxiliary → utils}/noneToZero.py +0 -0
- /general_manager/{auxiliary → utils}/pathMapping.py +0 -0
- {generalmanager-0.9.1.dist-info → generalmanager-0.10.1.dist-info}/WHEEL +0 -0
- {generalmanager-0.9.1.dist-info → generalmanager-0.10.1.dist-info}/licenses/LICENSE +0 -0
- {generalmanager-0.9.1.dist-info → generalmanager-0.10.1.dist-info}/top_level.txt +0 -0
general_manager/api/mutation.py
CHANGED
@@ -5,19 +5,19 @@ import graphene
|
|
5
5
|
from general_manager.api.graphql import GraphQL
|
6
6
|
from general_manager.manager.generalManager import GeneralManager
|
7
7
|
|
8
|
-
from general_manager.
|
8
|
+
from general_manager.utils.formatString import snake_to_camel
|
9
9
|
|
10
10
|
|
11
11
|
def graphQlMutation(needs_role: Optional[str] = None, auth_required: bool = False):
|
12
12
|
"""
|
13
13
|
Decorator that transforms a function into a GraphQL mutation class and registers it for use in a Graphene-based API.
|
14
|
-
|
14
|
+
|
15
15
|
The decorated function must have type hints for all parameters (except `info`) and a return annotation. The decorator dynamically generates a mutation class with arguments and output fields based on the function's signature and return type. It also enforces authentication if `auth_required` is set to True, returning an error if the user is not authenticated.
|
16
|
-
|
16
|
+
|
17
17
|
Parameters:
|
18
18
|
needs_role (Optional[str]): Reserved for future use to specify a required user role.
|
19
19
|
auth_required (bool): If True, the mutation requires an authenticated user.
|
20
|
-
|
20
|
+
|
21
21
|
Returns:
|
22
22
|
Callable: A decorator that registers the mutation and returns the original function.
|
23
23
|
"""
|
general_manager/apps.py
CHANGED
@@ -30,7 +30,7 @@ class GeneralmanagerConfig(AppConfig):
|
|
30
30
|
def ready(self):
|
31
31
|
"""
|
32
32
|
Performs initialization tasks for the general_manager app when Django starts.
|
33
|
-
|
33
|
+
|
34
34
|
Sets up synchronization and schema validation for read-only interfaces, initializes attributes and property accessors for general manager classes, and configures the GraphQL schema and endpoint if enabled in settings.
|
35
35
|
"""
|
36
36
|
self.handleReadOnlyInterface(GeneralManagerMeta.read_only_classes)
|
@@ -47,7 +47,7 @@ class GeneralmanagerConfig(AppConfig):
|
|
47
47
|
):
|
48
48
|
"""
|
49
49
|
Configures synchronization and schema validation for the provided read-only interface classes.
|
50
|
-
|
50
|
+
|
51
51
|
Ensures that each read-only interface is synchronized before Django management commands run, and registers system checks to validate that their schemas are up to date.
|
52
52
|
"""
|
53
53
|
GeneralmanagerConfig.patchReadOnlyInterfaceSync(read_only_classes)
|
@@ -72,7 +72,7 @@ class GeneralmanagerConfig(AppConfig):
|
|
72
72
|
):
|
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
|
@@ -83,10 +83,10 @@ class GeneralmanagerConfig(AppConfig):
|
|
83
83
|
# Ensure syncData is only called at real run of runserver
|
84
84
|
"""
|
85
85
|
Executes a Django management command, synchronizing all registered read-only interfaces before execution unless running in an autoreload subprocess of 'runserver'.
|
86
|
-
|
86
|
+
|
87
87
|
Parameters:
|
88
88
|
argv (list): Command-line arguments for the management command.
|
89
|
-
|
89
|
+
|
90
90
|
Returns:
|
91
91
|
The result of the original management command execution.
|
92
92
|
"""
|
@@ -113,7 +113,7 @@ class GeneralmanagerConfig(AppConfig):
|
|
113
113
|
):
|
114
114
|
"""
|
115
115
|
Initializes attributes and establishes dynamic relationships for GeneralManager classes.
|
116
|
-
|
116
|
+
|
117
117
|
For each class pending attribute initialization, assigns interface attributes and creates property accessors. Then, for all registered GeneralManager classes, connects input fields referencing other GeneralManager subclasses by adding GraphQL properties to enable filtered access to related objects.
|
118
118
|
"""
|
119
119
|
logger.debug("Initializing GeneralManager classes...")
|
@@ -144,6 +144,8 @@ class GeneralmanagerConfig(AppConfig):
|
|
144
144
|
f"{general_manager_class.__name__.lower()}_list",
|
145
145
|
graphQlProperty(func),
|
146
146
|
)
|
147
|
+
for general_manager_class in all_classes:
|
148
|
+
GeneralmanagerConfig.checkPermissionClass(general_manager_class)
|
147
149
|
|
148
150
|
@staticmethod
|
149
151
|
def handleGraphQL(
|
@@ -177,10 +179,10 @@ class GeneralmanagerConfig(AppConfig):
|
|
177
179
|
def addGraphqlUrl(schema):
|
178
180
|
"""
|
179
181
|
Adds a GraphQL endpoint to the Django URL configuration using the provided schema.
|
180
|
-
|
182
|
+
|
181
183
|
Parameters:
|
182
184
|
schema: The GraphQL schema to use for the endpoint.
|
183
|
-
|
185
|
+
|
184
186
|
Raises:
|
185
187
|
Exception: If the ROOT_URLCONF setting is not defined in Django settings.
|
186
188
|
"""
|
@@ -196,3 +198,24 @@ class GeneralmanagerConfig(AppConfig):
|
|
196
198
|
GraphQLView.as_view(graphiql=True, schema=schema),
|
197
199
|
)
|
198
200
|
)
|
201
|
+
|
202
|
+
@staticmethod
|
203
|
+
def checkPermissionClass(general_manager_class: Type[GeneralManager]):
|
204
|
+
"""
|
205
|
+
Checks if the class has a Permission attribute and if it is a subclass of BasePermission.
|
206
|
+
If so, it sets the Permission attribute on the class.
|
207
|
+
"""
|
208
|
+
from general_manager.permission.basePermission import BasePermission
|
209
|
+
from general_manager.permission.managerBasedPermission import (
|
210
|
+
ManagerBasedPermission,
|
211
|
+
)
|
212
|
+
|
213
|
+
if hasattr(general_manager_class, "Permission"):
|
214
|
+
permission = general_manager_class.Permission
|
215
|
+
if not issubclass(permission, BasePermission):
|
216
|
+
raise TypeError(
|
217
|
+
f"{permission.__name__} must be a subclass of BasePermission"
|
218
|
+
)
|
219
|
+
general_manager_class.Permission = permission
|
220
|
+
else:
|
221
|
+
general_manager_class.Permission = ManagerBasedPermission
|
@@ -15,10 +15,11 @@ from general_manager.interface.baseInterface import (
|
|
15
15
|
)
|
16
16
|
from general_manager.bucket.baseBucket import Bucket
|
17
17
|
from general_manager.manager.input import Input
|
18
|
-
from general_manager.
|
18
|
+
from general_manager.utils.filterParser import parse_filters
|
19
19
|
|
20
20
|
if TYPE_CHECKING:
|
21
21
|
from general_manager.manager.generalManager import GeneralManager
|
22
|
+
from general_manager.interface.calculationInterface import CalculationInterface
|
22
23
|
|
23
24
|
|
24
25
|
class CalculationBucket(Bucket[GeneralManagerType]):
|
@@ -90,7 +91,11 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
90
91
|
self._current_combinations = state.get("current_combinations")
|
91
92
|
|
92
93
|
def __or__(
|
93
|
-
self,
|
94
|
+
self,
|
95
|
+
other: (
|
96
|
+
Bucket[GeneralManagerType]
|
97
|
+
| GeneralManager[GeneralManagerType, CalculationInterface]
|
98
|
+
),
|
94
99
|
) -> CalculationBucket[GeneralManagerType]:
|
95
100
|
"""
|
96
101
|
Combines this CalculationBucket with another bucket or manager of the same type.
|
@@ -158,7 +163,7 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
158
163
|
def filter(self, **kwargs: Any) -> CalculationBucket:
|
159
164
|
"""
|
160
165
|
Returns a new CalculationBucket with additional filters applied.
|
161
|
-
|
166
|
+
|
162
167
|
Merges the provided filter criteria with existing filters to further restrict valid input combinations.
|
163
168
|
"""
|
164
169
|
filters = self.filters.copy()
|
@@ -316,12 +321,12 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
316
321
|
) -> List[dict[str, Any]]:
|
317
322
|
"""
|
318
323
|
Recursively generates all valid input combinations for the specified input fields, applying filters and exclusions.
|
319
|
-
|
324
|
+
|
320
325
|
Args:
|
321
326
|
sorted_inputs: Input field names ordered to respect dependency constraints.
|
322
327
|
filters: Mapping of input field names to filter definitions.
|
323
328
|
excludes: Mapping of input field names to exclusion definitions.
|
324
|
-
|
329
|
+
|
325
330
|
Returns:
|
326
331
|
A list of dictionaries, each representing a valid combination of input values that satisfy all filters and exclusions.
|
327
332
|
"""
|
@@ -329,7 +334,7 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
329
334
|
def helper(index, current_combo):
|
330
335
|
"""
|
331
336
|
Recursively generates all valid input combinations for calculation inputs.
|
332
|
-
|
337
|
+
|
333
338
|
Yields:
|
334
339
|
Dict[str, Any]: A dictionary representing a valid combination of input values, filtered and excluded according to the provided criteria.
|
335
340
|
"""
|
@@ -4,7 +4,7 @@ from django.core.cache import cache as django_cache
|
|
4
4
|
from general_manager.cache.cacheTracker import DependencyTracker
|
5
5
|
from general_manager.cache.dependencyIndex import record_dependencies, Dependency
|
6
6
|
from general_manager.cache.modelDependencyCollector import ModelDependencyCollector
|
7
|
-
from general_manager.
|
7
|
+
from general_manager.utils.makeCacheKey import make_cache_key
|
8
8
|
|
9
9
|
|
10
10
|
class CacheBackend(Protocol):
|
@@ -13,7 +13,7 @@ from typing import (
|
|
13
13
|
from datetime import datetime
|
14
14
|
from django.conf import settings
|
15
15
|
from django.db.models import Model
|
16
|
-
from general_manager.
|
16
|
+
from general_manager.utils import args_to_kwargs
|
17
17
|
|
18
18
|
if TYPE_CHECKING:
|
19
19
|
from general_manager.manager.input import Input
|
@@ -52,7 +52,7 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
52
52
|
):
|
53
53
|
"""
|
54
54
|
Initialize the interface and load the associated model instance.
|
55
|
-
|
55
|
+
|
56
56
|
If `search_date` is provided, loads the historical record as of that date; otherwise, loads the current record.
|
57
57
|
"""
|
58
58
|
super().__init__(*args, **kwargs)
|
@@ -141,9 +141,9 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
141
141
|
def getAttributeTypes(cls) -> dict[str, AttributeTypedDict]:
|
142
142
|
"""
|
143
143
|
Return a dictionary mapping attribute names to metadata describing their types and properties.
|
144
|
-
|
144
|
+
|
145
145
|
The dictionary includes all model fields, custom fields, foreign keys, many-to-many, and reverse relation fields. For each attribute, the metadata specifies its Python type (translated from Django field types when possible), whether it is required, editable, derived, and its default value. For related models with a general manager class, the type is set to that class.
|
146
|
-
|
146
|
+
|
147
147
|
Returns:
|
148
148
|
dict[str, AttributeTypedDict]: Mapping of attribute names to their type information and metadata.
|
149
149
|
"""
|
@@ -309,7 +309,7 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
309
309
|
model: Type[models.Model] | models.Model,
|
310
310
|
) -> tuple[list[str], list[str]]:
|
311
311
|
"""
|
312
|
-
Identifies custom fields on a model and their associated
|
312
|
+
Identifies custom fields on a model and their associated utils fields to ignore.
|
313
313
|
|
314
314
|
Returns:
|
315
315
|
A tuple containing a list of custom field names and a list of related field names
|
@@ -401,15 +401,15 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
401
401
|
# Felder aus der Interface-Klasse sammeln
|
402
402
|
"""
|
403
403
|
Dynamically generates a Django model class, its associated interface class, and a factory class from an interface definition.
|
404
|
-
|
404
|
+
|
405
405
|
This method collects fields and metadata from the provided interface class, creates a new Django model inheriting from the specified base model class, attaches custom validation rules if present, and constructs corresponding interface and factory classes. The updated attributes dictionary, the new interface class, and the newly created model class are returned for integration into the general manager framework.
|
406
|
-
|
406
|
+
|
407
407
|
Parameters:
|
408
408
|
name: The name for the dynamically created model class.
|
409
409
|
attrs: The attributes dictionary to be updated with the new interface and factory classes.
|
410
410
|
interface: The interface base class defining the model structure and metadata.
|
411
411
|
base_model_class: The base class to use for the new model (defaults to GeneralManagerModel).
|
412
|
-
|
412
|
+
|
413
413
|
Returns:
|
414
414
|
tuple: A tuple containing the updated attributes dictionary, the new interface class, and the newly created model class.
|
415
415
|
"""
|
@@ -9,6 +9,7 @@ from general_manager.interface.databaseBasedInterface import (
|
|
9
9
|
DBBasedInterface,
|
10
10
|
GeneralManagerModel,
|
11
11
|
)
|
12
|
+
from django.db.models import NOT_PROVIDED
|
12
13
|
|
13
14
|
|
14
15
|
class DatabaseInterface(DBBasedInterface[GeneralManagerModel]):
|
@@ -111,6 +112,8 @@ class DatabaseInterface(DBBasedInterface[GeneralManagerModel]):
|
|
111
112
|
if isinstance(value, GeneralManager):
|
112
113
|
value = value.identification["id"]
|
113
114
|
key = f"{key}_id"
|
115
|
+
if value is NOT_PROVIDED:
|
116
|
+
continue
|
114
117
|
try:
|
115
118
|
setattr(instance, key, value)
|
116
119
|
except ValueError as e:
|
@@ -158,8 +161,8 @@ class DatabaseInterface(DBBasedInterface[GeneralManagerModel]):
|
|
158
161
|
"""
|
159
162
|
instance.changed_by_id = creator_id
|
160
163
|
instance.full_clean()
|
164
|
+
instance.save()
|
161
165
|
if history_comment:
|
162
166
|
update_change_reason(instance, history_comment)
|
163
|
-
instance.save()
|
164
167
|
|
165
168
|
return instance.pk
|
@@ -20,12 +20,13 @@ class GeneralManager(
|
|
20
20
|
Generic[GeneralManagerType, InterfaceType], metaclass=GeneralManagerMeta
|
21
21
|
):
|
22
22
|
Interface: Type[InterfaceType]
|
23
|
+
Permission: Type[BasePermission]
|
23
24
|
_attributes: dict[str, Any]
|
24
25
|
|
25
26
|
def __init__(self, *args: Any, **kwargs: Any):
|
26
27
|
"""
|
27
28
|
Initialize the manager by creating an interface instance with the provided arguments and storing its identification.
|
28
|
-
|
29
|
+
|
29
30
|
The identification is registered with the dependency tracker for tracking purposes.
|
30
31
|
"""
|
31
32
|
self._interface = self.Interface(*args, **kwargs)
|
@@ -55,9 +56,9 @@ class GeneralManager(
|
|
55
56
|
) -> Bucket[GeneralManagerType]:
|
56
57
|
"""
|
57
58
|
Combine this manager with another manager of the same class or a Bucket using the union operator.
|
58
|
-
|
59
|
+
|
59
60
|
If combined with a Bucket, returns the union of the Bucket and this manager. If combined with another manager of the same class, returns a Bucket containing both instances. Raises a TypeError for unsupported types.
|
60
|
-
|
61
|
+
|
61
62
|
Returns:
|
62
63
|
Bucket[GeneralManagerType]: A Bucket containing the union of the involved managers.
|
63
64
|
"""
|
@@ -93,20 +94,19 @@ class GeneralManager(
|
|
93
94
|
) -> GeneralManager[GeneralManagerType, InterfaceType]:
|
94
95
|
"""
|
95
96
|
Creates a new managed object using the underlying interface and returns a corresponding manager instance.
|
96
|
-
|
97
|
+
|
97
98
|
Performs a permission check if a `Permission` class is defined and permission checks are not ignored. Passes all provided arguments to the interface's `create` method.
|
98
|
-
|
99
|
+
|
99
100
|
Parameters:
|
100
101
|
creator_id (int | None): Optional identifier for the creator of the object.
|
101
102
|
history_comment (str | None): Optional comment for audit or history tracking.
|
102
103
|
ignore_permission (bool): If True, skips the permission check.
|
103
|
-
|
104
|
+
|
104
105
|
Returns:
|
105
106
|
GeneralManager[GeneralManagerType, InterfaceType]: A new manager instance for the created object.
|
106
107
|
"""
|
107
|
-
|
108
|
-
|
109
|
-
Permission.checkCreatePermission(kwargs, cls, creator_id)
|
108
|
+
if not ignore_permission:
|
109
|
+
cls.Permission.checkCreatePermission(kwargs, cls, creator_id)
|
110
110
|
identification = cls.Interface.create(
|
111
111
|
creator_id=creator_id, history_comment=history_comment, **kwargs
|
112
112
|
)
|
@@ -122,19 +122,18 @@ class GeneralManager(
|
|
122
122
|
) -> GeneralManager[GeneralManagerType, InterfaceType]:
|
123
123
|
"""
|
124
124
|
Update the underlying interface object with new data and return a new manager instance.
|
125
|
-
|
125
|
+
|
126
126
|
Parameters:
|
127
127
|
creator_id (int | None): Optional identifier for the user performing the update.
|
128
128
|
history_comment (str | None): Optional comment describing the update.
|
129
129
|
ignore_permission (bool): If True, skips permission checks.
|
130
130
|
**kwargs: Additional fields to update on the interface object.
|
131
|
-
|
131
|
+
|
132
132
|
Returns:
|
133
133
|
GeneralManager[GeneralManagerType, InterfaceType]: A new manager instance reflecting the updated object.
|
134
134
|
"""
|
135
|
-
|
136
|
-
|
137
|
-
Permission.checkUpdatePermission(kwargs, self, creator_id)
|
135
|
+
if not ignore_permission:
|
136
|
+
self.Permission.checkUpdatePermission(kwargs, self, creator_id)
|
138
137
|
self._interface.update(
|
139
138
|
creator_id=creator_id,
|
140
139
|
history_comment=history_comment,
|
@@ -151,18 +150,17 @@ class GeneralManager(
|
|
151
150
|
) -> GeneralManager[GeneralManagerType, InterfaceType]:
|
152
151
|
"""
|
153
152
|
Deactivates the underlying interface object and returns a new manager instance.
|
154
|
-
|
153
|
+
|
155
154
|
Parameters:
|
156
155
|
creator_id (int | None): Optional identifier for the user performing the deactivation.
|
157
156
|
history_comment (str | None): Optional comment describing the reason for deactivation.
|
158
157
|
ignore_permission (bool): If True, skips permission checks.
|
159
|
-
|
158
|
+
|
160
159
|
Returns:
|
161
160
|
GeneralManager[GeneralManagerType, InterfaceType]: A new instance representing the deactivated object.
|
162
161
|
"""
|
163
|
-
|
164
|
-
|
165
|
-
Permission.checkDeletePermission(self, creator_id)
|
162
|
+
if not ignore_permission:
|
163
|
+
self.Permission.checkDeletePermission(self, creator_id)
|
166
164
|
self._interface.deactivate(
|
167
165
|
creator_id=creator_id, history_comment=history_comment
|
168
166
|
)
|
@@ -190,12 +188,12 @@ class GeneralManager(
|
|
190
188
|
def __parse_identification(kwargs: dict[str, Any]) -> dict[str, Any] | None:
|
191
189
|
"""
|
192
190
|
Return a dictionary with all GeneralManager instances in the input replaced by their identification dictionaries.
|
193
|
-
|
191
|
+
|
194
192
|
For each key-value pair in the input, any GeneralManager instance is replaced by its identification. Lists and tuples are processed recursively, substituting contained GeneralManager instances with their identifications. Returns None if the resulting dictionary is empty.
|
195
|
-
|
193
|
+
|
196
194
|
Parameters:
|
197
195
|
kwargs (dict[str, Any]): Dictionary to process.
|
198
|
-
|
196
|
+
|
199
197
|
Returns:
|
200
198
|
dict[str, Any] | None: Processed dictionary with identifications, or None if empty.
|
201
199
|
"""
|
general_manager/manager/meta.py
CHANGED
@@ -26,9 +26,9 @@ class GeneralManagerMeta(type):
|
|
26
26
|
def __new__(mcs, name: str, bases: tuple[type, ...], attrs: dict[str, Any]) -> type:
|
27
27
|
"""
|
28
28
|
Creates a new class using the metaclass, integrating interface hooks and registering the class for attribute initialization and tracking.
|
29
|
-
|
29
|
+
|
30
30
|
If the class definition includes an `Interface` attribute, validates it as a subclass of `InterfaceBase`, applies pre- and post-creation hooks from the interface, and registers the resulting class for attribute initialization and management. Regardless of interface presence, the new class is tracked for pending GraphQL interface creation.
|
31
|
-
|
31
|
+
|
32
32
|
Returns:
|
33
33
|
The newly created class, potentially augmented with interface integration and registration logic.
|
34
34
|
"""
|
@@ -38,7 +38,7 @@ class GeneralManagerMeta(type):
|
|
38
38
|
) -> Type[GeneralManager]:
|
39
39
|
"""
|
40
40
|
Create a new GeneralManager class using the standard metaclass instantiation process.
|
41
|
-
|
41
|
+
|
42
42
|
Returns:
|
43
43
|
The newly created GeneralManager subclass.
|
44
44
|
"""
|
@@ -59,6 +59,7 @@ class GeneralManagerMeta(type):
|
|
59
59
|
|
60
60
|
else:
|
61
61
|
new_class = createNewGeneralManagerClass(mcs, name, bases, attrs)
|
62
|
+
|
62
63
|
if getattr(settings, "AUTOCREATE_GRAPHQL", False):
|
63
64
|
mcs.pending_graphql_interfaces.append(new_class)
|
64
65
|
|
@@ -70,7 +71,7 @@ class GeneralManagerMeta(type):
|
|
70
71
|
):
|
71
72
|
"""
|
72
73
|
Dynamically assigns property descriptors to a class for each specified attribute name.
|
73
|
-
|
74
|
+
|
74
75
|
For each attribute, creates a descriptor that:
|
75
76
|
- Returns the field type from the class's interface when accessed on the class.
|
76
77
|
- Retrieves the value from the instance's `_attributes` dictionary when accessed on an instance.
|
@@ -81,9 +82,10 @@ class GeneralManagerMeta(type):
|
|
81
82
|
def desciptorMethod(attr_name: str, new_class: type):
|
82
83
|
"""
|
83
84
|
Creates a property descriptor for an attribute, enabling dynamic access and callable resolution.
|
84
|
-
|
85
|
+
|
85
86
|
When accessed on the class, returns the field type from the associated interface. When accessed on an instance, retrieves the attribute value from the instance's `_attributes` dictionary, invoking it with the instance's interface if the value is callable. Raises `AttributeError` if the attribute is missing or if a callable attribute raises an exception.
|
86
87
|
"""
|
88
|
+
|
87
89
|
class Descriptor(Generic[GeneralManagerType]):
|
88
90
|
def __init__(self, attr_name: str, new_class: Type[GeneralManager]):
|
89
91
|
self.attr_name = attr_name
|
@@ -22,12 +22,12 @@ for currency in currency_units:
|
|
22
22
|
class Measurement:
|
23
23
|
def __init__(self, value: Decimal | float | int | str, unit: str):
|
24
24
|
"""
|
25
|
-
|
26
|
-
|
25
|
+
Create a Measurement with the specified numeric value and unit.
|
26
|
+
|
27
27
|
Parameters:
|
28
|
-
value
|
29
|
-
unit
|
30
|
-
|
28
|
+
value: The numeric value to associate with the unit. Accepts Decimal, float, int, or a string convertible to Decimal.
|
29
|
+
unit: The unit of measurement as a string.
|
30
|
+
|
31
31
|
Raises:
|
32
32
|
TypeError: If the value cannot be converted to a Decimal.
|
33
33
|
"""
|
@@ -42,10 +42,10 @@ class Measurement:
|
|
42
42
|
|
43
43
|
def __getstate__(self):
|
44
44
|
"""
|
45
|
-
Return a serializable state
|
46
|
-
|
45
|
+
Return a dictionary representing the serializable state of the measurement, including its magnitude and unit as strings.
|
46
|
+
|
47
47
|
Returns:
|
48
|
-
dict:
|
48
|
+
dict: Contains 'magnitude' and 'unit' keys for serialization purposes.
|
49
49
|
"""
|
50
50
|
state = {
|
51
51
|
"magnitude": str(self.magnitude),
|
@@ -55,10 +55,10 @@ class Measurement:
|
|
55
55
|
|
56
56
|
def __setstate__(self, state):
|
57
57
|
"""
|
58
|
-
Restore the Measurement object from a serialized state
|
59
|
-
|
58
|
+
Restore the Measurement object from a serialized state.
|
59
|
+
|
60
60
|
Parameters:
|
61
|
-
state (dict):
|
61
|
+
state (dict): Dictionary with 'magnitude' (as a string) and 'unit' (as a string) representing the measurement.
|
62
62
|
"""
|
63
63
|
value = Decimal(state["magnitude"])
|
64
64
|
unit = state["unit"]
|
@@ -82,16 +82,15 @@ class Measurement:
|
|
82
82
|
@classmethod
|
83
83
|
def from_string(cls, value: str) -> Measurement:
|
84
84
|
"""
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
85
|
+
Creates a Measurement instance from a string containing a numeric value and a unit.
|
86
|
+
|
87
|
+
If the string contains only a value, it is treated as dimensionless. If the string contains both a value and a unit separated by a space, both are used to construct the Measurement. Raises ValueError if the format is invalid or the value cannot be parsed.
|
88
|
+
|
90
89
|
Returns:
|
91
|
-
Measurement: The
|
92
|
-
|
90
|
+
Measurement: The constructed Measurement object.
|
91
|
+
|
93
92
|
Raises:
|
94
|
-
ValueError: If the
|
93
|
+
ValueError: If the string format is invalid or the value cannot be parsed as a number.
|
95
94
|
"""
|
96
95
|
splitted = value.split(" ")
|
97
96
|
if len(splitted) == 1:
|
@@ -118,19 +117,19 @@ class Measurement:
|
|
118
117
|
|
119
118
|
def to(self, target_unit: str, exchange_rate: float | None = None):
|
120
119
|
"""
|
121
|
-
Convert
|
122
|
-
|
123
|
-
For currency
|
124
|
-
|
120
|
+
Convert this measurement to a specified target unit, supporting both currency and physical unit conversions.
|
121
|
+
|
122
|
+
For currency conversions between different currencies, an explicit exchange rate must be provided; if converting to the same currency, the original measurement is returned. For physical units, standard unit conversion is performed using the unit registry.
|
123
|
+
|
125
124
|
Parameters:
|
126
|
-
target_unit (str): The unit to convert
|
127
|
-
exchange_rate (float, optional):
|
128
|
-
|
125
|
+
target_unit (str): The unit to convert to.
|
126
|
+
exchange_rate (float, optional): Required for currency conversion between different currencies.
|
127
|
+
|
129
128
|
Returns:
|
130
|
-
Measurement:
|
131
|
-
|
129
|
+
Measurement: The converted measurement in the target unit.
|
130
|
+
|
132
131
|
Raises:
|
133
|
-
ValueError: If converting between different currencies without
|
132
|
+
ValueError: If converting between different currencies without an exchange rate.
|
134
133
|
"""
|
135
134
|
if self.is_currency():
|
136
135
|
if self.unit == ureg(target_unit):
|
@@ -159,10 +158,10 @@ class Measurement:
|
|
159
158
|
|
160
159
|
def __add__(self, other: Any) -> Measurement:
|
161
160
|
"""
|
162
|
-
Add
|
163
|
-
|
164
|
-
Addition is
|
165
|
-
|
161
|
+
Add this measurement to another, supporting both currency and physical units.
|
162
|
+
|
163
|
+
Addition is permitted only if both operands are currencies of the same unit or both are physical units with compatible dimensions. Raises a TypeError if operands are of different types (currency vs. physical unit) or not Measurement instances, and raises a ValueError if units are incompatible.
|
164
|
+
|
166
165
|
Returns:
|
167
166
|
Measurement: A new Measurement representing the sum.
|
168
167
|
"""
|
@@ -198,11 +197,11 @@ class Measurement:
|
|
198
197
|
def __sub__(self, other: Any) -> Measurement:
|
199
198
|
"""
|
200
199
|
Subtracts another Measurement from this one, enforcing unit compatibility.
|
201
|
-
|
202
|
-
Subtraction is
|
203
|
-
|
200
|
+
|
201
|
+
Subtraction is permitted only between two currency measurements of the same unit or two physical measurements with compatible dimensions. Raises a TypeError if the operand is not a Measurement or if subtracting between a currency and a physical unit. Raises a ValueError if subtracting different currencies or incompatible physical units.
|
202
|
+
|
204
203
|
Returns:
|
205
|
-
Measurement:
|
204
|
+
Measurement: A new Measurement representing the result of the subtraction.
|
206
205
|
"""
|
207
206
|
if not isinstance(other, Measurement):
|
208
207
|
raise TypeError(
|
@@ -229,15 +228,15 @@ class Measurement:
|
|
229
228
|
|
230
229
|
def __mul__(self, other: Any) -> Measurement:
|
231
230
|
"""
|
232
|
-
|
233
|
-
|
234
|
-
Multiplication between two currency measurements is not
|
235
|
-
|
231
|
+
Returns the product of this measurement and another measurement or numeric value.
|
232
|
+
|
233
|
+
Multiplication between two currency measurements is not permitted. When multiplied by another measurement, the result combines their units. When multiplied by a numeric value, the magnitude is scaled and the unit remains unchanged.
|
234
|
+
|
236
235
|
Returns:
|
237
|
-
Measurement:
|
238
|
-
|
236
|
+
Measurement: A new Measurement representing the result.
|
237
|
+
|
239
238
|
Raises:
|
240
|
-
TypeError: If
|
239
|
+
TypeError: If multiplying two currency measurements or if the operand is neither a Measurement nor a numeric value.
|
241
240
|
"""
|
242
241
|
if isinstance(other, Measurement):
|
243
242
|
if self.is_currency() or other.is_currency():
|
@@ -261,15 +260,22 @@ class Measurement:
|
|
261
260
|
def __truediv__(self, other: Any) -> Measurement:
|
262
261
|
"""
|
263
262
|
Divide this measurement by another measurement or a numeric value.
|
264
|
-
|
265
|
-
If dividing by another `Measurement
|
266
|
-
|
263
|
+
|
264
|
+
If dividing by another `Measurement`:
|
265
|
+
- Division between two *different* currencies is disallowed (raises TypeError).
|
266
|
+
- Division between the *same* currency is allowed and yields a dimensionless result.
|
267
|
+
Returns a new `Measurement` with the resulting value and unit.
|
268
|
+
|
267
269
|
Raises:
|
268
|
-
TypeError: If dividing two currency measurements, or if the operand is not a `Measurement` or numeric value.
|
270
|
+
TypeError: If dividing two currency measurements with different units, or if the operand is not a `Measurement` or numeric value.
|
271
|
+
Returns:
|
272
|
+
Measurement: The result of the division as a new `Measurement` instance.
|
269
273
|
"""
|
270
274
|
if isinstance(other, Measurement):
|
271
|
-
if self.is_currency() and other.is_currency():
|
272
|
-
raise TypeError(
|
275
|
+
if self.is_currency() and other.is_currency() and self.unit != other.unit:
|
276
|
+
raise TypeError(
|
277
|
+
"Division between two different currency amounts is not allowed."
|
278
|
+
)
|
273
279
|
result_quantity = self.quantity / other.quantity
|
274
280
|
return Measurement(
|
275
281
|
Decimal(str(result_quantity.magnitude)), str(result_quantity.units)
|
@@ -300,14 +306,14 @@ class Measurement:
|
|
300
306
|
|
301
307
|
def _compare(self, other: Any, operation: Callable[..., bool]) -> bool:
|
302
308
|
"""
|
303
|
-
|
304
|
-
|
305
|
-
If `other` is a string, it is parsed
|
306
|
-
|
309
|
+
Compares this Measurement to another using a specified comparison operation.
|
310
|
+
|
311
|
+
If `other` is a string, it is parsed as a Measurement. The comparison is performed after converting `other` to this instance's unit. Raises a TypeError if `other` is not a Measurement or a valid string, and a ValueError if the measurements have incompatible dimensions.
|
312
|
+
|
307
313
|
Parameters:
|
308
314
|
other: The object to compare, either a Measurement or a string in the format "value unit".
|
309
315
|
operation: A callable that takes two magnitudes and returns a boolean.
|
310
|
-
|
316
|
+
|
311
317
|
Returns:
|
312
318
|
bool: The result of the comparison.
|
313
319
|
"""
|
@@ -348,16 +354,16 @@ class Measurement:
|
|
348
354
|
|
349
355
|
def __ge__(self, other: Any) -> bool:
|
350
356
|
"""
|
351
|
-
Return True if this measurement is greater than or equal to another measurement.
|
352
|
-
|
353
|
-
The comparison
|
357
|
+
Return True if this measurement is greater than or equal to another measurement or compatible value.
|
358
|
+
|
359
|
+
The comparison converts the other operand to this measurement's unit before evaluating. Raises TypeError if the operand is not a Measurement or convertible string, or ValueError if units are incompatible.
|
354
360
|
"""
|
355
361
|
return self._compare(other, ge)
|
356
362
|
|
357
363
|
def __hash__(self) -> int:
|
358
364
|
"""
|
359
|
-
Return a hash
|
360
|
-
|
361
|
-
Enables
|
365
|
+
Return a hash based on the measurement's magnitude and unit.
|
366
|
+
|
367
|
+
Enables Measurement instances to be used in hash-based collections.
|
362
368
|
"""
|
363
369
|
return hash((self.magnitude, str(self.unit)))
|
@@ -1,21 +1,21 @@
|
|
1
1
|
import inspect
|
2
2
|
import json
|
3
|
-
from general_manager.
|
3
|
+
from general_manager.utils.jsonEncoder import CustomJSONEncoder
|
4
4
|
from hashlib import sha256
|
5
5
|
|
6
6
|
|
7
7
|
def make_cache_key(func, args, kwargs):
|
8
8
|
"""
|
9
9
|
Generates a unique, deterministic cache key for a specific function call.
|
10
|
-
|
10
|
+
|
11
11
|
The key is derived from the function's module, qualified name, and bound arguments,
|
12
12
|
serialized to JSON and hashed with SHA-256 to ensure uniqueness for each call signature.
|
13
|
-
|
13
|
+
|
14
14
|
Args:
|
15
15
|
func: The target function to be identified.
|
16
16
|
args: Positional arguments for the function call.
|
17
17
|
kwargs: Keyword arguments for the function call.
|
18
|
-
|
18
|
+
|
19
19
|
Returns:
|
20
20
|
A hexadecimal SHA-256 hash string uniquely representing the function call.
|
21
21
|
"""
|
general_manager/utils/testing.py
CHANGED
@@ -10,6 +10,14 @@ from general_manager.api.graphql import GraphQL
|
|
10
10
|
from django.apps import apps as global_apps
|
11
11
|
|
12
12
|
|
13
|
+
from unittest.mock import ANY
|
14
|
+
from general_manager.cache.cacheDecorator import _SENTINEL
|
15
|
+
|
16
|
+
|
17
|
+
from django.test import override_settings
|
18
|
+
from django.core.cache import caches
|
19
|
+
from django.core.cache.backends.locmem import LocMemCache
|
20
|
+
|
13
21
|
_original_get_app = global_apps.get_containing_app_config
|
14
22
|
|
15
23
|
|
@@ -61,9 +69,9 @@ class GMTestCaseMeta(type):
|
|
61
69
|
|
62
70
|
def __new__(mcs, name, bases, attrs):
|
63
71
|
"""
|
64
|
-
Creates a new test case class with a customized setUpClass
|
65
|
-
|
66
|
-
The generated setUpClass
|
72
|
+
Creates a new test case class with a customized setUpClass method for GeneralManager and GraphQL integration tests.
|
73
|
+
|
74
|
+
The generated setUpClass ensures the test environment is properly initialized by resetting GraphQL registries, applying any user-defined setup, clearing default GraphQL URL patterns, creating missing database tables for specified GeneralManager models and their history, initializing GeneralManager and GraphQL configurations, and invoking the base GraphQLTransactionTestCase setup.
|
67
75
|
"""
|
68
76
|
user_setup = attrs.get("setUpClass")
|
69
77
|
fallback_app = attrs.get("fallback_app", "general_manager")
|
@@ -72,9 +80,9 @@ class GMTestCaseMeta(type):
|
|
72
80
|
|
73
81
|
def wrapped_setUpClass(cls):
|
74
82
|
"""
|
75
|
-
Performs
|
76
|
-
|
77
|
-
|
83
|
+
Performs setup for a test case class by resetting GraphQL internals, configuring fallback app lookup, clearing default GraphQL URL patterns, ensuring database tables exist for specified GeneralManager models and their history, initializing GeneralManager and GraphQL configurations, and invoking the base test case setup.
|
84
|
+
|
85
|
+
Skips database table creation for any GeneralManager class lacking an `Interface` or `_model` attribute.
|
78
86
|
"""
|
79
87
|
GraphQL._query_class = None
|
80
88
|
GraphQL._mutation_class = None
|
@@ -97,6 +105,10 @@ class GMTestCaseMeta(type):
|
|
97
105
|
existing = connection.introspection.table_names()
|
98
106
|
with connection.schema_editor() as editor:
|
99
107
|
for manager_class in cls.general_manager_classes:
|
108
|
+
if not hasattr(manager_class, "Interface") or not hasattr(
|
109
|
+
manager_class.Interface, "_model"
|
110
|
+
):
|
111
|
+
continue
|
100
112
|
model_class = cast(
|
101
113
|
type[models.Model], manager_class.Interface._model # type: ignore
|
102
114
|
)
|
@@ -116,9 +128,105 @@ class GMTestCaseMeta(type):
|
|
116
128
|
return super().__new__(mcs, name, bases, attrs)
|
117
129
|
|
118
130
|
|
131
|
+
class LoggingCache(LocMemCache):
|
132
|
+
def __init__(self, *args, **kwargs):
|
133
|
+
"""
|
134
|
+
Initialize the LoggingCache and set up an empty list to record cache operations.
|
135
|
+
"""
|
136
|
+
super().__init__(*args, **kwargs)
|
137
|
+
self.ops = []
|
138
|
+
|
139
|
+
def get(self, key, default=None, version=None):
|
140
|
+
"""
|
141
|
+
Retrieve a value from the cache and log whether it was a cache hit or miss.
|
142
|
+
|
143
|
+
Parameters:
|
144
|
+
key (str): The cache key to retrieve.
|
145
|
+
default: The value to return if the key is not found.
|
146
|
+
version: Optional cache version.
|
147
|
+
|
148
|
+
Returns:
|
149
|
+
The cached value if found; otherwise, the default value.
|
150
|
+
"""
|
151
|
+
val = super().get(key, default)
|
152
|
+
self.ops.append(("get", key, val is not _SENTINEL))
|
153
|
+
return val
|
154
|
+
|
155
|
+
def set(self, key, value, timeout=None, version=None):
|
156
|
+
"""
|
157
|
+
Store a value in the cache and log the set operation.
|
158
|
+
|
159
|
+
Parameters:
|
160
|
+
key (str): The cache key to set.
|
161
|
+
value (Any): The value to store in the cache.
|
162
|
+
timeout (Optional[int]): The cache timeout in seconds.
|
163
|
+
version (Optional[int]): The cache version (unused).
|
164
|
+
"""
|
165
|
+
super().set(key, value, timeout)
|
166
|
+
self.ops.append(("set", key))
|
167
|
+
|
168
|
+
|
169
|
+
@override_settings(
|
170
|
+
CACHES={
|
171
|
+
"default": {
|
172
|
+
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
|
173
|
+
"LOCATION": "test-cache",
|
174
|
+
}
|
175
|
+
}
|
176
|
+
)
|
119
177
|
class GeneralManagerTransactionTestCase(
|
120
178
|
GraphQLTransactionTestCase, metaclass=GMTestCaseMeta
|
121
179
|
):
|
122
180
|
general_manager_classes: list[type[GeneralManager]] = []
|
123
181
|
read_only_classes: list[type[GeneralManager]] = []
|
124
182
|
fallback_app: str | None = "general_manager"
|
183
|
+
|
184
|
+
def setUp(self) -> None:
|
185
|
+
"""
|
186
|
+
Prepares the test environment by replacing the default cache with a LoggingCache and resetting the cache operations log.
|
187
|
+
"""
|
188
|
+
super().setUp()
|
189
|
+
setattr(caches._connections, "default", LoggingCache("test-cache", {})) # type: ignore
|
190
|
+
self.__resetCacheCounter()
|
191
|
+
|
192
|
+
#
|
193
|
+
def assertCacheMiss(self):
|
194
|
+
"""
|
195
|
+
Assert that a cache miss occurred, followed by a cache set operation.
|
196
|
+
|
197
|
+
Checks that the cache's `get` method was called and did not find a value, and that the `set` method was subsequently called to store a value. Resets the cache operation log after the assertion.
|
198
|
+
"""
|
199
|
+
ops = getattr(caches["default"], "ops")
|
200
|
+
self.assertIn(
|
201
|
+
("get", ANY, False),
|
202
|
+
ops,
|
203
|
+
"Cache.get should have been called and found nothing",
|
204
|
+
)
|
205
|
+
self.assertIn(("set", ANY), ops, "Cache.set should have stored the value")
|
206
|
+
self.__resetCacheCounter()
|
207
|
+
|
208
|
+
def assertCacheHit(self):
|
209
|
+
"""
|
210
|
+
Assert that a cache get operation resulted in a cache hit and no cache set operation occurred.
|
211
|
+
|
212
|
+
Raises an assertion error if the cache did not return a value for a get operation or if a set operation was performed. Resets the cache operation log after the check.
|
213
|
+
"""
|
214
|
+
ops = getattr(caches["default"], "ops")
|
215
|
+
self.assertIn(
|
216
|
+
("get", ANY, True),
|
217
|
+
ops,
|
218
|
+
"Cache.get should have been called and found something",
|
219
|
+
)
|
220
|
+
|
221
|
+
self.assertNotIn(
|
222
|
+
("set", ANY),
|
223
|
+
ops,
|
224
|
+
"Cache.set should not have stored anything",
|
225
|
+
)
|
226
|
+
self.__resetCacheCounter()
|
227
|
+
|
228
|
+
def __resetCacheCounter(self):
|
229
|
+
"""
|
230
|
+
Clear the log of cache operations recorded by the LoggingCache instance.
|
231
|
+
"""
|
232
|
+
caches["default"].ops = [] # type: ignore
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: GeneralManager
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.10.1
|
4
4
|
Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
|
5
5
|
Author-email: Tim Kleindick <tkleindick@yahoo.de>
|
6
6
|
License-Expression: MIT
|
@@ -1,21 +1,13 @@
|
|
1
1
|
general_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
general_manager/apps.py,sha256=
|
2
|
+
general_manager/apps.py,sha256=dNzdcOTLs53y7JAGjQ7rqY1VWLtwO43NTTGJhbmbq1s,9906
|
3
3
|
general_manager/api/graphql.py,sha256=ZMzlpgmyn2X9Zwxo12r-b5UZ2R-u1pXys_nhNczxyX8,31622
|
4
|
-
general_manager/api/mutation.py,sha256=
|
4
|
+
general_manager/api/mutation.py,sha256=muoQxXFCSJl4CGWQ9sqmyt4WzHB9H4lqDc0Qgtpg7II,5653
|
5
5
|
general_manager/api/property.py,sha256=oc93p1P8dcIvrNorRuqD1EJVsd6eYttYhZuAS0s28gs,696
|
6
|
-
general_manager/auxiliary/__init__.py,sha256=4IwKJzsNxGduF-Ej0u1BNHVaMhkql8PjHbVtx9DOTSY,76
|
7
|
-
general_manager/auxiliary/argsToKwargs.py,sha256=kmp1xonpQp4X_y8ZJG6c5uOW7zQwo0HtPqsHWVzXRSM,921
|
8
|
-
general_manager/auxiliary/filterParser.py,sha256=wmR4YzsnYgjI2Co5eyvCFROldotAraHx_GiCDJo79IY,5410
|
9
|
-
general_manager/auxiliary/formatString.py,sha256=gZSbsKvpywBmf5bIx6YW43LmNJcqsCP7ZfrB7YPvaFo,1436
|
10
|
-
general_manager/auxiliary/jsonEncoder.py,sha256=TDsgFQvheITHZgdmn-m8tk1_QCzpT0XwEHo7bY3Qe-M,638
|
11
|
-
general_manager/auxiliary/makeCacheKey.py,sha256=lczutqxlofLSUnheKRi8nazhOyPa04TZOFNxNn5vDu4,1126
|
12
|
-
general_manager/auxiliary/noneToZero.py,sha256=KfQtMQnrT6vsYST0K7lv6pVujkDcK3XL8czHYOhgqKQ,539
|
13
|
-
general_manager/auxiliary/pathMapping.py,sha256=nrz5owQg2a69Yig1eCXorR9U0NSw7NmBAk5OkeoHTdA,6842
|
14
6
|
general_manager/bucket/baseBucket.py,sha256=65oQbSE6C8TE0yVPP_Aoi_Fwq0Uo2TLVPWMG6l2qxyQ,7836
|
15
|
-
general_manager/bucket/calculationBucket.py,sha256=
|
7
|
+
general_manager/bucket/calculationBucket.py,sha256=7AjoqdTOrbUCfWu7kZDjtu7vaFFJ7SPHELvtOZ8wfQg,18578
|
16
8
|
general_manager/bucket/databaseBucket.py,sha256=V_xiPa8ErnPHVh_-i-oaH8qCa818UJxm5CVl80SVc1U,9060
|
17
9
|
general_manager/bucket/groupBucket.py,sha256=55QdUaH_qO1JFJ2Jc1f2WcxniiLE5LB3vNwbnksKk8A,10939
|
18
|
-
general_manager/cache/cacheDecorator.py,sha256=
|
10
|
+
general_manager/cache/cacheDecorator.py,sha256=XQvs322lDDafecS6osPKmf7DyuZgDq8kuQaYpMeXXYg,3264
|
19
11
|
general_manager/cache/cacheTracker.py,sha256=rRw3OhBDf86hTC2Xbt1ocRgZqwu8_kXk4lczamcADFg,2955
|
20
12
|
general_manager/cache/dependencyIndex.py,sha256=lxD7IfnWVsBNt9l0_yDfJlHDRHAFC7N7p-Typ2tJp88,11044
|
21
13
|
general_manager/cache/modelDependencyCollector.py,sha256=lIqBvo0QygoxxZPJ32_vMs_-eJaVJDznGyrEmxPV41E,2436
|
@@ -25,19 +17,19 @@ general_manager/factory/autoFactory.py,sha256=WBhSuMVsxkPAPLhlZhYXwHVIqiomUveS7v
|
|
25
17
|
general_manager/factory/factories.py,sha256=F6_nYFyJRYYc3LQApfoVFdctfLzsWUDHKafn6xjckB8,7224
|
26
18
|
general_manager/factory/factoryMethods.py,sha256=9Bag891j0XHe3dUBAFi7gUKcKeUwcBZN3cDLBobyBiI,3225
|
27
19
|
general_manager/interface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
28
|
-
general_manager/interface/baseInterface.py,sha256=
|
20
|
+
general_manager/interface/baseInterface.py,sha256=IWEtY--IuFiB3cjrCgtyXBTMpAPTayiFDnRQY7JZeaw,8600
|
29
21
|
general_manager/interface/calculationInterface.py,sha256=Kg_OqLw67tcLwdzYNLq31eKVLzkM7taw-8Mzmk0CYi0,4232
|
30
|
-
general_manager/interface/databaseBasedInterface.py,sha256=
|
31
|
-
general_manager/interface/databaseInterface.py,sha256=
|
22
|
+
general_manager/interface/databaseBasedInterface.py,sha256=OpKHpN509mcS86q3WgyAp7gQBwEqmA3BQPCFgF5q_Ig,20686
|
23
|
+
general_manager/interface/databaseInterface.py,sha256=rhKVXhg0ztdIxKikTWtgjrkA7cwZTOYlEsRh0RWajDQ,6732
|
32
24
|
general_manager/interface/models.py,sha256=gGYW5f1AUBpBakV3O0qsZwqMiWxZGdKRYXWaCBjt1oI,3334
|
33
25
|
general_manager/interface/readOnlyInterface.py,sha256=TkfbOeaa2wCq5kCv0a3IwJWcYOTVbtNsdNWmGAz0Mns,11217
|
34
26
|
general_manager/manager/__init__.py,sha256=l3RYp62aEhj3Y975_XUTIzo35LUnkTJHkb_hgChnXXI,111
|
35
|
-
general_manager/manager/generalManager.py,sha256=
|
27
|
+
general_manager/manager/generalManager.py,sha256=jzLLAZTKgUOrzzOTXiWQsYj0FQk6Md8ggIQz0ie44qo,8793
|
36
28
|
general_manager/manager/groupManager.py,sha256=8dpZUfm7aFL4lraUWv4qbbDRClQZaYxw4prclhBZYZs,4367
|
37
29
|
general_manager/manager/input.py,sha256=-pJXGJ-g2-OxZfl4Buj3mQkf05fN4p8MsR2Lh9BQcEo,3208
|
38
|
-
general_manager/manager/meta.py,sha256=
|
30
|
+
general_manager/manager/meta.py,sha256=EruJhpaYA1glWvb07HuTPAL919K3z7clU5_x2dG0ryA,5585
|
39
31
|
general_manager/measurement/__init__.py,sha256=X97meFujBldE5v0WMF7SmKeGpC5R0JTczfLo_Lq1Xek,84
|
40
|
-
general_manager/measurement/measurement.py,sha256=
|
32
|
+
general_manager/measurement/measurement.py,sha256=emd1z3IDClK7nrIrUL_JXPxNhJEq2HnXq6qNYv9ubcM,16090
|
41
33
|
general_manager/measurement/measurementField.py,sha256=iq9Hqe6ZGX8CxXm4nIqTAWTRkQVptzpqE9ExX-jFyNs,5928
|
42
34
|
general_manager/permission/__init__.py,sha256=5UlDERN60Vn8obGVkT-cOM8kHjzmoxgK5w5FgTCDhGE,59
|
43
35
|
general_manager/permission/basePermission.py,sha256=14iKo6qVmaUdg1sAz-gSZyNtpVKAAapIhutVAMDf93c,6056
|
@@ -48,9 +40,17 @@ general_manager/permission/permissionDataManager.py,sha256=Ji7fsnuaKTa6M8yzCGyzr
|
|
48
40
|
general_manager/rule/__init__.py,sha256=4Har5cfPD1fmOsilTDod-ZUz3Com-tkl58jz7yY4fD0,23
|
49
41
|
general_manager/rule/handler.py,sha256=z8SFHTIZ0LbLh3fV56Mud0V4_OvWkqJjlHvFqau7Qfk,7334
|
50
42
|
general_manager/rule/rule.py,sha256=3FVCKGL7BTVoStdgOTdWQwuoVRIxAIAilV4VOzouDpc,10759
|
51
|
-
general_manager/utils/
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
43
|
+
general_manager/utils/__init__.py,sha256=4IwKJzsNxGduF-Ej0u1BNHVaMhkql8PjHbVtx9DOTSY,76
|
44
|
+
general_manager/utils/argsToKwargs.py,sha256=kmp1xonpQp4X_y8ZJG6c5uOW7zQwo0HtPqsHWVzXRSM,921
|
45
|
+
general_manager/utils/filterParser.py,sha256=wmR4YzsnYgjI2Co5eyvCFROldotAraHx_GiCDJo79IY,5410
|
46
|
+
general_manager/utils/formatString.py,sha256=gZSbsKvpywBmf5bIx6YW43LmNJcqsCP7ZfrB7YPvaFo,1436
|
47
|
+
general_manager/utils/jsonEncoder.py,sha256=TDsgFQvheITHZgdmn-m8tk1_QCzpT0XwEHo7bY3Qe-M,638
|
48
|
+
general_manager/utils/makeCacheKey.py,sha256=UlFsxHXgsYy69AAelkF6GDvY4h7AImT2bBn6iD6dvi4,1110
|
49
|
+
general_manager/utils/noneToZero.py,sha256=KfQtMQnrT6vsYST0K7lv6pVujkDcK3XL8czHYOhgqKQ,539
|
50
|
+
general_manager/utils/pathMapping.py,sha256=nrz5owQg2a69Yig1eCXorR9U0NSw7NmBAk5OkeoHTdA,6842
|
51
|
+
general_manager/utils/testing.py,sha256=IdVvrgLuYIvHRgdSj_j9mC-damQ-2ivSJ7Ngguez5vo,9366
|
52
|
+
generalmanager-0.10.1.dist-info/licenses/LICENSE,sha256=YGFm0ieb4KpkMRRt2qnWue6uFh0cUMtobwEBkHwajhc,1450
|
53
|
+
generalmanager-0.10.1.dist-info/METADATA,sha256=1s7xiuMN9uzioICRAl19thEixmRzZPtD70aYJ6psR-w,6206
|
54
|
+
generalmanager-0.10.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
55
|
+
generalmanager-0.10.1.dist-info/top_level.txt,sha256=sTDtExP9ga-YP3h3h42yivUY-A2Q23C2nw6LNKOho4I,16
|
56
|
+
generalmanager-0.10.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|