GeneralManager 0.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- general_manager/__init__.py +0 -0
- general_manager/api/graphql.py +732 -0
- general_manager/api/mutation.py +143 -0
- general_manager/api/property.py +20 -0
- general_manager/apps.py +83 -0
- general_manager/auxiliary/__init__.py +2 -0
- general_manager/auxiliary/argsToKwargs.py +25 -0
- general_manager/auxiliary/filterParser.py +97 -0
- general_manager/auxiliary/noneToZero.py +12 -0
- general_manager/cache/cacheDecorator.py +72 -0
- general_manager/cache/cacheTracker.py +33 -0
- general_manager/cache/dependencyIndex.py +300 -0
- general_manager/cache/pathMapping.py +151 -0
- general_manager/cache/signals.py +48 -0
- general_manager/factory/__init__.py +5 -0
- general_manager/factory/factories.py +287 -0
- general_manager/factory/lazy_methods.py +38 -0
- general_manager/interface/__init__.py +3 -0
- general_manager/interface/baseInterface.py +308 -0
- general_manager/interface/calculationInterface.py +406 -0
- general_manager/interface/databaseInterface.py +726 -0
- general_manager/manager/__init__.py +3 -0
- general_manager/manager/generalManager.py +136 -0
- general_manager/manager/groupManager.py +288 -0
- general_manager/manager/input.py +48 -0
- general_manager/manager/meta.py +75 -0
- general_manager/measurement/__init__.py +2 -0
- general_manager/measurement/measurement.py +233 -0
- general_manager/measurement/measurementField.py +152 -0
- general_manager/permission/__init__.py +1 -0
- general_manager/permission/basePermission.py +178 -0
- general_manager/permission/fileBasedPermission.py +0 -0
- general_manager/permission/managerBasedPermission.py +171 -0
- general_manager/permission/permissionChecks.py +53 -0
- general_manager/permission/permissionDataManager.py +55 -0
- general_manager/rule/__init__.py +1 -0
- general_manager/rule/handler.py +122 -0
- general_manager/rule/rule.py +313 -0
- generalmanager-0.0.0.dist-info/METADATA +207 -0
- generalmanager-0.0.0.dist-info/RECORD +43 -0
- generalmanager-0.0.0.dist-info/WHEEL +5 -0
- generalmanager-0.0.0.dist-info/licenses/LICENSE +29 -0
- generalmanager-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,171 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import TYPE_CHECKING, Literal, Optional, Dict
|
3
|
+
from general_manager.permission.basePermission import BasePermission
|
4
|
+
|
5
|
+
if TYPE_CHECKING:
|
6
|
+
from general_manager.permission.permissionDataManager import (
|
7
|
+
PermissionDataManager,
|
8
|
+
)
|
9
|
+
from general_manager.manager.generalManager import GeneralManager
|
10
|
+
from django.contrib.auth.models import AbstractUser
|
11
|
+
|
12
|
+
type permission_type = Literal[
|
13
|
+
"create",
|
14
|
+
"read",
|
15
|
+
"update",
|
16
|
+
"delete",
|
17
|
+
]
|
18
|
+
|
19
|
+
|
20
|
+
class ManagerBasedPermission(BasePermission):
|
21
|
+
__based_on__: Optional[str] = None
|
22
|
+
__read__: list[str] = ["public"]
|
23
|
+
__create__: list[str] = ["isAuthenticated"]
|
24
|
+
__update__: list[str] = ["isAuthenticated"]
|
25
|
+
__delete__: list[str] = ["isAuthenticated"]
|
26
|
+
|
27
|
+
def __init__(
|
28
|
+
self,
|
29
|
+
instance: PermissionDataManager | GeneralManager,
|
30
|
+
request_user: AbstractUser,
|
31
|
+
) -> None:
|
32
|
+
|
33
|
+
self.__instance = instance
|
34
|
+
self.__request_user = request_user
|
35
|
+
self.__attribute_permissions = self.__getAttributePermissions()
|
36
|
+
self.__based_on_permission = self.__getBasedOnPermission()
|
37
|
+
self.__overall_results: Dict[permission_type, Optional[bool]] = {
|
38
|
+
"create": None,
|
39
|
+
"read": None,
|
40
|
+
"update": None,
|
41
|
+
"delete": None,
|
42
|
+
}
|
43
|
+
|
44
|
+
def __getBasedOnPermission(self) -> Optional[BasePermission]:
|
45
|
+
from general_manager.manager.generalManager import GeneralManager
|
46
|
+
|
47
|
+
__based_on__ = getattr(self, "__based_on__")
|
48
|
+
if __based_on__ is None:
|
49
|
+
return None
|
50
|
+
|
51
|
+
basis_object = getattr(self.instance, __based_on__, None)
|
52
|
+
if basis_object is None:
|
53
|
+
raise ValueError(
|
54
|
+
f"Based on object {__based_on__} not found in instance {self.__instance}"
|
55
|
+
)
|
56
|
+
if not isinstance(basis_object, GeneralManager) and not issubclass(
|
57
|
+
basis_object, GeneralManager
|
58
|
+
):
|
59
|
+
raise TypeError(f"Based on object {__based_on__} is not a GeneralManager")
|
60
|
+
|
61
|
+
Permission = getattr(basis_object, "Permission", None)
|
62
|
+
|
63
|
+
if Permission is None or not issubclass(
|
64
|
+
Permission,
|
65
|
+
BasePermission,
|
66
|
+
):
|
67
|
+
return None
|
68
|
+
|
69
|
+
return Permission(
|
70
|
+
instance=getattr(self.instance, __based_on__),
|
71
|
+
request_user=self.request_user,
|
72
|
+
)
|
73
|
+
|
74
|
+
@property
|
75
|
+
def instance(self) -> PermissionDataManager | GeneralManager:
|
76
|
+
return self.__instance
|
77
|
+
|
78
|
+
@property
|
79
|
+
def request_user(self) -> AbstractUser:
|
80
|
+
return self.__request_user
|
81
|
+
|
82
|
+
def __getAttributePermissions(
|
83
|
+
self,
|
84
|
+
) -> dict[str, dict[permission_type, list[str]]]:
|
85
|
+
attribute_permissions = {}
|
86
|
+
for attribute in self.__class__.__dict__:
|
87
|
+
if not attribute.startswith("__"):
|
88
|
+
attribute_permissions[attribute] = getattr(self, attribute)
|
89
|
+
return attribute_permissions
|
90
|
+
|
91
|
+
def checkPermission(
|
92
|
+
self,
|
93
|
+
action: permission_type,
|
94
|
+
attriubte: str,
|
95
|
+
) -> bool:
|
96
|
+
if (
|
97
|
+
self.__based_on_permission
|
98
|
+
and not self.__based_on_permission.checkPermission(action, attriubte)
|
99
|
+
):
|
100
|
+
return False
|
101
|
+
|
102
|
+
if action == "create":
|
103
|
+
permissions = self.__create__
|
104
|
+
elif action == "read":
|
105
|
+
permissions = self.__read__
|
106
|
+
elif action == "update":
|
107
|
+
permissions = self.__update__
|
108
|
+
elif action == "delete":
|
109
|
+
permissions = self.__delete__
|
110
|
+
else:
|
111
|
+
raise ValueError(f"Action {action} not found")
|
112
|
+
|
113
|
+
has_attribute_permissions = (
|
114
|
+
attriubte in self.__attribute_permissions
|
115
|
+
and action in self.__attribute_permissions[attriubte]
|
116
|
+
)
|
117
|
+
|
118
|
+
if not has_attribute_permissions:
|
119
|
+
last_result = self.__overall_results.get(action)
|
120
|
+
if last_result is not None:
|
121
|
+
return last_result
|
122
|
+
attribute_permission = True
|
123
|
+
else:
|
124
|
+
attribute_permission = self.__checkSpecificPermission(
|
125
|
+
self.__attribute_permissions[attriubte][action]
|
126
|
+
)
|
127
|
+
|
128
|
+
permission = self.__checkSpecificPermission(permissions)
|
129
|
+
self.__overall_results[action] = permission
|
130
|
+
return permission and attribute_permission
|
131
|
+
|
132
|
+
def __checkSpecificPermission(
|
133
|
+
self,
|
134
|
+
permissions: list[str],
|
135
|
+
) -> bool:
|
136
|
+
for permission in permissions:
|
137
|
+
if self.validatePermissionString(permission):
|
138
|
+
return True
|
139
|
+
return False
|
140
|
+
|
141
|
+
def getPermissionFilter(
|
142
|
+
self,
|
143
|
+
) -> list[dict[Literal["filter", "exclude"], dict[str, str]]]:
|
144
|
+
"""
|
145
|
+
Returns the filter for the permission
|
146
|
+
"""
|
147
|
+
__based_on__ = getattr(self, "__based_on__")
|
148
|
+
filters: list[dict[Literal["filter", "exclude"], dict[str, str]]] = []
|
149
|
+
|
150
|
+
if self.__based_on_permission is not None:
|
151
|
+
base_permissions = self.__based_on_permission.getPermissionFilter()
|
152
|
+
for permission in base_permissions:
|
153
|
+
filter = permission.get("filter", {})
|
154
|
+
exclude = permission.get("exclude", {})
|
155
|
+
filters.append(
|
156
|
+
{
|
157
|
+
"filter": {
|
158
|
+
f"{__based_on__}__{key}": value
|
159
|
+
for key, value in filter.items()
|
160
|
+
},
|
161
|
+
"exclude": {
|
162
|
+
f"{__based_on__}__{key}": value
|
163
|
+
for key, value in exclude.items()
|
164
|
+
},
|
165
|
+
}
|
166
|
+
)
|
167
|
+
|
168
|
+
for permission in self.__read__:
|
169
|
+
filters.append(self._getPermissionFilter(permission))
|
170
|
+
|
171
|
+
return filters
|
@@ -0,0 +1,53 @@
|
|
1
|
+
from typing import Any, Callable, TYPE_CHECKING, TypedDict, Literal
|
2
|
+
|
3
|
+
if TYPE_CHECKING:
|
4
|
+
from django.contrib.auth.models import AbstractUser, AnonymousUser
|
5
|
+
from general_manager.permission.permissionDataManager import (
|
6
|
+
PermissionDataManager,
|
7
|
+
)
|
8
|
+
from general_manager.manager.generalManager import GeneralManager
|
9
|
+
from general_manager.manager.meta import GeneralManagerMeta
|
10
|
+
|
11
|
+
|
12
|
+
type permission_filter = Callable[
|
13
|
+
[AbstractUser | AnonymousUser, list[str]],
|
14
|
+
dict[Literal["filter", "exclude"], dict[str, str]] | None,
|
15
|
+
]
|
16
|
+
|
17
|
+
type permission_method = Callable[
|
18
|
+
[
|
19
|
+
PermissionDataManager | GeneralManager | GeneralManagerMeta,
|
20
|
+
AbstractUser | AnonymousUser,
|
21
|
+
list[str],
|
22
|
+
],
|
23
|
+
bool,
|
24
|
+
]
|
25
|
+
|
26
|
+
|
27
|
+
class PermissionDict(TypedDict):
|
28
|
+
permission_method: permission_method
|
29
|
+
permission_filter: permission_filter
|
30
|
+
|
31
|
+
|
32
|
+
permission_functions: dict[str, PermissionDict] = {
|
33
|
+
"public": {
|
34
|
+
"permission_method": lambda instance, user, config: True,
|
35
|
+
"permission_filter": lambda user, config: None,
|
36
|
+
},
|
37
|
+
"ends_with": {
|
38
|
+
"permission_method": lambda instance, user, config: getattr(
|
39
|
+
instance, config[0]
|
40
|
+
).endswith(config[1]),
|
41
|
+
"permission_filter": lambda user, config: {
|
42
|
+
"filter": {f"{config[0]}__endswith": config[1]}
|
43
|
+
},
|
44
|
+
},
|
45
|
+
"admin": {
|
46
|
+
"permission_method": lambda instance, user, config: user.is_staff,
|
47
|
+
"permission_filter": lambda user, config: None,
|
48
|
+
},
|
49
|
+
"isAuthenticated": {
|
50
|
+
"permission_method": lambda instance, user, config: user.is_authenticated,
|
51
|
+
"permission_filter": lambda user, config: None,
|
52
|
+
},
|
53
|
+
}
|
@@ -0,0 +1,55 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import Callable, Dict, Any, Optional, TypeVar, Generic
|
3
|
+
from django.contrib.auth.models import AbstractUser
|
4
|
+
|
5
|
+
from general_manager.manager.generalManager import GeneralManager
|
6
|
+
|
7
|
+
GeneralManagerData = TypeVar("GeneralManagerData", bound=GeneralManager)
|
8
|
+
|
9
|
+
|
10
|
+
class PermissionDataManager(Generic[GeneralManagerData]):
|
11
|
+
def __init__(
|
12
|
+
self,
|
13
|
+
permission_data: Dict[str, Any] | GeneralManagerData,
|
14
|
+
manager: Optional[type[GeneralManagerData]] = None,
|
15
|
+
):
|
16
|
+
self.getData: Callable[[str], Any]
|
17
|
+
self._permission_data = permission_data
|
18
|
+
if isinstance(permission_data, GeneralManager):
|
19
|
+
self.getData = lambda name, permission_data=permission_data: getattr(
|
20
|
+
permission_data, name
|
21
|
+
)
|
22
|
+
self._manager = permission_data.__class__
|
23
|
+
elif isinstance(permission_data, dict):
|
24
|
+
self.getData = (
|
25
|
+
lambda name, permission_data=permission_data: permission_data.get(name)
|
26
|
+
)
|
27
|
+
if manager is None:
|
28
|
+
raise ValueError(
|
29
|
+
"Manager must be provided if permission_data is a dict"
|
30
|
+
)
|
31
|
+
self._manager = manager
|
32
|
+
else:
|
33
|
+
raise TypeError(
|
34
|
+
"permission_data must be either a dict or an instance of GeneralManager"
|
35
|
+
)
|
36
|
+
|
37
|
+
@classmethod
|
38
|
+
def forUpdate(
|
39
|
+
cls,
|
40
|
+
base_data: GeneralManagerData,
|
41
|
+
update_data: Dict[str, Any],
|
42
|
+
) -> PermissionDataManager:
|
43
|
+
merged_data = {**dict(base_data), **update_data}
|
44
|
+
return cls(merged_data, base_data.__class__)
|
45
|
+
|
46
|
+
@property
|
47
|
+
def permission_data(self) -> Dict[str, Any] | GeneralManagerData:
|
48
|
+
return self._permission_data
|
49
|
+
|
50
|
+
@property
|
51
|
+
def manager(self) -> type[GeneralManagerData]:
|
52
|
+
return self._manager
|
53
|
+
|
54
|
+
def __getattr__(self, name: str) -> Any:
|
55
|
+
return self.getData(name)
|
@@ -0,0 +1 @@
|
|
1
|
+
from .rule import Rule
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# generalManager/src/rule/handlers.py
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
import ast
|
5
|
+
from typing import Dict, Optional, TYPE_CHECKING, cast
|
6
|
+
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
# Forward-Reference auf Rule mit beliebigem Generic-Parameter
|
9
|
+
from general_manager.rule.rule import Rule
|
10
|
+
from general_manager.manager import GeneralManager
|
11
|
+
|
12
|
+
|
13
|
+
class BaseRuleHandler:
|
14
|
+
"""Schnittstelle für Rule-Handler."""
|
15
|
+
|
16
|
+
function_name: str # ClassVar, der Name, unter dem dieser Handler registriert wird
|
17
|
+
|
18
|
+
def handle(
|
19
|
+
self,
|
20
|
+
node: ast.AST,
|
21
|
+
left: Optional[ast.expr],
|
22
|
+
right: Optional[ast.expr],
|
23
|
+
op: Optional[ast.cmpop],
|
24
|
+
var_values: Dict[str, Optional[object]],
|
25
|
+
rule: Rule,
|
26
|
+
) -> Dict[str, str]:
|
27
|
+
"""
|
28
|
+
Erstelle Fehlermeldungen für den Vergleichs- oder Funktionsaufruf.
|
29
|
+
"""
|
30
|
+
raise NotImplementedError
|
31
|
+
|
32
|
+
|
33
|
+
class LenHandler(BaseRuleHandler):
|
34
|
+
function_name = "len"
|
35
|
+
|
36
|
+
def handle(
|
37
|
+
self,
|
38
|
+
node: ast.AST,
|
39
|
+
left: Optional[ast.expr],
|
40
|
+
right: Optional[ast.expr],
|
41
|
+
op: Optional[ast.cmpop],
|
42
|
+
var_values: Dict[str, Optional[object]],
|
43
|
+
rule: Rule,
|
44
|
+
) -> Dict[str, str]:
|
45
|
+
# Wir erwarten hier einen Compare-Knoten
|
46
|
+
if not isinstance(node, ast.Compare):
|
47
|
+
return {}
|
48
|
+
compare_node = node
|
49
|
+
|
50
|
+
left_node = compare_node.left
|
51
|
+
right_node = compare_node.comparators[0]
|
52
|
+
op_symbol = rule._get_op_symbol(op)
|
53
|
+
|
54
|
+
# Argument von len(...)
|
55
|
+
if not (isinstance(left_node, ast.Call) and left_node.args):
|
56
|
+
raise ValueError("Invalid left node for len function")
|
57
|
+
arg_node = left_node.args[0]
|
58
|
+
|
59
|
+
var_name = rule._get_node_name(arg_node)
|
60
|
+
var_value = var_values.get(var_name)
|
61
|
+
|
62
|
+
# --- Hier der Typ-Guard für right_value ---
|
63
|
+
raw = rule._eval_node(right_node)
|
64
|
+
if not isinstance(raw, (int, float)):
|
65
|
+
raise ValueError("Invalid arguments for len function")
|
66
|
+
right_value: int | float = raw
|
67
|
+
|
68
|
+
# Schwellenwert je nach Operator
|
69
|
+
if op_symbol == ">":
|
70
|
+
threshold = right_value + 1
|
71
|
+
elif op_symbol == ">=":
|
72
|
+
threshold = right_value
|
73
|
+
elif op_symbol == "<":
|
74
|
+
threshold = right_value - 1
|
75
|
+
elif op_symbol == "<=":
|
76
|
+
threshold = right_value
|
77
|
+
else:
|
78
|
+
threshold = right_value
|
79
|
+
|
80
|
+
# Fehlermeldung formulieren
|
81
|
+
if op_symbol in (">", ">="):
|
82
|
+
msg = f"[{var_name}] ({var_value}) is too short (min length {threshold})!"
|
83
|
+
elif op_symbol in ("<", "<="):
|
84
|
+
msg = f"[{var_name}] ({var_value}) is too long (max length {threshold})!"
|
85
|
+
else:
|
86
|
+
msg = f"[{var_name}] ({var_value}) must be {op_symbol} {right_value}!"
|
87
|
+
|
88
|
+
return {var_name: msg}
|
89
|
+
|
90
|
+
|
91
|
+
class IntersectionCheckHandler(BaseRuleHandler):
|
92
|
+
function_name = "intersectionCheck"
|
93
|
+
|
94
|
+
def handle(
|
95
|
+
self,
|
96
|
+
node: ast.AST,
|
97
|
+
left: Optional[ast.expr],
|
98
|
+
right: Optional[ast.expr],
|
99
|
+
op: Optional[ast.cmpop],
|
100
|
+
var_values: Dict[str, Optional[object]],
|
101
|
+
rule: Rule[GeneralManager],
|
102
|
+
) -> Dict[str, str]:
|
103
|
+
# Der Aufruf steht in `left`, nicht in `node`
|
104
|
+
call_node = cast(ast.Call, left)
|
105
|
+
if not isinstance(call_node, ast.Call):
|
106
|
+
return {"error": "Invalid arguments for intersectionCheck"}
|
107
|
+
|
108
|
+
args = call_node.args
|
109
|
+
if len(args) < 2:
|
110
|
+
return {"error": "Invalid arguments for intersectionCheck"}
|
111
|
+
|
112
|
+
start_node, end_node = args[0], args[1]
|
113
|
+
start_name = rule._get_node_name(start_node)
|
114
|
+
end_name = rule._get_node_name(end_node)
|
115
|
+
start_val = var_values.get(start_name)
|
116
|
+
end_val = var_values.get(end_name)
|
117
|
+
|
118
|
+
msg = (
|
119
|
+
f"[{start_name}] ({start_val}) and "
|
120
|
+
f"[{end_name}] ({end_val}) must not overlap with existing ranges."
|
121
|
+
)
|
122
|
+
return {start_name: msg, end_name: msg}
|