GeneralManager 0.19.2__py3-none-any.whl → 0.20.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of GeneralManager might be problematic. Click here for more details.

@@ -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, IntegrityError
5
10
 
6
- from typing import Type, Any, Callable, TYPE_CHECKING, cast, ClassVar
7
- from django.db import models, transaction
8
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
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):
@@ -131,7 +132,11 @@ class ReadOnlyInterface(DBBasedInterface[GeneralManagerBasisModel]):
131
132
  """
132
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
 
@@ -154,6 +159,7 @@ class ReadOnlyInterface(DBBasedInterface[GeneralManagerBasisModel]):
154
159
  data_list = cast(list[dict[str, Any]], parsed_data)
155
160
 
156
161
  unique_fields = cls.get_unique_fields(model)
162
+ unique_field_order = tuple(sorted(unique_fields))
157
163
  if not unique_fields:
158
164
  raise MissingUniqueFieldError(parent_class.__name__)
159
165
 
@@ -163,29 +169,43 @@ class ReadOnlyInterface(DBBasedInterface[GeneralManagerBasisModel]):
163
169
  "deactivated": [],
164
170
  }
165
171
 
172
+ editable_fields = {
173
+ f.name
174
+ for f in model._meta.local_fields
175
+ if getattr(f, "editable", True) and not getattr(f, "primary_key", False)
176
+ } - {"is_active"}
177
+
166
178
  with transaction.atomic():
167
- json_unique_values: set[Any] = set()
179
+ json_unique_values: set[tuple[Any, ...]] = set()
168
180
 
169
181
  # data synchronization
170
182
  for idx, data in enumerate(data_list):
171
183
  try:
172
- lookup = {field: data[field] for field in unique_fields}
184
+ lookup = {field: data[field] for field in unique_field_order}
173
185
  except KeyError as e:
174
186
  missing = e.args[0]
175
187
  raise InvalidReadOnlyDataFormatError() from KeyError(
176
188
  f"Item {idx} missing unique field '{missing}'."
177
189
  )
178
- unique_identifier = tuple(lookup[field] for field in unique_fields)
190
+ unique_identifier = tuple(lookup[field] for field in unique_field_order)
179
191
  json_unique_values.add(unique_identifier)
180
-
181
- instance, is_created = model.objects.get_or_create(**lookup)
192
+ instance = model.objects.filter(**lookup).first()
193
+ is_created = False
194
+ if instance is None:
195
+ # sanitize input and create with race-safety
196
+ allowed_fields = {f.name for f in model._meta.local_fields}
197
+ create_kwargs = {
198
+ k: v for k, v in data.items() if k in allowed_fields
199
+ }
200
+ try:
201
+ instance = model.objects.create(**create_kwargs)
202
+ is_created = True
203
+ except IntegrityError:
204
+ # created concurrently — fetch it
205
+ instance = model.objects.filter(**lookup).first()
206
+ if instance is None:
207
+ raise
182
208
  updated = False
183
- editable_fields = {
184
- f.name
185
- for f in model._meta.local_fields
186
- if getattr(f, "editable", True)
187
- and not getattr(f, "primary_key", False)
188
- } - {"is_active"}
189
209
  for field_name in editable_fields.intersection(data.keys()):
190
210
  value = data[field_name]
191
211
  if getattr(instance, field_name, None) != value:
@@ -199,8 +219,10 @@ class ReadOnlyInterface(DBBasedInterface[GeneralManagerBasisModel]):
199
219
  # deactivate instances not in JSON data
200
220
  existing_instances = model.objects.filter(is_active=True)
201
221
  for instance in existing_instances:
202
- lookup = {field: getattr(instance, field) for field in unique_fields}
203
- unique_identifier = tuple(lookup[field] for field in unique_fields)
222
+ lookup = {
223
+ field: getattr(instance, field) for field in unique_field_order
224
+ }
225
+ unique_identifier = tuple(lookup[field] for field in unique_field_order)
204
226
  if unique_identifier not in json_unique_values:
205
227
  instance.is_active = False
206
228
  instance.save()
@@ -208,10 +230,14 @@ class ReadOnlyInterface(DBBasedInterface[GeneralManagerBasisModel]):
208
230
 
209
231
  if changes["created"] or changes["updated"] or changes["deactivated"]:
210
232
  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'])}"
233
+ "readonly data synchronized",
234
+ context={
235
+ "manager": parent_class.__name__,
236
+ "model": model.__name__,
237
+ "created": len(changes["created"]),
238
+ "updated": len(changes["updated"]),
239
+ "deactivated": len(changes["deactivated"]),
240
+ },
215
241
  )
216
242
 
217
243
  @staticmethod
@@ -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
5
+ from general_manager.bucket.base_bucket import Bucket
6
6
  from general_manager.cache.cache_tracker import DependencyTracker
7
7
  from general_manager.cache.signals import data_change
8
- from general_manager.bucket.base_bucket import Bucket
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):
@@ -26,6 +27,9 @@ if TYPE_CHECKING:
26
27
  from general_manager.interface.base_interface import InterfaceBase
27
28
 
28
29
 
30
+ logger = get_logger("manager.general")
31
+
32
+
29
33
  class GeneralManager(metaclass=GeneralManagerMeta):
30
34
  Permission: Type[BasePermission]
31
35
  _attributes: dict[str, Any]
@@ -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."""
@@ -145,7 +156,17 @@ class GeneralManager(metaclass=GeneralManagerMeta):
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
171
  @data_change
151
172
  def update(
@@ -177,6 +198,16 @@ class GeneralManager(metaclass=GeneralManagerMeta):
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
213
  @data_change
@@ -205,6 +236,15 @@ class GeneralManager(metaclass=GeneralManagerMeta):
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
@@ -3,8 +3,10 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from django.conf import settings
6
- from typing import Any, Type, TYPE_CHECKING, ClassVar, TypeVar, Iterable, cast
6
+ from typing import TYPE_CHECKING, Any, ClassVar, Iterable, Type, TypeVar, cast
7
+
7
8
  from general_manager.interface.base_interface import InterfaceBase
9
+ from general_manager.logging import get_logger
8
10
 
9
11
  if TYPE_CHECKING:
10
12
  from general_manager.manager.general_manager import GeneralManager
@@ -12,6 +14,8 @@ if TYPE_CHECKING:
12
14
 
13
15
  GeneralManagerType = TypeVar("GeneralManagerType", bound="GeneralManager")
14
16
 
17
+ logger = get_logger("manager.meta")
18
+
15
19
 
16
20
  class InvalidInterfaceTypeError(TypeError):
17
21
  """Raised when a GeneralManager is configured with an incompatible Interface class."""
@@ -89,6 +93,14 @@ class GeneralManagerMeta(type):
89
93
  Returns:
90
94
  type: The newly created subclass, possibly modified by Interface hooks.
91
95
  """
96
+ logger.debug(
97
+ "creating manager class",
98
+ context={
99
+ "class_name": name,
100
+ "module": attrs.get("__module__"),
101
+ "has_interface": "Interface" in attrs,
102
+ },
103
+ )
92
104
 
93
105
  def create_new_general_manager_class(
94
106
  mcs: type["GeneralManagerMeta"],
@@ -109,12 +121,31 @@ class GeneralManagerMeta(type):
109
121
  post_creation(new_class, interface_cls, model)
110
122
  mcs.pending_attribute_initialization.append(new_class)
111
123
  mcs.all_classes.append(new_class)
124
+ logger.debug(
125
+ "registered manager class with interface",
126
+ context={
127
+ "class_name": new_class.__name__,
128
+ "interface": interface_cls.__name__,
129
+ },
130
+ )
112
131
 
113
132
  else:
114
133
  new_class = create_new_general_manager_class(mcs, name, bases, attrs)
134
+ logger.debug(
135
+ "registered manager class without interface",
136
+ context={
137
+ "class_name": new_class.__name__,
138
+ },
139
+ )
115
140
 
116
141
  if getattr(settings, "AUTOCREATE_GRAPHQL", False):
117
142
  mcs.pending_graphql_interfaces.append(new_class)
143
+ logger.debug(
144
+ "queued manager for graphql generation",
145
+ context={
146
+ "class_name": new_class.__name__,
147
+ },
148
+ )
118
149
 
119
150
  return new_class
120
151
 
@@ -179,6 +210,13 @@ class GeneralManagerMeta(type):
179
210
  return self._class.Interface.get_field_type(self._attr_name)
180
211
  attribute = instance._attributes.get(self._attr_name, _nonExistent)
181
212
  if attribute is _nonExistent:
213
+ logger.warning(
214
+ "missing attribute on manager instance",
215
+ context={
216
+ "attribute": self._attr_name,
217
+ "manager": instance.__class__.__name__,
218
+ },
219
+ )
182
220
  raise MissingAttributeError(
183
221
  self._attr_name, instance.__class__.__name__
184
222
  )
@@ -186,6 +224,14 @@ class GeneralManagerMeta(type):
186
224
  try:
187
225
  attribute = attribute(instance._interface)
188
226
  except Exception as e:
227
+ logger.exception(
228
+ "attribute evaluation failed",
229
+ context={
230
+ "attribute": self._attr_name,
231
+ "manager": instance.__class__.__name__,
232
+ "error": type(e).__name__,
233
+ },
234
+ )
189
235
  raise AttributeEvaluationError(self._attr_name, e) from e
190
236
  return attribute
191
237
 
@@ -1,23 +1,25 @@
1
1
  """Base permission contract used by GeneralManager instances."""
2
2
 
3
3
  from __future__ import annotations
4
+
4
5
  from abc import ABC, abstractmethod
5
6
  from typing import TYPE_CHECKING, Any, Literal, TypeAlias, cast
6
- from general_manager.permission.permission_checks import permission_functions
7
7
 
8
- from django.contrib.auth.models import AnonymousUser, AbstractBaseUser, AbstractUser
8
+ from django.contrib.auth.models import AbstractBaseUser, AbstractUser, AnonymousUser
9
+
10
+ from general_manager.logging import get_logger
11
+ from general_manager.permission.permission_checks import permission_functions
9
12
  from general_manager.permission.permission_data_manager import PermissionDataManager
10
13
  from general_manager.permission.utils import (
11
- validate_permission_string,
12
14
  PermissionNotFoundError,
15
+ validate_permission_string,
13
16
  )
14
- import logging
15
17
 
16
18
  if TYPE_CHECKING:
17
19
  from general_manager.manager.general_manager import GeneralManager
18
20
  from general_manager.manager.meta import GeneralManagerMeta
19
21
 
20
- logger = logging.getLogger(__name__)
22
+ logger = get_logger("permission.base")
21
23
 
22
24
  UserLike: TypeAlias = AbstractBaseUser | AnonymousUser
23
25
 
@@ -86,11 +88,18 @@ class BasePermission(ABC):
86
88
  errors = []
87
89
  permission_data = PermissionDataManager(permission_data=data, manager=manager)
88
90
  Permission = cls(permission_data, request_user)
91
+ user_identifier = getattr(request_user, "id", None)
89
92
  for key in data.keys():
90
93
  is_allowed = Permission.check_permission("create", key)
91
94
  if not is_allowed:
92
- logger.debug(
93
- f"Permission denied for {key} with value {data[key]} for user {request_user}"
95
+ logger.info(
96
+ "permission denied",
97
+ context={
98
+ "manager": manager.__name__,
99
+ "action": "create",
100
+ "attribute": key,
101
+ "user_id": user_identifier,
102
+ },
94
103
  )
95
104
  errors.append(f"Create permission denied for attribute '{key}'")
96
105
  if errors:
@@ -121,11 +130,18 @@ class BasePermission(ABC):
121
130
  base_data=old_manager_instance, update_data=data
122
131
  )
123
132
  Permission = cls(permission_data, request_user)
133
+ user_identifier = getattr(request_user, "id", None)
124
134
  for key in data.keys():
125
135
  is_allowed = Permission.check_permission("update", key)
126
136
  if not is_allowed:
127
- logger.debug(
128
- f"Permission denied for {key} with value {data[key]} for user {request_user}"
137
+ logger.info(
138
+ "permission denied",
139
+ context={
140
+ "manager": old_manager_instance.__class__.__name__,
141
+ "action": "update",
142
+ "attribute": key,
143
+ "user_id": user_identifier,
144
+ },
129
145
  )
130
146
  errors.append(f"Update permission denied for attribute '{key}'")
131
147
  if errors:
@@ -154,11 +170,18 @@ class BasePermission(ABC):
154
170
  errors = []
155
171
  permission_data = PermissionDataManager(manager_instance)
156
172
  Permission = cls(permission_data, request_user)
173
+ user_identifier = getattr(request_user, "id", None)
157
174
  for key in manager_instance.__dict__.keys():
158
175
  is_allowed = Permission.check_permission("delete", key)
159
176
  if not is_allowed:
160
- logger.debug(
161
- f"Permission denied for {key} with value {getattr(manager_instance, key)} for user {request_user}"
177
+ logger.info(
178
+ "permission denied",
179
+ context={
180
+ "manager": manager_instance.__class__.__name__,
181
+ "action": "delete",
182
+ "attribute": key,
183
+ "user_id": user_identifier,
184
+ },
162
185
  )
163
186
  errors.append(f"Delete permission denied for attribute '{key}'")
164
187
  if errors:
@@ -185,8 +208,8 @@ class BasePermission(ABC):
185
208
  if isinstance(user, (AbstractBaseUser, AnonymousUser)):
186
209
  return user
187
210
  try:
188
- return User.objects.get(id=user)
189
- except User.DoesNotExist:
211
+ return User.objects.get(pk=user)
212
+ except (User.DoesNotExist, ValueError, TypeError):
190
213
  return AnonymousUser()
191
214
 
192
215
  @abstractmethod
@@ -18,6 +18,7 @@ GENERAL_MANAGER_EXPORTS: LazyExportMap = {
18
18
  "graph_ql_mutation": ("general_manager.api.mutation", "graph_ql_mutation"),
19
19
  "GeneralManager": ("general_manager.manager.general_manager", "GeneralManager"),
20
20
  "Input": ("general_manager.manager.input", "Input"),
21
+ "get_logger": ("general_manager.logging", "get_logger"),
21
22
  "CalculationInterface": (
22
23
  "general_manager.interface.calculation_interface",
23
24
  "CalculationInterface",