GeneralManager 0.17.0__py3-none-any.whl → 0.18.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.
Potentially problematic release.
This version of GeneralManager might be problematic. Click here for more details.
- general_manager/__init__.py +11 -1
- general_manager/_types/api.py +0 -1
- general_manager/_types/bucket.py +0 -1
- general_manager/_types/cache.py +0 -1
- general_manager/_types/factory.py +0 -1
- general_manager/_types/general_manager.py +0 -1
- general_manager/_types/interface.py +0 -1
- general_manager/_types/manager.py +0 -1
- general_manager/_types/measurement.py +0 -1
- general_manager/_types/permission.py +0 -1
- general_manager/_types/rule.py +0 -1
- general_manager/_types/utils.py +0 -1
- general_manager/api/__init__.py +13 -1
- general_manager/api/graphql.py +356 -221
- general_manager/api/graphql_subscription_consumer.py +81 -78
- general_manager/api/mutation.py +85 -23
- general_manager/api/property.py +39 -13
- general_manager/apps.py +188 -47
- general_manager/bucket/__init__.py +10 -1
- general_manager/bucket/calculationBucket.py +155 -53
- general_manager/bucket/databaseBucket.py +157 -45
- general_manager/bucket/groupBucket.py +133 -44
- general_manager/cache/__init__.py +10 -1
- general_manager/cache/dependencyIndex.py +143 -45
- general_manager/cache/signals.py +9 -2
- general_manager/factory/__init__.py +10 -1
- general_manager/factory/autoFactory.py +55 -13
- general_manager/factory/factories.py +110 -40
- general_manager/factory/factoryMethods.py +122 -34
- general_manager/interface/__init__.py +10 -1
- general_manager/interface/baseInterface.py +129 -36
- general_manager/interface/calculationInterface.py +35 -18
- general_manager/interface/databaseBasedInterface.py +71 -45
- general_manager/interface/databaseInterface.py +96 -38
- general_manager/interface/models.py +5 -5
- general_manager/interface/readOnlyInterface.py +94 -20
- general_manager/manager/__init__.py +10 -1
- general_manager/manager/generalManager.py +25 -16
- general_manager/manager/groupManager.py +20 -6
- general_manager/manager/meta.py +84 -16
- general_manager/measurement/__init__.py +10 -1
- general_manager/measurement/measurement.py +289 -95
- general_manager/measurement/measurementField.py +42 -31
- general_manager/permission/__init__.py +10 -1
- general_manager/permission/basePermission.py +120 -38
- general_manager/permission/managerBasedPermission.py +72 -21
- general_manager/permission/mutationPermission.py +14 -9
- general_manager/permission/permissionChecks.py +14 -12
- general_manager/permission/permissionDataManager.py +24 -11
- general_manager/permission/utils.py +34 -6
- general_manager/public_api_registry.py +36 -10
- general_manager/rule/__init__.py +10 -1
- general_manager/rule/handler.py +133 -44
- general_manager/rule/rule.py +178 -39
- general_manager/utils/__init__.py +10 -1
- general_manager/utils/argsToKwargs.py +34 -9
- general_manager/utils/filterParser.py +22 -7
- general_manager/utils/formatString.py +1 -0
- general_manager/utils/pathMapping.py +23 -15
- general_manager/utils/public_api.py +33 -2
- general_manager/utils/testing.py +31 -33
- {generalmanager-0.17.0.dist-info → generalmanager-0.18.0.dist-info}/METADATA +2 -1
- generalmanager-0.18.0.dist-info/RECORD +77 -0
- {generalmanager-0.17.0.dist-info → generalmanager-0.18.0.dist-info}/licenses/LICENSE +1 -1
- generalmanager-0.17.0.dist-info/RECORD +0 -77
- {generalmanager-0.17.0.dist-info → generalmanager-0.18.0.dist-info}/WHEEL +0 -0
- {generalmanager-0.17.0.dist-info → generalmanager-0.18.0.dist-info}/top_level.txt +0 -0
|
@@ -36,6 +36,110 @@ class SortedFilters(TypedDict):
|
|
|
36
36
|
input_excludes: dict[str, Any]
|
|
37
37
|
|
|
38
38
|
|
|
39
|
+
class InvalidCalculationInterfaceError(TypeError):
|
|
40
|
+
"""Raised when a CalculationBucket is initialized with a non-CalculationInterface manager."""
|
|
41
|
+
|
|
42
|
+
def __init__(self) -> None:
|
|
43
|
+
"""
|
|
44
|
+
Indicates a manager's interface does not inherit from CalculationInterface.
|
|
45
|
+
|
|
46
|
+
Initializes the exception with the message "CalculationBucket requires a manager whose interface inherits from CalculationInterface."
|
|
47
|
+
"""
|
|
48
|
+
super().__init__(
|
|
49
|
+
"CalculationBucket requires a manager whose interface inherits from CalculationInterface."
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class IncompatibleBucketTypeError(TypeError):
|
|
54
|
+
"""Raised when attempting to combine buckets of different types."""
|
|
55
|
+
|
|
56
|
+
def __init__(self, bucket_type: type, other_type: type) -> None:
|
|
57
|
+
"""
|
|
58
|
+
Initialize the error indicating two bucket types cannot be combined.
|
|
59
|
+
|
|
60
|
+
Parameters:
|
|
61
|
+
bucket_type (type): The first bucket class involved in the attempted combination.
|
|
62
|
+
other_type (type): The second bucket class involved in the attempted combination.
|
|
63
|
+
|
|
64
|
+
Notes:
|
|
65
|
+
The exception message is formatted as "Cannot combine {bucket_type.__name__} with {other_type.__name__}."
|
|
66
|
+
"""
|
|
67
|
+
super().__init__(
|
|
68
|
+
f"Cannot combine {bucket_type.__name__} with {other_type.__name__}."
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class IncompatibleBucketManagerError(TypeError):
|
|
73
|
+
"""Raised when attempting to combine buckets with different manager classes."""
|
|
74
|
+
|
|
75
|
+
def __init__(self, first_manager: type, second_manager: type) -> None:
|
|
76
|
+
"""
|
|
77
|
+
Indicate that two buckets for different manager classes cannot be combined.
|
|
78
|
+
|
|
79
|
+
Parameters:
|
|
80
|
+
first_manager (type): The first manager class involved in the attempted combination.
|
|
81
|
+
second_manager (type): The second manager class involved in the attempted combination.
|
|
82
|
+
|
|
83
|
+
Description:
|
|
84
|
+
The exception message will include the class names of both managers.
|
|
85
|
+
"""
|
|
86
|
+
super().__init__(
|
|
87
|
+
f"Cannot combine buckets for {first_manager.__name__} and {second_manager.__name__}."
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class CyclicDependencyError(ValueError):
|
|
92
|
+
"""Raised when a cyclic dependency is detected in calculation sorting."""
|
|
93
|
+
|
|
94
|
+
def __init__(self, node: str) -> None:
|
|
95
|
+
"""
|
|
96
|
+
Initialize the CyclicDependencyError for a specific node involved in a dependency cycle.
|
|
97
|
+
|
|
98
|
+
Parameters:
|
|
99
|
+
node (str): The identifier of the node where a cycle was detected. The exception message will include this node, e.g. "Cyclic dependency detected: {node}."
|
|
100
|
+
"""
|
|
101
|
+
super().__init__(f"Cyclic dependency detected: {node}.")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class InvalidPossibleValuesError(TypeError):
|
|
105
|
+
"""Raised when an input field provides invalid possible value definitions."""
|
|
106
|
+
|
|
107
|
+
def __init__(self, key_name: str) -> None:
|
|
108
|
+
"""
|
|
109
|
+
Indicate that an input field defines an invalid `possible_values` configuration.
|
|
110
|
+
|
|
111
|
+
Parameters:
|
|
112
|
+
key_name (str): Name of the input field whose `possible_values` configuration is invalid.
|
|
113
|
+
"""
|
|
114
|
+
super().__init__(
|
|
115
|
+
f"Invalid possible_values configuration for input '{key_name}'."
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class MissingCalculationMatchError(ValueError):
|
|
120
|
+
"""Raised when no calculation matches the provided filters."""
|
|
121
|
+
|
|
122
|
+
def __init__(self) -> None:
|
|
123
|
+
"""
|
|
124
|
+
Exception raised when no calculation matches the provided filters.
|
|
125
|
+
|
|
126
|
+
Initializes the exception with the message "No matching calculation found."
|
|
127
|
+
"""
|
|
128
|
+
super().__init__("No matching calculation found.")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class MultipleCalculationMatchError(ValueError):
|
|
132
|
+
"""Raised when more than one calculation matches the provided filters."""
|
|
133
|
+
|
|
134
|
+
def __init__(self) -> None:
|
|
135
|
+
"""
|
|
136
|
+
Error raised when more than one calculation matches the provided filters.
|
|
137
|
+
|
|
138
|
+
Initializes the exception with the message "Multiple matching calculations found."
|
|
139
|
+
"""
|
|
140
|
+
super().__init__("Multiple matching calculations found.")
|
|
141
|
+
|
|
142
|
+
|
|
39
143
|
class CalculationBucket(Bucket[GeneralManagerType]):
|
|
40
144
|
"""Bucket that builds cartesian products of calculation input fields."""
|
|
41
145
|
|
|
@@ -48,20 +152,17 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
|
48
152
|
reverse: bool = False,
|
|
49
153
|
) -> None:
|
|
50
154
|
"""
|
|
51
|
-
|
|
155
|
+
Initialize a CalculationBucket configured to enumerate all valid input combinations for a manager.
|
|
52
156
|
|
|
53
157
|
Parameters:
|
|
54
|
-
manager_class (type[GeneralManagerType]): Manager subclass whose
|
|
55
|
-
filter_definitions (dict[str, dict] | None):
|
|
56
|
-
exclude_definitions (dict[str, dict] | None):
|
|
57
|
-
sort_key (str | tuple[str
|
|
58
|
-
reverse (bool):
|
|
59
|
-
|
|
60
|
-
Returns:
|
|
61
|
-
None
|
|
158
|
+
manager_class (type[GeneralManagerType]): Manager subclass whose Interface must inherit from CalculationInterface.
|
|
159
|
+
filter_definitions (dict[str, dict] | None): Mapping of input/property filter constraints to apply to generated combinations.
|
|
160
|
+
exclude_definitions (dict[str, dict] | None): Mapping of input/property exclude constraints to remove generated combinations.
|
|
161
|
+
sort_key (str | tuple[str] | None): Key name or tuple of key names used to order generated manager combinations.
|
|
162
|
+
reverse (bool): If True, reverse the ordering defined by `sort_key`.
|
|
62
163
|
|
|
63
164
|
Raises:
|
|
64
|
-
|
|
165
|
+
InvalidCalculationInterfaceError: If the manager_class.Interface does not inherit from CalculationInterface.
|
|
65
166
|
"""
|
|
66
167
|
from general_manager.interface.calculationInterface import (
|
|
67
168
|
CalculationInterface,
|
|
@@ -71,9 +172,7 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
|
71
172
|
|
|
72
173
|
interface_class = manager_class.Interface
|
|
73
174
|
if not issubclass(interface_class, CalculationInterface):
|
|
74
|
-
raise
|
|
75
|
-
"CalculationBucket can only be used with CalculationInterface subclasses"
|
|
76
|
-
)
|
|
175
|
+
raise InvalidCalculationInterfaceError()
|
|
77
176
|
self.input_fields = interface_class.input_fields
|
|
78
177
|
self.filter_definitions = (
|
|
79
178
|
{} if filter_definitions is None else filter_definitions
|
|
@@ -148,25 +247,28 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
|
148
247
|
other: Bucket[GeneralManagerType] | GeneralManagerType,
|
|
149
248
|
) -> CalculationBucket[GeneralManagerType]:
|
|
150
249
|
"""
|
|
151
|
-
|
|
250
|
+
Combine this bucket with another bucket or intersect it with a single manager instance.
|
|
152
251
|
|
|
153
252
|
Parameters:
|
|
154
|
-
other
|
|
253
|
+
other: A CalculationBucket or a GeneralManager instance to merge. If a manager instance of the same manager class is given, it is treated as a filter on that manager's identification.
|
|
155
254
|
|
|
156
255
|
Returns:
|
|
157
|
-
CalculationBucket
|
|
256
|
+
A new CalculationBucket representing the constraints common to both operands.
|
|
158
257
|
|
|
159
258
|
Raises:
|
|
160
|
-
|
|
259
|
+
IncompatibleBucketTypeError: If `other` is neither a CalculationBucket nor a compatible manager instance.
|
|
260
|
+
IncompatibleBucketManagerError: If `other` is a CalculationBucket for a different manager class.
|
|
161
261
|
"""
|
|
162
262
|
from general_manager.manager.generalManager import GeneralManager
|
|
163
263
|
|
|
164
264
|
if isinstance(other, GeneralManager) and other.__class__ == self._manager_class:
|
|
165
265
|
return self.__or__(self.filter(id__in=[other.identification]))
|
|
166
266
|
if not isinstance(other, self.__class__):
|
|
167
|
-
raise
|
|
267
|
+
raise IncompatibleBucketTypeError(self.__class__, type(other))
|
|
168
268
|
if self._manager_class != other._manager_class:
|
|
169
|
-
raise
|
|
269
|
+
raise IncompatibleBucketManagerError(
|
|
270
|
+
self._manager_class, other._manager_class
|
|
271
|
+
)
|
|
170
272
|
|
|
171
273
|
combined_filters = {
|
|
172
274
|
key: value
|
|
@@ -396,10 +498,10 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
|
396
498
|
Produce a dependency-respecting order of input fields.
|
|
397
499
|
|
|
398
500
|
Returns:
|
|
399
|
-
list[str]: Input names ordered so each dependency appears before
|
|
501
|
+
list[str]: Input names ordered so each dependency appears before its dependents.
|
|
400
502
|
|
|
401
503
|
Raises:
|
|
402
|
-
|
|
504
|
+
CyclicDependencyError: If the dependency graph contains a cycle; the exception's `node` identifies a node involved in the cycle.
|
|
403
505
|
"""
|
|
404
506
|
from collections import defaultdict
|
|
405
507
|
|
|
@@ -416,22 +518,19 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
|
416
518
|
|
|
417
519
|
def visit(node: str, temp_mark: set[str]) -> None:
|
|
418
520
|
"""
|
|
419
|
-
|
|
521
|
+
Depth-first search helper that orders dependency nodes and detects cycles.
|
|
420
522
|
|
|
421
523
|
Parameters:
|
|
422
|
-
node (str):
|
|
423
|
-
temp_mark (set[str]): Nodes
|
|
424
|
-
|
|
425
|
-
Returns:
|
|
426
|
-
None
|
|
524
|
+
node (str): The input field being visited.
|
|
525
|
+
temp_mark (set[str]): Nodes on the current DFS path used to detect cycles.
|
|
427
526
|
|
|
428
527
|
Raises:
|
|
429
|
-
|
|
528
|
+
CyclicDependencyError: If a cyclic dependency is detected involving `node`.
|
|
430
529
|
"""
|
|
431
530
|
if node in visited:
|
|
432
531
|
return
|
|
433
532
|
if node in temp_mark:
|
|
434
|
-
raise
|
|
533
|
+
raise CyclicDependencyError(node)
|
|
435
534
|
temp_mark.add(node)
|
|
436
535
|
for m in graph.get(node, []):
|
|
437
536
|
visit(m, temp_mark)
|
|
@@ -451,18 +550,18 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
|
451
550
|
) -> Union[Iterable[Any], Bucket[Any]]:
|
|
452
551
|
# Retrieve possible values
|
|
453
552
|
"""
|
|
454
|
-
Resolve
|
|
553
|
+
Resolve potential values for an input field based on the current partial input combination.
|
|
455
554
|
|
|
456
555
|
Parameters:
|
|
457
|
-
key_name (str): Name of the input field.
|
|
458
|
-
input_field (Input): Input definition
|
|
459
|
-
current_combo (dict):
|
|
556
|
+
key_name (str): Name of the input field used for error context.
|
|
557
|
+
input_field (Input): Input definition that may include `possible_values` and `depends_on`.
|
|
558
|
+
current_combo (dict): Partial mapping of already-selected input values required to evaluate dependencies.
|
|
460
559
|
|
|
461
560
|
Returns:
|
|
462
|
-
Iterable[Any] | Bucket[Any]:
|
|
561
|
+
Iterable[Any] | Bucket[Any]: An iterable of allowed values for the input or a Bucket supplying candidate values.
|
|
463
562
|
|
|
464
563
|
Raises:
|
|
465
|
-
|
|
564
|
+
InvalidPossibleValuesError: If the input field's `possible_values` is neither callable nor an iterable/Bucket.
|
|
466
565
|
"""
|
|
467
566
|
if callable(input_field.possible_values):
|
|
468
567
|
depends_on = input_field.depends_on
|
|
@@ -471,7 +570,7 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
|
471
570
|
elif isinstance(input_field.possible_values, (Iterable, Bucket)):
|
|
472
571
|
possible_values = input_field.possible_values
|
|
473
572
|
else:
|
|
474
|
-
raise
|
|
573
|
+
raise InvalidPossibleValuesError(key_name)
|
|
475
574
|
return possible_values
|
|
476
575
|
|
|
477
576
|
def _generate_input_combinations(
|
|
@@ -481,15 +580,15 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
|
481
580
|
excludes: dict[str, dict],
|
|
482
581
|
) -> List[dict[str, Any]]:
|
|
483
582
|
"""
|
|
484
|
-
Generate all valid input
|
|
583
|
+
Generate all valid assignments of input fields that satisfy the provided per-field filters and exclusions.
|
|
485
584
|
|
|
486
585
|
Parameters:
|
|
487
|
-
sorted_inputs (list[str]): Input names
|
|
488
|
-
filters (dict[str, dict]):
|
|
489
|
-
excludes (dict[str, dict]):
|
|
586
|
+
sorted_inputs (list[str]): Input names in dependency-respecting order.
|
|
587
|
+
filters (dict[str, dict]): Per-input filter definitions (may include `filter_funcs` or `filter_kwargs`).
|
|
588
|
+
excludes (dict[str, dict]): Per-input exclusion definitions (may include `filter_funcs` or `filter_kwargs`).
|
|
490
589
|
|
|
491
590
|
Returns:
|
|
492
|
-
list[dict[str, Any]]:
|
|
591
|
+
list[dict[str, Any]]: Completed input-to-value mappings that meet the filters and excludes.
|
|
493
592
|
"""
|
|
494
593
|
|
|
495
594
|
def helper(
|
|
@@ -681,38 +780,39 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
|
681
780
|
|
|
682
781
|
def get(self, **kwargs: Any) -> GeneralManagerType:
|
|
683
782
|
"""
|
|
684
|
-
|
|
783
|
+
Return the single manager instance that matches the provided field filters.
|
|
685
784
|
|
|
686
785
|
Parameters:
|
|
687
|
-
**kwargs (Any):
|
|
786
|
+
**kwargs (Any): Field filters to apply when selecting a calculation (e.g., property or input names mapped to expected values).
|
|
688
787
|
|
|
689
788
|
Returns:
|
|
690
|
-
|
|
789
|
+
The single manager instance that satisfies the provided filters.
|
|
691
790
|
|
|
692
791
|
Raises:
|
|
693
|
-
|
|
792
|
+
MissingCalculationMatchError: If no matching manager exists.
|
|
793
|
+
MultipleCalculationMatchError: If more than one matching manager exists.
|
|
694
794
|
"""
|
|
695
795
|
filtered_bucket = self.filter(**kwargs)
|
|
696
796
|
items = list(filtered_bucket)
|
|
697
797
|
if len(items) == 1:
|
|
698
798
|
return items[0]
|
|
699
799
|
elif len(items) == 0:
|
|
700
|
-
raise
|
|
800
|
+
raise MissingCalculationMatchError()
|
|
701
801
|
else:
|
|
702
|
-
raise
|
|
802
|
+
raise MultipleCalculationMatchError()
|
|
703
803
|
|
|
704
804
|
def sort(
|
|
705
805
|
self, key: str | tuple[str], reverse: bool = False
|
|
706
806
|
) -> CalculationBucket[GeneralManagerType]:
|
|
707
807
|
"""
|
|
708
|
-
|
|
808
|
+
Create a new CalculationBucket configured to order generated combinations by the given attribute key.
|
|
709
809
|
|
|
710
810
|
Parameters:
|
|
711
|
-
key
|
|
712
|
-
reverse
|
|
811
|
+
key: Attribute name or tuple of attribute names to use for ordering generated manager combinations.
|
|
812
|
+
reverse: If True, sort in descending order.
|
|
713
813
|
|
|
714
814
|
Returns:
|
|
715
|
-
CalculationBucket
|
|
815
|
+
A new CalculationBucket configured to sort combinations by the provided key and direction.
|
|
716
816
|
"""
|
|
717
817
|
return CalculationBucket(
|
|
718
818
|
self._manager_class,
|
|
@@ -731,6 +831,8 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
|
731
831
|
"""
|
|
732
832
|
own = self.all()
|
|
733
833
|
own._data = []
|
|
734
|
-
own.
|
|
735
|
-
own.
|
|
834
|
+
own.filter_definitions = {}
|
|
835
|
+
own.exclude_definitions = {}
|
|
836
|
+
own._filters = {}
|
|
837
|
+
own._excludes = {}
|
|
736
838
|
return own
|