GeneralManager 0.57.4__tar.gz → 0.59.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.57.4/src/GeneralManager.egg-info → generalmanager-0.59.0}/PKG-INFO +1 -1
- {generalmanager-0.57.4 → generalmanager-0.59.0}/pyproject.toml +1 -1
- {generalmanager-0.57.4 → generalmanager-0.59.0/src/GeneralManager.egg-info}/PKG-INFO +1 -1
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/graphql_search.py +162 -61
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/orm/mutations.py +34 -8
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +34 -1
- {generalmanager-0.57.4 → generalmanager-0.59.0}/LICENSE +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/README.md +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/setup.cfg +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/GeneralManager.egg-info/SOURCES.txt +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/GeneralManager.egg-info/requires.txt +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/GeneralManager.egg-info/top_level.txt +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/_types/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/_types/api.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/_types/bucket.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/_types/cache.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/_types/factory.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/_types/general_manager.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/_types/interface.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/_types/manager.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/_types/measurement.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/_types/permission.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/_types/rule.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/_types/search.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/_types/utils.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/graphql.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/graphql_errors.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/graphql_mutations.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/graphql_prefetch.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/graphql_resolvers.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/graphql_subscriptions.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/graphql_view.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/graphql_warmup.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/graphql_warmup_registry.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/graphql_warmup_tasks.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/mutation.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/property.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/registry.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/remote_api.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/remote_invalidation.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/remote_invalidation_client.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/apps.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/bootstrap.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/bucket/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/bucket/base_bucket.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/bucket/calculation_bucket.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/bucket/database_bucket.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/bucket/group_bucket.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/bucket/indexing.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/bucket/request_bucket.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/cache/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/cache/cache_decorator.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/cache/cache_tracker.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/cache/dependency_cache.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/cache/dependency_index.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/cache/dependency_matching.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/cache/dependency_publish.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/cache/dependency_shards.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/cache/model_dependency_collector.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/cache/run_context.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/cache/signals.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/audit.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/bootstrap.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/checks.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/consumer.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/errors.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/evals/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/evals/__main__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/evals/baseline.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/evals/diagnostics.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/evals/fingerprints.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/evals/fixtures.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/evals/judges/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/evals/judges/answer_quality.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/evals/judges/contract.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/evals/judges/result_accuracy.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/evals/judges/tool_sequence.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/evals/readiness.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/evals/runner.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/evals/traces.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/grounding.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/models.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/providers/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/providers/_shared.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/providers/anthropic.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/providers/base.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/providers/google.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/providers/ollama.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/providers/openai.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/rate_limits.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/routing.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/schema_index.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/settings.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/signals.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/system_prompt.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/tool_metadata.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/tools.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/chat/views.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/conf.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/factory/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/factory/auto_factory.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/factory/factories.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/factory/factory_methods.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/base_interface.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/bundles/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/bundles/calculation.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/bundles/database.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/bundles/remote_manager.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/bundles/request.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/base.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/builtin.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/calculation/lifecycle.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/configuration.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/core/observability.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/core/utils.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/exceptions.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/existing_model/resolution.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/factory.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/orm/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/orm/_compat.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/orm/history.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/orm/support.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/read_only/_types.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/registry.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/infrastructure/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/interfaces/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/interfaces/calculation.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/interfaces/database.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/interfaces/existing_model.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/interfaces/read_only.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/interfaces/request.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/manifests/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/manifests/capability_builder.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/manifests/capability_models.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/orm_interface.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/requests.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/utils/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/utils/errors.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/interface/utils/models.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/logging.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/management/commands/chat_cleanup.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/management/commands/graphql_warmup.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/management/commands/graphql_warmup_refresh_due.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/management/commands/search_index.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/management/commands/search_reconcile.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/management/commands/seed_manager_landscape.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/management/commands/shell.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/manager/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/manager/general_manager.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/manager/group_manager.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/manager/input.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/manager/meta.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/measurement/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/measurement/measurement.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/measurement/measurement_field.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/metrics/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/metrics/graphql.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/migrations/0001_initial.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/migrations/0004_chat_persistence_models.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/migrations/0004_search_index_state.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/migrations/0005_merge_chat_search.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/migrations/0006_chat_pending_confirmation_scoped_ids.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/migrations/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/models.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/permission/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/permission/audit.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/permission/base_permission.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/permission/graphql_capabilities.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/permission/manager_based_permission.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/permission/mutation_permission.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/permission/permission_checks.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/permission/permission_data_manager.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/permission/utils.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/public_api_registry.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/py.typed +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/rule/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/rule/handler.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/rule/rule.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/search/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/search/async_tasks.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/search/backend.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/search/backend_registry.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/search/backends/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/search/backends/dev.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/search/backends/meilisearch.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/search/backends/opensearch.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/search/backends/typesense.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/search/config.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/search/indexer.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/search/models.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/search/reconciliation.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/search/registry.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/search/tasks.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/search/utils.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/seeding/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/seeding/manager_landscape.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/utils/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/utils/args_to_kwargs.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/utils/filter_parser.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/utils/format_string.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/utils/json_encoder.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/utils/make_cache_key.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/utils/none_to_zero.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/utils/path_mapping.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/utils/public_api.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/utils/testing.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/utils/type_checks.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/workflow/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/workflow/actions.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/workflow/backend_registry.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/workflow/backends/__init__.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/workflow/backends/celery.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/workflow/backends/local.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/workflow/backends/n8n.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/workflow/config.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/workflow/engine.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/workflow/event_registry.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/workflow/events.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/workflow/models.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/workflow/signal_bridge.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/workflow/tasks.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/workflow/telemetry.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/tests/test_settings.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.0}/tests/test_urls.py +0 -0
- {generalmanager-0.57.4 → generalmanager-0.59.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.59.0
|
|
4
4
|
Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
|
|
5
5
|
Author-email: Tim Kleindick <tkleindick@yahoo.de>
|
|
6
6
|
License: MIT License
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "GeneralManager"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.59.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.59.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
|
|
@@ -14,6 +14,7 @@ from datetime import date, datetime
|
|
|
14
14
|
from decimal import Decimal
|
|
15
15
|
from typing import (
|
|
16
16
|
TYPE_CHECKING,
|
|
17
|
+
Literal,
|
|
17
18
|
cast,
|
|
18
19
|
get_args,
|
|
19
20
|
)
|
|
@@ -23,6 +24,7 @@ from graphql import GraphQLError
|
|
|
23
24
|
|
|
24
25
|
from django.utils import timezone
|
|
25
26
|
|
|
27
|
+
from general_manager.conf import get_setting
|
|
26
28
|
from general_manager.logging import get_logger
|
|
27
29
|
from general_manager.manager.general_manager import GeneralManager
|
|
28
30
|
from general_manager.measurement.measurement import Measurement
|
|
@@ -69,9 +71,20 @@ GrapheneReadMapper = Callable[
|
|
|
69
71
|
SearchResolverPayload = dict[str, object]
|
|
70
72
|
SearchHitEntry = tuple[float | None, SearchHit, GeneralManager]
|
|
71
73
|
SortableValue = float | datetime | str | None
|
|
74
|
+
SearchTotalMode = Literal["exact", "bounded"]
|
|
72
75
|
_BAD_USER_INPUT_CODE = "BAD_USER_INPUT"
|
|
73
76
|
_PAGE_MUST_BE_POSITIVE = "page must be a positive integer."
|
|
74
77
|
_PAGE_SIZE_MUST_BE_POSITIVE = "pageSize must be a positive integer."
|
|
78
|
+
DEFAULT_GRAPHQL_SEARCH_TOTAL_SCAN_LIMIT = 1000
|
|
79
|
+
SEARCH_TOTAL_MODE_ERROR = "must be one of: exact, bounded."
|
|
80
|
+
SEARCH_TOTAL_SCAN_LIMIT_ERROR = (
|
|
81
|
+
"GRAPHQL_SEARCH_TOTAL_SCAN_LIMIT must be a positive integer."
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _bad_user_input_error(message: str) -> GraphQLError:
|
|
86
|
+
"""Build a GraphQL user-input error with the shared extension code."""
|
|
87
|
+
return GraphQLError(message, extensions={"code": _BAD_USER_INPUT_CODE})
|
|
75
88
|
|
|
76
89
|
|
|
77
90
|
def _resolve_search_pagination(
|
|
@@ -96,6 +109,106 @@ def _resolve_search_pagination(
|
|
|
96
109
|
return current_page, limit
|
|
97
110
|
|
|
98
111
|
|
|
112
|
+
def _invalid_search_total_mode_error(setting_label: str) -> GraphQLError:
|
|
113
|
+
"""Build the validation error for an invalid total-mode value."""
|
|
114
|
+
message = f"{setting_label} {SEARCH_TOTAL_MODE_ERROR}"
|
|
115
|
+
return _bad_user_input_error(message)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def normalize_search_total_mode(total_mode: str | None = None) -> SearchTotalMode:
|
|
119
|
+
"""Resolve and validate the requested GraphQL search total-count mode."""
|
|
120
|
+
if total_mode is None:
|
|
121
|
+
raw_mode = get_setting("GRAPHQL_SEARCH_TOTAL_MODE", "exact")
|
|
122
|
+
setting_label = 'GENERAL_MANAGER["GRAPHQL_SEARCH_TOTAL_MODE"]'
|
|
123
|
+
else:
|
|
124
|
+
raw_mode = total_mode
|
|
125
|
+
setting_label = "totalMode"
|
|
126
|
+
|
|
127
|
+
if not isinstance(raw_mode, str):
|
|
128
|
+
raise _invalid_search_total_mode_error(setting_label)
|
|
129
|
+
|
|
130
|
+
normalized = raw_mode.strip().lower()
|
|
131
|
+
if normalized not in {"exact", "bounded"}:
|
|
132
|
+
raise _invalid_search_total_mode_error(setting_label)
|
|
133
|
+
return cast(SearchTotalMode, normalized)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def get_graphql_search_total_scan_limit() -> int:
|
|
137
|
+
"""Return the configured positive scan limit for bounded total counts."""
|
|
138
|
+
raw_limit = get_setting(
|
|
139
|
+
"GRAPHQL_SEARCH_TOTAL_SCAN_LIMIT",
|
|
140
|
+
DEFAULT_GRAPHQL_SEARCH_TOTAL_SCAN_LIMIT,
|
|
141
|
+
)
|
|
142
|
+
if not isinstance(raw_limit, int) or isinstance(raw_limit, bool) or raw_limit <= 0:
|
|
143
|
+
raise _bad_user_input_error(SEARCH_TOTAL_SCAN_LIMIT_ERROR)
|
|
144
|
+
return raw_limit
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def normalize_search_sort_value(value: object) -> SortableValue:
|
|
148
|
+
"""Normalise a search sort value to a comparable type."""
|
|
149
|
+
if value is None:
|
|
150
|
+
return None
|
|
151
|
+
if isinstance(value, (int, float, Decimal)):
|
|
152
|
+
return float(value)
|
|
153
|
+
if isinstance(value, datetime):
|
|
154
|
+
if timezone.is_naive(value):
|
|
155
|
+
return timezone.make_aware(value)
|
|
156
|
+
return value
|
|
157
|
+
if isinstance(value, date):
|
|
158
|
+
return timezone.make_aware(datetime.combine(value, datetime.min.time()))
|
|
159
|
+
if isinstance(value, str):
|
|
160
|
+
try:
|
|
161
|
+
parsed = datetime.fromisoformat(value)
|
|
162
|
+
except ValueError:
|
|
163
|
+
return value
|
|
164
|
+
if timezone.is_naive(parsed):
|
|
165
|
+
parsed = timezone.make_aware(parsed)
|
|
166
|
+
return parsed
|
|
167
|
+
return str(value)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def sort_search_hit_entries(
|
|
171
|
+
entries: list[SearchHitEntry],
|
|
172
|
+
*,
|
|
173
|
+
sort_by: str | None,
|
|
174
|
+
sort_desc: bool,
|
|
175
|
+
) -> None:
|
|
176
|
+
"""Sort retained search entries using GraphQL search response semantics."""
|
|
177
|
+
if sort_by:
|
|
178
|
+
|
|
179
|
+
def _sort_key(
|
|
180
|
+
item: SearchHitEntry,
|
|
181
|
+
) -> tuple[bool, SortableValue]:
|
|
182
|
+
"""Return a null-last comparable key for an indexed search hit."""
|
|
183
|
+
value = item[1].data.get(sort_by) if item[1].data else None
|
|
184
|
+
normalized = normalize_search_sort_value(value)
|
|
185
|
+
return (normalized is None, normalized)
|
|
186
|
+
|
|
187
|
+
entries.sort(key=_sort_key)
|
|
188
|
+
if sort_desc:
|
|
189
|
+
null_start = next(
|
|
190
|
+
(index for index, item in enumerate(entries) if _sort_key(item)[0]),
|
|
191
|
+
len(entries),
|
|
192
|
+
)
|
|
193
|
+
entries[:null_start] = reversed(entries[:null_start])
|
|
194
|
+
else:
|
|
195
|
+
entries.sort(key=lambda item: item[0] or 0, reverse=True)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def trim_search_hit_entries_to_window(
|
|
199
|
+
entries: list[SearchHitEntry],
|
|
200
|
+
*,
|
|
201
|
+
requested_count: int,
|
|
202
|
+
sort_by: str | None,
|
|
203
|
+
sort_desc: bool,
|
|
204
|
+
) -> None:
|
|
205
|
+
"""Keep only entries that can still appear in the requested global page."""
|
|
206
|
+
if len(entries) <= requested_count:
|
|
207
|
+
return
|
|
208
|
+
sort_search_hit_entries(entries, sort_by=sort_by, sort_desc=sort_desc)
|
|
209
|
+
del entries[requested_count:]
|
|
210
|
+
|
|
211
|
+
|
|
99
212
|
# ---------------------------------------------------------------------------
|
|
100
213
|
# Filter normalisation
|
|
101
214
|
# ---------------------------------------------------------------------------
|
|
@@ -343,7 +456,7 @@ def create_search_result_type(
|
|
|
343
456
|
|
|
344
457
|
Returns:
|
|
345
458
|
A ``SearchResult`` Graphene ObjectType with ``results``, ``total``,
|
|
346
|
-
``took_ms``, and ``raw`` fields.
|
|
459
|
+
``total_is_exact``, ``took_ms``, and ``raw`` fields.
|
|
347
460
|
"""
|
|
348
461
|
return type(
|
|
349
462
|
"SearchResult",
|
|
@@ -351,6 +464,7 @@ def create_search_result_type(
|
|
|
351
464
|
{
|
|
352
465
|
"results": graphene.List(union_type),
|
|
353
466
|
"total": graphene.Int(),
|
|
467
|
+
"total_is_exact": graphene.Boolean(),
|
|
354
468
|
"took_ms": graphene.Int(),
|
|
355
469
|
"raw": graphene.JSONString(),
|
|
356
470
|
},
|
|
@@ -767,13 +881,15 @@ def register_search_query(
|
|
|
767
881
|
|
|
768
882
|
Manager search order follows ``manager_registry.values()`` filtered to
|
|
769
883
|
managers with search configuration, unless the resolver receives ``types``;
|
|
770
|
-
unknown names in ``types`` are ignored. ``total`` in
|
|
771
|
-
the post-permission authorized total, not the
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
884
|
+
unknown names in ``types`` are ignored. In exact total mode, ``total`` in
|
|
885
|
+
the resolver payload is the post-permission authorized total, not the
|
|
886
|
+
backend raw total. In bounded mode, ``total`` is the authorized count found
|
|
887
|
+
before the per-manager scan cap and ``total_is_exact`` tells callers whether
|
|
888
|
+
that count is exact. Sorting uses hit ``data[sort_by]`` when available and
|
|
889
|
+
does not validate the sort field ahead of comparison. Configured filter-key
|
|
890
|
+
validation runs on the parsed top-level search filters before permission
|
|
891
|
+
filters or relation normalizers are applied; exclude-derived permission keys
|
|
892
|
+
are not validated by this search helper.
|
|
777
893
|
|
|
778
894
|
Parameters:
|
|
779
895
|
query_fields: Mutable dict of registered query fields; will be updated.
|
|
@@ -819,6 +935,7 @@ def register_search_query(
|
|
|
819
935
|
filters: GraphQLSearchFilterInput = None,
|
|
820
936
|
sort_by: str | None = None,
|
|
821
937
|
sort_desc: bool = False,
|
|
938
|
+
total_mode: str | None = None,
|
|
822
939
|
page: int | None = None,
|
|
823
940
|
page_size: int | None = None,
|
|
824
941
|
) -> SearchResolverPayload:
|
|
@@ -855,10 +972,13 @@ def register_search_query(
|
|
|
855
972
|
raw: list[object] = []
|
|
856
973
|
requested_count = offset + limit
|
|
857
974
|
fetch_limit = max(requested_count, limit)
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
975
|
+
resolved_total_mode = normalize_search_total_mode(total_mode)
|
|
976
|
+
total_scan_limit = (
|
|
977
|
+
max(get_graphql_search_total_scan_limit(), requested_count)
|
|
978
|
+
if resolved_total_mode == "bounded"
|
|
979
|
+
else None
|
|
980
|
+
)
|
|
981
|
+
total_is_exact = True
|
|
862
982
|
|
|
863
983
|
for manager_class in manager_classes:
|
|
864
984
|
type_label = manager_class.__name__
|
|
@@ -872,17 +992,24 @@ def register_search_query(
|
|
|
872
992
|
parsed_filters,
|
|
873
993
|
permission_plan.filters,
|
|
874
994
|
)
|
|
875
|
-
authorized_hits: list[SearchHitEntry] = []
|
|
876
995
|
total_hits_for_manager = 0
|
|
877
996
|
candidate_hits_for_manager = 0
|
|
878
|
-
appended_hits_for_manager = 0
|
|
879
997
|
offset_cursor = 0
|
|
998
|
+
scanned_hits_for_manager = 0
|
|
999
|
+
manager_total_is_exact = True
|
|
880
1000
|
while True:
|
|
1001
|
+
query_limit = fetch_limit
|
|
1002
|
+
if total_scan_limit is not None:
|
|
1003
|
+
remaining_scan_budget = total_scan_limit - scanned_hits_for_manager
|
|
1004
|
+
if remaining_scan_budget <= 0:
|
|
1005
|
+
manager_total_is_exact = False
|
|
1006
|
+
break
|
|
1007
|
+
query_limit = min(fetch_limit, remaining_scan_budget)
|
|
881
1008
|
result = backend.search(
|
|
882
1009
|
index_name,
|
|
883
1010
|
query,
|
|
884
1011
|
filters=filter_groups,
|
|
885
|
-
limit=
|
|
1012
|
+
limit=query_limit,
|
|
886
1013
|
offset=offset_cursor,
|
|
887
1014
|
types=[type_label],
|
|
888
1015
|
sort_by=sort_by,
|
|
@@ -896,6 +1023,7 @@ def register_search_query(
|
|
|
896
1023
|
raw.append(result.raw)
|
|
897
1024
|
if not result.hits:
|
|
898
1025
|
break
|
|
1026
|
+
scanned_hits_for_manager += len(result.hits)
|
|
899
1027
|
offset_cursor += len(result.hits)
|
|
900
1028
|
for hit in result.hits:
|
|
901
1029
|
try:
|
|
@@ -918,17 +1046,24 @@ def register_search_query(
|
|
|
918
1046
|
):
|
|
919
1047
|
continue
|
|
920
1048
|
total_hits_for_manager += 1
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
if len(result.hits) <
|
|
1049
|
+
hits.append((hit.score, hit, instance))
|
|
1050
|
+
trim_search_hit_entries_to_window(
|
|
1051
|
+
hits,
|
|
1052
|
+
requested_count=requested_count,
|
|
1053
|
+
sort_by=sort_by,
|
|
1054
|
+
sort_desc=sort_desc,
|
|
1055
|
+
)
|
|
1056
|
+
if len(result.hits) < query_limit:
|
|
1057
|
+
break
|
|
1058
|
+
if (
|
|
1059
|
+
total_scan_limit is not None
|
|
1060
|
+
and scanned_hits_for_manager >= total_scan_limit
|
|
1061
|
+
):
|
|
1062
|
+
manager_total_is_exact = False
|
|
929
1063
|
break
|
|
930
1064
|
total += total_hits_for_manager
|
|
931
|
-
|
|
1065
|
+
if not manager_total_is_exact:
|
|
1066
|
+
total_is_exact = False
|
|
932
1067
|
if permission_plan.requires_instance_check:
|
|
933
1068
|
logger.info(
|
|
934
1069
|
"graphql read authorization summary",
|
|
@@ -947,43 +1082,7 @@ def register_search_query(
|
|
|
947
1082
|
},
|
|
948
1083
|
)
|
|
949
1084
|
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
def _normalize_sort_value(value: object) -> SortableValue:
|
|
953
|
-
"""Normalise a sort value to a comparable type."""
|
|
954
|
-
if value is None:
|
|
955
|
-
return None
|
|
956
|
-
if isinstance(value, (int, float, Decimal)):
|
|
957
|
-
return float(value)
|
|
958
|
-
if isinstance(value, datetime):
|
|
959
|
-
if timezone.is_naive(value):
|
|
960
|
-
return timezone.make_aware(value)
|
|
961
|
-
return value
|
|
962
|
-
if isinstance(value, date):
|
|
963
|
-
return timezone.make_aware(
|
|
964
|
-
datetime.combine(value, datetime.min.time())
|
|
965
|
-
)
|
|
966
|
-
if isinstance(value, str):
|
|
967
|
-
try:
|
|
968
|
-
parsed = datetime.fromisoformat(value)
|
|
969
|
-
except ValueError:
|
|
970
|
-
return value
|
|
971
|
-
if timezone.is_naive(parsed):
|
|
972
|
-
parsed = timezone.make_aware(parsed)
|
|
973
|
-
return parsed
|
|
974
|
-
return str(value)
|
|
975
|
-
|
|
976
|
-
def _sort_key(
|
|
977
|
-
item: SearchHitEntry,
|
|
978
|
-
) -> tuple[bool, SortableValue]:
|
|
979
|
-
"""Sort key placing None-valued items last."""
|
|
980
|
-
value = item[1].data.get(sort_by) if item[1].data else None
|
|
981
|
-
normalized = _normalize_sort_value(value)
|
|
982
|
-
return (normalized is None, normalized)
|
|
983
|
-
|
|
984
|
-
hits.sort(key=_sort_key, reverse=sort_desc)
|
|
985
|
-
else:
|
|
986
|
-
hits.sort(key=lambda item: item[0] or 0, reverse=True)
|
|
1085
|
+
sort_search_hit_entries(hits, sort_by=sort_by, sort_desc=sort_desc)
|
|
987
1086
|
|
|
988
1087
|
items: list[GeneralManager] = []
|
|
989
1088
|
for _, _hit, instance in hits[offset : offset + limit]:
|
|
@@ -992,6 +1091,7 @@ def register_search_query(
|
|
|
992
1091
|
return {
|
|
993
1092
|
"results": items,
|
|
994
1093
|
"total": total,
|
|
1094
|
+
"total_is_exact": total_is_exact,
|
|
995
1095
|
"took_ms": took_ms,
|
|
996
1096
|
"raw": raw,
|
|
997
1097
|
}
|
|
@@ -1004,6 +1104,7 @@ def register_search_query(
|
|
|
1004
1104
|
filters=graphene.JSONString(),
|
|
1005
1105
|
sort_by=graphene.String(),
|
|
1006
1106
|
sort_desc=graphene.Boolean(),
|
|
1107
|
+
total_mode=graphene.String(),
|
|
1007
1108
|
page=graphene.Int(),
|
|
1008
1109
|
page_size=graphene.Int(),
|
|
1009
1110
|
resolver=resolver,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import TYPE_CHECKING, ClassVar, cast
|
|
5
|
+
from typing import TYPE_CHECKING, Any, ClassVar, cast
|
|
6
6
|
|
|
7
7
|
from django.contrib.auth import get_user_model
|
|
8
8
|
from django.db import models, transaction
|
|
@@ -10,6 +10,9 @@ from django.db.models import NOT_PROVIDED
|
|
|
10
10
|
|
|
11
11
|
from general_manager.interface.capabilities.base import CapabilityName
|
|
12
12
|
from general_manager.interface.capabilities.builtin import BaseCapability
|
|
13
|
+
from general_manager.interface.capabilities.orm_utils.payload_normalizer import (
|
|
14
|
+
PayloadNormalizer,
|
|
15
|
+
)
|
|
13
16
|
from general_manager.interface.utils.database_interface_protocols import (
|
|
14
17
|
SupportsActivation,
|
|
15
18
|
)
|
|
@@ -581,10 +584,8 @@ class OrmValidationCapability(BaseCapability):
|
|
|
581
584
|
"""
|
|
582
585
|
support = get_support_capability(interface_cls)
|
|
583
586
|
normalizer = support.get_payload_normalizer(interface_cls)
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
simple_kwargs, many_to_many_kwargs = normalizer.split_many_to_many(
|
|
587
|
-
payload_copy
|
|
587
|
+
simple_kwargs, many_to_many_kwargs = _validate_and_split_payload(
|
|
588
|
+
normalizer, payload
|
|
588
589
|
)
|
|
589
590
|
normalized_simple = normalizer.normalize_simple_values(simple_kwargs)
|
|
590
591
|
normalized_many = normalizer.normalize_many_values(many_to_many_kwargs)
|
|
@@ -629,9 +630,9 @@ def _normalize_payload(
|
|
|
629
630
|
)
|
|
630
631
|
support = get_support_capability(interface_cls)
|
|
631
632
|
normalizer = support.get_payload_normalizer(interface_cls)
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
633
|
+
simple_kwargs, many_to_many_kwargs = _validate_and_split_payload(
|
|
634
|
+
normalizer, payload
|
|
635
|
+
)
|
|
635
636
|
normalized_simple = normalizer.normalize_simple_values(simple_kwargs)
|
|
636
637
|
normalized_many = normalizer.normalize_many_values(many_to_many_kwargs)
|
|
637
638
|
return dict(normalized_simple), {
|
|
@@ -639,6 +640,31 @@ def _normalize_payload(
|
|
|
639
640
|
}
|
|
640
641
|
|
|
641
642
|
|
|
643
|
+
def _validate_and_split_payload(
|
|
644
|
+
normalizer: Any,
|
|
645
|
+
payload: MutationPayload,
|
|
646
|
+
) -> tuple[MutationPayload, MutationPayload]:
|
|
647
|
+
"""Validate and split payloads without exposing caller mappings to mutation."""
|
|
648
|
+
normalizer_payload = (
|
|
649
|
+
payload if type(normalizer) is PayloadNormalizer else dict(payload)
|
|
650
|
+
)
|
|
651
|
+
normalizer.validate_keys(normalizer_payload)
|
|
652
|
+
return _split_many_to_many_payload(normalizer, normalizer_payload)
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
def _split_many_to_many_payload(
|
|
656
|
+
normalizer: Any,
|
|
657
|
+
payload: MutationPayload,
|
|
658
|
+
) -> tuple[MutationPayload, MutationPayload]:
|
|
659
|
+
"""Split concrete normalizer payloads without mutation; copy for custom normalizers."""
|
|
660
|
+
if type(normalizer) is PayloadNormalizer:
|
|
661
|
+
return normalizer.split_many_to_many_non_mutating(payload)
|
|
662
|
+
return cast(
|
|
663
|
+
tuple[MutationPayload, MutationPayload],
|
|
664
|
+
normalizer.split_many_to_many(payload),
|
|
665
|
+
)
|
|
666
|
+
|
|
667
|
+
|
|
642
668
|
def _assign_history_actor(
|
|
643
669
|
instance: models.Model,
|
|
644
670
|
*,
|
|
@@ -88,7 +88,7 @@ class PayloadNormalizer:
|
|
|
88
88
|
self, kwargs: PayloadMapping
|
|
89
89
|
) -> tuple[PayloadMapping, PayloadMapping]:
|
|
90
90
|
"""
|
|
91
|
-
|
|
91
|
+
Mutate a kwargs mapping by moving many-to-many related entries out of it.
|
|
92
92
|
|
|
93
93
|
Parameters:
|
|
94
94
|
kwargs: Mapping of field lookups where keys for many-to-many relations are expected to end with `_id_list`.
|
|
@@ -106,6 +106,39 @@ class PayloadNormalizer:
|
|
|
106
106
|
many_kwargs[f"{base_key}_id_list"] = kwargs.pop(key)
|
|
107
107
|
return kwargs, many_kwargs
|
|
108
108
|
|
|
109
|
+
def split_many_to_many_non_mutating(
|
|
110
|
+
self, kwargs: PayloadMapping
|
|
111
|
+
) -> tuple[PayloadMapping, PayloadMapping]:
|
|
112
|
+
"""
|
|
113
|
+
Return separated many-to-many entries without mutating the input mapping.
|
|
114
|
+
|
|
115
|
+
Parameters:
|
|
116
|
+
kwargs: Mapping of field lookups where keys for many-to-many
|
|
117
|
+
relations may use either `<relation>_list` or
|
|
118
|
+
`<relation>_id_list`.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
tuple: A pair `(remaining_kwargs, many_kwargs)` where both mappings
|
|
122
|
+
are new dictionaries. `remaining_kwargs` contains non-matching
|
|
123
|
+
entries from `kwargs`, and `many_kwargs` contains matching
|
|
124
|
+
many-to-many entries under canonical `<relation>_id_list` keys.
|
|
125
|
+
|
|
126
|
+
Notes:
|
|
127
|
+
This is the non-mutating counterpart to `split_many_to_many()`. If
|
|
128
|
+
both `<relation>_list` and `<relation>_id_list` are present,
|
|
129
|
+
iteration order decides the last canonical value stored in
|
|
130
|
+
`many_kwargs`.
|
|
131
|
+
"""
|
|
132
|
+
remaining_kwargs: PayloadMapping = {}
|
|
133
|
+
many_kwargs: PayloadMapping = {}
|
|
134
|
+
for key, value in kwargs.items():
|
|
135
|
+
base_key = self._m2m_alias_base_key(key)
|
|
136
|
+
if base_key in self._many_to_many_fields:
|
|
137
|
+
many_kwargs[f"{base_key}_id_list"] = value
|
|
138
|
+
else:
|
|
139
|
+
remaining_kwargs[key] = value
|
|
140
|
+
return remaining_kwargs, many_kwargs
|
|
141
|
+
|
|
109
142
|
def normalize_simple_values(self, kwargs: PayloadMapping) -> PayloadMapping:
|
|
110
143
|
"""
|
|
111
144
|
Normalize simple (single-valued) payload entries by converting general-manager objects to their identifier form and adjusting keys.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{generalmanager-0.57.4 → generalmanager-0.59.0}/src/GeneralManager.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/_types/general_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/graphql_mutations.py
RENAMED
|
File without changes
|
|
File without changes
|
{generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/graphql_resolvers.py
RENAMED
|
File without changes
|
|
File without changes
|
{generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/graphql_subscriptions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/graphql_warmup_registry.py
RENAMED
|
File without changes
|
{generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/graphql_warmup_tasks.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/api/remote_invalidation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/bucket/calculation_bucket.py
RENAMED
|
File without changes
|
{generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/bucket/database_bucket.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/bucket/request_bucket.py
RENAMED
|
File without changes
|
|
File without changes
|
{generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/cache/cache_decorator.py
RENAMED
|
File without changes
|
|
File without changes
|
{generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/cache/dependency_cache.py
RENAMED
|
File without changes
|
{generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/cache/dependency_index.py
RENAMED
|
File without changes
|
{generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/cache/dependency_matching.py
RENAMED
|
File without changes
|
{generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/cache/dependency_publish.py
RENAMED
|
File without changes
|
{generalmanager-0.57.4 → generalmanager-0.59.0}/src/general_manager/cache/dependency_shards.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|