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.
Files changed (43) hide show
  1. general_manager/__init__.py +0 -0
  2. general_manager/api/graphql.py +732 -0
  3. general_manager/api/mutation.py +143 -0
  4. general_manager/api/property.py +20 -0
  5. general_manager/apps.py +83 -0
  6. general_manager/auxiliary/__init__.py +2 -0
  7. general_manager/auxiliary/argsToKwargs.py +25 -0
  8. general_manager/auxiliary/filterParser.py +97 -0
  9. general_manager/auxiliary/noneToZero.py +12 -0
  10. general_manager/cache/cacheDecorator.py +72 -0
  11. general_manager/cache/cacheTracker.py +33 -0
  12. general_manager/cache/dependencyIndex.py +300 -0
  13. general_manager/cache/pathMapping.py +151 -0
  14. general_manager/cache/signals.py +48 -0
  15. general_manager/factory/__init__.py +5 -0
  16. general_manager/factory/factories.py +287 -0
  17. general_manager/factory/lazy_methods.py +38 -0
  18. general_manager/interface/__init__.py +3 -0
  19. general_manager/interface/baseInterface.py +308 -0
  20. general_manager/interface/calculationInterface.py +406 -0
  21. general_manager/interface/databaseInterface.py +726 -0
  22. general_manager/manager/__init__.py +3 -0
  23. general_manager/manager/generalManager.py +136 -0
  24. general_manager/manager/groupManager.py +288 -0
  25. general_manager/manager/input.py +48 -0
  26. general_manager/manager/meta.py +75 -0
  27. general_manager/measurement/__init__.py +2 -0
  28. general_manager/measurement/measurement.py +233 -0
  29. general_manager/measurement/measurementField.py +152 -0
  30. general_manager/permission/__init__.py +1 -0
  31. general_manager/permission/basePermission.py +178 -0
  32. general_manager/permission/fileBasedPermission.py +0 -0
  33. general_manager/permission/managerBasedPermission.py +171 -0
  34. general_manager/permission/permissionChecks.py +53 -0
  35. general_manager/permission/permissionDataManager.py +55 -0
  36. general_manager/rule/__init__.py +1 -0
  37. general_manager/rule/handler.py +122 -0
  38. general_manager/rule/rule.py +313 -0
  39. generalmanager-0.0.0.dist-info/METADATA +207 -0
  40. generalmanager-0.0.0.dist-info/RECORD +43 -0
  41. generalmanager-0.0.0.dist-info/WHEEL +5 -0
  42. generalmanager-0.0.0.dist-info/licenses/LICENSE +29 -0
  43. generalmanager-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,313 @@
1
+ # generalManager/src/rule/rule.py
2
+
3
+ from __future__ import annotations
4
+ import ast
5
+ import inspect
6
+ import re
7
+ import textwrap
8
+ from typing import (
9
+ Callable,
10
+ ClassVar,
11
+ Dict,
12
+ Generic,
13
+ List,
14
+ Optional,
15
+ TypeVar,
16
+ cast,
17
+ )
18
+
19
+ from django.conf import settings
20
+ from django.utils.module_loading import import_string
21
+
22
+ from general_manager.rule.handler import (
23
+ BaseRuleHandler,
24
+ LenHandler,
25
+ IntersectionCheckHandler,
26
+ )
27
+ from general_manager.manager.generalManager import GeneralManager
28
+
29
+ GeneralManagerType = TypeVar("GeneralManagerType", bound=GeneralManager)
30
+
31
+
32
+ class Rule(Generic[GeneralManagerType]):
33
+ """
34
+ Rule kapselt eine boolsche Bedingungsfunktion und erzeugt bei Fehlschlag
35
+ automatisierte oder benutzerdefinierte Fehlermeldungen auf Basis des AST.
36
+ """
37
+
38
+ _func: Callable[[GeneralManagerType], bool]
39
+ _custom_error_message: Optional[str]
40
+ _ignore_if_none: bool
41
+ _last_result: Optional[bool]
42
+ _last_input: Optional[GeneralManagerType]
43
+ _tree: ast.AST
44
+ _variables: List[str]
45
+ _handlers: Dict[str, BaseRuleHandler]
46
+
47
+ def __init__(
48
+ self,
49
+ func: Callable[[GeneralManagerType], bool],
50
+ custom_error_message: Optional[str] = None,
51
+ ignore_if_none: bool = True,
52
+ ) -> None:
53
+ self._func = func
54
+ self._custom_error_message = custom_error_message
55
+ self._ignore_if_none = ignore_if_none
56
+ self._last_result = None
57
+ self._last_input = None
58
+
59
+ # 1) Quelltext holen, Decorators abschneiden, Dedent
60
+ src = inspect.getsource(func)
61
+ lines = src.splitlines()
62
+ if lines and lines[0].strip().startswith("@"):
63
+ idx = next(i for i, L in enumerate(lines) if not L.strip().startswith("@"))
64
+ src = "\n".join(lines[idx:])
65
+ src = textwrap.dedent(src)
66
+
67
+ # 2) AST parsen & Elternverweise setzen
68
+ self._tree = ast.parse(src)
69
+ for parent in ast.walk(self._tree):
70
+ for child in ast.iter_child_nodes(parent):
71
+ setattr(child, "parent", parent)
72
+
73
+ # 3) Variablen extrahieren
74
+ self._variables = self._extract_variables()
75
+
76
+ # 4) Handler registrieren
77
+ self._handlers = {} # type: Dict[str, BaseRuleHandler]
78
+ for cls in (LenHandler, IntersectionCheckHandler):
79
+ inst = cls()
80
+ self._handlers[inst.function_name] = inst
81
+ for path in getattr(settings, "RULE_HANDLERS", []):
82
+ handler_cls = import_string(path)
83
+ inst = handler_cls()
84
+ self._handlers[inst.function_name] = inst
85
+
86
+ @property
87
+ def func(self) -> Callable[[GeneralManagerType], bool]:
88
+ return self._func
89
+
90
+ @property
91
+ def customErrorMessage(self) -> Optional[str]:
92
+ return self._custom_error_message
93
+
94
+ @property
95
+ def variables(self) -> List[str]:
96
+ return self._variables
97
+
98
+ @property
99
+ def lastEvaluationResult(self) -> Optional[bool]:
100
+ return self._last_result
101
+
102
+ @property
103
+ def lastEvaluationInput(self) -> Optional[GeneralManagerType]:
104
+ return self._last_input
105
+
106
+ @property
107
+ def ignoreIfNone(self) -> bool:
108
+ return self._ignore_if_none
109
+
110
+ def evaluate(self, x: GeneralManagerType) -> Optional[bool]:
111
+ """
112
+ Führt die Regel aus. Gibt False bei Fehlschlag, True bei Erfolg
113
+ und None, falls ignore_if_none aktiv ist und eine Variable None war.
114
+ """
115
+ self._last_input = x
116
+ vals = self._extract_variable_values(x)
117
+ if self._ignore_if_none and any(v is None for v in vals.values()):
118
+ self._last_result = None
119
+ return None
120
+
121
+ self._last_result = self._func(x)
122
+ return self._last_result
123
+
124
+ def validateCustomErrorMessage(self) -> None:
125
+ """
126
+ Stellt sicher, dass in der custom_error_message alle Variablen
127
+ aus self._variables verwendet werden.
128
+ """
129
+ if not self._custom_error_message:
130
+ return
131
+
132
+ vars_in_msg = set(re.findall(r"{([^}]+)}", self._custom_error_message))
133
+ missing = [v for v in self._variables if v not in vars_in_msg]
134
+ if missing:
135
+ raise ValueError(
136
+ f"The custom error message does not contain all used variables: {missing}"
137
+ )
138
+
139
+ def getErrorMessage(self) -> Optional[Dict[str, str]]:
140
+ """
141
+ Liefert ein Dict variable→message, oder None, wenn kein Fehler.
142
+ """
143
+ if self._last_result or self._last_result is None:
144
+ return None
145
+ if self._last_input is None:
146
+ raise ValueError("No input provided for error message generation")
147
+
148
+ # Validierung und Ersetzen der Template-Platzhalter
149
+ self.validateCustomErrorMessage()
150
+ vals = self._extract_variable_values(self._last_input)
151
+
152
+ if self._custom_error_message:
153
+ formatted = re.sub(
154
+ r"{([^}]+)}",
155
+ lambda m: str(vals.get(m.group(1), m.group(0))),
156
+ self._custom_error_message,
157
+ )
158
+ return {v: formatted for v in self._variables}
159
+
160
+ errors = self._generate_error_messages(vals)
161
+ return errors or None
162
+
163
+ def _extract_variables(self) -> List[str]:
164
+ class VarVisitor(ast.NodeVisitor):
165
+ vars: set[str] = set()
166
+
167
+ def visit_Attribute(self, node: ast.Attribute) -> None:
168
+ parts: list[str] = []
169
+ curr: ast.AST = node
170
+ while isinstance(curr, ast.Attribute):
171
+ parts.append(curr.attr)
172
+ curr = curr.value
173
+ if isinstance(curr, ast.Name) and curr.id == "x":
174
+ self.vars.add(".".join(reversed(parts)))
175
+ self.generic_visit(node)
176
+
177
+ visitor = VarVisitor()
178
+ visitor.visit(self._tree)
179
+ return sorted(visitor.vars)
180
+
181
+ def _extract_variable_values(
182
+ self, x: GeneralManagerType
183
+ ) -> Dict[str, Optional[object]]:
184
+ out: Dict[str, Optional[object]] = {}
185
+ for var in self._variables:
186
+ obj: object = x # type: ignore
187
+ for part in var.split("."):
188
+ obj = getattr(obj, part)
189
+ if obj is None:
190
+ break
191
+ out[var] = obj
192
+ return out
193
+
194
+ def _extract_comparisons(self) -> list[ast.Compare]:
195
+ class CompVisitor(ast.NodeVisitor):
196
+ comps: list[ast.Compare] = []
197
+
198
+ def visit_Compare(self, node: ast.Compare) -> None:
199
+ self.comps.append(node)
200
+ self.generic_visit(node)
201
+
202
+ visitor = CompVisitor()
203
+ visitor.visit(self._tree)
204
+ return visitor.comps
205
+
206
+ def _contains_logical_ops(self) -> bool:
207
+ class LogicVisitor(ast.NodeVisitor):
208
+ found: bool = False
209
+
210
+ def visit_BoolOp(self, node: ast.BoolOp) -> None:
211
+ if isinstance(node.op, (ast.And, ast.Or)):
212
+ self.found = True
213
+ self.generic_visit(node)
214
+
215
+ visitor = LogicVisitor()
216
+ visitor.visit(self._tree)
217
+ return visitor.found
218
+
219
+ def _generate_error_messages(
220
+ self, var_values: Dict[str, Optional[object]]
221
+ ) -> Dict[str, str]:
222
+ errors: Dict[str, str] = {}
223
+ comparisons = self._extract_comparisons()
224
+ logical = self._contains_logical_ops()
225
+
226
+ if comparisons:
227
+ for cmp in comparisons:
228
+ left, rights, ops = cmp.left, cmp.comparators, cmp.ops
229
+ for right, op in zip(rights, ops):
230
+ # Spezial-Handler?
231
+ if isinstance(left, ast.Call):
232
+ fn = self._get_node_name(left.func)
233
+ handler = self._handlers.get(fn)
234
+ if handler:
235
+ errors.update(
236
+ handler.handle(cmp, left, right, op, var_values, self)
237
+ )
238
+ continue
239
+
240
+ # Standard-Fehler
241
+ lnm = self._get_node_name(left)
242
+ rnm = self._get_node_name(right)
243
+ lval = self._eval_node(left)
244
+ rval = self._eval_node(right)
245
+ ldisp = f"[{lnm}] ({lval})" if lnm in var_values else str(lval)
246
+ rdisp = f"[{rnm}] ({rval})" if rnm in var_values else str(rval)
247
+ sym = self._get_op_symbol(op)
248
+ msg = f"{ldisp} must be {sym} {rdisp}!"
249
+ if lnm in var_values:
250
+ errors[lnm] = msg
251
+ if rnm in var_values and rnm != lnm:
252
+ errors[rnm] = msg
253
+
254
+ if logical and not self._last_result:
255
+ combo = ", ".join(f"[{v}]" for v in self._variables)
256
+ msg = f"{combo} combination is not valid"
257
+ for v in self._variables:
258
+ errors[v] = msg
259
+
260
+ return errors
261
+
262
+ # kein Vergleich → pauschale Meldung
263
+ combo = ", ".join(f"[{v}]" for v in self._variables)
264
+ return {v: f"{combo} combination is not valid" for v in self._variables}
265
+
266
+ def _get_op_symbol(self, op: Optional[ast.cmpop]) -> str:
267
+ return {
268
+ ast.Lt: "<",
269
+ ast.LtE: "<=",
270
+ ast.Gt: ">",
271
+ ast.GtE: ">=",
272
+ ast.Eq: "==",
273
+ ast.NotEq: "!=",
274
+ ast.Is: "is",
275
+ ast.IsNot: "is not",
276
+ ast.In: "in",
277
+ ast.NotIn: "not in",
278
+ }.get(type(op), "?")
279
+
280
+ def _get_node_name(self, node: ast.AST) -> str:
281
+ if isinstance(node, ast.Attribute):
282
+ parts: list[str] = []
283
+ curr: ast.AST = node
284
+ while isinstance(curr, ast.Attribute):
285
+ parts.insert(0, curr.attr)
286
+ curr = curr.value
287
+ return ".".join(parts)
288
+ if isinstance(node, ast.Name):
289
+ return node.id
290
+ if isinstance(node, ast.Constant):
291
+ return ""
292
+ if isinstance(node, ast.Call):
293
+ fn = self._get_node_name(node.func)
294
+ args = ", ".join(self._get_node_name(a) for a in node.args)
295
+ return f"{fn}({args})"
296
+ try:
297
+ # ast.unparse gibt einen str zurück
298
+ return ast.unparse(node)
299
+ except Exception:
300
+ return ""
301
+
302
+ def _eval_node(self, node: ast.expr) -> Optional[object]:
303
+ """
304
+ Evaluiert einen AST-Ausdruck im Kontext von `x`.
305
+ """
306
+ if not isinstance(node, ast.expr):
307
+ return None
308
+ try:
309
+ expr = ast.Expression(body=node)
310
+ code = compile(expr, "<ast>", "eval")
311
+ return eval(code, {"x": self._last_input}, {})
312
+ except Exception:
313
+ return None
@@ -0,0 +1,207 @@
1
+ Metadata-Version: 2.4
2
+ Name: GeneralManager
3
+ Version: 0.0.0
4
+ Summary: Kurzbeschreibung deines Pakets
5
+ Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
+ License: Non-Commercial MIT License
7
+
8
+ Copyright (c) 2025 Tim Kleindick
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the “Software”), to deal
12
+ in the Software **solely for non-commercial purposes**, including without
13
+ limitation the rights to use, copy, modify, merge, publish, distribute,
14
+ sublicense, and/or sell copies of the Software **for non-commercial use only**,
15
+ and to permit persons to whom the Software is furnished to do so, subject to
16
+ the following conditions:
17
+
18
+ 1. Non-Commercial Use Only
19
+ The Software may **not** be used for commercial purposes. “Commercial” means
20
+ any activity intended for or directed toward commercial advantage or
21
+ monetary compensation.
22
+
23
+ 2. Copyright Notice & License Text
24
+ The above copyright notice and this permission notice shall be included in
25
+ all copies or substantial portions of the Software.
26
+
27
+ 3. Disclaimer of Warranty
28
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30
+ FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. IN NO EVENT SHALL
31
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
32
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
33
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
34
+ SOFTWARE.
35
+ Requires-Python: >=3.12
36
+ Description-Content-Type: text/markdown
37
+ License-File: LICENSE
38
+ Requires-Dist: asgiref>=3.8.1
39
+ Requires-Dist: Django>=5.2
40
+ Requires-Dist: django-simple-history>=3.8.0
41
+ Requires-Dist: exrex>=0.12.0
42
+ Requires-Dist: factory_boy>=3.3.3
43
+ Requires-Dist: Faker>=37.1.0
44
+ Requires-Dist: flexcache>=0.3
45
+ Requires-Dist: flexparser>=0.4
46
+ Requires-Dist: gitdb>=4.0.12
47
+ Requires-Dist: GitPython>=3.1.41
48
+ Requires-Dist: graphene>=3.4.3
49
+ Requires-Dist: graphene-django>=3.2.3
50
+ Requires-Dist: graphql-core>=3.2.6
51
+ Requires-Dist: graphql-relay>=3.2.0
52
+ Requires-Dist: numpy>=2.2.5
53
+ Requires-Dist: Pint>=0.24.4
54
+ Requires-Dist: platformdirs>=4.3.7
55
+ Requires-Dist: promise>=2.3
56
+ Requires-Dist: python-dateutil>=2.9.0.post0
57
+ Requires-Dist: setuptools>=75.6.0
58
+ Requires-Dist: six>=1.17.0
59
+ Requires-Dist: smmap>=5.0.2
60
+ Requires-Dist: sqlparse>=0.5.3
61
+ Requires-Dist: text-unidecode>=1.3
62
+ Requires-Dist: typing_extensions>=4.13.2
63
+ Requires-Dist: tzdata>=2025.2
64
+ Dynamic: license-file
65
+
66
+ # GeneralManager
67
+
68
+ ## Überblick
69
+
70
+ Das GeneralManager-Modul ist ein leistungsstarkes und flexibles Framework, das speziell für die Verwaltung und Verarbeitung von Daten entwickelt wurde. Es bietet eine modulare Struktur, die es Entwicklern ermöglicht, komplexe Geschäftslogiken effizient zu implementieren und zu verwalten. Das Modul ist vollständig in Python geschrieben und nutzt Django als Backend-Framework.
71
+
72
+ ## Hauptfunktionen
73
+
74
+ ### 1. **Datenmanagement**
75
+ - **Flexibilität**: Unterstützt die Verwaltung aller Arten von Daten, nicht nur Projekte und Derivate.
76
+ - **Datenbank-Integration**: Nahtlose Integration mit dem Django ORM für Datenbankoperationen.
77
+ - **Externe Schnittstellen**: Unterstützung für Schnittstellen zu anderen Programmen, wie z. B. Excel-Interfaces.
78
+
79
+ ### 2. **Datenmodellierung**
80
+ - **Django-Modelle**: Die Datenstruktur basiert auf Django-Modellen, die durch benutzerdefinierte Felder wie `MeasurementField` erweitert werden.
81
+ - **Regeln und Validierungen**: Definieren Sie Regeln für Datenvalidierungen, z. B. dass das Startdatum eines Projekts vor dem Enddatum liegen muss.
82
+
83
+ ### 3. **GraphQL-Integration**
84
+ - Automatische Generierung von GraphQL-Schnittstellen für alle Modelle.
85
+ - Unterstützung für benutzerdefinierte Abfragen und Mutationen.
86
+
87
+ ### 4. **Berechtigungssystem**
88
+ - **ManagerBasedPermission**: Ein flexibles Berechtigungssystem, das auf Benutzerrollen und Attributen basiert.
89
+ - Unterstützung für CRUD-Berechtigungen auf Attributebene.
90
+
91
+ ### 5. **Interfaces**
92
+ - **CalculationInterface**: Ermöglicht die Implementierung von Berechnungslogiken.
93
+ - **DatabaseInterface**: Bietet eine standardisierte Schnittstelle für Datenbankoperationen.
94
+ - **ReadOnlyInterface**: Für schreibgeschützte Datenzugriffe.
95
+
96
+ ### 6. **Datenverteilung und Berechnung**
97
+ - **Volumenverteilung**: Automatische Berechnung und Verteilung von Volumen über mehrere Jahre.
98
+ - **Kommerzielle Berechnungen**: Berechnung von Gesamtvolumen, Versandkosten und Einnahmen für Projekte.
99
+
100
+ ## Anwendung
101
+
102
+ ### Installation
103
+
104
+ Installieren Sie das Modul über `pip`:
105
+
106
+ ```bash
107
+ pip install GeneralManager
108
+ ```
109
+
110
+ ### Beispielcode
111
+
112
+ Hier ist ein Beispiel, wie Sie einen GeneralManager erstellen und Testdaten (in diesem Fall 10 Projekte) generieren können:
113
+
114
+ ```python
115
+ from general_manager import GeneralManager
116
+ from general_manager.interface.database import DatabaseInterface
117
+ from general_manager.measurement import MeasurementField, Measurement
118
+ from general_manager.permission import ManagerBasedPermission
119
+
120
+ class Project(GeneralManager):
121
+ name: str
122
+ start_date: Optional[date]
123
+ end_date: Optional[date]
124
+ total_capex: Optional[Measurement]
125
+ derivative_list: DatabaseBucket[Derivative]
126
+
127
+ class Interface(DatabaseInterface):
128
+ name = CharField(max_length=50)
129
+ number = CharField(max_length=7, validators=[RegexValidator(r"^AP\d{4,5}$")])
130
+ description = TextField(null=True, blank=True)
131
+ start_date = DateField(null=True, blank=True)
132
+ end_date = DateField(null=True, blank=True)
133
+ total_capex = MeasurementField(base_unit="EUR", null=True, blank=True)
134
+
135
+ class Meta:
136
+ constraints = [
137
+ constraints.UniqueConstraint(
138
+ fields=["name", "number"], name="unique_booking"
139
+ )
140
+ ]
141
+
142
+ rules = [
143
+ Rule["Project"](
144
+ lambda x: cast(date, x.start_date) < cast(date, x.end_date)
145
+ ),
146
+ Rule["Project"](lambda x: cast(Measurement, x.total_capex) >= "0 EUR"),
147
+ ]
148
+
149
+ class Factory:
150
+ name = LazyProjectName()
151
+ end_date = LazyDeltaDate(365 * 6, "start_date")
152
+ total_capex = LazyMeasurement(75_000, 1_000_000, "EUR")
153
+
154
+ class Permission(ManagerBasedPermission):
155
+ __read__ = ["ends_with:name:X-771", "public"]
156
+ __create__ = ["admin", "isMatchingKeyAccount"]
157
+ __update__ = ["admin", "isMatchingKeyAccount", "isProjectTeamMember"]
158
+ __delete__ = ["admin", "isMatchingKeyAccount", "isProjectTeamMember"]
159
+
160
+ total_capex = {"update": ["isSalesResponsible", "isProjectManager"]}
161
+
162
+ Project.Factory.createBatch(10)
163
+ ```
164
+
165
+ ### GraphQL-Integration
166
+
167
+ Das Modul generiert automatisch GraphQL-Schnittstellen für alle Modelle. Sie können Abfragen und Mutationen über die GraphQL-URL ausführen, die in den Django-Einstellungen definiert ist.
168
+
169
+ Beispiel für eine GraphQL-Abfrage:
170
+
171
+ ```graphql
172
+ query {
173
+ projectList {
174
+ name
175
+ startDate
176
+ endDate
177
+ totalCapex {
178
+ value
179
+ unit
180
+ }
181
+ }
182
+ }
183
+ ```
184
+
185
+ ## Vorteile
186
+
187
+ - **Modularität**: Einfach erweiterbar und anpassbar.
188
+ - **Flexibilität**: Unterstützt komplexe Geschäftslogiken und Berechnungen.
189
+ - **Integration**: Nahtlose Integration mit Django und GraphQL.
190
+ - **Berechtigungen**: Fein abgestimmte Berechtigungen für Benutzer und Attribute.
191
+ - **Datenvalidierung**: Automatische Validierung von Daten durch Regeln und Constraints.
192
+ - **Caching**: Automatische Cache-Generierung mit @cached Decorator, um die Leistung zu verbessern.
193
+
194
+ ## Anforderungen
195
+
196
+ - Python >= 3.12
197
+ - Django >= 5.2
198
+ - Zusätzliche Abhängigkeiten (siehe `requirements.txt`):
199
+ - `graphene`
200
+ - `numpy`
201
+ - `Pint`
202
+ - `factory_boy`
203
+ - uvm.
204
+
205
+ ## Lizenz
206
+
207
+ Dieses Projekt steht unter der **Non-Commercial MIT License**. Es darf nur für nicht-kommerzielle Zwecke verwendet werden. Weitere Details finden Sie in der [LICENSE](./LICENSE).
@@ -0,0 +1,43 @@
1
+ general_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ general_manager/apps.py,sha256=-AX9imxqht55Xr8q5abnoeKc48ubj-tzDq7WhUrcNUg,3313
3
+ general_manager/api/graphql.py,sha256=Virzi9njCD6qVcOLxmD9mdQOgd6ap8JeuKWovBZ73HA,27514
4
+ general_manager/api/mutation.py,sha256=uu5RVxc9wbb-Zrbtt4azegvyKymMqEsxk_CkerKCd9Q,5178
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=exmRGVwvdMPgN-65MLwAV2q6L23bI4gTWb6lWXO4ZuU,3108
9
+ general_manager/auxiliary/noneToZero.py,sha256=UtIdye6wPdiIZqOnHxSdHnc0aKcH_sMjeCHPs7uLwKo,276
10
+ general_manager/cache/cacheDecorator.py,sha256=RmUDyJfkouO2-R2KZZJF1n8RCB0FjVa2OoTeLQ0vUyg,2934
11
+ general_manager/cache/cacheTracker.py,sha256=vUQCarqABIW_O02hbKeRo0thurmPD0TfrOlIUh7kneI,1045
12
+ general_manager/cache/dependencyIndex.py,sha256=KUDroajKrWfJv0PO2EQVNTrYq7XxrpBT4MGKBgPwMwo,10676
13
+ general_manager/cache/pathMapping.py,sha256=WtECIek9fI-2_nqIYI4Ux9Lan6g8P9TMO_AfthkznX8,5656
14
+ general_manager/cache/signals.py,sha256=ZHeXKFMN7tj9t0J-vSqf_05_NhGqEF2sZtbZO3vaRqI,1234
15
+ general_manager/factory/__init__.py,sha256=DLSQbpSBpujPtDSZcruPc43OLWzKCCtf20gbalCDYRU,91
16
+ general_manager/factory/factories.py,sha256=31NyVw2Ju5ZDPJ6sax6_seyZesCU5UrviNbk-2PaKnA,11559
17
+ general_manager/factory/lazy_methods.py,sha256=UJC50a4Gbe4T5IQPh7ucyQqpaS2x4zhQzznwpJvyVLo,1155
18
+ general_manager/interface/__init__.py,sha256=6x5adQLefTugvrJeyPcAxstyqgLAYeaJ1EPdAbac9pE,213
19
+ general_manager/interface/baseInterface.py,sha256=mvSKUlA-0fazNnaIXGBwkiZxmX8DM_sOn-SaAIpaW8I,10273
20
+ general_manager/interface/calculationInterface.py,sha256=GzSNXjU6Z7bFz60gHyMKkI5xNUDIPuniV8wbyVtQT50,14250
21
+ general_manager/interface/databaseInterface.py,sha256=2Xzb04J7c1Z4ZKlA-6-8AOXaWNvF6zUVNNzM6k0jzLQ,27337
22
+ general_manager/manager/__init__.py,sha256=l3RYp62aEhj3Y975_XUTIzo35LUnkTJHkb_hgChnXXI,111
23
+ general_manager/manager/generalManager.py,sha256=xt7tvwTaPGhh7Z8VhbdMJuScsl78B9QDVQoMWv4amuo,5158
24
+ general_manager/manager/groupManager.py,sha256=O4FABqbm7KlZw6t36Ot3HU1FsBYN0h6Zhmk7ktN8a-Y,10087
25
+ general_manager/manager/input.py,sha256=NTZis3QsF0N3SODZBeQrR2R-_iMbmdEBvoYNuVKWNtE,1737
26
+ general_manager/manager/meta.py,sha256=5wHrCVnua5c38vpVZSCesrNvgydQDH8h6pxW6_QgCDg,3107
27
+ general_manager/measurement/__init__.py,sha256=X97meFujBldE5v0WMF7SmKeGpC5R0JTczfLo_Lq1Xek,84
28
+ general_manager/measurement/measurement.py,sha256=hTISANW7nAN2voakq9f-naJxa1MUcxWLDrcDW1Rdn3s,9340
29
+ general_manager/measurement/measurementField.py,sha256=iq9Hqe6ZGX8CxXm4nIqTAWTRkQVptzpqE9ExX-jFyNs,5928
30
+ general_manager/permission/__init__.py,sha256=5UlDERN60Vn8obGVkT-cOM8kHjzmoxgK5w5FgTCDhGE,59
31
+ general_manager/permission/basePermission.py,sha256=PsJiX-UNeSh6xUlcUwuQNCLYvHZipWUJ0kAoMkdxXJc,6113
32
+ general_manager/permission/fileBasedPermission.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
+ general_manager/permission/managerBasedPermission.py,sha256=VgZJVgkXWdaLk6K7c0kYxTbELnTYK6UY9mMuKH5u64w,5728
34
+ general_manager/permission/permissionChecks.py,sha256=T-9khBqiwM4ASBdey9p07sC_xgzceIU9EAE0reukguM,1655
35
+ general_manager/permission/permissionDataManager.py,sha256=Ji7fsnuaKTa6M8yzCGyzrIHyGa_ZvqJM7sXR97-uTrA,1937
36
+ general_manager/rule/__init__.py,sha256=4Har5cfPD1fmOsilTDod-ZUz3Com-tkl58jz7yY4fD0,23
37
+ general_manager/rule/handler.py,sha256=O3BZbTnUE9o3LTSWpoq6hgm3tKy1sEBjRcDrRaA54Gw,3936
38
+ general_manager/rule/rule.py,sha256=tu3pNe_avs6ayI16KYeaQX85o2lXcPip_LN9CJEhr8Y,10708
39
+ generalmanager-0.0.0.dist-info/licenses/LICENSE,sha256=YGFm0ieb4KpkMRRt2qnWue6uFh0cUMtobwEBkHwajhc,1450
40
+ generalmanager-0.0.0.dist-info/METADATA,sha256=xCCtFrVLe_ta1Y9q5Ezw5SeJhZTQ7aVIq53ggT3L88s,8188
41
+ generalmanager-0.0.0.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
42
+ generalmanager-0.0.0.dist-info/top_level.txt,sha256=sTDtExP9ga-YP3h3h42yivUY-A2Q23C2nw6LNKOho4I,16
43
+ generalmanager-0.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.3.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,29 @@
1
+ Non-Commercial MIT License
2
+
3
+ Copyright (c) 2025 Tim Kleindick
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the “Software”), to deal
7
+ in the Software **solely for non-commercial purposes**, including without
8
+ limitation the rights to use, copy, modify, merge, publish, distribute,
9
+ sublicense, and/or sell copies of the Software **for non-commercial use only**,
10
+ and to permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ 1. Non-Commercial Use Only
14
+ The Software may **not** be used for commercial purposes. “Commercial” means
15
+ any activity intended for or directed toward commercial advantage or
16
+ monetary compensation.
17
+
18
+ 2. Copyright Notice & License Text
19
+ The above copyright notice and this permission notice shall be included in
20
+ all copies or substantial portions of the Software.
21
+
22
+ 3. Disclaimer of Warranty
23
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. IN NO EVENT SHALL
26
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ general_manager