GeneralManager 0.63.0__tar.gz → 0.63.2__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 (286) hide show
  1. {generalmanager-0.63.0/src/GeneralManager.egg-info → generalmanager-0.63.2}/PKG-INFO +1 -1
  2. {generalmanager-0.63.0 → generalmanager-0.63.2}/pyproject.toml +1 -3
  3. {generalmanager-0.63.0 → generalmanager-0.63.2/src/GeneralManager.egg-info}/PKG-INFO +1 -1
  4. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/GeneralManager.egg-info/SOURCES.txt +1 -0
  5. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/existing_model/resolution.py +31 -4
  6. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/orm/__init__.py +3 -1
  7. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/orm/_compat.py +2 -2
  8. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/orm/mutations.py +70 -27
  9. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/orm/support.py +35 -8
  10. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/interfaces/existing_model.py +9 -7
  11. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/utils/errors.py +12 -0
  12. generalmanager-0.63.2/src/general_manager/interface/utils/history.py +139 -0
  13. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/utils/models.py +3 -2
  14. {generalmanager-0.63.0 → generalmanager-0.63.2}/LICENSE +0 -0
  15. {generalmanager-0.63.0 → generalmanager-0.63.2}/README.md +0 -0
  16. {generalmanager-0.63.0 → generalmanager-0.63.2}/setup.cfg +0 -0
  17. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
  18. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/GeneralManager.egg-info/requires.txt +0 -0
  19. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/GeneralManager.egg-info/top_level.txt +0 -0
  20. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/__init__.py +0 -0
  21. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/_types/__init__.py +0 -0
  22. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/_types/api.py +0 -0
  23. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/_types/bucket.py +0 -0
  24. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/_types/cache.py +0 -0
  25. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/_types/dataframes.py +0 -0
  26. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/_types/factory.py +0 -0
  27. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/_types/general_manager.py +0 -0
  28. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/_types/interface.py +0 -0
  29. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/_types/manager.py +0 -0
  30. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/_types/measurement.py +0 -0
  31. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/_types/permission.py +0 -0
  32. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/_types/rule.py +0 -0
  33. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/_types/search.py +0 -0
  34. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/_types/utils.py +0 -0
  35. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/api/__init__.py +0 -0
  36. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/api/graphql.py +0 -0
  37. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/api/graphql_errors.py +0 -0
  38. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/api/graphql_mutations.py +0 -0
  39. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/api/graphql_prefetch.py +0 -0
  40. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/api/graphql_resolvers.py +0 -0
  41. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/api/graphql_search.py +0 -0
  42. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
  43. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/api/graphql_subscriptions.py +0 -0
  44. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/api/graphql_view.py +0 -0
  45. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/api/graphql_warmup.py +0 -0
  46. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/api/graphql_warmup_registry.py +0 -0
  47. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/api/graphql_warmup_tasks.py +0 -0
  48. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/api/mutation.py +0 -0
  49. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/api/property.py +0 -0
  50. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/api/registry.py +0 -0
  51. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/api/remote_api.py +0 -0
  52. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/api/remote_invalidation.py +0 -0
  53. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/api/remote_invalidation_client.py +0 -0
  54. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/apps.py +0 -0
  55. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/bootstrap.py +0 -0
  56. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/bucket/__init__.py +0 -0
  57. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/bucket/base_bucket.py +0 -0
  58. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/bucket/calculation_bucket.py +0 -0
  59. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/bucket/database_bucket.py +0 -0
  60. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/bucket/group_bucket.py +0 -0
  61. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/bucket/indexing.py +0 -0
  62. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/bucket/request_bucket.py +0 -0
  63. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/cache/__init__.py +0 -0
  64. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/cache/cache_decorator.py +0 -0
  65. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/cache/cache_tracker.py +0 -0
  66. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/cache/dependency_cache.py +0 -0
  67. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/cache/dependency_index.py +0 -0
  68. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/cache/dependency_matching.py +0 -0
  69. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/cache/dependency_publish.py +0 -0
  70. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/cache/dependency_shards.py +0 -0
  71. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/cache/model_dependency_collector.py +0 -0
  72. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/cache/run_context.py +0 -0
  73. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/cache/signals.py +0 -0
  74. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/__init__.py +0 -0
  75. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/audit.py +0 -0
  76. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/bootstrap.py +0 -0
  77. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/checks.py +0 -0
  78. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/consumer.py +0 -0
  79. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/errors.py +0 -0
  80. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/evals/__init__.py +0 -0
  81. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/evals/__main__.py +0 -0
  82. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/evals/baseline.py +0 -0
  83. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/evals/datasets/basic_queries.yaml +0 -0
  84. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/evals/datasets/demo_readiness.yaml +0 -0
  85. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/evals/datasets/edge_cases.yaml +0 -0
  86. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/evals/datasets/follow_ups.yaml +0 -0
  87. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/evals/datasets/large_schema.yaml +0 -0
  88. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/evals/datasets/multi_hop.yaml +0 -0
  89. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/evals/diagnostics.py +0 -0
  90. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/evals/fingerprints.py +0 -0
  91. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/evals/fixtures.py +0 -0
  92. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/evals/judges/__init__.py +0 -0
  93. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/evals/judges/answer_quality.py +0 -0
  94. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/evals/judges/contract.py +0 -0
  95. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/evals/judges/result_accuracy.py +0 -0
  96. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/evals/judges/tool_sequence.py +0 -0
  97. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/evals/readiness.py +0 -0
  98. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/evals/runner.py +0 -0
  99. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/evals/traces.py +0 -0
  100. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/grounding.py +0 -0
  101. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/models.py +0 -0
  102. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/providers/__init__.py +0 -0
  103. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/providers/_shared.py +0 -0
  104. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/providers/anthropic.py +0 -0
  105. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/providers/base.py +0 -0
  106. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/providers/google.py +0 -0
  107. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/providers/ollama.py +0 -0
  108. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/providers/openai.py +0 -0
  109. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/rate_limits.py +0 -0
  110. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/routing.py +0 -0
  111. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/schema_index.py +0 -0
  112. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/settings.py +0 -0
  113. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/signals.py +0 -0
  114. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/system_prompt.py +0 -0
  115. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/tool_metadata.py +0 -0
  116. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/tools.py +0 -0
  117. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/chat/views.py +0 -0
  118. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/conf.py +0 -0
  119. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/dataframes/__init__.py +0 -0
  120. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/dataframes/measurements.py +0 -0
  121. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/factory/__init__.py +0 -0
  122. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/factory/auto_factory.py +0 -0
  123. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/factory/factories.py +0 -0
  124. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/factory/factory_methods.py +0 -0
  125. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/__init__.py +0 -0
  126. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/base_interface.py +0 -0
  127. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/bundles/__init__.py +0 -0
  128. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/bundles/calculation.py +0 -0
  129. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/bundles/database.py +0 -0
  130. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/bundles/remote_manager.py +0 -0
  131. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/bundles/request.py +0 -0
  132. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/__init__.py +0 -0
  133. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/base.py +0 -0
  134. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/builtin.py +0 -0
  135. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
  136. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
  137. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/calculation/lifecycle.py +0 -0
  138. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/configuration.py +0 -0
  139. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/core/observability.py +0 -0
  140. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/core/utils.py +0 -0
  141. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/exceptions.py +0 -0
  142. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
  143. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
  144. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/factory.py +0 -0
  145. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/orm/history.py +0 -0
  146. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
  147. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
  148. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
  149. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +0 -0
  150. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +0 -0
  151. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
  152. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
  153. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/read_only/_types.py +0 -0
  154. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
  155. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
  156. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/registry.py +0 -0
  157. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
  158. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
  159. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/infrastructure/__init__.py +0 -0
  160. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
  161. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
  162. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/interfaces/__init__.py +0 -0
  163. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/interfaces/calculation.py +0 -0
  164. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/interfaces/database.py +0 -0
  165. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/interfaces/read_only.py +0 -0
  166. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
  167. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/interfaces/request.py +0 -0
  168. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/manifests/__init__.py +0 -0
  169. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/manifests/capability_builder.py +0 -0
  170. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
  171. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/manifests/capability_models.py +0 -0
  172. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/orm_interface.py +0 -0
  173. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/requests.py +0 -0
  174. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/utils/__init__.py +0 -0
  175. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
  176. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/logging.py +0 -0
  177. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/management/commands/chat_cleanup.py +0 -0
  178. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/management/commands/cleanup_upload_intents.py +0 -0
  179. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/management/commands/graphql_warmup.py +0 -0
  180. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/management/commands/graphql_warmup_refresh_due.py +0 -0
  181. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/management/commands/search_index.py +0 -0
  182. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/management/commands/search_reconcile.py +0 -0
  183. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/management/commands/seed_manager_landscape.py +0 -0
  184. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/management/commands/shell.py +0 -0
  185. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
  186. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
  187. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/manager/__init__.py +0 -0
  188. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/manager/general_manager.py +0 -0
  189. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/manager/group_manager.py +0 -0
  190. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/manager/input.py +0 -0
  191. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/manager/meta.py +0 -0
  192. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/measurement/__init__.py +0 -0
  193. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/measurement/measurement.py +0 -0
  194. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/measurement/measurement_field.py +0 -0
  195. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/metrics/__init__.py +0 -0
  196. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/metrics/graphql.py +0 -0
  197. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/migrations/0001_initial.py +0 -0
  198. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
  199. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
  200. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/migrations/0004_chat_persistence_models.py +0 -0
  201. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/migrations/0004_search_index_state.py +0 -0
  202. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/migrations/0005_merge_chat_search.py +0 -0
  203. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/migrations/0006_chat_pending_confirmation_scoped_ids.py +0 -0
  204. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/migrations/0007_upload_intent.py +0 -0
  205. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/migrations/0008_upload_finalization_versions.py +0 -0
  206. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/migrations/0009_upload_cleanup_state.py +0 -0
  207. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/migrations/__init__.py +0 -0
  208. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/models.py +0 -0
  209. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/permission/__init__.py +0 -0
  210. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/permission/audit.py +0 -0
  211. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/permission/base_permission.py +0 -0
  212. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/permission/graphql_capabilities.py +0 -0
  213. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/permission/manager_based_permission.py +0 -0
  214. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/permission/mutation_permission.py +0 -0
  215. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/permission/permission_checks.py +0 -0
  216. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/permission/permission_data_manager.py +0 -0
  217. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/permission/utils.py +0 -0
  218. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/public_api_registry.py +0 -0
  219. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/py.typed +0 -0
  220. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/rule/__init__.py +0 -0
  221. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/rule/handler.py +0 -0
  222. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/rule/rule.py +0 -0
  223. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/search/__init__.py +0 -0
  224. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/search/async_tasks.py +0 -0
  225. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/search/backend.py +0 -0
  226. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/search/backend_registry.py +0 -0
  227. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/search/backends/__init__.py +0 -0
  228. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/search/backends/dev.py +0 -0
  229. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/search/backends/meilisearch.py +0 -0
  230. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/search/backends/opensearch.py +0 -0
  231. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/search/backends/typesense.py +0 -0
  232. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/search/config.py +0 -0
  233. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/search/indexer.py +0 -0
  234. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/search/models.py +0 -0
  235. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/search/reconciliation.py +0 -0
  236. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/search/registry.py +0 -0
  237. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/search/tasks.py +0 -0
  238. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/search/utils.py +0 -0
  239. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/seeding/__init__.py +0 -0
  240. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/seeding/manager_landscape.py +0 -0
  241. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/uploads/__init__.py +0 -0
  242. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/uploads/adapters.py +0 -0
  243. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/uploads/checks.py +0 -0
  244. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/uploads/config.py +0 -0
  245. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/uploads/errors.py +0 -0
  246. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/uploads/finalization.py +0 -0
  247. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/uploads/graphql.py +0 -0
  248. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/uploads/graphql_types.py +0 -0
  249. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/uploads/metrics.py +0 -0
  250. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/uploads/models.py +0 -0
  251. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/uploads/public.py +0 -0
  252. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/uploads/s3.py +0 -0
  253. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/uploads/services.py +0 -0
  254. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/uploads/tokens.py +0 -0
  255. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/uploads/types.py +0 -0
  256. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/uploads/urls.py +0 -0
  257. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/uploads/views.py +0 -0
  258. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/utils/__init__.py +0 -0
  259. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/utils/args_to_kwargs.py +0 -0
  260. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/utils/filter_parser.py +0 -0
  261. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/utils/format_string.py +0 -0
  262. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/utils/json_encoder.py +0 -0
  263. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/utils/make_cache_key.py +0 -0
  264. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/utils/none_to_zero.py +0 -0
  265. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/utils/path_mapping.py +0 -0
  266. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/utils/public_api.py +0 -0
  267. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/utils/testing.py +0 -0
  268. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/utils/type_checks.py +0 -0
  269. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/workflow/__init__.py +0 -0
  270. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/workflow/actions.py +0 -0
  271. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/workflow/backend_registry.py +0 -0
  272. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/workflow/backends/__init__.py +0 -0
  273. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/workflow/backends/celery.py +0 -0
  274. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/workflow/backends/local.py +0 -0
  275. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/workflow/backends/n8n.py +0 -0
  276. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/workflow/config.py +0 -0
  277. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/workflow/engine.py +0 -0
  278. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/workflow/event_registry.py +0 -0
  279. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/workflow/events.py +0 -0
  280. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/workflow/models.py +0 -0
  281. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/workflow/signal_bridge.py +0 -0
  282. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/workflow/tasks.py +0 -0
  283. {generalmanager-0.63.0 → generalmanager-0.63.2}/src/general_manager/workflow/telemetry.py +0 -0
  284. {generalmanager-0.63.0 → generalmanager-0.63.2}/tests/test_settings.py +0 -0
  285. {generalmanager-0.63.0 → generalmanager-0.63.2}/tests/test_urls.py +0 -0
  286. {generalmanager-0.63.0 → generalmanager-0.63.2}/tests/testing_asgi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.63.0
3
+ Version: 0.63.2
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.63.0"
7
+ version = "0.63.2"
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" }]
@@ -93,8 +93,6 @@ major_on_zero = false
93
93
  version_toml = ["pyproject.toml:project.version"]
94
94
  branch = "main"
95
95
  build_command = "python -m build"
96
- upload_to_PyPI = true
97
- upload_to_release = true
98
96
 
99
97
  [tool.semantic_release.remote]
100
98
  ignore_token_for_push = true
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.63.0
3
+ Version: 0.63.2
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
@@ -173,6 +173,7 @@ src/general_manager/interface/manifests/capability_models.py
173
173
  src/general_manager/interface/utils/__init__.py
174
174
  src/general_manager/interface/utils/database_interface_protocols.py
175
175
  src/general_manager/interface/utils/errors.py
176
+ src/general_manager/interface/utils/history.py
176
177
  src/general_manager/interface/utils/models.py
177
178
  src/general_manager/management/commands/chat_cleanup.py
178
179
  src/general_manager/management/commands/cleanup_upload_intents.py
@@ -6,13 +6,18 @@ from dataclasses import dataclass
6
6
  from typing import TYPE_CHECKING, ClassVar, cast
7
7
 
8
8
  from django.apps import apps
9
- from django.db import models
9
+ from django.db import DEFAULT_DB_ALIAS, models
10
10
  from simple_history import register
11
11
 
12
12
  from general_manager.factory.auto_factory import AutoFactory
13
13
  from general_manager.interface.utils.errors import (
14
14
  InvalidModelReferenceError,
15
15
  MissingModelConfigurationError,
16
+ UnsafeHistoryConfigurationError,
17
+ )
18
+ from general_manager.interface.utils.history import (
19
+ DATABASE_AWARE_HISTORY_MARKER,
20
+ DatabaseAwareHistoricalRecords,
16
21
  )
17
22
  from general_manager.interface.utils.models import get_full_clean_methode
18
23
 
@@ -127,14 +132,36 @@ class ExistingModelResolutionCapability(BaseCapability):
127
132
 
128
133
  def _perform() -> None:
129
134
  """
130
- Register simple history for the captured Django model if history is not already present.
135
+ Ensure the captured model has history tracking compatible with its interface database alias.
131
136
 
132
- If the model's _meta already exposes the simple_history manager attribute, no action is taken. Otherwise, collects the model's local many-to-many field names and registers the model with simple_history including those m2m fields.
137
+ An untracked model is registered with database-aware history and all local many-to-many fields. An existing tracker is accepted for the default alias; a non-default alias requires GeneralManager's database-aware marker and otherwise raises `UnsafeHistoryConfigurationError`.
133
138
  """
134
139
  if hasattr(model._meta, "simple_history_manager_attribute"):
140
+ database_alias = (
141
+ getattr(interface_cls, "database", None) if interface_cls else None
142
+ )
143
+ if database_alias and database_alias != DEFAULT_DB_ALIAS:
144
+ assert interface_cls is not None
145
+ manager_name = model._meta.simple_history_manager_attribute
146
+ history_model = getattr(model, manager_name).model
147
+ if not getattr(
148
+ history_model,
149
+ DATABASE_AWARE_HISTORY_MARKER,
150
+ False,
151
+ ):
152
+ raise UnsafeHistoryConfigurationError(
153
+ model.__name__,
154
+ interface_cls.__name__,
155
+ database_alias,
156
+ )
135
157
  return
136
158
  m2m_fields = [field.name for field in model._meta.local_many_to_many]
137
- register(model, m2m_fields=m2m_fields)
159
+ register(
160
+ model,
161
+ m2m_fields=m2m_fields,
162
+ records_class=DatabaseAwareHistoricalRecords,
163
+ use_base_model_db=True,
164
+ )
138
165
 
139
166
  target = interface_cls or model
140
167
  return call_with_observability(
@@ -5,7 +5,9 @@ from __future__ import annotations
5
5
  from general_manager.interface.capabilities.core.utils import (
6
6
  with_observability as with_observability,
7
7
  )
8
- from simple_history.utils import update_change_reason as update_change_reason
8
+ from general_manager.interface.utils.history import (
9
+ update_change_reason as update_change_reason,
10
+ )
9
11
 
10
12
  from .history import HistoryNotSupportedError, OrmHistoryCapability
11
13
  from .lifecycle import OrmLifecycleCapability
@@ -52,8 +52,8 @@ def call_update_change_reason(instance: models.Model, reason: str) -> None:
52
52
  Delegate invocation to the package-level `update_change_reason` callable.
53
53
 
54
54
  This resolves the callable from `general_manager.interface.capabilities.orm` at call time so that runtime patches to that attribute are respected.
55
- The default package-level callable is django-simple-history's
56
- `update_change_reason`. If simple-history is absent from the model, if a
55
+ The default package-level callable is GeneralManager's database-aware
56
+ change-reason wrapper. If simple-history is absent from the model, if a
57
57
  patched callable is missing expected behavior, or if the callable raises,
58
58
  that exception propagates unchanged.
59
59
 
@@ -2,6 +2,9 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from collections.abc import Iterator
6
+ from contextlib import contextmanager
7
+ from contextvars import ContextVar
5
8
  from typing import TYPE_CHECKING, Any, ClassVar, cast
6
9
 
7
10
  from django.contrib.auth import get_user_model
@@ -48,6 +51,21 @@ type MutationPayload = dict[str, object]
48
51
  type ManyToManyPayload = dict[str, list[object]]
49
52
  type MutationResult = dict[str, object]
50
53
 
54
+ _save_with_history_savepoint = ContextVar(
55
+ "save_with_history_savepoint",
56
+ default=True,
57
+ )
58
+
59
+
60
+ @contextmanager
61
+ def _without_save_with_history_savepoint() -> Iterator[None]:
62
+ """Suppress the nested savepoint within an encompassing mutation atomic."""
63
+ token = _save_with_history_savepoint.set(False)
64
+ try:
65
+ yield
66
+ finally:
67
+ _save_with_history_savepoint.reset(token)
68
+
51
69
 
52
70
  class OrmMutationCapability(BaseCapability):
53
71
  """Common utilities to modify ORM instances."""
@@ -156,11 +174,19 @@ class OrmMutationCapability(BaseCapability):
156
174
  database_alias = support.get_database_alias(interface_cls)
157
175
  if database_alias:
158
176
  instance._state.db = database_alias
159
- atomic_context = (
160
- transaction.atomic(using=database_alias)
161
- if database_alias
162
- else transaction.atomic()
163
- )
177
+ savepoint = _save_with_history_savepoint.get()
178
+ if database_alias:
179
+ atomic_context = (
180
+ transaction.atomic(using=database_alias)
181
+ if savepoint
182
+ else transaction.atomic(using=database_alias, savepoint=False)
183
+ )
184
+ else:
185
+ atomic_context = (
186
+ transaction.atomic()
187
+ if savepoint
188
+ else transaction.atomic(savepoint=False)
189
+ )
164
190
  with atomic_context:
165
191
  _assign_history_actor(
166
192
  instance,
@@ -360,18 +386,27 @@ class OrmCreateCapability(BaseCapability):
360
386
  instance = mutation.assign_simple_attributes(
361
387
  interface_cls, interface_cls._model(), normalized_simple
362
388
  )
363
- pk = mutation.save_with_history(
364
- interface_cls,
365
- instance,
366
- creator_id=creator_id,
367
- history_comment=history_comment,
368
- )
369
- mutation.apply_many_to_many(
370
- interface_cls,
371
- instance,
372
- many_to_many_kwargs=normalized_many,
373
- history_comment=history_comment,
389
+ support = get_support_capability(interface_cls)
390
+ database_alias = support.get_database_alias(interface_cls)
391
+ atomic_context = (
392
+ transaction.atomic(using=database_alias)
393
+ if database_alias
394
+ else transaction.atomic()
374
395
  )
396
+ with atomic_context:
397
+ with _without_save_with_history_savepoint():
398
+ pk = mutation.save_with_history(
399
+ interface_cls,
400
+ instance,
401
+ creator_id=creator_id,
402
+ history_comment=history_comment,
403
+ )
404
+ mutation.apply_many_to_many(
405
+ interface_cls,
406
+ instance,
407
+ many_to_many_kwargs=normalized_many,
408
+ history_comment=history_comment,
409
+ )
375
410
  return {"id": pk}
376
411
 
377
412
  return call_with_observability(
@@ -502,19 +537,27 @@ class OrmUpdateCapability(BaseCapability):
502
537
  instance = mutation.assign_simple_attributes(
503
538
  interface_instance.__class__, instance, normalized_simple
504
539
  )
505
- pk = mutation.save_with_history(
506
- interface_instance.__class__,
507
- instance,
508
- creator_id=creator_id,
509
- history_comment=history_comment,
540
+ database_alias = support.get_database_alias(interface_instance.__class__)
541
+ atomic_context = (
542
+ transaction.atomic(using=database_alias)
543
+ if database_alias
544
+ else transaction.atomic()
510
545
  )
546
+ with atomic_context:
547
+ with _without_save_with_history_savepoint():
548
+ pk = mutation.save_with_history(
549
+ interface_instance.__class__,
550
+ instance,
551
+ creator_id=creator_id,
552
+ history_comment=history_comment,
553
+ )
554
+ mutation.apply_many_to_many(
555
+ interface_instance.__class__,
556
+ instance,
557
+ many_to_many_kwargs=normalized_many,
558
+ history_comment=history_comment,
559
+ )
511
560
  discard_orm_instance_cache(interface_instance.__class__, pk)
512
- mutation.apply_many_to_many(
513
- interface_instance.__class__,
514
- instance,
515
- many_to_many_kwargs=normalized_many,
516
- history_comment=history_comment,
517
- )
518
561
  return {"id": pk}
519
562
 
520
563
  return call_with_observability(
@@ -9,7 +9,7 @@ from functools import lru_cache
9
9
  from typing import TYPE_CHECKING, Any, Callable, ClassVar, Type, cast
10
10
 
11
11
  from django.core.exceptions import FieldDoesNotExist
12
- from django.db import models
12
+ from django.db import DEFAULT_DB_ALIAS, connections, models
13
13
  from django.db.models import Subquery
14
14
  from django.utils import timezone
15
15
  from general_manager.bucket.database_bucket import DatabaseBucket
@@ -303,14 +303,22 @@ class OrmReadCapability(BaseCapability):
303
303
  raise model_cls.DoesNotExist
304
304
 
305
305
  context = current_calculation_run_context()
306
- read_func = (
307
- _perform
308
- if context is None
309
- else lambda: context.get_or_set(
310
- _orm_instance_cache_key(interface_instance),
311
- _perform,
306
+ if context is None:
307
+ read_func = _perform
308
+ else:
309
+ interface_cls = interface_instance.__class__
310
+ support = get_support_capability(interface_cls)
311
+ database_alias = (
312
+ support.get_database_alias(interface_cls) or DEFAULT_DB_ALIAS
313
+ )
314
+ read_func = (
315
+ _perform
316
+ if _connection_has_application_atomic_block(database_alias)
317
+ else lambda: context.get_or_set(
318
+ _orm_instance_cache_key(interface_instance),
319
+ _perform,
320
+ )
312
321
  )
313
- )
314
322
 
315
323
  return call_with_observability(
316
324
  interface_instance,
@@ -1072,6 +1080,25 @@ def _orm_instance_cache_key(
1072
1080
  )
1073
1081
 
1074
1082
 
1083
+ def _connection_has_application_atomic_block(database_alias: str) -> bool:
1084
+ """Return whether a connection is inside a non-TestCase atomic block."""
1085
+ connection = connections[database_alias]
1086
+ try:
1087
+ blocks = getattr(connection, "atomic_blocks") # noqa: B009
1088
+ except (AttributeError, TypeError):
1089
+ return bool(getattr(connection, "in_atomic_block", False))
1090
+ if not isinstance(blocks, (list, tuple)) or not blocks:
1091
+ return bool(getattr(connection, "in_atomic_block", False))
1092
+
1093
+ def _is_testcase_wrapper(block: object) -> bool:
1094
+ try:
1095
+ return getattr(block, "_from_testcase") is True # noqa: B009
1096
+ except (AttributeError, TypeError):
1097
+ return False
1098
+
1099
+ return any(not _is_testcase_wrapper(block) for block in blocks)
1100
+
1101
+
1075
1102
  def discard_orm_instance_cache(
1076
1103
  interface_cls: OrmInterfaceClass,
1077
1104
  pk: object,
@@ -28,13 +28,15 @@ class ExistingModelInterface(OrmInterfaceBase[ExistingModelT]):
28
28
  string accepted by ``django.apps.apps.get_model()``, such as
29
29
  ``settings.AUTH_USER_MODEL``. During manager class creation the
30
30
  ``existing_model_resolution`` lifecycle capability resolves that reference,
31
- caches the resolved model on the concrete interface, registers history when
32
- needed, applies interface rules, and builds a factory for the legacy model.
33
- History registration is skipped when the model already exposes a
34
- ``model._meta.simple_history_manager_attribute`` marker; otherwise local
35
- many-to-many fields are included in the registration. Interface rules are
36
- the optional ``Meta.rules`` sequence on the interface. Soft delete is
37
- enabled when the resolved model exposes an ``is_active`` attribute.
31
+ caches the resolved model on the concrete interface, auto-registers
32
+ database-aware history when needed, applies interface rules, and builds a
33
+ factory for the legacy model. Auto-registration includes local many-to-many
34
+ fields. A pre-registered tracker remains compatible on the default database;
35
+ on a configured non-default alias, its generated history model must carry
36
+ GeneralManager's database-aware marker or manager creation raises
37
+ ``UnsafeHistoryConfigurationError``. Interface rules are the optional
38
+ ``Meta.rules`` sequence on the interface. Soft delete is enabled when the
39
+ resolved model exposes an ``is_active`` attribute.
38
40
 
39
41
  Construction and row loading are inherited from ``OrmInterfaceBase``: the
40
42
  default public input is the wrapped row ``id`` parsed by ``Input(int)`` plus
@@ -16,6 +16,7 @@ __all__ = [
16
16
  "MissingUniqueFieldError",
17
17
  "ReadOnlyRelationLookupError",
18
18
  "UnknownFieldError",
19
+ "UnsafeHistoryConfigurationError",
19
20
  ]
20
21
 
21
22
 
@@ -226,3 +227,14 @@ class InvalidModelReferenceError(TypeError):
226
227
  `Invalid model reference '{reference}'.`
227
228
  """
228
229
  super().__init__(f"Invalid model reference '{reference}'.")
230
+
231
+
232
+ class UnsafeHistoryConfigurationError(ValueError):
233
+ """Raised when pre-registered history cannot follow an interface alias."""
234
+
235
+ def __init__(self, model_name: str, interface_name: str, alias: str) -> None:
236
+ """Build guidance for replacing an unsafe simple-history tracker."""
237
+ super().__init__(
238
+ f"{model_name} must use DatabaseAwareHistoricalRecords before "
239
+ f"{interface_name} configures non-default database alias '{alias}'."
240
+ )
@@ -0,0 +1,139 @@
1
+ """Database-aware django-simple-history integration helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, cast
6
+
7
+ from django.db import DEFAULT_DB_ALIAS, models
8
+ from django.db.models.fields.related import ForeignKey
9
+ from simple_history import utils as simple_history_utils
10
+ from simple_history.models import HistoricalRecords
11
+ from simple_history.signals import (
12
+ post_create_historical_m2m_records,
13
+ pre_create_historical_m2m_records,
14
+ )
15
+ from simple_history.utils import get_history_manager_for_model
16
+
17
+
18
+ DATABASE_AWARE_HISTORY_MARKER = "_general_manager_database_aware_history"
19
+
20
+
21
+ class DatabaseAwareHistoricalRecords(HistoricalRecords): # type: ignore[misc]
22
+ """Store generated history records on the base model's database alias."""
23
+
24
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
25
+ kwargs["use_base_model_db"] = True
26
+ super().__init__(*args, **kwargs)
27
+
28
+ def create_history_model(
29
+ self,
30
+ model: type[models.Model],
31
+ inherited: bool,
32
+ ) -> type[models.Model]:
33
+ """Mark the generated model as safe for non-default database aliases."""
34
+ history_model = cast(
35
+ type[models.Model],
36
+ super().create_history_model(model, inherited),
37
+ )
38
+ setattr(history_model, DATABASE_AWARE_HISTORY_MARKER, True)
39
+ return history_model
40
+
41
+ def create_historical_record(
42
+ self,
43
+ instance: models.Model,
44
+ history_type: str,
45
+ using: str | None = None,
46
+ ) -> None:
47
+ """Route base history even when an M2M signal omits ``using``."""
48
+ database_alias = using or instance._state.db or DEFAULT_DB_ALIAS
49
+ super().create_historical_record(
50
+ instance,
51
+ history_type,
52
+ using=database_alias,
53
+ )
54
+
55
+ def create_historical_record_m2ms(
56
+ self,
57
+ history_instance: models.Model,
58
+ instance: models.Model,
59
+ ) -> None:
60
+ """Snapshot tracked many-to-many rows on the base instance alias."""
61
+ database_alias = instance._state.db or history_instance._state.db
62
+ database_alias = database_alias or DEFAULT_DB_ALIAS
63
+ history_fields = cast(
64
+ list[Any],
65
+ history_instance._history_m2m_fields, # type: ignore[attr-defined]
66
+ )
67
+ for field in history_fields:
68
+ m2m_history_model = self.m2m_models[field]
69
+ original_instance = history_instance.instance # type: ignore[attr-defined]
70
+ through_model = getattr(original_instance, field.name).through
71
+ through_model_field_names = [
72
+ model_field.name for model_field in through_model._meta.fields
73
+ ]
74
+ through_model_fk_field_names = [
75
+ model_field.name
76
+ for model_field in through_model._meta.fields
77
+ if isinstance(model_field, ForeignKey)
78
+ ]
79
+
80
+ through_field_name = simple_history_utils.get_m2m_field_name(field)
81
+ rows = through_model.objects.using(database_alias).filter(
82
+ **{through_field_name: instance}
83
+ )
84
+ rows = rows.select_related(*through_model_fk_field_names)
85
+ insert_rows = []
86
+ for row in rows:
87
+ insert_row = m2m_history_model(
88
+ history_id=history_instance.pk,
89
+ **{
90
+ field_name: getattr(row, field_name)
91
+ for field_name in through_model_field_names
92
+ },
93
+ )
94
+ insert_row._state.db = database_alias
95
+ insert_rows.append(insert_row)
96
+
97
+ pre_create_historical_m2m_records.send(
98
+ sender=m2m_history_model,
99
+ rows=insert_rows,
100
+ history_instance=history_instance,
101
+ instance=instance,
102
+ field=field,
103
+ )
104
+ created_rows = m2m_history_model.objects.using(database_alias).bulk_create(
105
+ insert_rows
106
+ )
107
+ post_create_historical_m2m_records.send(
108
+ sender=m2m_history_model,
109
+ created_rows=created_rows,
110
+ history_instance=history_instance,
111
+ instance=instance,
112
+ field=field,
113
+ )
114
+
115
+
116
+ def update_change_reason(instance: models.Model, reason: str) -> None:
117
+ """Update the latest history row on the model instance's database alias."""
118
+ manager_owner: models.Model | type[models.Model]
119
+ manager_owner = instance if instance.pk is not None else type(instance)
120
+ history = get_history_manager_for_model(manager_owner)
121
+ database_alias = instance._state.db
122
+ if database_alias:
123
+ history = history.using(database_alias)
124
+
125
+ history_fields = {field.attname for field in history.model._meta.fields}
126
+ attrs: dict[str, object] = {}
127
+ for field in instance._meta.fields:
128
+ if field.attname not in history_fields:
129
+ continue
130
+ value = getattr(instance, field.attname)
131
+ if not field.primary_key or value is not None:
132
+ attrs[field.attname] = value
133
+
134
+ record = history.filter(**attrs).order_by("-history_date").first()
135
+ record.history_change_reason = reason
136
+ if database_alias:
137
+ record.save(using=database_alias)
138
+ else:
139
+ record.save()
@@ -7,9 +7,10 @@ from typing import TYPE_CHECKING, Callable, ClassVar, Protocol, TypeVar, cast
7
7
  from django.db import models
8
8
  from django.contrib.auth.models import AbstractBaseUser
9
9
  from django.core.exceptions import FieldDoesNotExist
10
- from simple_history.models import HistoricalRecords
11
10
  from django.core.exceptions import ValidationError
12
11
 
12
+ from general_manager.interface.utils.history import DatabaseAwareHistoricalRecords
13
+
13
14
 
14
15
  if TYPE_CHECKING:
15
16
  from django.core.exceptions import ValidationErrorMessageArg
@@ -201,7 +202,7 @@ class GeneralManagerBasisModel(models.Model):
201
202
  """
202
203
 
203
204
  _general_manager_class: ClassVar[type[GeneralManager]]
204
- history = HistoricalRecords(inherit=True)
205
+ history = DatabaseAwareHistoricalRecords(inherit=True)
205
206
 
206
207
  class Meta:
207
208
  abstract = True
File without changes