GeneralManager 0.19.1__py3-none-any.whl → 0.20.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.

Files changed (64) hide show
  1. general_manager/_types/api.py +4 -4
  2. general_manager/_types/bucket.py +4 -4
  3. general_manager/_types/cache.py +6 -6
  4. general_manager/_types/factory.py +35 -35
  5. general_manager/_types/general_manager.py +11 -9
  6. general_manager/_types/interface.py +5 -5
  7. general_manager/_types/manager.py +4 -4
  8. general_manager/_types/measurement.py +1 -1
  9. general_manager/_types/permission.py +3 -3
  10. general_manager/_types/utils.py +12 -12
  11. general_manager/api/graphql.py +207 -98
  12. general_manager/api/mutation.py +9 -9
  13. general_manager/api/property.py +4 -4
  14. general_manager/apps.py +120 -65
  15. general_manager/bucket/{baseBucket.py → base_bucket.py} +5 -5
  16. general_manager/bucket/{calculationBucket.py → calculation_bucket.py} +10 -10
  17. general_manager/bucket/{databaseBucket.py → database_bucket.py} +16 -19
  18. general_manager/bucket/{groupBucket.py → group_bucket.py} +8 -8
  19. general_manager/cache/{cacheDecorator.py → cache_decorator.py} +27 -6
  20. general_manager/cache/{cacheTracker.py → cache_tracker.py} +1 -1
  21. general_manager/cache/{dependencyIndex.py → dependency_index.py} +24 -8
  22. general_manager/cache/{modelDependencyCollector.py → model_dependency_collector.py} +4 -4
  23. general_manager/cache/signals.py +1 -1
  24. general_manager/factory/{autoFactory.py → auto_factory.py} +24 -19
  25. general_manager/factory/factories.py +10 -13
  26. general_manager/factory/{factoryMethods.py → factory_methods.py} +19 -17
  27. general_manager/interface/{baseInterface.py → base_interface.py} +30 -22
  28. general_manager/interface/{calculationInterface.py → calculation_interface.py} +10 -10
  29. general_manager/interface/{databaseBasedInterface.py → database_based_interface.py} +42 -42
  30. general_manager/interface/{databaseInterface.py → database_interface.py} +21 -21
  31. general_manager/interface/models.py +3 -3
  32. general_manager/interface/{readOnlyInterface.py → read_only_interface.py} +34 -25
  33. general_manager/logging.py +133 -0
  34. general_manager/manager/{generalManager.py → general_manager.py} +75 -17
  35. general_manager/manager/{groupManager.py → group_manager.py} +6 -6
  36. general_manager/manager/input.py +1 -1
  37. general_manager/manager/meta.py +63 -17
  38. general_manager/measurement/measurement.py +3 -3
  39. general_manager/permission/{basePermission.py → base_permission.py} +55 -32
  40. general_manager/permission/{managerBasedPermission.py → manager_based_permission.py} +21 -21
  41. general_manager/permission/{mutationPermission.py → mutation_permission.py} +12 -12
  42. general_manager/permission/{permissionChecks.py → permission_checks.py} +2 -2
  43. general_manager/permission/{permissionDataManager.py → permission_data_manager.py} +6 -6
  44. general_manager/permission/utils.py +6 -6
  45. general_manager/public_api_registry.py +76 -66
  46. general_manager/rule/handler.py +2 -2
  47. general_manager/rule/rule.py +102 -11
  48. general_manager/utils/{filterParser.py → filter_parser.py} +3 -3
  49. general_manager/utils/{jsonEncoder.py → json_encoder.py} +1 -1
  50. general_manager/utils/{makeCacheKey.py → make_cache_key.py} +1 -1
  51. general_manager/utils/{noneToZero.py → none_to_zero.py} +1 -1
  52. general_manager/utils/{pathMapping.py → path_mapping.py} +14 -14
  53. general_manager/utils/public_api.py +19 -0
  54. general_manager/utils/testing.py +14 -14
  55. {generalmanager-0.19.1.dist-info → generalmanager-0.20.0.dist-info}/METADATA +1 -1
  56. generalmanager-0.20.0.dist-info/RECORD +78 -0
  57. generalmanager-0.19.1.dist-info/RECORD +0 -77
  58. /general_manager/measurement/{measurementField.py → measurement_field.py} +0 -0
  59. /general_manager/permission/{fileBasedPermission.py → file_based_permission.py} +0 -0
  60. /general_manager/utils/{argsToKwargs.py → args_to_kwargs.py} +0 -0
  61. /general_manager/utils/{formatString.py → format_string.py} +0 -0
  62. {generalmanager-0.19.1.dist-info → generalmanager-0.20.0.dist-info}/WHEEL +0 -0
  63. {generalmanager-0.19.1.dist-info → generalmanager-0.20.0.dist-info}/licenses/LICENSE +0 -0
  64. {generalmanager-0.19.1.dist-info → generalmanager-0.20.0.dist-info}/top_level.txt +0 -0
@@ -8,7 +8,7 @@ from typing import (
8
8
  )
9
9
  from django.db import models, transaction
10
10
  from simple_history.utils import update_change_reason # type: ignore
11
- from general_manager.interface.databaseBasedInterface import (
11
+ from general_manager.interface.database_based_interface import (
12
12
  DBBasedInterface,
13
13
  GeneralManagerModel,
14
14
  )
@@ -69,7 +69,7 @@ class DatabaseInterface(DBBasedInterface[GeneralManagerModel]):
69
69
  @classmethod
70
70
  def create(
71
71
  cls, creator_id: int | None, history_comment: str | None = None, **kwargs: Any
72
- ) -> int:
72
+ ) -> dict[str, Any]:
73
73
  """
74
74
  Create a new model instance using the provided field values.
75
75
 
@@ -86,16 +86,16 @@ class DatabaseInterface(DBBasedInterface[GeneralManagerModel]):
86
86
  ValidationError: If model validation fails during save.
87
87
  """
88
88
  model_cls = cast(type[GeneralManagerModel], cls._model)
89
- cls._checkForInvalidKwargs(model_cls, kwargs=kwargs)
90
- kwargs, many_to_many_kwargs = cls._sortKwargs(model_cls, kwargs)
91
- instance = cls.__setAttrForWrite(model_cls(), kwargs)
89
+ cls._check_for_invalid_kwargs(model_cls, kwargs=kwargs)
90
+ kwargs, many_to_many_kwargs = cls._sort_kwargs(model_cls, kwargs)
91
+ instance = cls.__set_attr_for_write(model_cls(), kwargs)
92
92
  pk = cls._save_with_history(instance, creator_id, history_comment)
93
- cls.__setManyToManyAttributes(instance, many_to_many_kwargs)
94
- return pk
93
+ cls.__set_many_to_many_attributes(instance, many_to_many_kwargs)
94
+ return {"id": pk}
95
95
 
96
96
  def update(
97
97
  self, creator_id: int | None, history_comment: str | None = None, **kwargs: Any
98
- ) -> int:
98
+ ) -> dict[str, Any]:
99
99
  """
100
100
  Update this instance with the provided field values.
101
101
 
@@ -112,16 +112,16 @@ class DatabaseInterface(DBBasedInterface[GeneralManagerModel]):
112
112
  ValidationError: If model validation fails during save.
113
113
  """
114
114
  model_cls = cast(type[GeneralManagerModel], self._model)
115
- self._checkForInvalidKwargs(model_cls, kwargs=kwargs)
116
- kwargs, many_to_many_kwargs = self._sortKwargs(model_cls, kwargs)
117
- instance = self.__setAttrForWrite(model_cls.objects.get(pk=self.pk), kwargs)
115
+ self._check_for_invalid_kwargs(model_cls, kwargs=kwargs)
116
+ kwargs, many_to_many_kwargs = self._sort_kwargs(model_cls, kwargs)
117
+ instance = self.__set_attr_for_write(model_cls.objects.get(pk=self.pk), kwargs)
118
118
  pk = self._save_with_history(instance, creator_id, history_comment)
119
- self.__setManyToManyAttributes(instance, many_to_many_kwargs)
120
- return pk
119
+ self.__set_many_to_many_attributes(instance, many_to_many_kwargs)
120
+ return {"id": pk}
121
121
 
122
122
  def deactivate(
123
123
  self, creator_id: int | None, history_comment: str | None = None
124
- ) -> int:
124
+ ) -> dict[str, Any]:
125
125
  """
126
126
  Mark the current model instance as inactive and record the change.
127
127
 
@@ -139,10 +139,10 @@ class DatabaseInterface(DBBasedInterface[GeneralManagerModel]):
139
139
  history_comment = f"{history_comment} (deactivated)"
140
140
  else:
141
141
  history_comment = "Deactivated"
142
- return self._save_with_history(instance, creator_id, history_comment)
142
+ return {"id": self._save_with_history(instance, creator_id, history_comment)}
143
143
 
144
144
  @staticmethod
145
- def __setManyToManyAttributes(
145
+ def __set_many_to_many_attributes(
146
146
  instance: GeneralManagerModel, many_to_many_kwargs: dict[str, list[Any]]
147
147
  ) -> GeneralManagerModel:
148
148
  """
@@ -155,7 +155,7 @@ class DatabaseInterface(DBBasedInterface[GeneralManagerModel]):
155
155
  Returns:
156
156
  GeneralManagerModel: Updated instance.
157
157
  """
158
- from general_manager.manager.generalManager import GeneralManager
158
+ from general_manager.manager.general_manager import GeneralManager
159
159
 
160
160
  for key, value in many_to_many_kwargs.items():
161
161
  if value is None or value is NOT_PROVIDED:
@@ -173,7 +173,7 @@ class DatabaseInterface(DBBasedInterface[GeneralManagerModel]):
173
173
  return instance
174
174
 
175
175
  @staticmethod
176
- def __setAttrForWrite(
176
+ def __set_attr_for_write(
177
177
  instance: GeneralManagerModel,
178
178
  kwargs: dict[str, Any],
179
179
  ) -> GeneralManagerModel:
@@ -193,7 +193,7 @@ class DatabaseInterface(DBBasedInterface[GeneralManagerModel]):
193
193
  InvalidFieldValueError: If setting an attribute raises a `ValueError`.
194
194
  InvalidFieldTypeError: If setting an attribute raises a `TypeError`.
195
195
  """
196
- from general_manager.manager.generalManager import GeneralManager
196
+ from general_manager.manager.general_manager import GeneralManager
197
197
 
198
198
  for key, value in kwargs.items():
199
199
  if isinstance(value, GeneralManager):
@@ -210,7 +210,7 @@ class DatabaseInterface(DBBasedInterface[GeneralManagerModel]):
210
210
  return instance
211
211
 
212
212
  @staticmethod
213
- def _checkForInvalidKwargs(
213
+ def _check_for_invalid_kwargs(
214
214
  model: Type[models.Model], kwargs: dict[str, Any]
215
215
  ) -> None:
216
216
  """
@@ -231,7 +231,7 @@ class DatabaseInterface(DBBasedInterface[GeneralManagerModel]):
231
231
  raise UnknownFieldError(key, model.__name__)
232
232
 
233
233
  @staticmethod
234
- def _sortKwargs(
234
+ def _sort_kwargs(
235
235
  model: Type[models.Model], kwargs: dict[Any, Any]
236
236
  ) -> tuple[dict[str, Any], dict[str, list[Any]]]:
237
237
  """
@@ -10,13 +10,13 @@ from django.core.exceptions import ValidationError
10
10
 
11
11
 
12
12
  if TYPE_CHECKING:
13
- from general_manager.manager.generalManager import GeneralManager
13
+ from general_manager.manager.general_manager import GeneralManager
14
14
  from general_manager.rule.rule import Rule
15
15
 
16
16
  modelsModel = TypeVar("modelsModel", bound=models.Model)
17
17
 
18
18
 
19
- def getFullCleanMethode(model: Type[models.Model]) -> Callable[..., None]:
19
+ def get_full_clean_methode(model: Type[models.Model]) -> Callable[..., None]:
20
20
  """
21
21
  Return a custom `full_clean` method for a Django model that performs both standard validation and additional rule-based checks.
22
22
 
@@ -44,7 +44,7 @@ def getFullCleanMethode(model: Type[models.Model]) -> Callable[..., None]:
44
44
  rules: list[Rule] = getattr(self._meta, "rules", [])
45
45
  for rule in rules:
46
46
  if rule.evaluate(self) is False:
47
- error_message = rule.getErrorMessage()
47
+ error_message = rule.get_error_message()
48
48
  if error_message:
49
49
  errors.update(error_message)
50
50
 
@@ -1,28 +1,29 @@
1
1
  """Read-only interface that mirrors JSON datasets into Django models."""
2
2
 
3
3
  from __future__ import annotations
4
+
4
5
  import json
6
+ from typing import TYPE_CHECKING, Any, Callable, ClassVar, Type, cast
7
+
8
+ from django.core.checks import Warning
9
+ from django.db import connection, models, transaction
5
10
 
6
- from typing import Type, Any, Callable, TYPE_CHECKING, cast, ClassVar
7
- from django.db import models, transaction
8
- from general_manager.interface.databaseBasedInterface import (
11
+ from general_manager.interface.database_based_interface import (
9
12
  DBBasedInterface,
10
13
  GeneralManagerBasisModel,
11
- classPreCreationMethod,
14
+ attributes,
12
15
  classPostCreationMethod,
16
+ classPreCreationMethod,
13
17
  generalManagerClassName,
14
- attributes,
15
18
  interfaceBaseClass,
16
19
  )
17
- from django.db import connection
18
- from django.core.checks import Warning
19
- import logging
20
+ from general_manager.logging import get_logger
20
21
 
21
22
  if TYPE_CHECKING:
22
- from general_manager.manager.generalManager import GeneralManager
23
+ from general_manager.manager.general_manager import GeneralManager
23
24
 
24
25
 
25
- logger = logging.getLogger(__name__)
26
+ logger = get_logger("interface.read_only")
26
27
 
27
28
 
28
29
  class MissingReadOnlyDataError(ValueError):
@@ -86,7 +87,7 @@ class ReadOnlyInterface(DBBasedInterface[GeneralManagerBasisModel]):
86
87
  _parent_class: ClassVar[Type["GeneralManager"]]
87
88
 
88
89
  @staticmethod
89
- def getUniqueFields(model: Type[models.Model]) -> set[str]:
90
+ def get_unique_fields(model: Type[models.Model]) -> set[str]:
90
91
  """
91
92
  Determine which fields on the given Django model uniquely identify its instances.
92
93
 
@@ -117,7 +118,7 @@ class ReadOnlyInterface(DBBasedInterface[GeneralManagerBasisModel]):
117
118
  return unique_fields
118
119
 
119
120
  @classmethod
120
- def syncData(cls) -> None:
121
+ def sync_data(cls) -> None:
121
122
  """
122
123
  Synchronize the Django model with the parent manager's class-level `_data` JSON.
123
124
 
@@ -129,9 +130,13 @@ class ReadOnlyInterface(DBBasedInterface[GeneralManagerBasisModel]):
129
130
  InvalidReadOnlyDataTypeError: If `_data` is neither a string nor a list.
130
131
  MissingUniqueFieldError: If the model exposes no unique fields to identify records.
131
132
  """
132
- if cls.ensureSchemaIsUpToDate(cls._parent_class, cls._model):
133
+ if cls.ensure_schema_is_up_to_date(cls._parent_class, cls._model):
133
134
  logger.warning(
134
- f"Schema for ReadOnlyInterface '{cls._parent_class.__name__}' is not up to date."
135
+ "readonly schema out of date",
136
+ context={
137
+ "manager": cls._parent_class.__name__,
138
+ "model": cls._model.__name__,
139
+ },
135
140
  )
136
141
  return
137
142
 
@@ -153,7 +158,7 @@ class ReadOnlyInterface(DBBasedInterface[GeneralManagerBasisModel]):
153
158
 
154
159
  data_list = cast(list[dict[str, Any]], parsed_data)
155
160
 
156
- unique_fields = cls.getUniqueFields(model)
161
+ unique_fields = cls.get_unique_fields(model)
157
162
  if not unique_fields:
158
163
  raise MissingUniqueFieldError(parent_class.__name__)
159
164
 
@@ -208,14 +213,18 @@ class ReadOnlyInterface(DBBasedInterface[GeneralManagerBasisModel]):
208
213
 
209
214
  if changes["created"] or changes["updated"] or changes["deactivated"]:
210
215
  logger.info(
211
- f"Data changes for ReadOnlyInterface '{parent_class.__name__}': "
212
- f"Created: {len(changes['created'])}, "
213
- f"Updated: {len(changes['updated'])}, "
214
- f"Deactivated: {len(changes['deactivated'])}"
216
+ "readonly data synchronized",
217
+ context={
218
+ "manager": parent_class.__name__,
219
+ "model": model.__name__,
220
+ "created": len(changes["created"]),
221
+ "updated": len(changes["updated"]),
222
+ "deactivated": len(changes["deactivated"]),
223
+ },
215
224
  )
216
225
 
217
226
  @staticmethod
218
- def ensureSchemaIsUpToDate(
227
+ def ensure_schema_is_up_to_date(
219
228
  new_manager_class: Type[GeneralManager], model: Type[models.Model]
220
229
  ) -> list[Warning]:
221
230
  """
@@ -286,7 +295,7 @@ class ReadOnlyInterface(DBBasedInterface[GeneralManagerBasisModel]):
286
295
  return []
287
296
 
288
297
  @staticmethod
289
- def readOnlyPostCreate(func: Callable[..., Any]) -> Callable[..., Any]:
298
+ def read_only_post_create(func: Callable[..., Any]) -> Callable[..., Any]:
290
299
  """
291
300
  Decorator for post-creation hooks that registers a new manager class as read-only.
292
301
 
@@ -311,7 +320,7 @@ class ReadOnlyInterface(DBBasedInterface[GeneralManagerBasisModel]):
311
320
  return wrapper
312
321
 
313
322
  @staticmethod
314
- def readOnlyPreCreate(func: Callable[..., Any]) -> Callable[..., Any]:
323
+ def read_only_pre_create(func: Callable[..., Any]) -> Callable[..., Any]:
315
324
  """
316
325
  Decorator for pre-creation hook functions that ensures the base model class is set to `GeneralManagerBasisModel`.
317
326
 
@@ -344,7 +353,7 @@ class ReadOnlyInterface(DBBasedInterface[GeneralManagerBasisModel]):
344
353
  return wrapper
345
354
 
346
355
  @classmethod
347
- def handleInterface(cls) -> tuple[classPreCreationMethod, classPostCreationMethod]:
356
+ def handle_interface(cls) -> tuple[classPreCreationMethod, classPostCreationMethod]:
348
357
  """
349
358
  Return the pre- and post-creation hook methods for integrating the interface with a manager meta-class system.
350
359
 
@@ -355,6 +364,6 @@ class ReadOnlyInterface(DBBasedInterface[GeneralManagerBasisModel]):
355
364
  Returns:
356
365
  tuple: The pre-creation and post-creation hook methods for manager class lifecycle integration.
357
366
  """
358
- return cls.readOnlyPreCreate(cls._preCreate), cls.readOnlyPostCreate(
359
- cls._postCreate
367
+ return cls.read_only_pre_create(cls._pre_create), cls.read_only_post_create(
368
+ cls._post_create
360
369
  )
@@ -0,0 +1,133 @@
1
+ """
2
+ Shared logging utilities for the GeneralManager package.
3
+
4
+ The helpers defined here keep logger names consistent (``general_manager.*``),
5
+ expose lightweight context support, and stay fully compatible with Django's
6
+ ``LOGGING`` settings.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import logging
12
+ from collections.abc import Mapping, MutableMapping
13
+ from typing import Any, cast
14
+
15
+ BASE_LOGGER_NAME = "general_manager"
16
+ COMPONENT_EXTRA_FIELD = "component"
17
+ CONTEXT_EXTRA_FIELD = "context"
18
+
19
+
20
+ class InvalidContextError(TypeError):
21
+ def __init__(self) -> None:
22
+ super().__init__("context must be a mapping when provided.")
23
+
24
+
25
+ class InvalidExtraError(TypeError):
26
+ def __init__(self) -> None:
27
+ super().__init__("extra must be a mutable mapping.")
28
+
29
+
30
+ class BlankComponentError(ValueError):
31
+ def __init__(self) -> None:
32
+ super().__init__("component cannot be blank or only dots.")
33
+
34
+
35
+ __all__ = [
36
+ "BASE_LOGGER_NAME",
37
+ "COMPONENT_EXTRA_FIELD",
38
+ "CONTEXT_EXTRA_FIELD",
39
+ "GeneralManagerLoggerAdapter",
40
+ "build_logger_name",
41
+ "get_logger",
42
+ ]
43
+
44
+
45
+ class GeneralManagerLoggerAdapter(logging.LoggerAdapter[Any]):
46
+ """
47
+ Attach structured metadata (component + context) to log records.
48
+
49
+ The adapter keeps ``extra`` mutable, merges ``context`` mappings, and can be
50
+ used anywhere ``logging.Logger`` is expected.
51
+ """
52
+
53
+ def log(self, level: int, msg: Any, *args: Any, **kwargs: Any) -> None: # type: ignore[override]
54
+ context_mapping = self._pop_context(kwargs)
55
+ if context_mapping is not None:
56
+ kwargs["context"] = context_mapping
57
+ super().log(level, msg, *args, **kwargs)
58
+
59
+ @staticmethod
60
+ def _pop_context(
61
+ kwargs: MutableMapping[str, Any],
62
+ ) -> Mapping[str, Any] | None:
63
+ context = kwargs.pop("context", None)
64
+ if context is None:
65
+ return None
66
+ if not isinstance(context, Mapping):
67
+ raise InvalidContextError()
68
+ return context
69
+
70
+ def process(
71
+ self, msg: Any, kwargs: MutableMapping[str, Any]
72
+ ) -> tuple[Any, MutableMapping[str, Any]]:
73
+ context = self._pop_context(kwargs)
74
+
75
+ extra_obj = kwargs.setdefault("extra", {})
76
+ if not isinstance(extra_obj, MutableMapping):
77
+ raise InvalidExtraError()
78
+ extra = cast(MutableMapping[str, Any], extra_obj)
79
+
80
+ extra_metadata = cast(Mapping[str, Any], self.extra or {})
81
+ component = extra_metadata.get(COMPONENT_EXTRA_FIELD)
82
+ if component is not None:
83
+ extra.setdefault(COMPONENT_EXTRA_FIELD, component)
84
+
85
+ if context is not None:
86
+ current_context = cast(Mapping[str, Any], context)
87
+ existing_context = extra.get(CONTEXT_EXTRA_FIELD)
88
+ if existing_context is None:
89
+ merged_context: dict[str, Any] = dict(current_context)
90
+ elif isinstance(existing_context, Mapping):
91
+ merged_context = {**dict(existing_context), **current_context}
92
+ else:
93
+ raise InvalidContextError()
94
+
95
+ extra[CONTEXT_EXTRA_FIELD] = merged_context
96
+
97
+ return msg, kwargs
98
+
99
+
100
+ def _normalize_component_name(component: str | None) -> str | None:
101
+ if component is None:
102
+ return None
103
+
104
+ normalized = component.strip().strip(".")
105
+ if not normalized:
106
+ raise BlankComponentError()
107
+
108
+ return normalized.replace(" ", "_")
109
+
110
+
111
+ def build_logger_name(component: str | None = None) -> str:
112
+ """
113
+ Build a fully-qualified logger name within the ``general_manager`` namespace.
114
+ """
115
+
116
+ normalized_component = _normalize_component_name(component)
117
+ if not normalized_component:
118
+ return BASE_LOGGER_NAME
119
+
120
+ return ".".join([BASE_LOGGER_NAME, normalized_component])
121
+
122
+
123
+ def get_logger(component: str | None = None) -> GeneralManagerLoggerAdapter:
124
+ """
125
+ Return a ``GeneralManagerLoggerAdapter`` scoped to the requested component.
126
+ """
127
+
128
+ normalized_component = _normalize_component_name(component)
129
+ logger_name = build_logger_name(normalized_component)
130
+ adapter_extra: dict[str, Any] = {}
131
+ if normalized_component:
132
+ adapter_extra[COMPONENT_EXTRA_FIELD] = normalized_component
133
+ return GeneralManagerLoggerAdapter(logging.getLogger(logger_name), adapter_extra)
@@ -1,11 +1,12 @@
1
1
  from __future__ import annotations
2
2
  from typing import TYPE_CHECKING, Any, Iterator, Self, Type
3
- from general_manager.manager.meta import GeneralManagerMeta
4
3
 
5
4
  from general_manager.api.property import GraphQLProperty
6
- from general_manager.cache.cacheTracker import DependencyTracker
7
- from general_manager.cache.signals import dataChange
8
- from general_manager.bucket.baseBucket import Bucket
5
+ from general_manager.bucket.base_bucket import Bucket
6
+ from general_manager.cache.cache_tracker import DependencyTracker
7
+ from general_manager.cache.signals import data_change
8
+ from general_manager.logging import get_logger
9
+ from general_manager.manager.meta import GeneralManagerMeta
9
10
 
10
11
 
11
12
  class UnsupportedUnionOperandError(TypeError):
@@ -22,8 +23,11 @@ class UnsupportedUnionOperandError(TypeError):
22
23
 
23
24
 
24
25
  if TYPE_CHECKING:
25
- from general_manager.permission.basePermission import BasePermission
26
- from general_manager.interface.baseInterface import InterfaceBase
26
+ from general_manager.permission.base_permission import BasePermission
27
+ from general_manager.interface.base_interface import InterfaceBase
28
+
29
+
30
+ logger = get_logger("manager.general")
27
31
 
28
32
 
29
33
  class GeneralManager(metaclass=GeneralManagerMeta):
@@ -45,6 +49,13 @@ class GeneralManager(metaclass=GeneralManagerMeta):
45
49
  DependencyTracker.track(
46
50
  self.__class__.__name__, "identification", f"{self.__id}"
47
51
  )
52
+ logger.debug(
53
+ "instantiated manager",
54
+ context={
55
+ "manager": self.__class__.__name__,
56
+ "identification": self.__id,
57
+ },
58
+ )
48
59
 
49
60
  def __str__(self) -> str:
50
61
  """Return a user-friendly representation showing the identification."""
@@ -117,7 +128,7 @@ class GeneralManager(metaclass=GeneralManagerMeta):
117
128
  yield name, getattr(self, name)
118
129
 
119
130
  @classmethod
120
- @dataChange
131
+ @data_change
121
132
  def create(
122
133
  cls,
123
134
  creator_id: int | None = None,
@@ -141,13 +152,23 @@ class GeneralManager(metaclass=GeneralManagerMeta):
141
152
  PermissionError: Propagated if the permission check fails.
142
153
  """
143
154
  if not ignore_permission:
144
- cls.Permission.checkCreatePermission(kwargs, cls, creator_id)
155
+ cls.Permission.check_create_permission(kwargs, cls, creator_id)
145
156
  identification = cls.Interface.create(
146
157
  creator_id=creator_id, history_comment=history_comment, **kwargs
147
158
  )
148
- return cls(identification)
159
+ logger.info(
160
+ "manager created",
161
+ context={
162
+ "manager": cls.__name__,
163
+ "creator_id": creator_id,
164
+ "ignore_permission": ignore_permission,
165
+ "fields": sorted(kwargs.keys()),
166
+ "identification": identification,
167
+ },
168
+ )
169
+ return cls(**identification)
149
170
 
150
- @dataChange
171
+ @data_change
151
172
  def update(
152
173
  self,
153
174
  creator_id: int | None = None,
@@ -171,15 +192,25 @@ class GeneralManager(metaclass=GeneralManagerMeta):
171
192
  PermissionError: Propagated if the permission check fails.
172
193
  """
173
194
  if not ignore_permission:
174
- self.Permission.checkUpdatePermission(kwargs, self, creator_id)
195
+ self.Permission.check_update_permission(kwargs, self, creator_id)
175
196
  self._interface.update(
176
197
  creator_id=creator_id,
177
198
  history_comment=history_comment,
178
199
  **kwargs,
179
200
  )
201
+ logger.info(
202
+ "manager updated",
203
+ context={
204
+ "manager": self.__class__.__name__,
205
+ "creator_id": creator_id,
206
+ "ignore_permission": ignore_permission,
207
+ "fields": sorted(kwargs.keys()),
208
+ "identification": self.identification,
209
+ },
210
+ )
180
211
  return self.__class__(**self.identification)
181
212
 
182
- @dataChange
213
+ @data_change
183
214
  def deactivate(
184
215
  self,
185
216
  creator_id: int | None = None,
@@ -201,10 +232,19 @@ class GeneralManager(metaclass=GeneralManagerMeta):
201
232
  PermissionError: Propagated if the permission check fails.
202
233
  """
203
234
  if not ignore_permission:
204
- self.Permission.checkDeletePermission(self, creator_id)
235
+ self.Permission.check_delete_permission(self, creator_id)
205
236
  self._interface.deactivate(
206
237
  creator_id=creator_id, history_comment=history_comment
207
238
  )
239
+ logger.info(
240
+ "manager deactivated",
241
+ context={
242
+ "manager": self.__class__.__name__,
243
+ "creator_id": creator_id,
244
+ "ignore_permission": ignore_permission,
245
+ "identification": self.identification,
246
+ },
247
+ )
208
248
  return self.__class__(**self.identification)
209
249
 
210
250
  @classmethod
@@ -218,8 +258,14 @@ class GeneralManager(metaclass=GeneralManagerMeta):
218
258
  Returns:
219
259
  Bucket[Self]: Bucket of matching manager instances.
220
260
  """
221
- DependencyTracker.track(
222
- cls.__name__, "filter", f"{cls.__parse_identification(kwargs)}"
261
+ identifier_map = cls.__parse_identification(kwargs) or kwargs
262
+ DependencyTracker.track(cls.__name__, "filter", repr(identifier_map))
263
+ logger.debug(
264
+ "manager filter",
265
+ context={
266
+ "manager": cls.__name__,
267
+ "filters": identifier_map,
268
+ },
223
269
  )
224
270
  return cls.Interface.filter(**kwargs)
225
271
 
@@ -234,14 +280,26 @@ class GeneralManager(metaclass=GeneralManagerMeta):
234
280
  Returns:
235
281
  Bucket[Self]: Bucket of manager instances that do not satisfy the lookups.
236
282
  """
237
- DependencyTracker.track(
238
- cls.__name__, "exclude", f"{cls.__parse_identification(kwargs)}"
283
+ identifier_map = cls.__parse_identification(kwargs) or kwargs
284
+ DependencyTracker.track(cls.__name__, "exclude", repr(identifier_map))
285
+ logger.debug(
286
+ "manager exclude",
287
+ context={
288
+ "manager": cls.__name__,
289
+ "filters": identifier_map,
290
+ },
239
291
  )
240
292
  return cls.Interface.exclude(**kwargs)
241
293
 
242
294
  @classmethod
243
295
  def all(cls) -> Bucket[Self]:
244
296
  """Return a bucket containing every managed object of this class."""
297
+ logger.debug(
298
+ "manager all",
299
+ context={
300
+ "manager": cls.__name__,
301
+ },
302
+ )
245
303
  return cls.Interface.filter()
246
304
 
247
305
  @staticmethod
@@ -5,8 +5,8 @@ from typing import Any, Generic, Iterator, Type, cast, get_args
5
5
  from datetime import datetime, date, time
6
6
  from general_manager.api.property import GraphQLProperty
7
7
  from general_manager.measurement import Measurement
8
- from general_manager.manager.generalManager import GeneralManager
9
- from general_manager.bucket.baseBucket import (
8
+ from general_manager.manager.general_manager import GeneralManager
9
+ from general_manager.bucket.base_bucket import (
10
10
  Bucket,
11
11
  GeneralManagerType,
12
12
  )
@@ -99,7 +99,7 @@ class GroupManager(Generic[GeneralManagerType]):
99
99
  Yields:
100
100
  tuple[str, Any]: Attribute name and aggregated value pairs.
101
101
  """
102
- for attribute in self._manager_class.Interface.getAttributes().keys():
102
+ for attribute in self._manager_class.Interface.get_attributes().keys():
103
103
  yield attribute, getattr(self, attribute)
104
104
  for attribute, attr_value in self._manager_class.__dict__.items():
105
105
  if isinstance(attr_value, GraphQLProperty):
@@ -121,10 +121,10 @@ class GroupManager(Generic[GeneralManagerType]):
121
121
  if item in self._group_by_value:
122
122
  return self._group_by_value[item]
123
123
  if item not in self._grouped_data.keys():
124
- self._grouped_data[item] = self.combineValue(item)
124
+ self._grouped_data[item] = self.combine_value(item)
125
125
  return self._grouped_data[item]
126
126
 
127
- def combineValue(self, item: str) -> Any:
127
+ def combine_value(self, item: str) -> Any:
128
128
  """
129
129
  Aggregate the values of a named attribute across all records in the group.
130
130
 
@@ -140,7 +140,7 @@ class GroupManager(Generic[GeneralManagerType]):
140
140
  if item == "id":
141
141
  return None
142
142
 
143
- attribute_types = self._manager_class.Interface.getAttributeTypes()
143
+ attribute_types = self._manager_class.Interface.get_attribute_types()
144
144
  attr_info = attribute_types.get(item)
145
145
  data_type = attr_info["type"] if attr_info else None
146
146
  if data_type is None and item in self._manager_class.__dict__:
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
  from typing import Iterable, Optional, Callable, List, TypeVar, Generic, Any, Type, cast
5
5
  import inspect
6
6
 
7
- from general_manager.manager.generalManager import GeneralManager
7
+ from general_manager.manager.general_manager import GeneralManager
8
8
  from datetime import date, datetime
9
9
  from general_manager.measurement import Measurement
10
10