GeneralManager 0.57.3__tar.gz → 0.58.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.3/src/GeneralManager.egg-info → generalmanager-0.58.0}/PKG-INFO +1 -1
- {generalmanager-0.57.3 → generalmanager-0.58.0}/pyproject.toml +1 -1
- {generalmanager-0.57.3 → generalmanager-0.58.0/src/GeneralManager.egg-info}/PKG-INFO +1 -1
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/api/graphql_search.py +162 -61
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/bucket/calculation_bucket.py +152 -21
- {generalmanager-0.57.3 → generalmanager-0.58.0}/LICENSE +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/README.md +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/setup.cfg +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/GeneralManager.egg-info/SOURCES.txt +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/GeneralManager.egg-info/requires.txt +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/GeneralManager.egg-info/top_level.txt +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/_types/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/_types/api.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/_types/bucket.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/_types/cache.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/_types/factory.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/_types/general_manager.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/_types/interface.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/_types/manager.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/_types/measurement.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/_types/permission.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/_types/rule.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/_types/search.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/_types/utils.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/api/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/api/graphql.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/api/graphql_errors.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/api/graphql_mutations.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/api/graphql_prefetch.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/api/graphql_resolvers.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/api/graphql_subscriptions.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/api/graphql_view.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/api/graphql_warmup.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/api/graphql_warmup_registry.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/api/graphql_warmup_tasks.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/api/mutation.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/api/property.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/api/registry.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/api/remote_api.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/api/remote_invalidation.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/api/remote_invalidation_client.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/apps.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/bootstrap.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/bucket/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/bucket/base_bucket.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/bucket/database_bucket.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/bucket/group_bucket.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/bucket/indexing.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/bucket/request_bucket.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/cache/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/cache/cache_decorator.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/cache/cache_tracker.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/cache/dependency_cache.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/cache/dependency_index.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/cache/dependency_matching.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/cache/dependency_publish.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/cache/dependency_shards.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/cache/model_dependency_collector.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/cache/run_context.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/cache/signals.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/audit.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/bootstrap.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/checks.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/consumer.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/errors.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/evals/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/evals/__main__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/evals/baseline.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/evals/diagnostics.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/evals/fingerprints.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/evals/fixtures.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/evals/judges/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/evals/judges/answer_quality.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/evals/judges/contract.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/evals/judges/result_accuracy.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/evals/judges/tool_sequence.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/evals/readiness.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/evals/runner.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/evals/traces.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/grounding.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/models.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/providers/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/providers/_shared.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/providers/anthropic.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/providers/base.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/providers/google.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/providers/ollama.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/providers/openai.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/rate_limits.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/routing.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/schema_index.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/settings.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/signals.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/system_prompt.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/tool_metadata.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/tools.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/chat/views.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/conf.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/factory/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/factory/auto_factory.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/factory/factories.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/factory/factory_methods.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/base_interface.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/bundles/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/bundles/calculation.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/bundles/database.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/bundles/remote_manager.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/bundles/request.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/base.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/builtin.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/calculation/lifecycle.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/configuration.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/core/observability.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/core/utils.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/exceptions.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/existing_model/resolution.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/factory.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/orm/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/orm/_compat.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/orm/history.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/orm/mutations.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/orm/support.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/read_only/_types.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/registry.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/infrastructure/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/interfaces/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/interfaces/calculation.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/interfaces/database.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/interfaces/existing_model.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/interfaces/read_only.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/interfaces/request.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/manifests/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/manifests/capability_builder.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/manifests/capability_models.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/orm_interface.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/requests.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/utils/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/utils/errors.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/interface/utils/models.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/logging.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/management/commands/chat_cleanup.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/management/commands/graphql_warmup.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/management/commands/graphql_warmup_refresh_due.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/management/commands/search_index.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/management/commands/search_reconcile.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/management/commands/seed_manager_landscape.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/management/commands/shell.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/manager/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/manager/general_manager.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/manager/group_manager.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/manager/input.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/manager/meta.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/measurement/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/measurement/measurement.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/measurement/measurement_field.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/metrics/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/metrics/graphql.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/migrations/0001_initial.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/migrations/0004_chat_persistence_models.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/migrations/0004_search_index_state.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/migrations/0005_merge_chat_search.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/migrations/0006_chat_pending_confirmation_scoped_ids.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/migrations/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/models.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/permission/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/permission/audit.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/permission/base_permission.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/permission/graphql_capabilities.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/permission/manager_based_permission.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/permission/mutation_permission.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/permission/permission_checks.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/permission/permission_data_manager.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/permission/utils.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/public_api_registry.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/py.typed +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/rule/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/rule/handler.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/rule/rule.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/search/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/search/async_tasks.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/search/backend.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/search/backend_registry.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/search/backends/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/search/backends/dev.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/search/backends/meilisearch.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/search/backends/opensearch.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/search/backends/typesense.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/search/config.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/search/indexer.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/search/models.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/search/reconciliation.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/search/registry.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/search/tasks.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/search/utils.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/seeding/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/seeding/manager_landscape.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/utils/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/utils/args_to_kwargs.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/utils/filter_parser.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/utils/format_string.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/utils/json_encoder.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/utils/make_cache_key.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/utils/none_to_zero.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/utils/path_mapping.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/utils/public_api.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/utils/testing.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/utils/type_checks.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/workflow/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/workflow/actions.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/workflow/backend_registry.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/workflow/backends/__init__.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/workflow/backends/celery.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/workflow/backends/local.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/workflow/backends/n8n.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/workflow/config.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/workflow/engine.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/workflow/event_registry.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/workflow/events.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/workflow/models.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/workflow/signal_bridge.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/workflow/tasks.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/workflow/telemetry.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/tests/test_settings.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.0}/tests/test_urls.py +0 -0
- {generalmanager-0.57.3 → generalmanager-0.58.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.58.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.58.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.58.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,
|
{generalmanager-0.57.3 → generalmanager-0.58.0}/src/general_manager/bucket/calculation_bucket.py
RENAMED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"""Bucket implementation that enumerates calculation interface combinations."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
|
-
from collections.abc import Hashable, Iterable
|
|
4
|
+
from collections.abc import Hashable, Iterable, Iterator
|
|
5
5
|
from types import UnionType
|
|
6
|
+
from itertools import islice
|
|
6
7
|
from typing import (
|
|
7
8
|
Type,
|
|
8
9
|
TYPE_CHECKING,
|
|
@@ -25,6 +26,7 @@ from general_manager.bucket.base_bucket import Bucket
|
|
|
25
26
|
from general_manager.bucket.indexing import freeze_bucket_index_value
|
|
26
27
|
from general_manager.manager.input import Input, InputDomain
|
|
27
28
|
from general_manager.utils.filter_parser import (
|
|
29
|
+
FilterFunction,
|
|
28
30
|
ParsedFilters,
|
|
29
31
|
parse_filters,
|
|
30
32
|
)
|
|
@@ -47,6 +49,15 @@ class SortedFilters(TypedDict):
|
|
|
47
49
|
input_excludes: ParsedFilters
|
|
48
50
|
|
|
49
51
|
|
|
52
|
+
def _build_exclude_filter(exclude_func: FilterFunction) -> FilterFunction:
|
|
53
|
+
"""Create a lazy predicate that keeps values an exclude does not reject."""
|
|
54
|
+
|
|
55
|
+
def includes_value(value: object) -> bool:
|
|
56
|
+
return not exclude_func(value)
|
|
57
|
+
|
|
58
|
+
return includes_value
|
|
59
|
+
|
|
60
|
+
|
|
50
61
|
class InvalidCalculationInterfaceError(TypeError):
|
|
51
62
|
"""Raised when a CalculationBucket is initialized with a non-CalculationInterface manager."""
|
|
52
63
|
|
|
@@ -308,25 +319,95 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
|
308
319
|
|
|
309
320
|
def __str__(self) -> str:
|
|
310
321
|
"""
|
|
311
|
-
Return a compact preview of
|
|
322
|
+
Return a compact preview of generated combinations.
|
|
323
|
+
|
|
324
|
+
Cached buckets include the exact combination count. Uncached buckets avoid
|
|
325
|
+
materializing all combinations for string formatting; when more than the
|
|
326
|
+
preview limit exists, the count is reported as a lower-bound label.
|
|
312
327
|
|
|
313
328
|
Returns:
|
|
314
329
|
str: Human-readable summary of up to five combinations.
|
|
315
330
|
"""
|
|
316
331
|
PRINT_MAX = 5
|
|
317
|
-
combinations = self.
|
|
318
|
-
prefix = f"CalculationBucket ({
|
|
332
|
+
combinations, count_label, has_more = self._str_combinations_preview(PRINT_MAX)
|
|
333
|
+
prefix = f"CalculationBucket ({count_label})["
|
|
319
334
|
main = ",".join(
|
|
320
|
-
[
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
335
|
+
[f"{self._manager_class.__name__}(**{comb})" for comb in combinations]
|
|
336
|
+
)
|
|
337
|
+
suffix = "]"
|
|
338
|
+
if has_more:
|
|
339
|
+
suffix = ", ...]"
|
|
340
|
+
|
|
341
|
+
return f"{prefix}{main}{suffix}"
|
|
342
|
+
|
|
343
|
+
def _str_combinations_preview(
|
|
344
|
+
self, limit: int
|
|
345
|
+
) -> tuple[list[Combination], str, bool]:
|
|
346
|
+
"""
|
|
347
|
+
Return combinations, count label, and overflow flag for ``__str__``.
|
|
348
|
+
|
|
349
|
+
Sorted or reversed buckets use normal materialization so the preview
|
|
350
|
+
reflects the final global ordering. Unsorted uncached buckets read at
|
|
351
|
+
most ``limit + 1`` matching combinations and leave ``_data`` untouched.
|
|
352
|
+
"""
|
|
353
|
+
if self._data is not None:
|
|
354
|
+
return self._data[:limit], str(len(self._data)), len(self._data) > limit
|
|
355
|
+
|
|
356
|
+
if self._normalized_sort_key() is not None or self.reverse:
|
|
357
|
+
combinations = self.generate_combinations()
|
|
358
|
+
return (
|
|
359
|
+
combinations[:limit],
|
|
360
|
+
str(len(combinations)),
|
|
361
|
+
len(combinations) > limit,
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
from general_manager.cache.run_context import ensure_calculation_run_context
|
|
365
|
+
|
|
366
|
+
with ensure_calculation_run_context():
|
|
367
|
+
sorted_inputs = self.topological_sort_inputs()
|
|
368
|
+
sorted_filters = self._sort_filters(sorted_inputs)
|
|
369
|
+
if self._uses_static_iterator_possible_values(sorted_inputs):
|
|
370
|
+
combinations = self.generate_combinations()
|
|
371
|
+
return (
|
|
372
|
+
combinations[:limit],
|
|
373
|
+
str(len(combinations)),
|
|
374
|
+
len(combinations) > limit,
|
|
375
|
+
)
|
|
376
|
+
snapshot_iterables = self._uses_dependent_possible_values(sorted_inputs)
|
|
377
|
+
preview_iterator = self._iter_input_combinations(
|
|
378
|
+
sorted_inputs,
|
|
379
|
+
sorted_filters["input_filters"],
|
|
380
|
+
sorted_filters["input_excludes"],
|
|
381
|
+
snapshot_iterables=snapshot_iterables,
|
|
382
|
+
)
|
|
383
|
+
if sorted_filters["prop_filters"] or sorted_filters["prop_excludes"]:
|
|
384
|
+
preview_iterator = self._iter_prop_filtered_identifications(
|
|
385
|
+
preview_iterator,
|
|
386
|
+
sorted_filters["prop_filters"],
|
|
387
|
+
sorted_filters["prop_excludes"],
|
|
388
|
+
)
|
|
389
|
+
preview = list(islice(preview_iterator, limit + 1))
|
|
390
|
+
|
|
391
|
+
has_more = len(preview) > limit
|
|
392
|
+
if has_more:
|
|
393
|
+
preview = preview[:limit]
|
|
394
|
+
count_label = f"{limit}+" if has_more else str(len(preview))
|
|
395
|
+
return preview, count_label, has_more
|
|
396
|
+
|
|
397
|
+
def _uses_static_iterator_possible_values(self, sorted_inputs: list[str]) -> bool:
|
|
398
|
+
"""Return whether previewing would consume a one-shot static iterator."""
|
|
399
|
+
return any(
|
|
400
|
+
isinstance(self.input_fields[input_name].possible_values, Iterator)
|
|
401
|
+
for input_name in sorted_inputs
|
|
324
402
|
)
|
|
325
|
-
sufix = "]"
|
|
326
|
-
if len(combinations) > PRINT_MAX:
|
|
327
|
-
sufix = ", ...]"
|
|
328
403
|
|
|
329
|
-
|
|
404
|
+
def _uses_dependent_possible_values(self, sorted_inputs: list[str]) -> bool:
|
|
405
|
+
"""Return whether previewing should snapshot values before dependencies."""
|
|
406
|
+
return any(
|
|
407
|
+
bool(self.input_fields[input_name].depends_on)
|
|
408
|
+
and self.input_fields[input_name].possible_values is not None
|
|
409
|
+
for input_name in sorted_inputs
|
|
410
|
+
)
|
|
330
411
|
|
|
331
412
|
def __repr__(self) -> str:
|
|
332
413
|
"""
|
|
@@ -549,12 +630,14 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
|
549
630
|
self,
|
|
550
631
|
combinations: list[Combination],
|
|
551
632
|
) -> list[GeneralManagerType]:
|
|
633
|
+
"""Instantiate managers for each raw input-combination dictionary."""
|
|
552
634
|
return [self._manager_class(**combo) for combo in combinations]
|
|
553
635
|
|
|
554
636
|
@staticmethod
|
|
555
637
|
def _manager_identifications(
|
|
556
638
|
managers: list[GeneralManagerType],
|
|
557
639
|
) -> list[Combination]:
|
|
640
|
+
"""Return the identification dictionaries from manager instances."""
|
|
558
641
|
return [manager.identification for manager in managers]
|
|
559
642
|
|
|
560
643
|
def generate_combinations(self) -> List[Combination]:
|
|
@@ -730,22 +813,25 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
|
730
813
|
raise InvalidPossibleValuesError(key_name)
|
|
731
814
|
return possible_values
|
|
732
815
|
|
|
733
|
-
def
|
|
816
|
+
def _iter_input_combinations(
|
|
734
817
|
self,
|
|
735
818
|
sorted_inputs: List[str],
|
|
736
819
|
filters: ParsedFilters,
|
|
737
820
|
excludes: ParsedFilters,
|
|
738
|
-
|
|
821
|
+
*,
|
|
822
|
+
snapshot_iterables: bool,
|
|
823
|
+
) -> Generator[Combination, None, None]:
|
|
739
824
|
"""
|
|
740
|
-
|
|
825
|
+
Yield valid assignments of input fields satisfying filters and excludes.
|
|
741
826
|
|
|
742
827
|
Parameters:
|
|
743
828
|
sorted_inputs (list[str]): Input names in dependency-respecting order.
|
|
744
829
|
filters (dict[str, dict]): Per-input filter definitions (may include `filter_funcs` or `filter_kwargs`).
|
|
745
830
|
excludes (dict[str, dict]): Per-input exclusion definitions (may include `filter_funcs` or `filter_kwargs`).
|
|
746
831
|
|
|
747
|
-
|
|
748
|
-
|
|
832
|
+
Yields:
|
|
833
|
+
Combination: Completed input-to-value mappings that meet the
|
|
834
|
+
filters and excludes.
|
|
749
835
|
"""
|
|
750
836
|
|
|
751
837
|
def input_passes_filters(
|
|
@@ -812,10 +898,10 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
|
812
898
|
exclude_funcs = field_excludes.get("filter_funcs", [])
|
|
813
899
|
for exclude_func in exclude_funcs:
|
|
814
900
|
possible_values = filter(
|
|
815
|
-
|
|
901
|
+
_build_exclude_filter(exclude_func), possible_values
|
|
816
902
|
)
|
|
817
|
-
|
|
818
|
-
|
|
903
|
+
if snapshot_iterables:
|
|
904
|
+
possible_values = list(possible_values)
|
|
819
905
|
|
|
820
906
|
for value in possible_values:
|
|
821
907
|
if not isinstance(value, input_field.type):
|
|
@@ -824,7 +910,52 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
|
824
910
|
yield from helper(index + 1, current_combo)
|
|
825
911
|
del current_combo[input_name]
|
|
826
912
|
|
|
827
|
-
|
|
913
|
+
yield from helper(0, {})
|
|
914
|
+
|
|
915
|
+
def _generate_input_combinations(
|
|
916
|
+
self,
|
|
917
|
+
sorted_inputs: List[str],
|
|
918
|
+
filters: ParsedFilters,
|
|
919
|
+
excludes: ParsedFilters,
|
|
920
|
+
) -> List[Combination]:
|
|
921
|
+
"""
|
|
922
|
+
Generate all valid assignments of input fields that satisfy filters.
|
|
923
|
+
|
|
924
|
+
Parameters:
|
|
925
|
+
sorted_inputs (list[str]): Input names in dependency-respecting order.
|
|
926
|
+
filters (dict[str, dict]): Per-input filter definitions.
|
|
927
|
+
excludes (dict[str, dict]): Per-input exclusion definitions.
|
|
928
|
+
|
|
929
|
+
Returns:
|
|
930
|
+
list[Combination]: Completed input-to-value mappings that meet the
|
|
931
|
+
filters and excludes.
|
|
932
|
+
"""
|
|
933
|
+
return list(
|
|
934
|
+
self._iter_input_combinations(
|
|
935
|
+
sorted_inputs,
|
|
936
|
+
filters,
|
|
937
|
+
excludes,
|
|
938
|
+
snapshot_iterables=True,
|
|
939
|
+
)
|
|
940
|
+
)
|
|
941
|
+
|
|
942
|
+
def _iter_prop_filtered_identifications(
|
|
943
|
+
self,
|
|
944
|
+
combinations: Iterable[Combination],
|
|
945
|
+
prop_filters: ParsedFilters,
|
|
946
|
+
prop_excludes: ParsedFilters,
|
|
947
|
+
) -> Generator[Combination, None, None]:
|
|
948
|
+
"""
|
|
949
|
+
Lazily apply property filters and yield manager identifications.
|
|
950
|
+
|
|
951
|
+
This mirrors the property-filter materialization path used by
|
|
952
|
+
:meth:`generate_combinations`, but lets ``__str__`` stop after enough
|
|
953
|
+
matching combinations have been found.
|
|
954
|
+
"""
|
|
955
|
+
for combo in combinations:
|
|
956
|
+
manager = self._manager_class(**combo)
|
|
957
|
+
if self._filter_prop_combinations([manager], prop_filters, prop_excludes):
|
|
958
|
+
yield manager.identification
|
|
828
959
|
|
|
829
960
|
def _filter_prop_combinations(
|
|
830
961
|
self,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{generalmanager-0.57.3 → generalmanager-0.58.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.3 → generalmanager-0.58.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
|