GeneralManager 0.40.6__tar.gz → 0.41.0__tar.gz

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 (194) hide show
  1. {generalmanager-0.40.6/src/GeneralManager.egg-info → generalmanager-0.41.0}/PKG-INFO +1 -1
  2. {generalmanager-0.40.6 → generalmanager-0.41.0}/pyproject.toml +1 -1
  3. {generalmanager-0.40.6 → generalmanager-0.41.0/src/GeneralManager.egg-info}/PKG-INFO +1 -1
  4. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/api/graphql.py +25 -3
  5. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/api/graphql_resolvers.py +68 -3
  6. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/api/graphql_search.py +129 -2
  7. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/base_interface.py +2 -0
  8. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +10 -0
  9. {generalmanager-0.40.6 → generalmanager-0.41.0}/LICENSE +0 -0
  10. {generalmanager-0.40.6 → generalmanager-0.41.0}/README.md +0 -0
  11. {generalmanager-0.40.6 → generalmanager-0.41.0}/setup.cfg +0 -0
  12. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/GeneralManager.egg-info/SOURCES.txt +0 -0
  13. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
  14. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/GeneralManager.egg-info/requires.txt +0 -0
  15. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/GeneralManager.egg-info/top_level.txt +0 -0
  16. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/__init__.py +0 -0
  17. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/_types/__init__.py +0 -0
  18. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/_types/api.py +0 -0
  19. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/_types/bucket.py +0 -0
  20. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/_types/cache.py +0 -0
  21. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/_types/factory.py +0 -0
  22. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/_types/general_manager.py +0 -0
  23. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/_types/interface.py +0 -0
  24. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/_types/manager.py +0 -0
  25. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/_types/measurement.py +0 -0
  26. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/_types/permission.py +0 -0
  27. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/_types/rule.py +0 -0
  28. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/_types/search.py +0 -0
  29. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/_types/utils.py +0 -0
  30. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/api/__init__.py +0 -0
  31. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/api/graphql_errors.py +0 -0
  32. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/api/graphql_mutations.py +0 -0
  33. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
  34. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/api/graphql_subscriptions.py +0 -0
  35. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/api/graphql_view.py +0 -0
  36. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/api/mutation.py +0 -0
  37. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/api/property.py +0 -0
  38. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/api/registry.py +0 -0
  39. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/api/remote_api.py +0 -0
  40. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/api/remote_invalidation.py +0 -0
  41. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/api/remote_invalidation_client.py +0 -0
  42. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/apps.py +0 -0
  43. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/bootstrap.py +0 -0
  44. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/bucket/__init__.py +0 -0
  45. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/bucket/base_bucket.py +0 -0
  46. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/bucket/calculation_bucket.py +0 -0
  47. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/bucket/database_bucket.py +0 -0
  48. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/bucket/group_bucket.py +0 -0
  49. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/bucket/request_bucket.py +0 -0
  50. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/cache/__init__.py +0 -0
  51. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/cache/cache_decorator.py +0 -0
  52. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/cache/cache_tracker.py +0 -0
  53. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/cache/dependency_index.py +0 -0
  54. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/cache/model_dependency_collector.py +0 -0
  55. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/cache/signals.py +0 -0
  56. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/conf.py +0 -0
  57. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/factory/__init__.py +0 -0
  58. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/factory/auto_factory.py +0 -0
  59. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/factory/factories.py +0 -0
  60. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/factory/factory_methods.py +0 -0
  61. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/__init__.py +0 -0
  62. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/bundles/__init__.py +0 -0
  63. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/bundles/calculation.py +0 -0
  64. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/bundles/database.py +0 -0
  65. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/bundles/remote_manager.py +0 -0
  66. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/bundles/request.py +0 -0
  67. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/__init__.py +0 -0
  68. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/base.py +0 -0
  69. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/builtin.py +0 -0
  70. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
  71. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
  72. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/calculation/lifecycle.py +0 -0
  73. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/configuration.py +0 -0
  74. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/core/observability.py +0 -0
  75. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/core/utils.py +0 -0
  76. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/exceptions.py +0 -0
  77. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
  78. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
  79. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/existing_model/resolution.py +0 -0
  80. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/factory.py +0 -0
  81. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/orm/__init__.py +0 -0
  82. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/orm/_compat.py +0 -0
  83. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/orm/history.py +0 -0
  84. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
  85. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/orm/mutations.py +0 -0
  86. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/orm/support.py +0 -0
  87. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
  88. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
  89. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +0 -0
  90. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
  91. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
  92. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
  93. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
  94. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/registry.py +0 -0
  95. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
  96. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
  97. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
  98. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
  99. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/interfaces/__init__.py +0 -0
  100. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/interfaces/calculation.py +0 -0
  101. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/interfaces/database.py +0 -0
  102. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/interfaces/existing_model.py +0 -0
  103. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/interfaces/read_only.py +0 -0
  104. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
  105. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/interfaces/request.py +0 -0
  106. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/manifests/__init__.py +0 -0
  107. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/manifests/capability_builder.py +0 -0
  108. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
  109. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/manifests/capability_models.py +0 -0
  110. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/orm_interface.py +0 -0
  111. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/requests.py +0 -0
  112. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/utils/__init__.py +0 -0
  113. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
  114. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/utils/errors.py +0 -0
  115. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/interface/utils/models.py +0 -0
  116. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/logging.py +0 -0
  117. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/management/commands/search_index.py +0 -0
  118. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/management/commands/seed_manager_landscape.py +0 -0
  119. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/management/commands/shell.py +0 -0
  120. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
  121. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
  122. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/manager/__init__.py +0 -0
  123. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/manager/general_manager.py +0 -0
  124. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/manager/group_manager.py +0 -0
  125. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/manager/input.py +0 -0
  126. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/manager/meta.py +0 -0
  127. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/measurement/__init__.py +0 -0
  128. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/measurement/measurement.py +0 -0
  129. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/measurement/measurement_field.py +0 -0
  130. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/metrics/__init__.py +0 -0
  131. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/metrics/graphql.py +0 -0
  132. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/migrations/0001_initial.py +0 -0
  133. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
  134. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
  135. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/migrations/__init__.py +0 -0
  136. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/models.py +0 -0
  137. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/permission/__init__.py +0 -0
  138. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/permission/audit.py +0 -0
  139. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/permission/base_permission.py +0 -0
  140. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/permission/graphql_capabilities.py +0 -0
  141. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/permission/manager_based_permission.py +0 -0
  142. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/permission/mutation_permission.py +0 -0
  143. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/permission/permission_checks.py +0 -0
  144. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/permission/permission_data_manager.py +0 -0
  145. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/permission/utils.py +0 -0
  146. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/public_api_registry.py +0 -0
  147. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/py.typed +0 -0
  148. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/rule/__init__.py +0 -0
  149. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/rule/handler.py +0 -0
  150. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/rule/rule.py +0 -0
  151. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/search/__init__.py +0 -0
  152. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/search/async_tasks.py +0 -0
  153. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/search/backend.py +0 -0
  154. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/search/backend_registry.py +0 -0
  155. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/search/backends/__init__.py +0 -0
  156. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/search/backends/dev.py +0 -0
  157. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/search/backends/meilisearch.py +0 -0
  158. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/search/backends/opensearch.py +0 -0
  159. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/search/backends/typesense.py +0 -0
  160. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/search/config.py +0 -0
  161. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/search/indexer.py +0 -0
  162. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/search/registry.py +0 -0
  163. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/search/utils.py +0 -0
  164. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/seeding/__init__.py +0 -0
  165. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/seeding/manager_landscape.py +0 -0
  166. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/utils/__init__.py +0 -0
  167. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/utils/args_to_kwargs.py +0 -0
  168. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/utils/filter_parser.py +0 -0
  169. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/utils/format_string.py +0 -0
  170. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/utils/json_encoder.py +0 -0
  171. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/utils/make_cache_key.py +0 -0
  172. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/utils/none_to_zero.py +0 -0
  173. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/utils/path_mapping.py +0 -0
  174. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/utils/public_api.py +0 -0
  175. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/utils/testing.py +0 -0
  176. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/utils/type_checks.py +0 -0
  177. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/workflow/__init__.py +0 -0
  178. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/workflow/actions.py +0 -0
  179. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/workflow/backend_registry.py +0 -0
  180. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/workflow/backends/__init__.py +0 -0
  181. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/workflow/backends/celery.py +0 -0
  182. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/workflow/backends/local.py +0 -0
  183. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/workflow/backends/n8n.py +0 -0
  184. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/workflow/config.py +0 -0
  185. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/workflow/engine.py +0 -0
  186. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/workflow/event_registry.py +0 -0
  187. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/workflow/events.py +0 -0
  188. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/workflow/models.py +0 -0
  189. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/workflow/signal_bridge.py +0 -0
  190. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/workflow/tasks.py +0 -0
  191. {generalmanager-0.40.6 → generalmanager-0.41.0}/src/general_manager/workflow/telemetry.py +0 -0
  192. {generalmanager-0.40.6 → generalmanager-0.41.0}/tests/test_settings.py +0 -0
  193. {generalmanager-0.40.6 → generalmanager-0.41.0}/tests/test_urls.py +0 -0
  194. {generalmanager-0.40.6 → generalmanager-0.41.0}/tests/testing_asgi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.40.6
3
+ Version: 0.41.0
4
4
  Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "GeneralManager"
7
- version = "0.40.6"
7
+ version = "0.41.0"
8
8
  description = "Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching."
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Tim Kleindick", email = "tkleindick@yahoo.de" }]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.40.6
3
+ Version: 0.41.0
4
4
  Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License: MIT License
@@ -102,6 +102,7 @@ from general_manager.api.graphql_search import (
102
102
  passes_permission_filters as _passes_permission_filters_fn,
103
103
  get_filter_options as _get_filter_options_fn,
104
104
  create_filter_options as _create_filter_options_fn,
105
+ normalize_filter_input as _normalize_filter_input_fn,
105
106
  )
106
107
  from general_manager.api.graphql_subscriptions import (
107
108
  get_channel_layer_safe as _get_channel_layer_fn,
@@ -704,10 +705,12 @@ class GraphQL:
704
705
  field_type: Type[GeneralManager],
705
706
  ) -> type[graphene.InputObjectType] | None:
706
707
  """Build filter InputObjectType for *field_type*. See ``graphql_search.create_filter_options``."""
708
+ relation_depth = int(get_setting("GRAPHQL_FILTER_RELATION_DEPTH", 1) or 0)
707
709
  return _create_filter_options_fn(
708
710
  field_type,
709
711
  GraphQL.graphql_filter_type_registry,
710
712
  GraphQL._map_field_to_graphene_read,
713
+ relation_depth=relation_depth,
711
714
  )
712
715
 
713
716
  @staticmethod
@@ -779,6 +782,14 @@ class GraphQL:
779
782
  """Normalise a filter/exclude input into a plain dict. See ``graphql_resolvers.parse_input``."""
780
783
  return _parse_input_fn(input_val)
781
784
 
785
+ @staticmethod
786
+ def _normalize_filter_input(
787
+ field_type: Type[GeneralManager],
788
+ filter_input: dict[str, Any],
789
+ ) -> dict[str, dict[str, Any]]:
790
+ """Flatten nested relation filters. See ``graphql_search.normalize_filter_input``."""
791
+ return _normalize_filter_input_fn(field_type, filter_input)
792
+
782
793
  @staticmethod
783
794
  def _apply_query_parameters(
784
795
  queryset: Bucket[GeneralManager],
@@ -786,10 +797,17 @@ class GraphQL:
786
797
  exclude_input: dict[str, Any] | str | None,
787
798
  sort_by: graphene.Enum | None,
788
799
  reverse: bool,
800
+ filter_normalizer: Callable[[dict[str, Any]], dict[str, dict[str, Any]]]
801
+ | None = None,
789
802
  ) -> Bucket[GeneralManager]:
790
803
  """Apply filter/exclude/sort to *queryset*. See ``graphql_resolvers.apply_query_parameters``."""
791
804
  return _apply_query_parameters_fn(
792
- queryset, filter_input, exclude_input, sort_by, reverse
805
+ queryset,
806
+ filter_input,
807
+ exclude_input,
808
+ sort_by,
809
+ reverse,
810
+ filter_normalizer=filter_normalizer,
793
811
  )
794
812
 
795
813
  @staticmethod
@@ -814,7 +832,11 @@ class GraphQL:
814
832
  fallback_manager_class: type[GeneralManager],
815
833
  ) -> Callable[..., Any]:
816
834
  """Build a list-field resolver. See ``graphql_resolvers.create_list_resolver``."""
817
- return _create_list_resolver_fn(base_getter, fallback_manager_class)
835
+ return _create_list_resolver_fn(
836
+ base_getter,
837
+ fallback_manager_class,
838
+ _normalize_filter_input_fn,
839
+ )
818
840
 
819
841
  @staticmethod
820
842
  def _apply_pagination(
@@ -845,7 +867,7 @@ class GraphQL:
845
867
  @classmethod
846
868
  def _create_resolver(cls, field_name: str, field_type: type) -> Callable[..., Any]:
847
869
  """Dispatch to the appropriate resolver factory. See ``graphql_resolvers.create_resolver``."""
848
- return _create_resolver_fn(field_name, field_type)
870
+ return _create_resolver_fn(field_name, field_type, _normalize_filter_input_fn)
849
871
 
850
872
  @classmethod
851
873
  def _get_or_create_page_type(
@@ -39,6 +39,15 @@ GeneralManagerT = TypeVar("GeneralManagerT", bound=GeneralManager)
39
39
  logger = get_logger("api.graphql")
40
40
 
41
41
 
42
+ class UnsupportedExcludeNoneRelationFilterError(ValueError):
43
+ """Raised when `none` relation filters are used in GraphQL exclude input."""
44
+
45
+ def __init__(self) -> None:
46
+ super().__init__(
47
+ "`none` relation filters are not supported inside `exclude` inputs."
48
+ )
49
+
50
+
42
51
  @dataclass(slots=True)
43
52
  class ReadAuthorizationResult(Generic[GeneralManagerT]):
44
53
  queryset: Bucket[GeneralManagerT]
@@ -72,6 +81,17 @@ def parse_input(input_val: dict[str, Any] | str | None) -> dict[str, Any]:
72
81
  return input_val
73
82
 
74
83
 
84
+ def contains_none_relation_filter(input_val: Any) -> bool:
85
+ """Return True when a nested relation filter contains a ``none`` operator."""
86
+ if isinstance(input_val, dict):
87
+ if "none" in input_val:
88
+ return True
89
+ return any(contains_none_relation_filter(value) for value in input_val.values())
90
+ if isinstance(input_val, list):
91
+ return any(contains_none_relation_filter(value) for value in input_val)
92
+ return False
93
+
94
+
75
95
  # ---------------------------------------------------------------------------
76
96
  # Queryset modifiers
77
97
  # ---------------------------------------------------------------------------
@@ -83,6 +103,9 @@ def apply_query_parameters(
83
103
  exclude_input: dict[str, Any] | str | None,
84
104
  sort_by: graphene.Enum | None,
85
105
  reverse: bool,
106
+ *,
107
+ filter_normalizer: Callable[[dict[str, Any]], dict[str, dict[str, Any]]]
108
+ | None = None,
86
109
  ) -> Bucket[GeneralManager]:
87
110
  """
88
111
  Apply filtering, exclusion, and sorting to *queryset*.
@@ -96,13 +119,27 @@ def apply_query_parameters(
96
119
  Returns:
97
120
  The queryset after filters, exclusions, and sorting are applied.
98
121
  """
122
+ normalized_excludes: dict[str, Any] = {}
123
+
99
124
  filters = parse_input(filter_input)
125
+ if filters and filter_normalizer is not None:
126
+ normalized = filter_normalizer(filters)
127
+ filters = normalized["filter"]
128
+ normalized_excludes = normalized["exclude"]
100
129
  if filters:
101
130
  queryset = queryset.filter(**filters)
102
131
 
103
132
  excludes = parse_input(exclude_input)
133
+ if excludes and filter_normalizer is not None:
134
+ if contains_none_relation_filter(excludes):
135
+ raise UnsupportedExcludeNoneRelationFilterError
136
+ normalized = filter_normalizer(excludes)
137
+ excludes = normalized["filter"]
138
+ normalized_excludes = {**normalized_excludes, **normalized["exclude"]}
104
139
  if excludes:
105
140
  queryset = queryset.exclude(**excludes)
141
+ if normalized_excludes:
142
+ queryset = queryset.exclude(**normalized_excludes)
106
143
 
107
144
  if sort_by:
108
145
  sort_by_str = cast(str, getattr(sort_by, "value", sort_by))
@@ -440,6 +477,10 @@ def create_normal_resolver(field_name: str) -> Callable[..., Any]:
440
477
  def create_list_resolver(
441
478
  base_getter: Callable[[Any, bool], Any],
442
479
  fallback_manager_class: type[GeneralManager],
480
+ filter_normalizer: Callable[
481
+ [type[GeneralManager], dict[str, Any]], dict[str, dict[str, Any]]
482
+ ]
483
+ | None = None,
443
484
  ) -> Callable[..., Any]:
444
485
  """
445
486
  Build a resolver for list fields that applies filters, permissions, and pagination.
@@ -479,7 +520,22 @@ def create_list_resolver(
479
520
  ):
480
521
  manager_class = fallback_manager_class
481
522
  qs = apply_permission_filters(base_queryset, manager_class, info)
482
- qs = apply_query_parameters(qs, filter, exclude, sort_by, reverse)
523
+ bound_filter_normalizer = None
524
+ if filter_normalizer is not None:
525
+
526
+ def bound_filter_normalizer(
527
+ filters: dict[str, Any],
528
+ ) -> dict[str, dict[str, Any]]:
529
+ return filter_normalizer(manager_class, filters)
530
+
531
+ qs = apply_query_parameters(
532
+ qs,
533
+ filter,
534
+ exclude,
535
+ sort_by,
536
+ reverse,
537
+ filter_normalizer=bound_filter_normalizer,
538
+ )
483
539
  qs_grouped = apply_grouping(qs, group_by)
484
540
 
485
541
  total_count = len(qs_grouped)
@@ -575,7 +631,14 @@ def _selection_set_includes_path(
575
631
  return False
576
632
 
577
633
 
578
- def create_resolver(field_name: str, field_type: type) -> Callable[..., Any]:
634
+ def create_resolver(
635
+ field_name: str,
636
+ field_type: type,
637
+ filter_normalizer: Callable[
638
+ [type[GeneralManager], dict[str, Any]], dict[str, dict[str, Any]]
639
+ ]
640
+ | None = None,
641
+ ) -> Callable[..., Any]:
579
642
  """
580
643
  Return the appropriate resolver for *field_name* based on *field_type*.
581
644
 
@@ -586,7 +649,9 @@ def create_resolver(field_name: str, field_type: type) -> Callable[..., Any]:
586
649
  """
587
650
  if field_name.endswith("_list") and safe_issubclass(field_type, GeneralManager):
588
651
  return create_list_resolver(
589
- lambda self, _include_inactive: getattr(self, field_name), field_type
652
+ lambda self, _include_inactive: getattr(self, field_name),
653
+ field_type,
654
+ filter_normalizer,
590
655
  )
591
656
  if safe_issubclass(field_type, Measurement):
592
657
  return create_measurement_resolver(field_name)
@@ -357,10 +357,66 @@ def get_filter_options(
357
357
  )
358
358
 
359
359
 
360
+ def get_relation_filter_option(
361
+ attribute_type: type,
362
+ attribute_name: str,
363
+ attr_info: Mapping[str, Any],
364
+ graphql_filter_type_registry: dict[str, type[graphene.InputObjectType]],
365
+ map_field_to_graphene_read: Callable[[type, str, Mapping[str, Any] | None], Any],
366
+ remaining_depth: int,
367
+ ) -> tuple[str, Any] | None:
368
+ """Build a nested relation filter field for direct and collection relations."""
369
+ if remaining_depth <= 0:
370
+ return None
371
+ if not safe_issubclass(attribute_type, GeneralManager):
372
+ return None
373
+
374
+ relation_kind = attr_info.get("relation_kind")
375
+ if relation_kind not in {"collection", "direct"}:
376
+ return None
377
+
378
+ nested_type = create_filter_options(
379
+ attribute_type,
380
+ graphql_filter_type_registry,
381
+ map_field_to_graphene_read,
382
+ relation_depth=remaining_depth - 1,
383
+ _remaining_depth=remaining_depth - 1,
384
+ )
385
+ if nested_type is None:
386
+ return None
387
+
388
+ if relation_kind == "collection":
389
+ relation_type_name = (
390
+ f"{attribute_type.__name__}{attribute_name.title().replace('_', '')}"
391
+ f"RelationFilterTypeDepth{remaining_depth - 1}"
392
+ )
393
+ if relation_type_name not in graphql_filter_type_registry:
394
+ graphql_filter_type_registry[relation_type_name] = type(
395
+ relation_type_name,
396
+ (graphene.InputObjectType,),
397
+ {
398
+ "any": graphene.InputField(nested_type),
399
+ "none": graphene.InputField(nested_type),
400
+ },
401
+ )
402
+ return (
403
+ attribute_name,
404
+ graphene.InputField(graphql_filter_type_registry[relation_type_name]),
405
+ )
406
+
407
+ if relation_kind == "direct":
408
+ return attribute_name, graphene.InputField(nested_type)
409
+
410
+ return None
411
+
412
+
360
413
  def create_filter_options(
361
414
  field_type: Type[GeneralManager],
362
415
  graphql_filter_type_registry: dict[str, type[graphene.InputObjectType]],
363
416
  map_field_to_graphene_read: Callable[[type, str, Mapping[str, Any] | None], Any],
417
+ *,
418
+ relation_depth: int = 1,
419
+ _remaining_depth: int | None = None,
364
420
  ) -> type[graphene.InputObjectType] | None:
365
421
  """
366
422
  Create (or retrieve from cache) a Graphene InputObjectType exposing all
@@ -378,13 +434,27 @@ def create_filter_options(
378
434
  A Graphene ``InputObjectType`` for *field_type*, or ``None`` if no
379
435
  filterable fields exist.
380
436
  """
381
- graphene_filter_type_name = f"{field_type.__name__}FilterType"
437
+ remaining_depth = relation_depth if _remaining_depth is None else _remaining_depth
438
+ graphene_filter_type_name = f"{field_type.__name__}FilterTypeDepth{remaining_depth}"
382
439
  if graphene_filter_type_name in graphql_filter_type_registry:
383
440
  return graphql_filter_type_registry[graphene_filter_type_name]
384
441
 
385
442
  filter_fields: dict[str, Any] = {}
386
443
  for attr_name, attr_info in field_type.Interface.get_attribute_types().items():
387
444
  attr_type = attr_info["type"]
445
+ if safe_issubclass(attr_type, GeneralManager):
446
+ relation_option = get_relation_filter_option(
447
+ attr_type,
448
+ attr_name,
449
+ attr_info,
450
+ graphql_filter_type_registry,
451
+ map_field_to_graphene_read,
452
+ remaining_depth,
453
+ )
454
+ if relation_option is not None:
455
+ key, value = relation_option
456
+ filter_fields[key] = value
457
+ continue
388
458
  filter_fields = {
389
459
  **filter_fields,
390
460
  **{
@@ -423,6 +493,63 @@ def create_filter_options(
423
493
  return filter_class
424
494
 
425
495
 
496
+ def normalize_filter_input(
497
+ field_type: Type[GeneralManager],
498
+ filter_input: dict[str, Any],
499
+ ) -> dict[str, dict[str, Any]]:
500
+ """Flatten nested GraphQL relation filters into filter and exclude kwargs."""
501
+ interface = getattr(field_type, "Interface", None)
502
+ get_attribute_types = getattr(interface, "get_attribute_types", None)
503
+ if not callable(get_attribute_types):
504
+ return {"filter": dict(filter_input), "exclude": {}}
505
+
506
+ filters: dict[str, Any] = {}
507
+ excludes: dict[str, Any] = {}
508
+ attr_types = get_attribute_types()
509
+
510
+ for key, value in filter_input.items():
511
+ attr_info = attr_types.get(key)
512
+ if not attr_info or not isinstance(value, dict):
513
+ filters[key] = value
514
+ continue
515
+
516
+ attr_type = attr_info.get("type")
517
+ relation_kind = attr_info.get("relation_kind")
518
+ lookup = attr_info.get("filter_lookup", key)
519
+ if not safe_issubclass(attr_type, GeneralManager) or relation_kind is None:
520
+ filters[key] = value
521
+ continue
522
+
523
+ if relation_kind == "direct":
524
+ nested = normalize_filter_input(attr_type, value)
525
+ for nested_key, nested_value in nested["filter"].items():
526
+ filters[f"{lookup}__{nested_key}"] = nested_value
527
+ for nested_key, nested_value in nested["exclude"].items():
528
+ excludes[f"{lookup}__{nested_key}"] = nested_value
529
+ continue
530
+
531
+ if relation_kind == "collection":
532
+ any_value = value.get("any")
533
+ none_value = value.get("none")
534
+ if isinstance(any_value, dict):
535
+ nested = normalize_filter_input(attr_type, any_value)
536
+ for nested_key, nested_value in nested["filter"].items():
537
+ filters[f"{lookup}__{nested_key}"] = nested_value
538
+ for nested_key, nested_value in nested["exclude"].items():
539
+ excludes[f"{lookup}__{nested_key}"] = nested_value
540
+ if isinstance(none_value, dict):
541
+ nested = normalize_filter_input(attr_type, none_value)
542
+ for nested_key, nested_value in nested["filter"].items():
543
+ excludes[f"{lookup}__{nested_key}"] = nested_value
544
+ for nested_key, nested_value in nested["exclude"].items():
545
+ filters[f"{lookup}__{nested_key}"] = nested_value
546
+ continue
547
+
548
+ filters[key] = value
549
+
550
+ return {"filter": filters, "exclude": excludes}
551
+
552
+
426
553
  # ---------------------------------------------------------------------------
427
554
  # Top-level search query registration
428
555
  # ---------------------------------------------------------------------------
@@ -634,7 +761,7 @@ def register_search_query(
634
761
 
635
762
  hits.sort(key=_sort_key, reverse=sort_desc)
636
763
  else:
637
- hits.sort(key=lambda item: (item[0] or 0), reverse=True)
764
+ hits.sort(key=lambda item: item[0] or 0, reverse=True)
638
765
 
639
766
  items: list[GeneralManager] = []
640
767
  for _, _hit, instance in hits[offset : offset + limit]:
@@ -72,6 +72,8 @@ class AttributeTypedDict(TypedDict):
72
72
 
73
73
  type: type
74
74
  graphql_scalar: NotRequired[str]
75
+ relation_kind: NotRequired[str]
76
+ filter_lookup: NotRequired[str]
75
77
  default: Any
76
78
  is_required: bool
77
79
  is_editable: bool
@@ -205,6 +205,8 @@ class _FieldDescriptorBuilder:
205
205
  default=default,
206
206
  is_derived=False,
207
207
  accessor=accessor,
208
+ relation_kind="direct",
209
+ filter_lookup=field.name,
208
210
  )
209
211
 
210
212
  def _add_collection_relations(self) -> None:
@@ -303,6 +305,8 @@ class _FieldDescriptorBuilder:
303
305
  default=None,
304
306
  is_derived=is_derived,
305
307
  accessor=accessor,
308
+ relation_kind="collection",
309
+ filter_lookup=getattr(field, "name", accessor_name),
306
310
  )
307
311
 
308
312
  def _resolve_collection_base_name(
@@ -347,6 +351,8 @@ class _FieldDescriptorBuilder:
347
351
  default: Any,
348
352
  is_derived: bool,
349
353
  accessor: DescriptorAccessor,
354
+ relation_kind: str | None = None,
355
+ filter_lookup: str | None = None,
350
356
  ) -> None:
351
357
  """
352
358
  Register a FieldDescriptor for a named interface attribute.
@@ -375,6 +381,10 @@ class _FieldDescriptorBuilder:
375
381
  graphql_scalar = _graphql_scalar_hint(raw_type)
376
382
  if graphql_scalar is not None:
377
383
  metadata["graphql_scalar"] = graphql_scalar
384
+ if relation_kind is not None:
385
+ metadata["relation_kind"] = relation_kind
386
+ if filter_lookup is not None:
387
+ metadata["filter_lookup"] = filter_lookup
378
388
  self._descriptors[attribute_name] = FieldDescriptor(
379
389
  name=attribute_name,
380
390
  metadata=metadata,
File without changes