GeneralManager 0.64.3__tar.gz → 0.65.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.
- {generalmanager-0.64.3/src/GeneralManager.egg-info → generalmanager-0.65.0}/PKG-INFO +5 -2
- {generalmanager-0.64.3 → generalmanager-0.65.0}/README.md +4 -1
- {generalmanager-0.64.3 → generalmanager-0.65.0}/pyproject.toml +1 -1
- {generalmanager-0.64.3 → generalmanager-0.65.0/src/GeneralManager.egg-info}/PKG-INFO +5 -2
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/GeneralManager.egg-info/SOURCES.txt +6 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/_types/general_manager.py +4 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/_types/search.py +4 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/apps.py +9 -2
- generalmanager-0.65.0/src/general_manager/cache/data_change_context.py +93 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/cache/signals.py +110 -52
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/models.py +70 -5
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/orm/mutations.py +27 -30
- generalmanager-0.65.0/src/general_manager/migrations/0010_chat_pending_confirmation_portable_uniqueness.py +64 -0
- generalmanager-0.65.0/src/general_manager/migrations/0011_search_index_state_dirty_generation.py +20 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/public_api_registry.py +10 -0
- generalmanager-0.65.0/src/general_manager/search/async_tasks.py +507 -0
- generalmanager-0.65.0/src/general_manager/search/checks.py +259 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/search/config.py +41 -7
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/search/indexer.py +164 -119
- generalmanager-0.65.0/src/general_manager/search/invalidation.py +1231 -0
- generalmanager-0.65.0/src/general_manager/search/m2m_invalidation.py +372 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/search/models.py +55 -37
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/search/reconciliation.py +221 -53
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/uploads/finalization.py +51 -2
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/utils/testing.py +219 -179
- generalmanager-0.65.0/tests/test_settings.py +79 -0
- generalmanager-0.64.3/src/general_manager/search/async_tasks.py +0 -217
- generalmanager-0.64.3/tests/test_settings.py +0 -34
- {generalmanager-0.64.3 → generalmanager-0.65.0}/LICENSE +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/setup.cfg +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/GeneralManager.egg-info/requires.txt +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/GeneralManager.egg-info/top_level.txt +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/_types/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/_types/api.py +3 -3
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/_types/bucket.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/_types/cache.py +2 -2
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/_types/dataframes.py +2 -2
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/_types/factory.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/_types/interface.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/_types/manager.py +2 -2
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/_types/measurement.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/_types/permission.py +2 -2
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/_types/rule.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/_types/utils.py +2 -2
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/graphql.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/graphql_errors.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/graphql_mutations.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/graphql_prefetch.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/graphql_relations.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/graphql_resolvers.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/graphql_search.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/graphql_subscriptions.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/graphql_view.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/graphql_warmup.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/graphql_warmup_registry.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/graphql_warmup_tasks.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/mutation.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/notification_batching.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/property.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/registry.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/remote_api.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/remote_invalidation.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/api/remote_invalidation_client.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/bootstrap.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/bucket/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/bucket/base_bucket.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/bucket/calculation_bucket.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/bucket/database_bucket.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/bucket/group_bucket.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/bucket/indexing.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/bucket/request_bucket.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/cache/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/cache/cache_decorator.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/cache/cache_tracker.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/cache/dependency_cache.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/cache/dependency_index.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/cache/dependency_matching.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/cache/dependency_publish.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/cache/dependency_shards.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/cache/model_dependency_collector.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/cache/run_context.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/audit.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/bootstrap.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/checks.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/consumer.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/errors.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/evals/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/evals/__main__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/evals/baseline.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/evals/datasets/basic_queries.yaml +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/evals/datasets/demo_readiness.yaml +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/evals/datasets/edge_cases.yaml +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/evals/datasets/follow_ups.yaml +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/evals/datasets/large_schema.yaml +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/evals/datasets/multi_hop.yaml +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/evals/diagnostics.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/evals/fingerprints.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/evals/fixtures.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/evals/judges/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/evals/judges/answer_quality.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/evals/judges/contract.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/evals/judges/result_accuracy.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/evals/judges/tool_sequence.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/evals/readiness.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/evals/runner.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/evals/traces.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/grounding.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/providers/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/providers/_shared.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/providers/anthropic.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/providers/base.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/providers/google.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/providers/ollama.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/providers/openai.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/rate_limits.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/routing.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/schema_index.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/settings.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/signals.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/system_prompt.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/tool_metadata.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/tools.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/chat/views.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/conf.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/dataframes/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/dataframes/measurements.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/factory/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/factory/auto_factory.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/factory/factories.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/factory/factory_methods.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/base_interface.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/bundles/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/bundles/calculation.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/bundles/database.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/bundles/remote_manager.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/bundles/request.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/base.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/builtin.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/calculation/lifecycle.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/configuration.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/core/observability.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/core/utils.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/exceptions.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/existing_model/resolution.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/factory.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/orm/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/orm/_compat.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/orm/history.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/orm/support.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/read_only/_types.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/registry.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/infrastructure/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/interfaces/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/interfaces/calculation.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/interfaces/database.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/interfaces/existing_model.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/interfaces/read_only.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/interfaces/request.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/manifests/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/manifests/capability_builder.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/manifests/capability_models.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/orm_interface.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/requests.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/utils/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/utils/errors.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/utils/history.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/interface/utils/models.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/logging.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/management/commands/chat_cleanup.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/management/commands/cleanup_upload_intents.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/management/commands/graphql_warmup.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/management/commands/graphql_warmup_refresh_due.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/management/commands/search_index.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/management/commands/search_reconcile.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/management/commands/seed_manager_landscape.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/management/commands/shell.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/manager/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/manager/general_manager.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/manager/group_manager.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/manager/input.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/manager/meta.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/measurement/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/measurement/measurement.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/measurement/measurement_field.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/metrics/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/metrics/graphql.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/migrations/0001_initial.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/migrations/0004_chat_persistence_models.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/migrations/0004_search_index_state.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/migrations/0005_merge_chat_search.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/migrations/0006_chat_pending_confirmation_scoped_ids.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/migrations/0007_upload_intent.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/migrations/0008_upload_finalization_versions.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/migrations/0009_upload_cleanup_state.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/migrations/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/models.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/permission/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/permission/audit.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/permission/base_permission.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/permission/graphql_capabilities.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/permission/manager_based_permission.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/permission/mutation_permission.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/permission/permission_checks.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/permission/permission_data_manager.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/permission/utils.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/py.typed +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/rule/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/rule/handler.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/rule/rule.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/search/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/search/backend.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/search/backend_registry.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/search/backends/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/search/backends/dev.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/search/backends/meilisearch.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/search/backends/opensearch.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/search/backends/typesense.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/search/registry.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/search/tasks.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/search/utils.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/seeding/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/seeding/manager_landscape.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/uploads/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/uploads/adapters.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/uploads/checks.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/uploads/config.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/uploads/errors.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/uploads/graphql.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/uploads/graphql_types.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/uploads/metrics.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/uploads/models.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/uploads/public.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/uploads/s3.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/uploads/services.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/uploads/tokens.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/uploads/types.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/uploads/urls.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/uploads/views.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/utils/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/utils/args_to_kwargs.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/utils/filter_parser.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/utils/format_string.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/utils/json_encoder.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/utils/make_cache_key.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/utils/none_to_zero.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/utils/path_mapping.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/utils/public_api.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/utils/type_checks.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/workflow/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/workflow/actions.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/workflow/backend_registry.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/workflow/backends/__init__.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/workflow/backends/celery.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/workflow/backends/local.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/workflow/backends/n8n.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/workflow/config.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/workflow/engine.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/workflow/event_registry.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/workflow/events.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/workflow/models.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/workflow/signal_bridge.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/workflow/tasks.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/workflow/telemetry.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/tests/test_urls.py +0 -0
- {generalmanager-0.64.3 → generalmanager-0.65.0}/tests/testing_asgi.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: GeneralManager
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.65.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
|
|
@@ -149,7 +149,10 @@ and [interface concepts](https://timkleindick.github.io/general_manager/concepts
|
|
|
149
149
|
- CI exercises Python 3.12, 3.13, and 3.14 on SQLite
|
|
150
150
|
|
|
151
151
|
GeneralManager builds on Django's database layer, but this project only claims
|
|
152
|
-
backend support covered by its tests or maintained examples.
|
|
152
|
+
backend support covered by its tests or maintained examples. SQLite,
|
|
153
|
+
PostgreSQL, and MariaDB are exercised by CI and are fully supported. Other
|
|
154
|
+
Django-supported backends may work, but are not currently claimed as supported
|
|
155
|
+
by GeneralManager.
|
|
153
156
|
|
|
154
157
|
## Five-minute setup
|
|
155
158
|
|
|
@@ -60,7 +60,10 @@ and [interface concepts](https://timkleindick.github.io/general_manager/concepts
|
|
|
60
60
|
- CI exercises Python 3.12, 3.13, and 3.14 on SQLite
|
|
61
61
|
|
|
62
62
|
GeneralManager builds on Django's database layer, but this project only claims
|
|
63
|
-
backend support covered by its tests or maintained examples.
|
|
63
|
+
backend support covered by its tests or maintained examples. SQLite,
|
|
64
|
+
PostgreSQL, and MariaDB are exercised by CI and are fully supported. Other
|
|
65
|
+
Django-supported backends may work, but are not currently claimed as supported
|
|
66
|
+
by GeneralManager.
|
|
64
67
|
|
|
65
68
|
## Five-minute setup
|
|
66
69
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "GeneralManager"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.65.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.
|
|
3
|
+
Version: 0.65.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
|
|
@@ -149,7 +149,10 @@ and [interface concepts](https://timkleindick.github.io/general_manager/concepts
|
|
|
149
149
|
- CI exercises Python 3.12, 3.13, and 3.14 on SQLite
|
|
150
150
|
|
|
151
151
|
GeneralManager builds on Django's database layer, but this project only claims
|
|
152
|
-
backend support covered by its tests or maintained examples.
|
|
152
|
+
backend support covered by its tests or maintained examples. SQLite,
|
|
153
|
+
PostgreSQL, and MariaDB are exercised by CI and are fully supported. Other
|
|
154
|
+
Django-supported backends may work, but are not currently claimed as supported
|
|
155
|
+
by GeneralManager.
|
|
153
156
|
|
|
154
157
|
## Five-minute setup
|
|
155
158
|
|
|
@@ -59,6 +59,7 @@ src/general_manager/bucket/request_bucket.py
|
|
|
59
59
|
src/general_manager/cache/__init__.py
|
|
60
60
|
src/general_manager/cache/cache_decorator.py
|
|
61
61
|
src/general_manager/cache/cache_tracker.py
|
|
62
|
+
src/general_manager/cache/data_change_context.py
|
|
62
63
|
src/general_manager/cache/dependency_cache.py
|
|
63
64
|
src/general_manager/cache/dependency_index.py
|
|
64
65
|
src/general_manager/cache/dependency_matching.py
|
|
@@ -207,6 +208,8 @@ src/general_manager/migrations/0006_chat_pending_confirmation_scoped_ids.py
|
|
|
207
208
|
src/general_manager/migrations/0007_upload_intent.py
|
|
208
209
|
src/general_manager/migrations/0008_upload_finalization_versions.py
|
|
209
210
|
src/general_manager/migrations/0009_upload_cleanup_state.py
|
|
211
|
+
src/general_manager/migrations/0010_chat_pending_confirmation_portable_uniqueness.py
|
|
212
|
+
src/general_manager/migrations/0011_search_index_state_dirty_generation.py
|
|
210
213
|
src/general_manager/migrations/__init__.py
|
|
211
214
|
src/general_manager/permission/__init__.py
|
|
212
215
|
src/general_manager/permission/audit.py
|
|
@@ -224,8 +227,11 @@ src/general_manager/search/__init__.py
|
|
|
224
227
|
src/general_manager/search/async_tasks.py
|
|
225
228
|
src/general_manager/search/backend.py
|
|
226
229
|
src/general_manager/search/backend_registry.py
|
|
230
|
+
src/general_manager/search/checks.py
|
|
227
231
|
src/general_manager/search/config.py
|
|
228
232
|
src/general_manager/search/indexer.py
|
|
233
|
+
src/general_manager/search/invalidation.py
|
|
234
|
+
src/general_manager/search/m2m_invalidation.py
|
|
229
235
|
src/general_manager/search/models.py
|
|
230
236
|
src/general_manager/search/reconciliation.py
|
|
231
237
|
src/general_manager/search/registry.py
|
{generalmanager-0.64.3 → generalmanager-0.65.0}/src/general_manager/_types/general_manager.py
RENAMED
|
@@ -19,9 +19,11 @@ __all__ = [
|
|
|
19
19
|
"RequestInterface",
|
|
20
20
|
"Rule",
|
|
21
21
|
"SearchBackend",
|
|
22
|
+
"SearchChange",
|
|
22
23
|
"SearchConfigProtocol",
|
|
23
24
|
"SearchConfigSpec",
|
|
24
25
|
"SearchIndexer",
|
|
26
|
+
"SearchInvalidationRule",
|
|
25
27
|
"configure_search_backend",
|
|
26
28
|
"configure_search_backend_from_settings",
|
|
27
29
|
"get_logger",
|
|
@@ -54,9 +56,11 @@ from general_manager.interface.interfaces.remote_manager import RemoteManagerInt
|
|
|
54
56
|
from general_manager.interface.interfaces.request import RequestInterface
|
|
55
57
|
from general_manager.rule.rule import Rule
|
|
56
58
|
from general_manager.search.backend import SearchBackend
|
|
59
|
+
from general_manager.search.config import SearchChange
|
|
57
60
|
from general_manager.search.config import SearchConfigProtocol
|
|
58
61
|
from general_manager.search.config import SearchConfigSpec
|
|
59
62
|
from general_manager.search.indexer import SearchIndexer
|
|
63
|
+
from general_manager.search.config import SearchInvalidationRule
|
|
60
64
|
from general_manager.search.backend_registry import configure_search_backend
|
|
61
65
|
from general_manager.search.backend_registry import (
|
|
62
66
|
configure_search_backend_from_settings,
|
|
@@ -13,11 +13,13 @@ __all__ = [
|
|
|
13
13
|
"SearchBackendError",
|
|
14
14
|
"SearchBackendNotConfiguredError",
|
|
15
15
|
"SearchBackendNotImplementedError",
|
|
16
|
+
"SearchChange",
|
|
16
17
|
"SearchConfigProtocol",
|
|
17
18
|
"SearchConfigSpec",
|
|
18
19
|
"SearchDocument",
|
|
19
20
|
"SearchHit",
|
|
20
21
|
"SearchIndexer",
|
|
22
|
+
"SearchInvalidationRule",
|
|
21
23
|
"SearchResult",
|
|
22
24
|
"TypesenseBackend",
|
|
23
25
|
"configure_search_backend",
|
|
@@ -37,11 +39,13 @@ from general_manager.search.backend import SearchBackendClientMissingError
|
|
|
37
39
|
from general_manager.search.backend import SearchBackendError
|
|
38
40
|
from general_manager.search.backend import SearchBackendNotConfiguredError
|
|
39
41
|
from general_manager.search.backend import SearchBackendNotImplementedError
|
|
42
|
+
from general_manager.search.config import SearchChange
|
|
40
43
|
from general_manager.search.config import SearchConfigProtocol
|
|
41
44
|
from general_manager.search.config import SearchConfigSpec
|
|
42
45
|
from general_manager.search.backend import SearchDocument
|
|
43
46
|
from general_manager.search.backend import SearchHit
|
|
44
47
|
from general_manager.search.indexer import SearchIndexer
|
|
48
|
+
from general_manager.search.config import SearchInvalidationRule
|
|
45
49
|
from general_manager.search.backend import SearchResult
|
|
46
50
|
from general_manager.search.backends.typesense import TypesenseBackend
|
|
47
51
|
from general_manager.search.backend_registry import configure_search_backend
|
|
@@ -168,8 +168,10 @@ class GeneralmanagerConfig(AppConfig):
|
|
|
168
168
|
self.install_startup_hook_runner()
|
|
169
169
|
self.register_system_checks()
|
|
170
170
|
from general_manager.uploads.checks import register_upload_checks
|
|
171
|
+
from general_manager.search.checks import register_search_checks
|
|
171
172
|
|
|
172
173
|
register_upload_checks()
|
|
174
|
+
register_search_checks()
|
|
173
175
|
_autoload_app_managers_modules()
|
|
174
176
|
self.initialize_general_manager_classes(
|
|
175
177
|
GeneralManagerMeta.pending_attribute_initialization,
|
|
@@ -178,14 +180,19 @@ class GeneralmanagerConfig(AppConfig):
|
|
|
178
180
|
handle_remote_api(GeneralManagerMeta.all_classes)
|
|
179
181
|
configure_audit_logger_from_settings(settings)
|
|
180
182
|
configure_search_backend_from_settings(settings)
|
|
183
|
+
from general_manager.search.invalidation import configure_search_invalidation
|
|
184
|
+
from general_manager.search.m2m_invalidation import (
|
|
185
|
+
configure_search_m2m_invalidation,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
configure_search_invalidation()
|
|
189
|
+
configure_search_m2m_invalidation()
|
|
181
190
|
configure_workflow_engine_from_settings(settings)
|
|
182
191
|
configure_event_registry_from_settings(settings)
|
|
183
192
|
configure_workflow_signal_bridge_from_settings(settings)
|
|
184
193
|
configure_workflow_beat_schedule_from_settings(settings)
|
|
185
194
|
configure_search_reconcile_beat_schedule_from_settings(settings)
|
|
186
195
|
configure_graphql_warmup_beat_schedule_from_settings(settings)
|
|
187
|
-
import general_manager.search.indexer as _search_indexer # noqa: F401
|
|
188
|
-
|
|
189
196
|
from general_manager.conf import get_setting
|
|
190
197
|
|
|
191
198
|
if get_setting("AUTOCREATE_GRAPHQL", False):
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""Context-local ownership metadata for ORM data-change transactions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Iterator
|
|
6
|
+
from contextlib import contextmanager
|
|
7
|
+
from contextvars import ContextVar
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class _DataChangeTransaction:
|
|
13
|
+
"""Live marker for one framework-owned transaction envelope."""
|
|
14
|
+
|
|
15
|
+
database_alias: str
|
|
16
|
+
caller_in_atomic_block: bool
|
|
17
|
+
active: bool = True
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
_owned_transactions: ContextVar[tuple[_DataChangeTransaction, ...]] = ContextVar(
|
|
21
|
+
"owned_data_change_transactions",
|
|
22
|
+
default=(),
|
|
23
|
+
)
|
|
24
|
+
_authorized_operations: ContextVar[tuple[_DataChangeTransaction, ...]] = ContextVar(
|
|
25
|
+
"authorized_data_change_operations",
|
|
26
|
+
default=(),
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _current_owner(database_alias: str) -> _DataChangeTransaction | None:
|
|
31
|
+
for owner in reversed(_owned_transactions.get()):
|
|
32
|
+
if owner.active and owner.database_alias == database_alias:
|
|
33
|
+
return owner
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@contextmanager
|
|
38
|
+
def own_data_change_transaction(
|
|
39
|
+
database_alias: str,
|
|
40
|
+
*,
|
|
41
|
+
caller_in_atomic_block: bool,
|
|
42
|
+
) -> Iterator[None]:
|
|
43
|
+
"""Record a live framework envelope without inspecting Django internals."""
|
|
44
|
+
owner = _DataChangeTransaction(
|
|
45
|
+
database_alias=database_alias,
|
|
46
|
+
caller_in_atomic_block=caller_in_atomic_block,
|
|
47
|
+
)
|
|
48
|
+
token = _owned_transactions.set((*_owned_transactions.get(), owner))
|
|
49
|
+
try:
|
|
50
|
+
yield
|
|
51
|
+
finally:
|
|
52
|
+
owner.active = False
|
|
53
|
+
_owned_transactions.reset(token)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@contextmanager
|
|
57
|
+
def authorize_data_change_operation(database_alias: str) -> Iterator[None]:
|
|
58
|
+
"""Authorize only the decorated manager method to reuse its envelope."""
|
|
59
|
+
owner = _current_owner(database_alias)
|
|
60
|
+
if owner is None:
|
|
61
|
+
yield
|
|
62
|
+
return
|
|
63
|
+
token = _authorized_operations.set((*_authorized_operations.get(), owner))
|
|
64
|
+
try:
|
|
65
|
+
yield
|
|
66
|
+
finally:
|
|
67
|
+
_authorized_operations.reset(token)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def owns_data_change_transaction(database_alias: str) -> bool:
|
|
71
|
+
"""Return whether this context has a live framework envelope for an alias."""
|
|
72
|
+
return _current_owner(database_alias) is not None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def is_data_change_operation_authorized(database_alias: str) -> bool:
|
|
76
|
+
"""Return whether the current owner is running its decorated method body."""
|
|
77
|
+
owner = _current_owner(database_alias)
|
|
78
|
+
if owner is None:
|
|
79
|
+
return False
|
|
80
|
+
return any(
|
|
81
|
+
authorized is owner and authorized.active
|
|
82
|
+
for authorized in _authorized_operations.get()
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def may_reuse_data_change_transaction(database_alias: str) -> bool:
|
|
87
|
+
"""Conservatively authorize reuse when the manager opened the outer block."""
|
|
88
|
+
owner = _current_owner(database_alias)
|
|
89
|
+
return bool(
|
|
90
|
+
owner is not None
|
|
91
|
+
and not owner.caller_in_atomic_block
|
|
92
|
+
and is_data_change_operation_authorized(database_alias)
|
|
93
|
+
)
|
|
@@ -2,12 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from contextlib import nullcontext
|
|
5
6
|
from copy import deepcopy
|
|
6
|
-
from django.dispatch import Signal
|
|
7
|
-
from typing import Callable, TypeVar, ParamSpec, cast, overload
|
|
8
|
-
|
|
9
7
|
from functools import wraps
|
|
8
|
+
from typing import Callable, ParamSpec, TypeVar, cast, overload
|
|
9
|
+
|
|
10
|
+
from django.db import DEFAULT_DB_ALIAS, connections, transaction
|
|
11
|
+
from django.dispatch import Signal
|
|
12
|
+
from django.utils.connection import ConnectionDoesNotExist
|
|
10
13
|
|
|
14
|
+
from general_manager.cache.data_change_context import (
|
|
15
|
+
authorize_data_change_operation,
|
|
16
|
+
own_data_change_transaction,
|
|
17
|
+
)
|
|
11
18
|
from general_manager.logging import get_logger
|
|
12
19
|
|
|
13
20
|
post_data_change = Signal()
|
|
@@ -20,6 +27,16 @@ R = TypeVar("R")
|
|
|
20
27
|
logger = get_logger("cache.signals")
|
|
21
28
|
|
|
22
29
|
|
|
30
|
+
def _caller_in_atomic_block(database_alias: str) -> bool:
|
|
31
|
+
"""Read public transaction state when the configured alias is available."""
|
|
32
|
+
try:
|
|
33
|
+
return bool(connections[database_alias].in_atomic_block)
|
|
34
|
+
except ConnectionDoesNotExist:
|
|
35
|
+
# Preserve lightweight callers that replace ``transaction.atomic`` in
|
|
36
|
+
# isolation. A real atomic call will still reject an unknown alias.
|
|
37
|
+
return False
|
|
38
|
+
|
|
39
|
+
|
|
23
40
|
@overload
|
|
24
41
|
def data_change(func: Callable[P, R]) -> Callable[P, R]: ...
|
|
25
42
|
|
|
@@ -39,9 +56,12 @@ def data_change(
|
|
|
39
56
|
clears run-scoped ORM bucket/index caches, emits `pre_data_change`, invokes
|
|
40
57
|
the wrapped callable, then emits `post_data_change` with the changed
|
|
41
58
|
instance, previous instance, action name, identification, and copied
|
|
42
|
-
`_old_values` payload.
|
|
43
|
-
|
|
44
|
-
|
|
59
|
+
`_old_values` payload. Both signals receive a per-call `change_context` and
|
|
60
|
+
the manager interface's database alias. ORM-backed signal dispatch and the
|
|
61
|
+
mutation share one transaction. GraphQL warm-up requeue keys collected
|
|
62
|
+
during signal handling are drained only after the outermost active
|
|
63
|
+
data-change barrier has closed; failed mutations drain pending keys but do
|
|
64
|
+
not enqueue rewarm work.
|
|
45
65
|
|
|
46
66
|
Parameters:
|
|
47
67
|
func: Function that performs a data mutation. Methods named `create`
|
|
@@ -85,6 +105,29 @@ def data_change(
|
|
|
85
105
|
is_dependency_data_change_active,
|
|
86
106
|
)
|
|
87
107
|
from general_manager.cache.run_context import current_calculation_run_context
|
|
108
|
+
from general_manager.interface.orm_interface import OrmInterfaceBase
|
|
109
|
+
|
|
110
|
+
action = decorator_source.__name__
|
|
111
|
+
if action == "create":
|
|
112
|
+
sender = args[0]
|
|
113
|
+
instance_before = None
|
|
114
|
+
else:
|
|
115
|
+
instance_before = args[0]
|
|
116
|
+
sender = instance_before.__class__
|
|
117
|
+
|
|
118
|
+
interface = getattr(sender, "Interface", None)
|
|
119
|
+
is_orm_backed = isinstance(interface, type) and issubclass(
|
|
120
|
+
interface, OrmInterfaceBase
|
|
121
|
+
)
|
|
122
|
+
database_alias = DEFAULT_DB_ALIAS
|
|
123
|
+
if is_orm_backed:
|
|
124
|
+
database_alias = getattr(interface, "database", None) or DEFAULT_DB_ALIAS
|
|
125
|
+
change_context: dict[str, object] = {}
|
|
126
|
+
signal_kwargs = {
|
|
127
|
+
**kwargs,
|
|
128
|
+
"change_context": change_context,
|
|
129
|
+
"database_alias": database_alias,
|
|
130
|
+
}
|
|
88
131
|
|
|
89
132
|
primary_exc: BaseException | None = None
|
|
90
133
|
completed = False
|
|
@@ -95,55 +138,70 @@ def data_change(
|
|
|
95
138
|
context.clear_bucket_indexes()
|
|
96
139
|
context.clear_trusted_orm_managers()
|
|
97
140
|
try:
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
else:
|
|
103
|
-
instance = args[0]
|
|
104
|
-
sender = instance.__class__
|
|
105
|
-
instance_before = instance
|
|
106
|
-
pre_data_change.send(
|
|
107
|
-
sender=sender,
|
|
108
|
-
instance=instance_before,
|
|
109
|
-
action=action,
|
|
110
|
-
**kwargs,
|
|
141
|
+
transaction_context = (
|
|
142
|
+
transaction.atomic(using=database_alias)
|
|
143
|
+
if is_orm_backed
|
|
144
|
+
else nullcontext()
|
|
111
145
|
)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
getattr(instance_before, "identification", None)
|
|
146
|
+
caller_in_atomic_block = (
|
|
147
|
+
_caller_in_atomic_block(database_alias) if is_orm_backed else False
|
|
115
148
|
)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if context is not None:
|
|
124
|
-
context.clear_orm_bucket_results()
|
|
125
|
-
context.clear_bucket_indexes()
|
|
126
|
-
context.clear_trusted_orm_managers()
|
|
127
|
-
|
|
128
|
-
instance = result
|
|
129
|
-
identification = getattr(instance, "identification", None)
|
|
130
|
-
if identification is None:
|
|
131
|
-
identification = pre_identification
|
|
132
|
-
|
|
133
|
-
post_data_change.send(
|
|
134
|
-
sender=sender,
|
|
135
|
-
instance=instance,
|
|
136
|
-
previous_instance=instance_before,
|
|
137
|
-
identification=identification,
|
|
138
|
-
action=action,
|
|
139
|
-
old_relevant_values=old_relevant_values,
|
|
140
|
-
**kwargs,
|
|
149
|
+
ownership_context = (
|
|
150
|
+
own_data_change_transaction(
|
|
151
|
+
database_alias,
|
|
152
|
+
caller_in_atomic_block=caller_in_atomic_block,
|
|
153
|
+
)
|
|
154
|
+
if is_orm_backed
|
|
155
|
+
else nullcontext()
|
|
141
156
|
)
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
157
|
+
with transaction_context, ownership_context:
|
|
158
|
+
pre_data_change.send(
|
|
159
|
+
sender=sender,
|
|
160
|
+
instance=instance_before,
|
|
161
|
+
action=action,
|
|
162
|
+
**signal_kwargs,
|
|
163
|
+
)
|
|
164
|
+
old_relevant_values = getattr(instance_before, "_old_values", {})
|
|
165
|
+
pre_identification = deepcopy(
|
|
166
|
+
getattr(instance_before, "identification", None)
|
|
167
|
+
)
|
|
168
|
+
operation_context = (
|
|
169
|
+
authorize_data_change_operation(database_alias)
|
|
170
|
+
if is_orm_backed
|
|
171
|
+
else nullcontext()
|
|
172
|
+
)
|
|
173
|
+
with operation_context:
|
|
174
|
+
if isinstance(func, classmethod):
|
|
175
|
+
inner = cast(Callable[P, R], func.__func__)
|
|
176
|
+
result = inner(*args, **kwargs)
|
|
177
|
+
else:
|
|
178
|
+
result = func(*args, **kwargs)
|
|
179
|
+
|
|
180
|
+
context = current_calculation_run_context()
|
|
181
|
+
if context is not None:
|
|
182
|
+
context.clear_orm_bucket_results()
|
|
183
|
+
context.clear_bucket_indexes()
|
|
184
|
+
context.clear_trusted_orm_managers()
|
|
185
|
+
|
|
186
|
+
instance = result
|
|
187
|
+
identification = getattr(instance, "identification", None)
|
|
188
|
+
if identification is None:
|
|
189
|
+
identification = pre_identification
|
|
190
|
+
|
|
191
|
+
post_data_change.send(
|
|
192
|
+
sender=sender,
|
|
193
|
+
instance=instance,
|
|
194
|
+
previous_instance=instance_before,
|
|
195
|
+
identification=identification,
|
|
196
|
+
action=action,
|
|
197
|
+
old_relevant_values=old_relevant_values,
|
|
198
|
+
**signal_kwargs,
|
|
199
|
+
)
|
|
200
|
+
if instance_before is not None:
|
|
201
|
+
try:
|
|
202
|
+
delattr(instance_before, "_old_values")
|
|
203
|
+
except AttributeError:
|
|
204
|
+
pass
|
|
147
205
|
completed = True
|
|
148
206
|
except BaseException as error:
|
|
149
207
|
primary_exc = error
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from collections.abc import Iterable
|
|
5
6
|
from datetime import timedelta
|
|
6
7
|
from typing import Any, ClassVar, cast
|
|
7
8
|
|
|
@@ -103,6 +104,11 @@ class ChatPendingConfirmation(models.Model):
|
|
|
103
104
|
payload: Any = models.JSONField(default=dict)
|
|
104
105
|
expires_at: Any = models.DateTimeField()
|
|
105
106
|
resolved_at: Any = models.DateTimeField(null=True, blank=True)
|
|
107
|
+
unresolved_marker: Any = models.BooleanField(
|
|
108
|
+
null=True,
|
|
109
|
+
default=True,
|
|
110
|
+
editable=False,
|
|
111
|
+
)
|
|
106
112
|
created_at: Any = models.DateTimeField(auto_now_add=True)
|
|
107
113
|
|
|
108
114
|
class Meta:
|
|
@@ -113,12 +119,70 @@ class ChatPendingConfirmation(models.Model):
|
|
|
113
119
|
]
|
|
114
120
|
constraints: ClassVar[list[models.BaseConstraint]] = [
|
|
115
121
|
models.UniqueConstraint(
|
|
116
|
-
fields=["conversation", "confirmation_id"],
|
|
117
|
-
condition=models.Q(resolved_at__isnull=True),
|
|
122
|
+
fields=["conversation", "confirmation_id", "unresolved_marker"],
|
|
118
123
|
name="gm_chat_pending_conv_conf_uniq",
|
|
119
|
-
)
|
|
124
|
+
),
|
|
125
|
+
models.CheckConstraint(
|
|
126
|
+
condition=(
|
|
127
|
+
models.Q(
|
|
128
|
+
resolved_at__isnull=True,
|
|
129
|
+
unresolved_marker=True,
|
|
130
|
+
unresolved_marker__isnull=False,
|
|
131
|
+
)
|
|
132
|
+
| models.Q(
|
|
133
|
+
resolved_at__isnull=False,
|
|
134
|
+
unresolved_marker__isnull=True,
|
|
135
|
+
)
|
|
136
|
+
),
|
|
137
|
+
name="gm_chat_pending_resolution_state",
|
|
138
|
+
),
|
|
120
139
|
]
|
|
121
140
|
|
|
141
|
+
def save(
|
|
142
|
+
self,
|
|
143
|
+
*args: Any,
|
|
144
|
+
force_insert: bool = False,
|
|
145
|
+
force_update: bool = False,
|
|
146
|
+
using: str | None = None,
|
|
147
|
+
update_fields: Iterable[str] | None = None,
|
|
148
|
+
) -> None:
|
|
149
|
+
"""Keep the internal uniqueness marker aligned with resolution state."""
|
|
150
|
+
if args:
|
|
151
|
+
parse_save_params = getattr(
|
|
152
|
+
cast(Any, self),
|
|
153
|
+
"_parse_save_params",
|
|
154
|
+
None,
|
|
155
|
+
)
|
|
156
|
+
if parse_save_params is None:
|
|
157
|
+
cast(Any, super().save)(
|
|
158
|
+
*args,
|
|
159
|
+
force_insert=force_insert,
|
|
160
|
+
force_update=force_update,
|
|
161
|
+
using=using,
|
|
162
|
+
update_fields=update_fields,
|
|
163
|
+
)
|
|
164
|
+
return
|
|
165
|
+
force_insert, force_update, using, update_fields = parse_save_params(
|
|
166
|
+
*args,
|
|
167
|
+
method_name="save",
|
|
168
|
+
force_insert=force_insert,
|
|
169
|
+
force_update=force_update,
|
|
170
|
+
using=using,
|
|
171
|
+
update_fields=update_fields,
|
|
172
|
+
)
|
|
173
|
+
if update_fields is not None:
|
|
174
|
+
update_fields = set(update_fields)
|
|
175
|
+
if update_fields is None or "resolved_at" in update_fields:
|
|
176
|
+
self.unresolved_marker = True if self.resolved_at is None else None
|
|
177
|
+
if update_fields is not None:
|
|
178
|
+
update_fields.add("unresolved_marker")
|
|
179
|
+
super().save(
|
|
180
|
+
force_insert=force_insert,
|
|
181
|
+
force_update=force_update,
|
|
182
|
+
using=using,
|
|
183
|
+
update_fields=update_fields,
|
|
184
|
+
)
|
|
185
|
+
|
|
122
186
|
@classmethod
|
|
123
187
|
def active_for_conversation(
|
|
124
188
|
cls,
|
|
@@ -161,7 +225,8 @@ class ChatPendingConfirmation(models.Model):
|
|
|
161
225
|
if pending is None:
|
|
162
226
|
return None
|
|
163
227
|
pending.resolved_at = current_time
|
|
164
|
-
pending.
|
|
228
|
+
pending.unresolved_marker = None
|
|
229
|
+
pending.save(update_fields=["resolved_at", "unresolved_marker"])
|
|
165
230
|
return pending
|
|
166
231
|
|
|
167
232
|
|
|
@@ -283,7 +348,7 @@ def create_pending_confirmation(
|
|
|
283
348
|
confirmation_id=confirmation_id,
|
|
284
349
|
resolved_at__isnull=True,
|
|
285
350
|
expires_at__lte=current_time,
|
|
286
|
-
).update(resolved_at=current_time)
|
|
351
|
+
).update(resolved_at=current_time, unresolved_marker=None)
|
|
287
352
|
unresolved_duplicate_exists = ChatPendingConfirmation.objects.filter(
|
|
288
353
|
conversation=conversation,
|
|
289
354
|
confirmation_id=confirmation_id,
|