GeneralManager 0.19.1__py3-none-any.whl → 0.19.2__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 (62) 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 +9 -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 +118 -95
  12. general_manager/api/mutation.py +9 -9
  13. general_manager/api/property.py +4 -4
  14. general_manager/apps.py +31 -31
  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} +5 -5
  20. general_manager/cache/{cacheTracker.py → cache_tracker.py} +1 -1
  21. general_manager/cache/{dependencyIndex.py → dependency_index.py} +1 -1
  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} +29 -21
  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} +15 -15
  31. general_manager/interface/models.py +3 -3
  32. general_manager/interface/{readOnlyInterface.py → read_only_interface.py} +12 -12
  33. general_manager/manager/{generalManager.py → general_manager.py} +11 -11
  34. general_manager/manager/{groupManager.py → group_manager.py} +6 -6
  35. general_manager/manager/input.py +1 -1
  36. general_manager/manager/meta.py +15 -15
  37. general_manager/measurement/measurement.py +3 -3
  38. general_manager/permission/{basePermission.py → base_permission.py} +21 -21
  39. general_manager/permission/{managerBasedPermission.py → manager_based_permission.py} +21 -21
  40. general_manager/permission/{mutationPermission.py → mutation_permission.py} +12 -12
  41. general_manager/permission/{permissionChecks.py → permission_checks.py} +2 -2
  42. general_manager/permission/{permissionDataManager.py → permission_data_manager.py} +6 -6
  43. general_manager/permission/utils.py +6 -6
  44. general_manager/public_api_registry.py +75 -66
  45. general_manager/rule/handler.py +2 -2
  46. general_manager/rule/rule.py +17 -11
  47. general_manager/utils/{filterParser.py → filter_parser.py} +3 -3
  48. general_manager/utils/{jsonEncoder.py → json_encoder.py} +1 -1
  49. general_manager/utils/{makeCacheKey.py → make_cache_key.py} +1 -1
  50. general_manager/utils/{noneToZero.py → none_to_zero.py} +1 -1
  51. general_manager/utils/{pathMapping.py → path_mapping.py} +14 -14
  52. general_manager/utils/testing.py +14 -14
  53. {generalmanager-0.19.1.dist-info → generalmanager-0.19.2.dist-info}/METADATA +1 -1
  54. generalmanager-0.19.2.dist-info/RECORD +77 -0
  55. generalmanager-0.19.1.dist-info/RECORD +0 -77
  56. /general_manager/measurement/{measurementField.py → measurement_field.py} +0 -0
  57. /general_manager/permission/{fileBasedPermission.py → file_based_permission.py} +0 -0
  58. /general_manager/utils/{argsToKwargs.py → args_to_kwargs.py} +0 -0
  59. /general_manager/utils/{formatString.py → format_string.py} +0 -0
  60. {generalmanager-0.19.1.dist-info → generalmanager-0.19.2.dist-info}/WHEEL +0 -0
  61. {generalmanager-0.19.1.dist-info → generalmanager-0.19.2.dist-info}/licenses/LICENSE +0 -0
  62. {generalmanager-0.19.1.dist-info → generalmanager-0.19.2.dist-info}/top_level.txt +0 -0
@@ -11,6 +11,7 @@ from copy import deepcopy
11
11
  from datetime import date, datetime
12
12
  from decimal import Decimal
13
13
  import hashlib
14
+ import re
14
15
  from types import UnionType
15
16
  from typing import (
16
17
  Any,
@@ -38,12 +39,12 @@ from graphql.language.ast import (
38
39
  from asgiref.sync import async_to_sync
39
40
  from channels.layers import BaseChannelLayer, get_channel_layer
40
41
 
41
- from general_manager.cache.cacheTracker import DependencyTracker
42
- from general_manager.cache.dependencyIndex import Dependency
42
+ from general_manager.cache.cache_tracker import DependencyTracker
43
+ from general_manager.cache.dependency_index import Dependency
43
44
  from general_manager.cache.signals import post_data_change
44
- from general_manager.bucket.baseBucket import Bucket
45
- from general_manager.interface.baseInterface import InterfaceBase
46
- from general_manager.manager.generalManager import GeneralManager
45
+ from general_manager.bucket.base_bucket import Bucket
46
+ from general_manager.interface.base_interface import InterfaceBase
47
+ from general_manager.manager.general_manager import GeneralManager
47
48
  from general_manager.measurement.measurement import Measurement
48
49
 
49
50
  from django.core.exceptions import ValidationError
@@ -52,7 +53,7 @@ from graphql import GraphQLError
52
53
 
53
54
 
54
55
  if TYPE_CHECKING:
55
- from general_manager.permission.basePermission import BasePermission
56
+ from general_manager.permission.base_permission import BasePermission
56
57
  from graphene import ResolveInfo as GraphQLResolveInfo
57
58
 
58
59
 
@@ -191,7 +192,7 @@ class PageInfo(graphene.ObjectType):
191
192
  total_pages = graphene.Int(required=True)
192
193
 
193
194
 
194
- def getReadPermissionFilter(
195
+ def get_read_permission_filter(
195
196
  generalManagerClass: Type[GeneralManager],
196
197
  info: GraphQLResolveInfo,
197
198
  ) -> list[tuple[dict[str, Any], dict[str, Any]]]:
@@ -212,7 +213,7 @@ def getReadPermissionFilter(
212
213
  if PermissionClass:
213
214
  permission_filters = PermissionClass(
214
215
  generalManagerClass, info.context.user
215
- ).getPermissionFilter()
216
+ ).get_permission_filter()
216
217
  for permission_filter in permission_filters:
217
218
  filter_dict = permission_filter.get("filter", {})
218
219
  exclude_dict = permission_filter.get("exclude", {})
@@ -317,7 +318,7 @@ class GraphQL:
317
318
  pass
318
319
 
319
320
  @classmethod
320
- def createGraphqlMutation(cls, generalManagerClass: type[GeneralManager]) -> None:
321
+ def create_graphql_mutation(cls, generalManagerClass: type[GeneralManager]) -> None:
321
322
  """
322
323
  Register GraphQL mutation classes for a GeneralManager based on its Interface.
323
324
 
@@ -341,24 +342,26 @@ class GraphQL:
341
342
  }
342
343
  if InterfaceBase.create.__code__ != interface_cls.create.__code__:
343
344
  create_name = f"create{generalManagerClass.__name__}"
344
- cls._mutations[create_name] = cls.generateCreateMutationClass(
345
+ cls._mutations[create_name] = cls.generate_create_mutation_class(
345
346
  generalManagerClass, default_return_values
346
347
  )
347
348
 
348
349
  if InterfaceBase.update.__code__ != interface_cls.update.__code__:
349
350
  update_name = f"update{generalManagerClass.__name__}"
350
- cls._mutations[update_name] = cls.generateUpdateMutationClass(
351
+ cls._mutations[update_name] = cls.generate_update_mutation_class(
351
352
  generalManagerClass, default_return_values
352
353
  )
353
354
 
354
355
  if InterfaceBase.deactivate.__code__ != interface_cls.deactivate.__code__:
355
356
  delete_name = f"delete{generalManagerClass.__name__}"
356
- cls._mutations[delete_name] = cls.generateDeleteMutationClass(
357
+ cls._mutations[delete_name] = cls.generate_delete_mutation_class(
357
358
  generalManagerClass, default_return_values
358
359
  )
359
360
 
360
361
  @classmethod
361
- def createGraphqlInterface(cls, generalManagerClass: Type[GeneralManager]) -> None:
362
+ def create_graphql_interface(
363
+ cls, generalManagerClass: Type[GeneralManager]
364
+ ) -> None:
362
365
  """
363
366
  Create and register a Graphene ObjectType for a GeneralManager class and expose its queries and subscription.
364
367
 
@@ -377,17 +380,17 @@ class GraphQL:
377
380
  fields: dict[str, Any] = {}
378
381
 
379
382
  # Map Attribute Types to Graphene Fields
380
- for field_name, field_info in interface_cls.getAttributeTypes().items():
383
+ for field_name, field_info in interface_cls.get_attribute_types().items():
381
384
  field_type = field_info["type"]
382
- fields[field_name] = cls._mapFieldToGrapheneRead(field_type, field_name)
385
+ fields[field_name] = cls._map_field_to_graphene_read(field_type, field_name)
383
386
  resolver_name = f"resolve_{field_name}"
384
- fields[resolver_name] = cls._createResolver(field_name, field_type)
387
+ fields[resolver_name] = cls._create_resolver(field_name, field_type)
385
388
 
386
389
  # handle GraphQLProperty attributes
387
390
  for (
388
391
  attr_name,
389
392
  attr_value,
390
- ) in generalManagerClass.Interface.getGraphQLProperties().items():
393
+ ) in generalManagerClass.Interface.get_graph_ql_properties().items():
391
394
  raw_hint = attr_value.graphql_type_hint
392
395
  origin = get_origin(raw_hint)
393
396
  type_args = [t for t in get_args(raw_hint) if t is not type(None)]
@@ -406,7 +409,7 @@ class GraphQL:
406
409
  ]
407
410
  )
408
411
  else:
409
- base_type = GraphQL._mapFieldToGrapheneBaseType(
412
+ base_type = GraphQL._map_field_to_graphene_base_type(
410
413
  cast(type, element if isinstance(element, type) else str)
411
414
  )
412
415
  graphene_field = graphene.List(base_type)
@@ -417,21 +420,23 @@ class GraphQL:
417
420
  resolved_type = (
418
421
  cast(type, type_args[0]) if type_args else cast(type, raw_hint)
419
422
  )
420
- graphene_field = cls._mapFieldToGrapheneRead(resolved_type, attr_name)
423
+ graphene_field = cls._map_field_to_graphene_read(
424
+ resolved_type, attr_name
425
+ )
421
426
 
422
427
  fields[attr_name] = graphene_field
423
- fields[f"resolve_{attr_name}"] = cls._createResolver(
428
+ fields[f"resolve_{attr_name}"] = cls._create_resolver(
424
429
  attr_name, resolved_type
425
430
  )
426
431
 
427
432
  graphene_type = type(graphene_type_name, (graphene.ObjectType,), fields)
428
433
  cls.graphql_type_registry[generalManagerClass.__name__] = graphene_type
429
434
  cls.manager_registry[generalManagerClass.__name__] = generalManagerClass
430
- cls._addQueriesToSchema(graphene_type, generalManagerClass)
431
- cls._addSubscriptionField(graphene_type, generalManagerClass)
435
+ cls._add_queries_to_schema(graphene_type, generalManagerClass)
436
+ cls._add_subscription_field(graphene_type, generalManagerClass)
432
437
 
433
438
  @staticmethod
434
- def _sortByOptions(
439
+ def _sort_by_options(
435
440
  generalManagerClass: Type[GeneralManager],
436
441
  ) -> type[graphene.Enum] | None:
437
442
  """
@@ -444,7 +449,7 @@ class GraphQL:
444
449
  for (
445
450
  field_name,
446
451
  field_info,
447
- ) in generalManagerClass.Interface.getAttributeTypes().items():
452
+ ) in generalManagerClass.Interface.get_attribute_types().items():
448
453
  field_type = field_info["type"]
449
454
  if issubclass(field_type, GeneralManager):
450
455
  continue
@@ -454,7 +459,7 @@ class GraphQL:
454
459
  for (
455
460
  prop_name,
456
461
  prop,
457
- ) in generalManagerClass.Interface.getGraphQLProperties().items():
462
+ ) in generalManagerClass.Interface.get_graph_ql_properties().items():
458
463
  if prop.sortable is False:
459
464
  continue
460
465
  type_hints = [
@@ -475,7 +480,7 @@ class GraphQL:
475
480
  )
476
481
 
477
482
  @staticmethod
478
- def _getFilterOptions(
483
+ def _get_filter_options(
479
484
  attribute_type: type, attribute_name: str
480
485
  ) -> Generator[
481
486
  tuple[
@@ -517,20 +522,20 @@ class GraphQL:
517
522
  else:
518
523
  yield (
519
524
  attribute_name,
520
- GraphQL._mapFieldToGrapheneRead(attribute_type, attribute_name),
525
+ GraphQL._map_field_to_graphene_read(attribute_type, attribute_name),
521
526
  )
522
527
  if issubclass(attribute_type, (int, float, Decimal, date, datetime)):
523
528
  for option in number_options:
524
529
  yield (
525
530
  f"{attribute_name}__{option}",
526
531
  (
527
- GraphQL._mapFieldToGrapheneRead(
532
+ GraphQL._map_field_to_graphene_read(
528
533
  attribute_type, attribute_name
529
534
  )
530
535
  ),
531
536
  )
532
537
  elif issubclass(attribute_type, str):
533
- base_type = GraphQL._mapFieldToGrapheneBaseType(attribute_type)
538
+ base_type = GraphQL._map_field_to_graphene_base_type(attribute_type)
534
539
  for option in string_options:
535
540
  if option == "in":
536
541
  yield f"{attribute_name}__in", graphene.List(base_type)
@@ -538,14 +543,14 @@ class GraphQL:
538
543
  yield (
539
544
  f"{attribute_name}__{option}",
540
545
  (
541
- GraphQL._mapFieldToGrapheneRead(
546
+ GraphQL._map_field_to_graphene_read(
542
547
  attribute_type, attribute_name
543
548
  )
544
549
  ),
545
550
  )
546
551
 
547
552
  @staticmethod
548
- def _createFilterOptions(
553
+ def _create_filter_options(
549
554
  field_type: Type[GeneralManager],
550
555
  ) -> type[graphene.InputObjectType] | None:
551
556
  """
@@ -565,17 +570,17 @@ class GraphQL:
565
570
  return GraphQL.graphql_filter_type_registry[graphene_filter_type_name]
566
571
 
567
572
  filter_fields: dict[str, Any] = {}
568
- for attr_name, attr_info in field_type.Interface.getAttributeTypes().items():
573
+ for attr_name, attr_info in field_type.Interface.get_attribute_types().items():
569
574
  attr_type = attr_info["type"]
570
575
  filter_fields = {
571
576
  **filter_fields,
572
577
  **{
573
578
  k: v
574
- for k, v in GraphQL._getFilterOptions(attr_type, attr_name)
579
+ for k, v in GraphQL._get_filter_options(attr_type, attr_name)
575
580
  if v is not None
576
581
  },
577
582
  }
578
- for prop_name, prop in field_type.Interface.getGraphQLProperties().items():
583
+ for prop_name, prop in field_type.Interface.get_graph_ql_properties().items():
579
584
  if not prop.filterable:
580
585
  continue
581
586
  hints = [t for t in get_args(prop.graphql_type_hint) if t is not type(None)]
@@ -584,7 +589,7 @@ class GraphQL:
584
589
  **filter_fields,
585
590
  **{
586
591
  k: v
587
- for k, v in GraphQL._getFilterOptions(prop_type, prop_name)
592
+ for k, v in GraphQL._get_filter_options(prop_type, prop_name)
588
593
  if v is not None
589
594
  },
590
595
  }
@@ -601,7 +606,7 @@ class GraphQL:
601
606
  return filter_class
602
607
 
603
608
  @staticmethod
604
- def _mapFieldToGrapheneRead(field_type: type, field_name: str) -> Any:
609
+ def _map_field_to_graphene_read(field_type: type, field_name: str) -> Any:
605
610
  """
606
611
  Map a field type and name to the appropriate Graphene field for reads.
607
612
 
@@ -622,16 +627,16 @@ class GraphQL:
622
627
  "page_size": graphene.Int(),
623
628
  "group_by": graphene.List(graphene.String),
624
629
  }
625
- filter_options = GraphQL._createFilterOptions(field_type)
630
+ filter_options = GraphQL._create_filter_options(field_type)
626
631
  if filter_options:
627
632
  attributes["filter"] = graphene.Argument(filter_options)
628
633
  attributes["exclude"] = graphene.Argument(filter_options)
629
634
 
630
- sort_by_options = GraphQL._sortByOptions(field_type)
635
+ sort_by_options = GraphQL._sort_by_options(field_type)
631
636
  if sort_by_options:
632
637
  attributes["sort_by"] = graphene.Argument(sort_by_options)
633
638
 
634
- page_type = GraphQL._getOrCreatePageType(
639
+ page_type = GraphQL._get_or_create_page_type(
635
640
  field_type.__name__ + "Page",
636
641
  lambda: GraphQL.graphql_type_registry[field_type.__name__],
637
642
  )
@@ -641,10 +646,10 @@ class GraphQL:
641
646
  lambda: GraphQL.graphql_type_registry[field_type.__name__]
642
647
  )
643
648
  else:
644
- return GraphQL._mapFieldToGrapheneBaseType(field_type)()
649
+ return GraphQL._map_field_to_graphene_base_type(field_type)()
645
650
 
646
651
  @staticmethod
647
- def _mapFieldToGrapheneBaseType(field_type: type) -> Type[Any]:
652
+ def _map_field_to_graphene_base_type(field_type: type) -> Type[Any]:
648
653
  """
649
654
  Map a Python interface type to the corresponding Graphene scalar or custom scalar.
650
655
 
@@ -682,7 +687,7 @@ class GraphQL:
682
687
  return graphene.String
683
688
 
684
689
  @staticmethod
685
- def _parseInput(input_val: dict[str, Any] | str | None) -> dict[str, Any]:
690
+ def _parse_input(input_val: dict[str, Any] | str | None) -> dict[str, Any]:
686
691
  """
687
692
  Normalize a filter or exclude input into a dictionary.
688
693
 
@@ -704,7 +709,7 @@ class GraphQL:
704
709
  return input_val
705
710
 
706
711
  @staticmethod
707
- def _applyQueryParameters(
712
+ def _apply_query_parameters(
708
713
  queryset: Bucket[GeneralManager],
709
714
  filter_input: dict[str, Any] | str | None,
710
715
  exclude_input: dict[str, Any] | str | None,
@@ -723,11 +728,11 @@ class GraphQL:
723
728
  Returns:
724
729
  The queryset after applying filters, exclusions, and sorting.
725
730
  """
726
- filters = GraphQL._parseInput(filter_input)
731
+ filters = GraphQL._parse_input(filter_input)
727
732
  if filters:
728
733
  queryset = queryset.filter(**filters)
729
734
 
730
- excludes = GraphQL._parseInput(exclude_input)
735
+ excludes = GraphQL._parse_input(exclude_input)
731
736
  if excludes:
732
737
  queryset = queryset.exclude(**excludes)
733
738
 
@@ -738,7 +743,7 @@ class GraphQL:
738
743
  return queryset
739
744
 
740
745
  @staticmethod
741
- def _applyPermissionFilters(
746
+ def _apply_permission_filters(
742
747
  queryset: Bucket,
743
748
  general_manager_class: type[GeneralManager],
744
749
  info: GraphQLResolveInfo,
@@ -754,7 +759,7 @@ class GraphQL:
754
759
  Returns:
755
760
  Bucket: Queryset constrained by read permissions.
756
761
  """
757
- permission_filters = getReadPermissionFilter(general_manager_class, info)
762
+ permission_filters = get_read_permission_filter(general_manager_class, info)
758
763
  if not permission_filters:
759
764
  return queryset
760
765
 
@@ -766,7 +771,7 @@ class GraphQL:
766
771
  return filtered_queryset
767
772
 
768
773
  @staticmethod
769
- def _checkReadPermission(
774
+ def _check_read_permission(
770
775
  instance: GeneralManager, info: GraphQLResolveInfo, field_name: str
771
776
  ) -> bool:
772
777
  """Return True if the user may read ``field_name`` on ``instance``."""
@@ -774,13 +779,13 @@ class GraphQL:
774
779
  instance, "Permission", None
775
780
  )
776
781
  if PermissionClass:
777
- return PermissionClass(instance, info.context.user).checkPermission(
782
+ return PermissionClass(instance, info.context.user).check_permission(
778
783
  "read", field_name
779
784
  )
780
785
  return True
781
786
 
782
787
  @staticmethod
783
- def _createListResolver(
788
+ def _create_list_resolver(
784
789
  base_getter: Callable[[Any], Any], fallback_manager_class: type[GeneralManager]
785
790
  ) -> Callable[..., Any]:
786
791
  """
@@ -827,13 +832,13 @@ class GraphQL:
827
832
  manager_class = getattr(
828
833
  base_queryset, "_manager_class", fallback_manager_class
829
834
  )
830
- qs = GraphQL._applyPermissionFilters(base_queryset, manager_class, info)
831
- qs = GraphQL._applyQueryParameters(qs, filter, exclude, sort_by, reverse)
832
- qs = GraphQL._applyGrouping(qs, group_by)
835
+ qs = GraphQL._apply_permission_filters(base_queryset, manager_class, info)
836
+ qs = GraphQL._apply_query_parameters(qs, filter, exclude, sort_by, reverse)
837
+ qs = GraphQL._apply_grouping(qs, group_by)
833
838
 
834
839
  total_count = len(qs)
835
840
 
836
- qs_paginated = GraphQL._applyPagination(qs, page, page_size)
841
+ qs_paginated = GraphQL._apply_pagination(qs, page, page_size)
837
842
 
838
843
  page_info = {
839
844
  "total_count": total_count,
@@ -851,7 +856,7 @@ class GraphQL:
851
856
  return resolver
852
857
 
853
858
  @staticmethod
854
- def _applyPagination(
859
+ def _apply_pagination(
855
860
  queryset: Bucket[GeneralManager], page: int | None, page_size: int | None
856
861
  ) -> Bucket[GeneralManager]:
857
862
  """
@@ -874,7 +879,7 @@ class GraphQL:
874
879
  return queryset
875
880
 
876
881
  @staticmethod
877
- def _applyGrouping(
882
+ def _apply_grouping(
878
883
  queryset: Bucket[GeneralManager], group_by: list[str] | None
879
884
  ) -> Bucket[GeneralManager]:
880
885
  """
@@ -890,7 +895,7 @@ class GraphQL:
890
895
  return queryset
891
896
 
892
897
  @staticmethod
893
- def _createMeasurementResolver(field_name: str) -> Callable[..., Any]:
898
+ def _create_measurement_resolver(field_name: str) -> Callable[..., Any]:
894
899
  """
895
900
  Creates a resolver for a Measurement field that returns its value and unit, with optional unit conversion.
896
901
 
@@ -902,7 +907,7 @@ class GraphQL:
902
907
  info: GraphQLResolveInfo,
903
908
  target_unit: str | None = None,
904
909
  ) -> dict[str, Any] | None:
905
- if not GraphQL._checkReadPermission(self, info, field_name):
910
+ if not GraphQL._check_read_permission(self, info, field_name):
906
911
  return None
907
912
  result = getattr(self, field_name)
908
913
  if not isinstance(result, Measurement):
@@ -917,35 +922,35 @@ class GraphQL:
917
922
  return resolver
918
923
 
919
924
  @staticmethod
920
- def _createNormalResolver(field_name: str) -> Callable[..., Any]:
925
+ def _create_normal_resolver(field_name: str) -> Callable[..., Any]:
921
926
  """
922
- Erzeugt einen Resolver für Standardfelder (keine Listen, keine Measurements).
927
+ Create a resolver for scalar fields (no lists, no Measurement instances).
923
928
  """
924
929
 
925
930
  def resolver(self: GeneralManager, info: GraphQLResolveInfo) -> Any:
926
- if not GraphQL._checkReadPermission(self, info, field_name):
931
+ if not GraphQL._check_read_permission(self, info, field_name):
927
932
  return None
928
933
  return getattr(self, field_name)
929
934
 
930
935
  return resolver
931
936
 
932
937
  @classmethod
933
- def _createResolver(cls, field_name: str, field_type: type) -> Callable[..., Any]:
938
+ def _create_resolver(cls, field_name: str, field_type: type) -> Callable[..., Any]:
934
939
  """
935
940
  Returns a resolver function for a field, selecting list, measurement, or standard resolution based on the field's type and name.
936
941
 
937
942
  For fields ending with `_list` referencing a `GeneralManager` subclass, provides a resolver supporting pagination and filtering. For `Measurement` fields, returns a resolver that handles unit conversion and permission checks. For all other fields, returns a standard resolver with permission enforcement.
938
943
  """
939
944
  if field_name.endswith("_list") and issubclass(field_type, GeneralManager):
940
- return cls._createListResolver(
945
+ return cls._create_list_resolver(
941
946
  lambda self: getattr(self, field_name), field_type
942
947
  )
943
948
  if issubclass(field_type, Measurement):
944
- return cls._createMeasurementResolver(field_name)
945
- return cls._createNormalResolver(field_name)
949
+ return cls._create_measurement_resolver(field_name)
950
+ return cls._create_normal_resolver(field_name)
946
951
 
947
952
  @classmethod
948
- def _getOrCreatePageType(
953
+ def _get_or_create_page_type(
949
954
  cls,
950
955
  page_type_name: str,
951
956
  item_type: type[graphene.ObjectType] | Callable[[], type[graphene.ObjectType]],
@@ -977,7 +982,7 @@ class GraphQL:
977
982
  return cls._page_type_registry[page_type_name]
978
983
 
979
984
  @classmethod
980
- def _buildIdentificationArguments(
985
+ def _build_identification_arguments(
981
986
  cls, generalManagerClass: Type[GeneralManager]
982
987
  ) -> dict[str, Any]:
983
988
  """
@@ -1006,14 +1011,14 @@ class GraphQL:
1006
1011
  graphene.ID, required=True
1007
1012
  )
1008
1013
  else:
1009
- base_type = cls._mapFieldToGrapheneBaseType(input_field.type)
1014
+ base_type = cls._map_field_to_graphene_base_type(input_field.type)
1010
1015
  identification_fields[input_field_name] = graphene.Argument(
1011
1016
  base_type, required=True
1012
1017
  )
1013
1018
  return identification_fields
1014
1019
 
1015
1020
  @classmethod
1016
- def _addQueriesToSchema(
1021
+ def _add_queries_to_schema(
1017
1022
  cls, graphene_type: type, generalManagerClass: Type[GeneralManager]
1018
1023
  ) -> None:
1019
1024
  """
@@ -1040,15 +1045,15 @@ class GraphQL:
1040
1045
  "page_size": graphene.Int(),
1041
1046
  "group_by": graphene.List(graphene.String),
1042
1047
  }
1043
- filter_options = cls._createFilterOptions(generalManagerClass)
1048
+ filter_options = cls._create_filter_options(generalManagerClass)
1044
1049
  if filter_options:
1045
1050
  attributes["filter"] = graphene.Argument(filter_options)
1046
1051
  attributes["exclude"] = graphene.Argument(filter_options)
1047
- sort_by_options = cls._sortByOptions(generalManagerClass)
1052
+ sort_by_options = cls._sort_by_options(generalManagerClass)
1048
1053
  if sort_by_options:
1049
1054
  attributes["sort_by"] = graphene.Argument(sort_by_options)
1050
1055
 
1051
- page_type = cls._getOrCreatePageType(
1056
+ page_type = cls._get_or_create_page_type(
1052
1057
  graphene_type.__name__ + "Page", graphene_type
1053
1058
  )
1054
1059
  list_field = graphene.Field(page_type, **attributes)
@@ -1062,13 +1067,13 @@ class GraphQL:
1062
1067
  """
1063
1068
  return generalManagerClass.all()
1064
1069
 
1065
- list_resolver = cls._createListResolver(_all_items, generalManagerClass)
1070
+ list_resolver = cls._create_list_resolver(_all_items, generalManagerClass)
1066
1071
  cls._query_fields[list_field_name] = list_field
1067
1072
  cls._query_fields[f"resolve_{list_field_name}"] = list_resolver
1068
1073
 
1069
1074
  # resolver and field for the single item query
1070
1075
  item_field_name = generalManagerClass.__name__.lower()
1071
- identification_fields = cls._buildIdentificationArguments(generalManagerClass)
1076
+ identification_fields = cls._build_identification_arguments(generalManagerClass)
1072
1077
  item_field = graphene.Field(graphene_type, **identification_fields)
1073
1078
 
1074
1079
  def resolver(
@@ -1088,6 +1093,23 @@ class GraphQL:
1088
1093
  cls._query_fields[item_field_name] = item_field
1089
1094
  cls._query_fields[f"resolve_{item_field_name}"] = resolver
1090
1095
 
1096
+ @staticmethod
1097
+ def _normalize_graphql_name(name: str) -> str:
1098
+ """
1099
+ Convert a GraphQL selection name (potentially camelCase) to the corresponding Python attribute name.
1100
+
1101
+ Parameters:
1102
+ name (str): GraphQL field name from a selection set.
1103
+
1104
+ Returns:
1105
+ str: The snake_case representation matching the GraphQLProperty definition.
1106
+ """
1107
+ if "_" in name:
1108
+ return name
1109
+ snake = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name)
1110
+ snake = re.sub("([a-z0-9])([A-Z])", r"\1_\2", snake)
1111
+ return snake.lower()
1112
+
1091
1113
  @staticmethod
1092
1114
  def _prime_graphql_properties(
1093
1115
  instance: GeneralManager, property_names: Iterable[str] | None = None
@@ -1104,7 +1126,7 @@ class GraphQL:
1104
1126
  interface_cls = getattr(instance.__class__, "Interface", None)
1105
1127
  if interface_cls is None:
1106
1128
  return
1107
- available_properties = interface_cls.getGraphQLProperties()
1129
+ available_properties = interface_cls.get_graph_ql_properties()
1108
1130
  if property_names is None:
1109
1131
  names = available_properties.keys()
1110
1132
  else:
@@ -1162,7 +1184,7 @@ class GraphQL:
1162
1184
  interface_cls = getattr(manager_class, "Interface", None)
1163
1185
  if interface_cls is None:
1164
1186
  return set()
1165
- available_properties = set(interface_cls.getGraphQLProperties().keys())
1187
+ available_properties = set(interface_cls.get_graph_ql_properties().keys())
1166
1188
  if not available_properties:
1167
1189
  return set()
1168
1190
 
@@ -1185,8 +1207,9 @@ class GraphQL:
1185
1207
  for selection in selection_set.selections:
1186
1208
  if isinstance(selection, FieldNode):
1187
1209
  name = selection.name.value
1188
- if name in available_properties:
1189
- property_names.add(name)
1210
+ normalized = cls._normalize_graphql_name(name)
1211
+ if normalized in available_properties:
1212
+ property_names.add(normalized)
1190
1213
  elif isinstance(selection, FragmentSpreadNode):
1191
1214
  fragment = info.fragments.get(selection.name.value)
1192
1215
  if fragment is not None:
@@ -1338,7 +1361,7 @@ class GraphQL:
1338
1361
  return instance, set()
1339
1362
 
1340
1363
  @classmethod
1341
- def _addSubscriptionField(
1364
+ def _add_subscription_field(
1342
1365
  cls,
1343
1366
  graphene_type: type[graphene.ObjectType],
1344
1367
  generalManagerClass: Type[GeneralManager],
@@ -1380,7 +1403,7 @@ class GraphQL:
1380
1403
  payload_type
1381
1404
  )
1382
1405
 
1383
- identification_args = cls._buildIdentificationArguments(generalManagerClass)
1406
+ identification_args = cls._build_identification_arguments(generalManagerClass)
1384
1407
  subscription_field = graphene.Field(payload_type, **identification_args)
1385
1408
 
1386
1409
  async def subscribe(
@@ -1501,7 +1524,7 @@ class GraphQL:
1501
1524
  cls._subscription_fields[f"resolve_{field_name}"] = resolve
1502
1525
 
1503
1526
  @classmethod
1504
- def createWriteFields(cls, interface_cls: InterfaceBase) -> dict[str, Any]:
1527
+ def create_write_fields(cls, interface_cls: InterfaceBase) -> dict[str, Any]:
1505
1528
  """
1506
1529
  Create Graphene input fields for writable attributes defined by an Interface.
1507
1530
 
@@ -1515,7 +1538,7 @@ class GraphQL:
1515
1538
  """
1516
1539
  fields: dict[str, Any] = {}
1517
1540
 
1518
- for name, info in interface_cls.getAttributeTypes().items():
1541
+ for name, info in interface_cls.get_attribute_types().items():
1519
1542
  if name in ["changed_by", "created_at", "updated_at"]:
1520
1543
  continue
1521
1544
  if info["is_derived"]:
@@ -1539,7 +1562,7 @@ class GraphQL:
1539
1562
  default_value=default,
1540
1563
  )
1541
1564
  else:
1542
- base_cls = cls._mapFieldToGrapheneBaseType(typ)
1565
+ base_cls = cls._map_field_to_graphene_base_type(typ)
1543
1566
  fld = base_cls(
1544
1567
  required=req,
1545
1568
  default_value=default,
@@ -1557,7 +1580,7 @@ class GraphQL:
1557
1580
  return fields
1558
1581
 
1559
1582
  @classmethod
1560
- def generateCreateMutationClass(
1583
+ def generate_create_mutation_class(
1561
1584
  cls,
1562
1585
  generalManagerClass: type[GeneralManager],
1563
1586
  default_return_values: dict[str, Any],
@@ -1603,7 +1626,7 @@ class GraphQL:
1603
1626
  **kwargs, creator_id=info.context.user.id
1604
1627
  )
1605
1628
  except HANDLED_MANAGER_ERRORS as error:
1606
- raise GraphQL._handleGraphQLError(error) from error
1629
+ raise GraphQL._handle_graph_ql_error(error) from error
1607
1630
 
1608
1631
  return {
1609
1632
  "success": True,
@@ -1621,7 +1644,7 @@ class GraphQL:
1621
1644
  (),
1622
1645
  {
1623
1646
  field_name: field
1624
- for field_name, field in cls.createWriteFields(
1647
+ for field_name, field in cls.create_write_fields(
1625
1648
  interface_cls
1626
1649
  ).items()
1627
1650
  if field_name not in generalManagerClass.Interface.input_fields
@@ -1632,7 +1655,7 @@ class GraphQL:
1632
1655
  )
1633
1656
 
1634
1657
  @classmethod
1635
- def generateUpdateMutationClass(
1658
+ def generate_update_mutation_class(
1636
1659
  cls,
1637
1660
  generalManagerClass: type[GeneralManager],
1638
1661
  default_return_values: dict[str, Any],
@@ -1668,13 +1691,13 @@ class GraphQL:
1668
1691
  """
1669
1692
  manager_id = kwargs.pop("id", None)
1670
1693
  if manager_id is None:
1671
- raise GraphQL._handleGraphQLError(MissingManagerIdentifierError())
1694
+ raise GraphQL._handle_graph_ql_error(MissingManagerIdentifierError())
1672
1695
  try:
1673
1696
  instance = generalManagerClass(id=manager_id).update(
1674
1697
  creator_id=info.context.user.id, **kwargs
1675
1698
  )
1676
1699
  except HANDLED_MANAGER_ERRORS as error:
1677
- raise GraphQL._handleGraphQLError(error) from error
1700
+ raise GraphQL._handle_graph_ql_error(error) from error
1678
1701
 
1679
1702
  return {
1680
1703
  "success": True,
@@ -1694,7 +1717,7 @@ class GraphQL:
1694
1717
  "id": graphene.ID(required=True),
1695
1718
  **{
1696
1719
  field_name: field
1697
- for field_name, field in cls.createWriteFields(
1720
+ for field_name, field in cls.create_write_fields(
1698
1721
  interface_cls
1699
1722
  ).items()
1700
1723
  if field.editable
@@ -1706,7 +1729,7 @@ class GraphQL:
1706
1729
  )
1707
1730
 
1708
1731
  @classmethod
1709
- def generateDeleteMutationClass(
1732
+ def generate_delete_mutation_class(
1710
1733
  cls,
1711
1734
  generalManagerClass: type[GeneralManager],
1712
1735
  default_return_values: dict[str, Any],
@@ -1738,13 +1761,13 @@ class GraphQL:
1738
1761
  """
1739
1762
  manager_id = kwargs.pop("id", None)
1740
1763
  if manager_id is None:
1741
- raise GraphQL._handleGraphQLError(MissingManagerIdentifierError())
1764
+ raise GraphQL._handle_graph_ql_error(MissingManagerIdentifierError())
1742
1765
  try:
1743
1766
  instance = generalManagerClass(id=manager_id).deactivate(
1744
1767
  creator_id=info.context.user.id
1745
1768
  )
1746
1769
  except HANDLED_MANAGER_ERRORS as error:
1747
- raise GraphQL._handleGraphQLError(error) from error
1770
+ raise GraphQL._handle_graph_ql_error(error) from error
1748
1771
 
1749
1772
  return {
1750
1773
  "success": True,
@@ -1762,7 +1785,7 @@ class GraphQL:
1762
1785
  (),
1763
1786
  {
1764
1787
  field_name: field
1765
- for field_name, field in cls.createWriteFields(
1788
+ for field_name, field in cls.create_write_fields(
1766
1789
  interface_cls
1767
1790
  ).items()
1768
1791
  if field_name in generalManagerClass.Interface.input_fields
@@ -1773,7 +1796,7 @@ class GraphQL:
1773
1796
  )
1774
1797
 
1775
1798
  @staticmethod
1776
- def _handleGraphQLError(error: Exception) -> GraphQLError:
1799
+ def _handle_graph_ql_error(error: Exception) -> GraphQLError:
1777
1800
  """
1778
1801
  Convert an exception into a GraphQL error with an appropriate extensions['code'].
1779
1802