quantum-framework 0.17.0__tar.gz → 0.18.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.
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/PKG-INFO +1 -1
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/pyproject.toml +1 -1
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/cli/check.py +23 -2
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/ast_nodes.py +6 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/ui_engine/src/ast_nodes.py +27 -1
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/ui_engine/src/parser.py +39 -1
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/data/query_parser.py +21 -1
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/forms/action_parser.py +15 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/action_handler.py +82 -22
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/component.py +5 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/data/query_executor.py +38 -4
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/renderer.py +2 -0
- quantum_framework-0.18.0/quantum/runtime/table_params.py +163 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/ui_console.py +48 -3
- quantum_framework-0.18.0/quantum/runtime/ui_form_rules.py +193 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/ui_html_adapter.py +1 -1
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/ui_page_renderer.py +168 -8
- quantum_framework-0.18.0/quantum/runtime/ui_pager.py +73 -0
- quantum_framework-0.18.0/quantum/runtime/ui_table_edit.py +139 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/ui_view.py +74 -10
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/web_server.py +85 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_framework.egg-info/PKG-INFO +1 -1
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_framework.egg-info/SOURCES.txt +4 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/LICENSE +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/README.md +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/cli/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/cli/admin.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/cli/commands/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/cli/commands/build.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/cli/commands/dev.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/cli/commands/docs.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/cli/commands/lint.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/cli/commands/migrate.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/cli/commands/new.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/cli/commands/serve.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/cli/commands/test.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/cli/deploy.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/cli/hot_reload.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/cli/jobs.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/cli/mq.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/cli/pkg.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/cli/runner.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/cli/server_process.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/cli/utils.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/compiler/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/compiler/base_generator.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/compiler/cli.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/compiler/expression_transformer.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/compiler/javascript/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/compiler/javascript/generator.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/compiler/optimizer.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/compiler/python/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/compiler/python/generator.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/compiler/python/runtime.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/compiler/transpiler.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/config_env.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/docs_links.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/expression_diagnostics.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/expression_stdlib.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/expressions.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/agents/src/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/agents/src/ast_node.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/conditionals/src/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/conditionals/src/ast_node.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/data_import/src/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/data_import/src/ast_node.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/data_import/src/runtime.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/dump/src/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/dump/src/ast_node.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/dump/src/parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/dump/src/runtime.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/functions/src/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/functions/src/ast_node.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/game_engine_2d/src/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/game_engine_2d/src/ast_nodes.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/game_engine_2d/src/parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/invocation/src/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/invocation/src/ast_node.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/invocation/src/runtime.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/knowledge_base/src/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/knowledge_base/src/ast_node.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/knowledge_base/src/parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/logging/src/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/logging/src/ast_node.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/logging/src/parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/logging/src/runtime.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/loops/src/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/loops/src/ast_node.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/query/src/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/query/src/query_validators.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/state_management/src/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/state_management/src/ast_node.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/terminal_engine/src/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/terminal_engine/src/ast_nodes.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/terminal_engine/src/parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/testing_engine/src/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/testing_engine/src/ast_nodes.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/testing_engine/src/parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/theming/src/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/theming/src/ast_node.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/theming/src/presets.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/ui_engine/src/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/websocket/src/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/websocket/src/ast_node.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/guards.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/html_compat.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parser_registry.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/ai/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/ai/agent_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/ai/knowledge_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/ai/llm_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/ai/team_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/base.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/composition/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/composition/import_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/composition/slot_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/control_flow/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/control_flow/if_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/control_flow/loop_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/control_flow/set_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/data/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/data/data_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/data/invoke_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/data/transaction_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/events/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/events/dispatch_event_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/forms/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/forms/flash_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/forms/redirect_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/functions/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/functions/function_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/functions/param_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/functions/return_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/html/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/html/component_call_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/html/html_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/jobs/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/jobs/job_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/jobs/schedule_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/jobs/thread_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/messaging/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/messaging/message_ack_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/messaging/message_nack_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/messaging/message_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/messaging/queue_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/messaging/websocket_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/routing/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/routing/route_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/scripting/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/scripting/pyclass_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/scripting/pydecorator_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/scripting/pyimport_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/scripting/python_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/services/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/services/dump_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/services/file_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/services/log_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/services/mail_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/tiers.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/xml_lines.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/packages/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/packages/manager.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/packages/manifest.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/packages/registry.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/packages/resolver.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/plugins/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/plugins/hooks.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/plugins/loader.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/plugins/manifest.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/plugins/registry.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/adapters/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/adapters/memory_adapter.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/adapters/rabbitmq_adapter.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/adapters/redis_adapter.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/adapters/sqlite_adapter.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/agent_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/ast_cache.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/auth_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/component_composer.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/component_resolver.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/conditions.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/database_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/db_schema.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/dev_panel.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/email_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/error_handler.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/execution_context.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executor_registry.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/ai/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/ai/agent_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/ai/knowledge_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/ai/llm_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/ai/team_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/base.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/control_flow/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/control_flow/if_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/control_flow/loop_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/control_flow/redirect_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/control_flow/set_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/data/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/data/data_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/data/invoke_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/data/transaction_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/jobs/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/jobs/job_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/jobs/schedule_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/jobs/thread_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/messaging/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/messaging/message_ack_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/messaging/message_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/messaging/queue_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/messaging/websocket_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/scripting/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/scripting/pyclass_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/scripting/pyimport_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/scripting/python_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/services/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/services/dump_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/services/file_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/services/log_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/executors/services/mail_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/expression_cache.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/file_upload_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/function_registry.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/game_builder.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/game_code_generator.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/game_templates.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/godot_code_generator.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/godot_templates.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/job_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/knowledge_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/llm_cache.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/llm_providers.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/llm_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/local_server.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/logging_setup.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/message_broker.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/message_queue_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/param_validation.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/pypy_compat.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/python_bridge.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/query_validators.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/service_container.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/terminal_builder.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/terminal_code_generator.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/terminal_templates.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/testing_builder.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/testing_code_generator.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/testing_templates.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/ui_builder.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/ui_desktop.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/ui_html_templates.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/ui_mobile_adapter.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/ui_mobile_templates.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/ui_textual_adapter.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/ui_textual_templates.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/ui_tokens.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/ui_validator.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/validators.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/websocket_adapter.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/websocket_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/websocket_transport.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/runtime/wsgi.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/services.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/utils/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/assets/quantum-admin-connectors.js +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/assets/quantum-admin.css +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/assets/quantum-source-highlight.js +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/audit_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/auth_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/component_discovery.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/config_generator.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/connector_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/crud.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/database.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/db_setup_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/deploy_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/docker_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/environment_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/git_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/health_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/job_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/log_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/main.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/models.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/pipeline_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/resource_manager.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/schemas.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/secret_manager.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/settings_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/template_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/test_execution_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/test_generator.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/webhook_service.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/backend/websocket_manager.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/components/admin/_layout/AdminShell.q +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/components/admin/agents.q +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/components/admin/app/[name].q +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/components/admin/applications.q +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/components/admin/component/[...path].q +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/components/admin/components.q +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/components/admin/connectors.q +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/components/admin/dashboard.q +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/components/admin/database.q +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/components/admin/features.q +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/components/admin/index.q +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/components/admin/jobs.q +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/components/admin/login.q +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/components/admin/logout.q +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/components/admin/settings.q +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/components/admin/source.q +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/components/admin/tests.q +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/run.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/services/__init__.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/services/_base.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/services/_component_test_generator.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/services/_yaml_editor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/services/apps.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/services/auth.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/services/components.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/services/connectors.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/services/projects.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/services/repository.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/services/settings.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_admin/services/yaml_import.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_framework.egg-info/dependency_links.txt +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_framework.egg-info/entry_points.txt +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_framework.egg-info/requires.txt +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum_framework.egg-info/top_level.txt +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/setup.cfg +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_agent.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_animations.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_ast_cache.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_cli_commands.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_component_library.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_expression_cache.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_form_validation.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_game_codegen.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_game_e2e.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_game_engine.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_game_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_godot_codegen.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_job_executor.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_llm_providers.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_message_broker.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_message_queue.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_mobile_target.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_multi_agent.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_packages.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_plugins.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_pypy_compat.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_python_scripting.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_terminal_codegen.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_terminal_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_testing_codegen.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_testing_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_theming.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_ui_html_adapter.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_ui_parity.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_ui_parser.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_ui_textual_adapter.py +0 -0
- {quantum_framework-0.17.0 → quantum_framework-0.18.0}/tests/test_websocket.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "quantum-framework"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.18.0"
|
|
8
8
|
description = "Full-stack declarative framework for building web, desktop, and mobile applications"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -102,9 +102,30 @@ class ProjectChecker:
|
|
|
102
102
|
self._fields(ast, rel, colunas, {nome: nome for nome in colunas}, None)
|
|
103
103
|
|
|
104
104
|
def _queries(self, node, rel: str, colunas: Dict[str, List[str]]) -> None:
|
|
105
|
-
from quantum.core.ast_nodes import QueryNode
|
|
106
|
-
from quantum.runtime.db_schema import check_sql
|
|
105
|
+
from quantum.core.ast_nodes import ActionNode, QueryNode
|
|
106
|
+
from quantum.runtime.db_schema import check_sql, read_schema
|
|
107
107
|
for filho in _children(node):
|
|
108
|
+
if isinstance(filho, ActionNode) and getattr(filho, 'table', None):
|
|
109
|
+
# UI-10: the table the action takes its params from exists, with those columns.
|
|
110
|
+
ds = filho.table_datasource
|
|
111
|
+
if ds not in self.datasources:
|
|
112
|
+
self.problems.append(Problem(rel, getattr(filho, 'source_line', None),
|
|
113
|
+
f'<q:action name="{filho.name}">: datasource {ds!r} is not '
|
|
114
|
+
f'declared in quantum.config.yaml'))
|
|
115
|
+
elif self._connection(ds) is not None:
|
|
116
|
+
esquema = read_schema(self._connection(ds))
|
|
117
|
+
tabela = esquema.get(filho.table)
|
|
118
|
+
if tabela is None:
|
|
119
|
+
self.problems.append(Problem(rel, getattr(filho, 'source_line', None),
|
|
120
|
+
f'<q:action name="{filho.name}" table="{filho.table}">: '
|
|
121
|
+
f'no such table (tables: {", ".join(esquema) or "none"})'))
|
|
122
|
+
else:
|
|
123
|
+
for coluna in filho.table_columns or []:
|
|
124
|
+
if coluna not in tabela.columns:
|
|
125
|
+
self.problems.append(Problem(
|
|
126
|
+
rel, getattr(filho, 'source_line', None),
|
|
127
|
+
f'<q:action name="{filho.name}" columns=...>: table "{filho.table}" '
|
|
128
|
+
f'has no column "{coluna}"'))
|
|
108
129
|
if isinstance(filho, QueryNode):
|
|
109
130
|
ds = filho.datasource
|
|
110
131
|
if not ds or ds.startswith('knowledge:') or getattr(filho, 'source', None):
|
|
@@ -698,6 +698,8 @@ class QueryNode(QuantumNode):
|
|
|
698
698
|
self.interval = None
|
|
699
699
|
self.paginate = False # Enable automatic pagination
|
|
700
700
|
self.page = None
|
|
701
|
+
self.page_expr = None # DB-9: page as an expression (default {query.page})
|
|
702
|
+
self.sortable = False # UI-13: ORDER BY the URL's ?sort=/&dir=
|
|
701
703
|
self.page_size = 20
|
|
702
704
|
self.timeout = None
|
|
703
705
|
self.maxrows = None
|
|
@@ -1137,6 +1139,10 @@ class ActionNode(QuantumNode):
|
|
|
1137
1139
|
self.method = method.upper()
|
|
1138
1140
|
self.params: List[QuantumParam] = []
|
|
1139
1141
|
self.body: List[QuantumNode] = []
|
|
1142
|
+
# UI-10 (M17): params from a table's schema — see quantum/runtime/table_params.py
|
|
1143
|
+
self.table: Optional[str] = None
|
|
1144
|
+
self.table_datasource: Optional[str] = None
|
|
1145
|
+
self.table_columns: Optional[List[str]] = None
|
|
1140
1146
|
|
|
1141
1147
|
# Action configuration
|
|
1142
1148
|
self.validate_csrf = True # Auto CSRF protection
|
|
@@ -319,6 +319,9 @@ class UIFormNode(QuantumNode, UILayoutMixin):
|
|
|
319
319
|
|
|
320
320
|
def __init__(self):
|
|
321
321
|
self.on_submit: Optional[str] = None
|
|
322
|
+
self.rules: Optional[str] = None # UI-9: 'off' = no rules from the action
|
|
323
|
+
self.values: Optional[str] = None # UI-10: {row} whose fields fill the form
|
|
324
|
+
self.submit: Optional[str] = None # UI-10: text of the generated button
|
|
322
325
|
self.children: List[QuantumNode] = []
|
|
323
326
|
|
|
324
327
|
# Validation mode: 'client', 'server', or 'both' (default)
|
|
@@ -550,6 +553,8 @@ class UIInputNode(QuantumNode, UILayoutMixin):
|
|
|
550
553
|
def __init__(self):
|
|
551
554
|
self.bind: Optional[str] = None
|
|
552
555
|
self.value: Optional[str] = None # initial value (UI-6)
|
|
556
|
+
self.search: Optional[str] = None # UI-12: id of the region it refreshes as you type
|
|
557
|
+
self.delay: Optional[int] = None # UI-12: ms after the last key (300 with search=)
|
|
553
558
|
self.placeholder: Optional[str] = None
|
|
554
559
|
self.input_type: str = "text" # text, password, email, number
|
|
555
560
|
self.on_change: Optional[str] = None
|
|
@@ -673,6 +678,9 @@ class UITableNode(QuantumNode, UILayoutMixin):
|
|
|
673
678
|
def __init__(self):
|
|
674
679
|
self.source: Optional[str] = None
|
|
675
680
|
self.as_var: Optional[str] = None # row variable for column content (UI-5)
|
|
681
|
+
self.sort: bool = False # UI-13: headers sort the query
|
|
682
|
+
self.edit: Optional[str] = None # UI-13: table whose cells are edited in place
|
|
683
|
+
self.edit_datasource: Optional[str] = None
|
|
676
684
|
self.children: List[QuantumNode] = [] # UIColumnNode children
|
|
677
685
|
self._init_layout_attrs()
|
|
678
686
|
|
|
@@ -697,6 +705,7 @@ class UIColumnNode(QuantumNode):
|
|
|
697
705
|
self.label: Optional[str] = None
|
|
698
706
|
self.column_width: Optional[str] = None
|
|
699
707
|
self.align: Optional[str] = None # left, center, right
|
|
708
|
+
self.edit: bool = True # UI-13: edit="false" keeps it read-only
|
|
700
709
|
# UI-5: a column with content draws it once per row (buttons, links).
|
|
701
710
|
self.children: List[QuantumNode] = []
|
|
702
711
|
|
|
@@ -1870,6 +1879,23 @@ class UIDatePickerNode(QuantumNode, UILayoutMixin):
|
|
|
1870
1879
|
return self._validate_layout()
|
|
1871
1880
|
|
|
1872
1881
|
|
|
1882
|
+
class UIPagerNode(QuantumNode, UILayoutMixin):
|
|
1883
|
+
"""<ui:pager for="query"> — previous, page numbers and next over a paginated query (UI-11)."""
|
|
1884
|
+
|
|
1885
|
+
def __init__(self):
|
|
1886
|
+
self.for_query: Optional[str] = None
|
|
1887
|
+
self.param: str = 'page'
|
|
1888
|
+
self.window: int = 2
|
|
1889
|
+
self.children: List[QuantumNode] = []
|
|
1890
|
+
self._init_layout_attrs()
|
|
1891
|
+
|
|
1892
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
1893
|
+
return {"type": "ui_pager", "for": self.for_query, "param": self.param}
|
|
1894
|
+
|
|
1895
|
+
def validate(self) -> List[str]:
|
|
1896
|
+
return [] if self.for_query else ["ui:pager needs for="]
|
|
1897
|
+
|
|
1898
|
+
|
|
1873
1899
|
def is_ui_node(node) -> bool:
|
|
1874
1900
|
"""A ui:* element (UI-1): markup, drawn by a renderer, never executed."""
|
|
1875
1901
|
return type(node).__module__ == __name__
|
|
@@ -1882,6 +1908,6 @@ CORE_TAGS = frozenset({
|
|
|
1882
1908
|
'window', 'hbox', 'vbox', 'grid', 'panel', 'section', 'scrollbox', 'spacer', 'rule',
|
|
1883
1909
|
'header', 'footer', 'card', 'card-header', 'card-body', 'card-footer', 'tabpanel', 'tab',
|
|
1884
1910
|
'text', 'badge', 'alert', 'link', 'image', 'progress',
|
|
1885
|
-
'table', 'column', 'list', 'item',
|
|
1911
|
+
'table', 'column', 'list', 'item', 'pager',
|
|
1886
1912
|
'form', 'formitem', 'input', 'checkbox', 'switch', 'radio', 'select', 'option', 'button',
|
|
1887
1913
|
})
|
{quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/features/ui_engine/src/parser.py
RENAMED
|
@@ -28,7 +28,7 @@ from .ast_nodes import (
|
|
|
28
28
|
UIToastNode, UIToastContainerNode,
|
|
29
29
|
UICarouselNode, UISlideNode,
|
|
30
30
|
UIStepperNode, UIStepNode,
|
|
31
|
-
UICalendarNode, UIDatePickerNode,
|
|
31
|
+
UICalendarNode, UIDatePickerNode, UIPagerNode,
|
|
32
32
|
)
|
|
33
33
|
from quantum.core.features.theming.src import UIThemeNode, UIColorNode
|
|
34
34
|
from quantum.core.ast_nodes import TextNode
|
|
@@ -81,6 +81,7 @@ class UIParser:
|
|
|
81
81
|
'select': '_parse_ui_select',
|
|
82
82
|
'table': '_parse_ui_table',
|
|
83
83
|
'column': '_parse_ui_column',
|
|
84
|
+
'pager': '_parse_ui_pager',
|
|
84
85
|
'list': '_parse_ui_list',
|
|
85
86
|
'item': '_parse_ui_item',
|
|
86
87
|
'image': '_parse_ui_image',
|
|
@@ -359,6 +360,13 @@ class UIParser:
|
|
|
359
360
|
node = UIFormNode()
|
|
360
361
|
node.on_submit = element.get('on-submit')
|
|
361
362
|
node.send = element.get('with')
|
|
363
|
+
# UI-9: rules="off" stops the form from taking its fields' rules from the action.
|
|
364
|
+
node.rules = element.get('rules')
|
|
365
|
+
# UI-10: an edit form opens with a row's values; the generated button's text.
|
|
366
|
+
node.values = element.get('values')
|
|
367
|
+
node.submit = element.get('submit')
|
|
368
|
+
if node.rules not in (None, 'on', 'off'):
|
|
369
|
+
raise UIParseError(f'<ui:form rules="{node.rules}">: use on (the default) or off')
|
|
362
370
|
|
|
363
371
|
# Validation attributes
|
|
364
372
|
node.validation_mode = element.get('validation', 'both')
|
|
@@ -430,6 +438,14 @@ class UIParser:
|
|
|
430
438
|
node = UIInputNode()
|
|
431
439
|
node.bind = element.get('bind')
|
|
432
440
|
node.value = element.get('value')
|
|
441
|
+
# UI-12: search as you type — refresh the element with this id.
|
|
442
|
+
node.search = element.get('search')
|
|
443
|
+
espera = element.get('delay')
|
|
444
|
+
if espera is not None and not espera.isdigit():
|
|
445
|
+
raise UIParseError(f'<ui:input delay="{espera}">: milliseconds, e.g. delay="300"')
|
|
446
|
+
node.delay = int(espera) if espera is not None else (300 if node.search else None)
|
|
447
|
+
if node.search is not None and not node.bind:
|
|
448
|
+
raise UIParseError('<ui:input search=...> needs bind= (the URL parameter it sets)')
|
|
433
449
|
node.placeholder = element.get('placeholder')
|
|
434
450
|
node.input_type = element.get('type', 'text')
|
|
435
451
|
node.on_change = element.get('on-change')
|
|
@@ -498,6 +514,14 @@ class UIParser:
|
|
|
498
514
|
node = UITableNode()
|
|
499
515
|
node.source = element.get('source')
|
|
500
516
|
node.as_var = element.get('as')
|
|
517
|
+
# UI-13: sortable headers and cells edited in place.
|
|
518
|
+
node.sort = self._parse_bool(element.get('sort'))
|
|
519
|
+
node.edit = element.get('edit')
|
|
520
|
+
node.edit_datasource = element.get('datasource')
|
|
521
|
+
if node.edit and not node.edit_datasource:
|
|
522
|
+
raise UIParseError(f'<ui:table edit="{node.edit}"> needs datasource= (where the table is)')
|
|
523
|
+
if (node.sort or node.edit) and not node.source:
|
|
524
|
+
raise UIParseError('<ui:table sort/edit> needs source="{query}"')
|
|
501
525
|
self._apply_layout_attrs(node, element)
|
|
502
526
|
# Parse child <ui:column> elements
|
|
503
527
|
self._parse_children(element, node)
|
|
@@ -509,9 +533,23 @@ class UIParser:
|
|
|
509
533
|
node.label = element.get('label')
|
|
510
534
|
node.column_width = element.get('width')
|
|
511
535
|
node.align = element.get('align')
|
|
536
|
+
node.edit = element.get('edit', 'true') != 'false'
|
|
512
537
|
self._parse_children(element, node)
|
|
513
538
|
return node
|
|
514
539
|
|
|
540
|
+
def _parse_ui_pager(self, element: ET.Element) -> UIPagerNode:
|
|
541
|
+
node = UIPagerNode()
|
|
542
|
+
node.for_query = element.get('for')
|
|
543
|
+
if not node.for_query:
|
|
544
|
+
raise UIParseError('<ui:pager> needs for="<the paginated q:query>"')
|
|
545
|
+
node.param = element.get('param') or 'page'
|
|
546
|
+
janela = element.get('window')
|
|
547
|
+
if janela is not None and not janela.isdigit():
|
|
548
|
+
raise UIParseError(f'<ui:pager window="{janela}">: the number of pages shown each side of the current one')
|
|
549
|
+
node.window = int(janela) if janela is not None else 2
|
|
550
|
+
self._apply_layout_attrs(node, element)
|
|
551
|
+
return node
|
|
552
|
+
|
|
515
553
|
def _parse_ui_list(self, element: ET.Element) -> UIListNode:
|
|
516
554
|
node = UIListNode()
|
|
517
555
|
node.source = element.get('source')
|
{quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/data/query_parser.py
RENAMED
|
@@ -77,7 +77,27 @@ class QueryParser(BaseTagParser):
|
|
|
77
77
|
# Parse attributes
|
|
78
78
|
query_node.source = source
|
|
79
79
|
query_node.paginate = self.get_bool_attr(element, 'paginate', False)
|
|
80
|
-
|
|
80
|
+
# UI-13: ordered by the URL's ?sort=/&dir= (a <ui:table sort="true"> writes them)
|
|
81
|
+
query_node.sortable = self.get_bool_attr(element, 'sortable', False)
|
|
82
|
+
# DB-9: page= is an expression, resolved when the query runs; it was
|
|
83
|
+
# read as an int at parse time, so page="{query.page}" was always
|
|
84
|
+
# page 1. With paginate="true" and no page=, the page is the URL's
|
|
85
|
+
# `page` parameter — the one <ui:pager> writes.
|
|
86
|
+
pagina = element.get('page')
|
|
87
|
+
query_node.page = None
|
|
88
|
+
query_node.page_expr = None
|
|
89
|
+
if pagina is not None:
|
|
90
|
+
if '{' in pagina:
|
|
91
|
+
query_node.page_expr = pagina
|
|
92
|
+
elif pagina.strip().isdigit():
|
|
93
|
+
query_node.page = int(pagina)
|
|
94
|
+
else:
|
|
95
|
+
from quantum.core.parser import QuantumParseError
|
|
96
|
+
raise QuantumParseError(
|
|
97
|
+
f'<q:query name="{name}" page="{pagina}">: a page number or an expression, '
|
|
98
|
+
f'e.g. page="{{query.pagina}}"')
|
|
99
|
+
elif query_node.paginate:
|
|
100
|
+
query_node.page_expr = '{query.page}'
|
|
81
101
|
# Accept both spellings: the codebase convention is camelCase, but
|
|
82
102
|
# snake_case reads naturally in XML and the examples used it —
|
|
83
103
|
# the mismatch made page_size None and crashed pagination with
|
{quantum_framework-0.17.0 → quantum_framework-0.18.0}/quantum/core/parsers/forms/action_parser.py
RENAMED
|
@@ -48,6 +48,21 @@ class ActionParser(BaseTagParser):
|
|
|
48
48
|
f'<q:action name="{name}"> {attr}= is not supported: it was accepted '
|
|
49
49
|
f'and never enforced. {porque}')
|
|
50
50
|
|
|
51
|
+
# UI-10 (M17): the action's params come from a table's schema, read
|
|
52
|
+
# from the database when the action is used; a q:param written wins.
|
|
53
|
+
action_node.table = element.get('table')
|
|
54
|
+
action_node.table_datasource = element.get('datasource')
|
|
55
|
+
colunas = element.get('columns')
|
|
56
|
+
action_node.table_columns = [c.strip() for c in colunas.split(',') if c.strip()] if colunas else None
|
|
57
|
+
if (action_node.table_datasource or colunas) and not action_node.table:
|
|
58
|
+
from quantum.core.parser import QuantumParseError
|
|
59
|
+
raise QuantumParseError(
|
|
60
|
+
f'<q:action name="{name}"> datasource= and columns= describe table=, which is missing')
|
|
61
|
+
if action_node.table and not action_node.table_datasource:
|
|
62
|
+
from quantum.core.parser import QuantumParseError
|
|
63
|
+
raise QuantumParseError(
|
|
64
|
+
f'<q:action name="{name}" table="{action_node.table}"> needs datasource= (where the table is)')
|
|
65
|
+
|
|
51
66
|
# Parse children (params, statements)
|
|
52
67
|
for child in element:
|
|
53
68
|
child_type = self._get_element_name(child)
|
|
@@ -24,8 +24,23 @@ logger = logging.getLogger('quantum.action')
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class ValidationError(Exception):
|
|
27
|
-
"""Parameter validation error
|
|
28
|
-
|
|
27
|
+
"""Parameter validation error.
|
|
28
|
+
|
|
29
|
+
`fields` maps each field that failed to its own message (UI-9): the form
|
|
30
|
+
shows each one next to its field. The exception's text is the first one,
|
|
31
|
+
the flash message it always was."""
|
|
32
|
+
|
|
33
|
+
def __init__(self, message: str, fields: Optional[Dict[str, str]] = None):
|
|
34
|
+
super().__init__(message)
|
|
35
|
+
self.fields = fields or {}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _field_message(nome: str, mensagem: str) -> str:
|
|
39
|
+
""""Parameter 'titulo' must be at least 3 characters" -> "Must be at least 3 characters"."""
|
|
40
|
+
texto = re.sub(rf"^Parameter '{re.escape(nome)}' ", '', mensagem)
|
|
41
|
+
if texto == 'is required':
|
|
42
|
+
return 'Required'
|
|
43
|
+
return texto[:1].upper() + texto[1:]
|
|
29
44
|
|
|
30
45
|
|
|
31
46
|
class ActionHandler:
|
|
@@ -70,7 +85,7 @@ class ActionHandler:
|
|
|
70
85
|
form_data = self._extract_form_data()
|
|
71
86
|
|
|
72
87
|
# 2. Validate parameters
|
|
73
|
-
validated_params = self._validate_parameters(action
|
|
88
|
+
validated_params = self._validate_parameters(self._params_of(action), form_data, action)
|
|
74
89
|
|
|
75
90
|
# 3. Add validated params to context
|
|
76
91
|
for key, value in validated_params.items():
|
|
@@ -129,6 +144,15 @@ class ActionHandler:
|
|
|
129
144
|
except ValidationError as e:
|
|
130
145
|
# Validation failed - set error flash and redirect back
|
|
131
146
|
self._set_flash_message(str(e), 'error')
|
|
147
|
+
# UI-9: the next render of the form shows each error by its field
|
|
148
|
+
# and the values that were sent (never a password, never a file).
|
|
149
|
+
session['form_state'] = {
|
|
150
|
+
'action': getattr(action, 'name', None),
|
|
151
|
+
'errors': e.fields,
|
|
152
|
+
'values': {n: v for n, v in request.form.items()
|
|
153
|
+
if n != 'action' and n not in self._secret_fields(action)},
|
|
154
|
+
}
|
|
155
|
+
session.modified = True
|
|
132
156
|
# Redirect to referer or home
|
|
133
157
|
referer = request.referrer or '/'
|
|
134
158
|
return referer, 302
|
|
@@ -206,10 +230,16 @@ class ActionHandler:
|
|
|
206
230
|
|
|
207
231
|
return data
|
|
208
232
|
|
|
233
|
+
def _params_of(self, action) -> List[QuantumParam]:
|
|
234
|
+
"""The action's q:params, plus its table's when it declares one (UI-10)."""
|
|
235
|
+
from quantum.runtime.table_params import params_of
|
|
236
|
+
return params_of(action, getattr(self.component_runtime, 'config', None) or {})
|
|
237
|
+
|
|
209
238
|
def _validate_parameters(
|
|
210
239
|
self,
|
|
211
240
|
params: List[QuantumParam],
|
|
212
|
-
form_data: Dict[str, Any]
|
|
241
|
+
form_data: Dict[str, Any],
|
|
242
|
+
action=None,
|
|
213
243
|
) -> Dict[str, Any]:
|
|
214
244
|
"""
|
|
215
245
|
Validate form data against q:param definitions.
|
|
@@ -218,30 +248,60 @@ class ActionHandler:
|
|
|
218
248
|
"""
|
|
219
249
|
|
|
220
250
|
validated = {}
|
|
251
|
+
primeira = None
|
|
252
|
+
por_campo: Dict[str, str] = {}
|
|
221
253
|
|
|
222
254
|
for param in params:
|
|
223
255
|
value = form_data.get(param.name)
|
|
256
|
+
try:
|
|
257
|
+
# Check required
|
|
258
|
+
if param.required and (value is None or value == ''):
|
|
259
|
+
raise ValidationError(f"Parameter '{param.name}' is required")
|
|
260
|
+
|
|
261
|
+
# Skip validation if optional and not provided
|
|
262
|
+
if value is None or value == '':
|
|
263
|
+
if param.default is not None:
|
|
264
|
+
validated[param.name] = param.default
|
|
265
|
+
elif getattr(param, 'nullable', False):
|
|
266
|
+
validated[param.name] = None # UI-10: a NULL column left blank
|
|
267
|
+
continue
|
|
224
268
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
269
|
+
# Type validation and conversion
|
|
270
|
+
validated_value = self._validate_type(param, value)
|
|
271
|
+
|
|
272
|
+
# Additional validation rules
|
|
273
|
+
self._validate_rules(param, validated_value)
|
|
274
|
+
|
|
275
|
+
# UI-10: a foreign key names a row that exists.
|
|
276
|
+
referencia = getattr(param, 'references', None)
|
|
277
|
+
if referencia and action is not None and getattr(action, 'table_datasource', None):
|
|
278
|
+
from quantum.runtime.table_params import reference_exists
|
|
279
|
+
if not reference_exists(getattr(self.component_runtime, 'config', None) or {},
|
|
280
|
+
action.table_datasource, referencia, validated_value):
|
|
281
|
+
raise ValidationError(
|
|
282
|
+
f"Parameter '{param.name}' must name an existing row of {referencia[0]} "
|
|
283
|
+
f"(no {referencia[1]} = {validated_value})")
|
|
284
|
+
|
|
285
|
+
validated[param.name] = validated_value
|
|
286
|
+
except ValidationError as erro:
|
|
287
|
+
# UI-9: every field is checked, so the form can mark each one.
|
|
288
|
+
primeira = primeira or str(erro)
|
|
289
|
+
por_campo[param.name] = _field_message(param.name, str(erro))
|
|
290
|
+
|
|
291
|
+
if primeira:
|
|
292
|
+
raise ValidationError(primeira, por_campo)
|
|
243
293
|
return validated
|
|
244
294
|
|
|
295
|
+
@staticmethod
|
|
296
|
+
def _secret_fields(action) -> set:
|
|
297
|
+
"""Fields never sent back to a form: passwords and uploads (UI-9)."""
|
|
298
|
+
nomes = set()
|
|
299
|
+
for param in getattr(action, 'params', None) or []: # table columns are never secrets
|
|
300
|
+
tipo = str(getattr(param, 'type', '') or '').lower()
|
|
301
|
+
if tipo in ('password', 'file') or 'senha' in param.name.lower() or 'password' in param.name.lower():
|
|
302
|
+
nomes.add(param.name)
|
|
303
|
+
return nomes
|
|
304
|
+
|
|
245
305
|
def _validate_type(self, param: QuantumParam, value: Any) -> Any:
|
|
246
306
|
"""Converte o valor para o tipo declarado, ou levanta ValidationError.
|
|
247
307
|
|
|
@@ -232,6 +232,11 @@ class ComponentRuntime:
|
|
|
232
232
|
"""Access to service container"""
|
|
233
233
|
return self._services
|
|
234
234
|
|
|
235
|
+
@property
|
|
236
|
+
def config(self) -> Dict[str, Any]:
|
|
237
|
+
"""The project configuration this runtime runs with."""
|
|
238
|
+
return self._services._config or {}
|
|
239
|
+
|
|
235
240
|
@property
|
|
236
241
|
def executor_registry(self) -> ExecutorRegistry:
|
|
237
242
|
"""Access to executor registry (may be None if not using modular executors)"""
|
|
@@ -93,10 +93,11 @@ class QueryExecutor(BaseExecutor):
|
|
|
93
93
|
|
|
94
94
|
# Handle pagination
|
|
95
95
|
pagination_metadata = None
|
|
96
|
-
sql_to_execute = node
|
|
96
|
+
sql_to_execute = self._sorted_sql(node, params, exec_context) # UI-13
|
|
97
97
|
|
|
98
98
|
if node.paginate:
|
|
99
|
-
pagination_metadata, sql_to_execute = self._handle_pagination(
|
|
99
|
+
pagination_metadata, sql_to_execute = self._handle_pagination(
|
|
100
|
+
node, params, exec_context, base_sql=sql_to_execute)
|
|
100
101
|
|
|
101
102
|
# Execute query
|
|
102
103
|
result = self.services.database.execute_query(
|
|
@@ -110,7 +111,32 @@ class QueryExecutor(BaseExecutor):
|
|
|
110
111
|
|
|
111
112
|
return result
|
|
112
113
|
|
|
113
|
-
def
|
|
114
|
+
def _sorted_sql(self, node: QueryNode, params: Dict[str, Any], exec_context) -> str:
|
|
115
|
+
"""UI-13: a sortable query orders by the URL's ?sort= (and ?dir=asc|desc).
|
|
116
|
+
|
|
117
|
+
The column must be one the query returns — found with LIMIT 0, never
|
|
118
|
+
taken from the URL otherwise; an unknown one leaves the order as
|
|
119
|
+
written (a URL is not an error)."""
|
|
120
|
+
if not getattr(node, 'sortable', False):
|
|
121
|
+
return node.sql
|
|
122
|
+
try:
|
|
123
|
+
consulta = exec_context.get_variable('query') if exec_context is not None else None
|
|
124
|
+
except Exception:
|
|
125
|
+
consulta = None
|
|
126
|
+
consulta = consulta if isinstance(consulta, dict) else {}
|
|
127
|
+
coluna = str(consulta.get('sort') or '').strip()
|
|
128
|
+
direcao = 'DESC' if str(consulta.get('dir') or '').lower() == 'desc' else 'ASC'
|
|
129
|
+
if not coluna:
|
|
130
|
+
return node.sql
|
|
131
|
+
base = node.sql.strip().rstrip(';')
|
|
132
|
+
sonda = self.services.database.execute_query(
|
|
133
|
+
node.datasource, f'SELECT * FROM ({base}) AS quantum_ordenada LIMIT 0', params)
|
|
134
|
+
if coluna not in (sonda.column_list or []):
|
|
135
|
+
return node.sql
|
|
136
|
+
return f'SELECT * FROM ({base}) AS quantum_ordenada ORDER BY "{coluna}" {direcao}'
|
|
137
|
+
|
|
138
|
+
def _handle_pagination(self, node: QueryNode, params: Dict[str, Any], exec_context=None,
|
|
139
|
+
base_sql: str = None):
|
|
114
140
|
"""Handle query pagination"""
|
|
115
141
|
count_sql = self._generate_count_query(node.sql)
|
|
116
142
|
count_result = self.services.database.execute_query(
|
|
@@ -122,13 +148,21 @@ class QueryExecutor(BaseExecutor):
|
|
|
122
148
|
total_records = count_result.data[0]['count'] if count_result.data else 0
|
|
123
149
|
|
|
124
150
|
page = node.page if node.page is not None else 1
|
|
151
|
+
if getattr(node, 'page_expr', None):
|
|
152
|
+
# DB-9: from the URL, so never an error — ?page=abc or ?page=-3 is page 1.
|
|
153
|
+
variaveis = exec_context.get_all_variables() if exec_context is not None else None
|
|
154
|
+
bruto = self.apply_databinding(node.page_expr, variaveis)
|
|
155
|
+
try:
|
|
156
|
+
page = max(1, int(str(bruto).strip()))
|
|
157
|
+
except (TypeError, ValueError):
|
|
158
|
+
page = 1
|
|
125
159
|
# paginate="true" without a page size is a config error, not a
|
|
126
160
|
# reason to crash on None arithmetic further down.
|
|
127
161
|
page_size = node.page_size or 10
|
|
128
162
|
total_pages = (total_records + page_size - 1) // page_size
|
|
129
163
|
|
|
130
164
|
offset = (page - 1) * page_size
|
|
131
|
-
sql_to_execute = f"{node.sql}\nLIMIT {page_size} OFFSET {offset}"
|
|
165
|
+
sql_to_execute = f"{base_sql or node.sql}\nLIMIT {page_size} OFFSET {offset}"
|
|
132
166
|
|
|
133
167
|
pagination_metadata = {
|
|
134
168
|
'totalRecords': total_records,
|
|
@@ -121,6 +121,8 @@ class HTMLRenderer:
|
|
|
121
121
|
# component rendered is the page; a called component posts to it.
|
|
122
122
|
if not hasattr(self, '_ui_actions'):
|
|
123
123
|
self._ui_actions = [s.name for s in node.statements if isinstance(s, ActionNode)]
|
|
124
|
+
# UI-9: a ui:form takes its fields' rules from the action it posts to.
|
|
125
|
+
self._ui_action_nodes = {s.name: s for s in node.statements if isinstance(s, ActionNode)}
|
|
124
126
|
self.imports = {
|
|
125
127
|
imp.alias or imp.component: (imp.component, imp.from_path)
|
|
126
128
|
for imp in node.statements
|