precis-cli 0.1.3__py3-none-any.whl
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.
- app/__init__.py +16 -0
- app/api/__init__.py +32 -0
- app/api/dependencies.py +185 -0
- app/api/main.py +301 -0
- app/api/middleware/__init__.py +34 -0
- app/api/middleware/exception_handler.py +126 -0
- app/api/middleware/request_logging.py +97 -0
- app/api/middleware/token_auth.py +215 -0
- app/api/models/__init__.py +151 -0
- app/api/models/connection_rules.py +98 -0
- app/api/models/files.py +99 -0
- app/api/models/full_validation.py +554 -0
- app/api/models/project.py +117 -0
- app/api/models/projects.py +80 -0
- app/api/models/schema.py +121 -0
- app/api/models/v2_responses.py +46 -0
- app/api/models/validation.py +303 -0
- app/api/models/workspace.py +118 -0
- app/api/routers/__init__.py +56 -0
- app/api/routers/ai/__init__.py +36 -0
- app/api/routers/ai/chat.py +243 -0
- app/api/routers/ai/generate.py +156 -0
- app/api/routers/ai/hardware.py +199 -0
- app/api/routers/ai/jobs.py +544 -0
- app/api/routers/ai/migrate.py +471 -0
- app/api/routers/ai/models.py +352 -0
- app/api/routers/ai/ollama.py +108 -0
- app/api/routers/ai/providers.py +491 -0
- app/api/routers/ai/router.py +44 -0
- app/api/routers/ai/stream.py +281 -0
- app/api/routers/ai/utils.py +182 -0
- app/api/routers/core/__init__.py +34 -0
- app/api/routers/core/connection_rules.py +188 -0
- app/api/routers/core/data_sources.py +374 -0
- app/api/routers/core/regex.py +333 -0
- app/api/routers/core/reporting.py +263 -0
- app/api/routers/files/__init__.py +24 -0
- app/api/routers/files/ops.py +179 -0
- app/api/routers/files/transfer.py +101 -0
- app/api/routers/preview/__init__.py +35 -0
- app/api/routers/preview/content_mode.py +264 -0
- app/api/routers/preview/header_row.py +154 -0
- app/api/routers/preview/models.py +81 -0
- app/api/routers/preview/path_mode.py +440 -0
- app/api/routers/preview/router.py +30 -0
- app/api/routers/project/__init__.py +61 -0
- app/api/routers/project/base.py +111 -0
- app/api/routers/project/constraint.py +345 -0
- app/api/routers/project/full_config.py +411 -0
- app/api/routers/project/full_config_writer.py +327 -0
- app/api/routers/project/helpers.py +167 -0
- app/api/routers/project/inspection_fix.py +330 -0
- app/api/routers/project/manifest.py +605 -0
- app/api/routers/project/models.py +177 -0
- app/api/routers/project/pattern.py +268 -0
- app/api/routers/project/regex.py +358 -0
- app/api/routers/project/scanner.py +157 -0
- app/api/routers/project/schema.py +567 -0
- app/api/routers/project/schema_helpers.py +149 -0
- app/api/routers/project/settings.py +399 -0
- app/api/routers/project/template.py +325 -0
- app/api/routers/project/validation.py +227 -0
- app/api/routers/project/view.py +177 -0
- app/api/routers/project/workspaces.py +167 -0
- app/api/routers/projects/__init__.py +25 -0
- app/api/routers/projects/create.py +121 -0
- app/api/routers/projects/open.py +103 -0
- app/api/routers/projects/scan.py +100 -0
- app/api/routers/validation/__init__.py +39 -0
- app/api/routers/validation/common.py +263 -0
- app/api/routers/validation/content_mode.py +401 -0
- app/api/routers/validation/history.py +235 -0
- app/api/routers/validation/inline_mode.py +196 -0
- app/api/routers/validation/path_mode.py +314 -0
- app/api/routers/validation/router.py +51 -0
- app/api/services/full_validation_response_builder.py +412 -0
- app/api/services/io_error_messages.py +41 -0
- app/api/services/preview_service.py +248 -0
- app/cli/__init__.py +18 -0
- app/cli/__main__.py +30 -0
- app/cli/shared_services/__init__.py +42 -0
- app/cli/shared_services/config_ops.py +581 -0
- app/cli/shared_services/generation_ops.py +160 -0
- app/cli/shared_services/project_ops.py +288 -0
- app/cli/shell/__init__.py +48 -0
- app/cli/shell/commands/__init__.py +63 -0
- app/cli/shell/commands/ai/__init__.py +260 -0
- app/cli/shell/commands/ai/chat.py +331 -0
- app/cli/shell/commands/ai/delete.py +165 -0
- app/cli/shell/commands/ai/diff.py +70 -0
- app/cli/shell/commands/ai/display.py +428 -0
- app/cli/shell/commands/ai/executor.py +225 -0
- app/cli/shell/commands/ai/executor_utils.py +271 -0
- app/cli/shell/commands/ai/generate.py +285 -0
- app/cli/shell/commands/ai/interaction.py +262 -0
- app/cli/shell/commands/ai/migrate.py +281 -0
- app/cli/shell/commands/ai/resolver.py +224 -0
- app/cli/shell/commands/ai/status.py +101 -0
- app/cli/shell/commands/ai/switch.py +158 -0
- app/cli/shell/commands/ai/utils.py +52 -0
- app/cli/shell/commands/base.py +343 -0
- app/cli/shell/commands/config/__init__.py +131 -0
- app/cli/shell/commands/config/base.py +80 -0
- app/cli/shell/commands/config/check.py +246 -0
- app/cli/shell/commands/config/edit.py +148 -0
- app/cli/shell/commands/config/get.py +111 -0
- app/cli/shell/commands/config/init.py +123 -0
- app/cli/shell/commands/config/inspect.py +164 -0
- app/cli/shell/commands/config/list.py +104 -0
- app/cli/shell/commands/config/set.py +119 -0
- app/cli/shell/commands/config/show.py +150 -0
- app/cli/shell/commands/exit.py +67 -0
- app/cli/shell/commands/help.py +104 -0
- app/cli/shell/commands/infer_schema.py +156 -0
- app/cli/shell/commands/open.py +201 -0
- app/cli/shell/commands/project.py +215 -0
- app/cli/shell/commands/provider.py +619 -0
- app/cli/shell/commands/system.py +170 -0
- app/cli/shell/commands/validate.py +455 -0
- app/cli/shell/completer.py +231 -0
- app/cli/shell/config_storage.py +191 -0
- app/cli/shell/exceptions.py +90 -0
- app/cli/shell/formatter.py +371 -0
- app/cli/shell/interactive_menu.py +348 -0
- app/cli/shell/main.py +286 -0
- app/cli/shell/parser.py +266 -0
- app/cli/start.py +180 -0
- app/cli_main.py +52 -0
- app/mcp_server.py +311 -0
- app/shared/core/__init__.py +18 -0
- app/shared/core/app_version.py +59 -0
- app/shared/core/config/__init__.py +119 -0
- app/shared/core/config/server.py +193 -0
- app/shared/core/data_source/__init__.py +106 -0
- app/shared/core/data_source/loader.py +348 -0
- app/shared/core/data_source/loaders/__init__.py +213 -0
- app/shared/core/data_source/loaders/base.py +215 -0
- app/shared/core/data_source/loaders/converter.py +337 -0
- app/shared/core/data_source/loaders/csv_loader.py +252 -0
- app/shared/core/data_source/loaders/excel_loader.py +666 -0
- app/shared/core/data_source/loaders/extractor.py +268 -0
- app/shared/core/data_source/loaders/json_loader.py +354 -0
- app/shared/core/data_source/loaders/registry.py +88 -0
- app/shared/core/data_source/loaders/sql_loader.py +313 -0
- app/shared/core/data_source/loaders/strategies/__init__.py +100 -0
- app/shared/core/data_source/loaders/strategies/array_parser.py +277 -0
- app/shared/core/data_source/loaders/strategies/lines_parser.py +355 -0
- app/shared/core/data_source/loaders/strategies/object_parser.py +278 -0
- app/shared/core/data_source/schema_info.py +50 -0
- app/shared/core/data_source/specs/__init__.py +128 -0
- app/shared/core/data_source/specs/base.py +258 -0
- app/shared/core/data_source/specs/csv_source.py +137 -0
- app/shared/core/data_source/specs/excel_source.py +141 -0
- app/shared/core/data_source/specs/file_base.py +252 -0
- app/shared/core/data_source/specs/json_source.py +226 -0
- app/shared/core/data_source/specs/sql_source.py +161 -0
- app/shared/core/encoding.py +36 -0
- app/shared/core/io/__init__.py +24 -0
- app/shared/core/io/yaml.py +379 -0
- app/shared/core/manifest_schema/__init__.py +52 -0
- app/shared/core/manifest_schema/types.py +99 -0
- app/shared/core/manifest_schema/version.py +175 -0
- app/shared/core/patterns/__init__.py +24 -0
- app/shared/core/patterns/loader.py +134 -0
- app/shared/core/patterns/writer.py +244 -0
- app/shared/core/project/__init__.py +47 -0
- app/shared/core/project/constraint/builders/__init__.py +50 -0
- app/shared/core/project/constraint/builders/base.py +100 -0
- app/shared/core/project/constraint/builders/composite.py +77 -0
- app/shared/core/project/constraint/builders/conditional.py +67 -0
- app/shared/core/project/constraint/builders/foreign_key.py +53 -0
- app/shared/core/project/constraint/builders/registry.py +64 -0
- app/shared/core/project/constraint/builders/scripted.py +51 -0
- app/shared/core/project/constraint/builders/single_column.py +86 -0
- app/shared/core/project/constraint/builders/unique.py +53 -0
- app/shared/core/project/constraint/factory.py +170 -0
- app/shared/core/project/constraint/reader.py +214 -0
- app/shared/core/project/constraint/registry.py +233 -0
- app/shared/core/project/constraint/types/__init__.py +63 -0
- app/shared/core/project/constraint/types/constraint_file.py +261 -0
- app/shared/core/project/constraint/types/refs.py +460 -0
- app/shared/core/project/constraint/types.py +28 -0
- app/shared/core/project/constraint/writer.py +181 -0
- app/shared/core/project/loader/__init__.py +56 -0
- app/shared/core/project/loader/loader.py +30 -0
- app/shared/core/project/loader/loader_parts/config_inspector.py +137 -0
- app/shared/core/project/loader/loader_parts/embedded_constraints.py +224 -0
- app/shared/core/project/loader/loader_parts/file_loaders.py +58 -0
- app/shared/core/project/loader/loader_parts/inspection_ids.py +85 -0
- app/shared/core/project/loader/loader_parts/inspector_helpers.py +312 -0
- app/shared/core/project/loader/loader_parts/inspector_id_checks.py +274 -0
- app/shared/core/project/loader/loader_parts/inspector_reference_checks.py +690 -0
- app/shared/core/project/loader/loader_parts/inspector_uniqueness_checks.py +286 -0
- app/shared/core/project/loader/loader_parts/loading_error_messages.py +298 -0
- app/shared/core/project/loader/loader_parts/main.py +432 -0
- app/shared/core/project/loader/loader_parts/path_validation.py +117 -0
- app/shared/core/project/loader/loader_parts/runtime.py +125 -0
- app/shared/core/project/loader/types.py +246 -0
- app/shared/core/project/manifest/coverage.py +391 -0
- app/shared/core/project/manifest/reader.py +260 -0
- app/shared/core/project/manifest/types.py +91 -0
- app/shared/core/project/manifest/types_parts/__init__.py +60 -0
- app/shared/core/project/manifest/types_parts/constants.py +40 -0
- app/shared/core/project/manifest/types_parts/data_source.py +68 -0
- app/shared/core/project/manifest/types_parts/info.py +59 -0
- app/shared/core/project/manifest/types_parts/manifest.py +294 -0
- app/shared/core/project/manifest/types_parts/refs.py +153 -0
- app/shared/core/project/manifest/types_parts/settings.py +92 -0
- app/shared/core/project/manifest/types_parts/settings_file_processing.py +64 -0
- app/shared/core/project/manifest/types_parts/settings_script_security.py +78 -0
- app/shared/core/project/manifest/types_parts/settings_validation.py +83 -0
- app/shared/core/project/manifest/types_parts/template.py +66 -0
- app/shared/core/project/manifest/writer.py +262 -0
- app/shared/core/project/manual_data/__init__.py +27 -0
- app/shared/core/project/manual_data/types.py +75 -0
- app/shared/core/project/regex/reader.py +197 -0
- app/shared/core/project/regex/types.py +405 -0
- app/shared/core/project/regex/writer.py +123 -0
- app/shared/core/project/schema/reader.py +170 -0
- app/shared/core/project/schema/types.py +47 -0
- app/shared/core/project/schema/types_parts/__init__.py +36 -0
- app/shared/core/project/schema/types_parts/column.py +174 -0
- app/shared/core/project/schema/types_parts/column_utils.py +72 -0
- app/shared/core/project/schema/types_parts/constraint.py +165 -0
- app/shared/core/project/schema/types_parts/schema_id.py +66 -0
- app/shared/core/project/schema/types_parts/source.py +255 -0
- app/shared/core/project/schema/types_parts/source_options.py +347 -0
- app/shared/core/project/schema/types_parts/table.py +230 -0
- app/shared/core/project/schema/writer.py +139 -0
- app/shared/core/project/schema_ref_check.py +95 -0
- app/shared/core/project/template/__init__.py +27 -0
- app/shared/core/project/template/expander.py +263 -0
- app/shared/core/project/template/reader.py +120 -0
- app/shared/core/project/template/types.py +114 -0
- app/shared/core/project/transform/reader.py +76 -0
- app/shared/core/project/transform/types.py +116 -0
- app/shared/core/project/transform/writer.py +84 -0
- app/shared/core/pydantic_messages.py +59 -0
- app/shared/core/reporter/__init__.py +46 -0
- app/shared/core/reporter/reporter.py +220 -0
- app/shared/core/reporter/reporters/__init__.py +65 -0
- app/shared/core/reporter/reporters/base.py +188 -0
- app/shared/core/reporter/reporters/dingtalk_app_reporter.py +274 -0
- app/shared/core/reporter/reporters/email_reporter.py +271 -0
- app/shared/core/reporter/reporters/feishu_app_reporter.py +467 -0
- app/shared/core/reporter/reporters/local_file_reporter.py +208 -0
- app/shared/core/reporter/reporters/wecom_app_reporter.py +268 -0
- app/shared/core/utils/__init__.py +18 -0
- app/shared/core/utils/path_utils.py +60 -0
- app/shared/core/utils/regex_extract.py +113 -0
- app/shared/domain/__init__.py +114 -0
- app/shared/domain/constraints/__init__.py +76 -0
- app/shared/domain/constraints/allowed_values.py +211 -0
- app/shared/domain/constraints/base.py +141 -0
- app/shared/domain/constraints/charset.py +337 -0
- app/shared/domain/constraints/composite.py +174 -0
- app/shared/domain/constraints/condition_registry.py +130 -0
- app/shared/domain/constraints/conditional.py +629 -0
- app/shared/domain/constraints/date_logic.py +731 -0
- app/shared/domain/constraints/foreign_key.py +261 -0
- app/shared/domain/constraints/key_normalization.py +56 -0
- app/shared/domain/constraints/not_null.py +185 -0
- app/shared/domain/constraints/range.py +360 -0
- app/shared/domain/constraints/regex.py +218 -0
- app/shared/domain/constraints/scripted.py +276 -0
- app/shared/domain/constraints/unique.py +233 -0
- app/shared/domain/data_engine.py +384 -0
- app/shared/domain/data_types.py +113 -0
- app/shared/domain/data_types_parts/__init__.py +67 -0
- app/shared/domain/data_types_parts/base.py +204 -0
- app/shared/domain/data_types_parts/composite.py +250 -0
- app/shared/domain/data_types_parts/expression.py +202 -0
- app/shared/domain/data_types_parts/extracted.py +106 -0
- app/shared/domain/data_types_parts/json_types.py +234 -0
- app/shared/domain/data_types_parts/scalars.py +719 -0
- app/shared/domain/data_types_parts/sequence.py +129 -0
- app/shared/domain/dataset_schema.py +48 -0
- app/shared/domain/eval_sandbox.py +63 -0
- app/shared/domain/expression_system.py +366 -0
- app/shared/domain/regex_flags.py +53 -0
- app/shared/domain/schema/builder.py +223 -0
- app/shared/domain/schema/models.py +339 -0
- app/shared/domain/transforms/__init__.py +28 -0
- app/shared/domain/transforms/aggregate.py +141 -0
- app/shared/domain/transforms/base.py +171 -0
- app/shared/domain/transforms/cast_type.py +120 -0
- app/shared/domain/transforms/concat.py +103 -0
- app/shared/domain/transforms/conditional_assign.py +114 -0
- app/shared/domain/transforms/date_format.py +81 -0
- app/shared/domain/transforms/digits.py +77 -0
- app/shared/domain/transforms/drop_duplicates.py +94 -0
- app/shared/domain/transforms/fill_na.py +94 -0
- app/shared/domain/transforms/filter_rows.py +97 -0
- app/shared/domain/transforms/lookup.py +78 -0
- app/shared/domain/transforms/lower_case.py +70 -0
- app/shared/domain/transforms/map_value.py +90 -0
- app/shared/domain/transforms/math_expr.py +112 -0
- app/shared/domain/transforms/modulo.py +82 -0
- app/shared/domain/transforms/regex_extract.py +103 -0
- app/shared/domain/transforms/registry.py +95 -0
- app/shared/domain/transforms/replace.py +88 -0
- app/shared/domain/transforms/sort_rows.py +94 -0
- app/shared/domain/transforms/string_split.py +84 -0
- app/shared/domain/transforms/strip.py +73 -0
- app/shared/domain/transforms/substring.py +92 -0
- app/shared/domain/transforms/upper_case.py +70 -0
- app/shared/domain/transforms/weighted_sum.py +104 -0
- app/shared/domain/validation_constraints.py +69 -0
- app/shared/services/__init__.py +62 -0
- app/shared/services/ai/__init__.py +48 -0
- app/shared/services/ai/agent/__init__.py +40 -0
- app/shared/services/ai/agent/chat_tools/__init__.py +47 -0
- app/shared/services/ai/agent/chat_tools/apply_actions.py +592 -0
- app/shared/services/ai/agent/chat_tools/ask_user.py +237 -0
- app/shared/services/ai/agent/chat_tools/read_canvas.py +140 -0
- app/shared/services/ai/agent/chat_tools/read_project.py +160 -0
- app/shared/services/ai/agent/chat_tools/read_table.py +301 -0
- app/shared/services/ai/agent/chat_tools/schemas.py +105 -0
- app/shared/services/ai/agent/chat_tools/validate_table.py +131 -0
- app/shared/services/ai/agent/executor.py +554 -0
- app/shared/services/ai/agent/memory.py +256 -0
- app/shared/services/ai/agent/planner.py +282 -0
- app/shared/services/ai/agent/tool_registry.py +296 -0
- app/shared/services/ai/agent/tools/__init__.py +32 -0
- app/shared/services/ai/agent/tools/config_generate.py +133 -0
- app/shared/services/ai/agent/tools/config_refine.py +109 -0
- app/shared/services/ai/agent/tools/config_validate.py +403 -0
- app/shared/services/ai/agent/tools/merge_results.py +136 -0
- app/shared/services/ai/agent/tools/plan_chunks.py +79 -0
- app/shared/services/ai/agent/tools/script_parse.py +359 -0
- app/shared/services/ai/agent/types.py +138 -0
- app/shared/services/ai/chat_agent_runner.py +596 -0
- app/shared/services/ai/chat_orchestrator.py +725 -0
- app/shared/services/ai/failure_messages.py +65 -0
- app/shared/services/ai/job_storage.py +274 -0
- app/shared/services/ai/migrate_service.py +550 -0
- app/shared/services/ai/streaming/__init__.py +65 -0
- app/shared/services/ai/streaming/event_journal.py +197 -0
- app/shared/services/ai/streaming/orchestrator.py +262 -0
- app/shared/services/ai/streaming/pending_interaction_store.py +227 -0
- app/shared/services/ai/streaming/sse_response.py +148 -0
- app/shared/services/ai/streaming/types.py +73 -0
- app/shared/services/ai/types.py +213 -0
- app/shared/services/ai/utils.py +322 -0
- app/shared/services/diff/config_diff.py +320 -0
- app/shared/services/hardware.py +237 -0
- app/shared/services/llm/__init__.py +62 -0
- app/shared/services/llm/actions/__init__.py +42 -0
- app/shared/services/llm/actions/_canvas_validator.py +147 -0
- app/shared/services/llm/actions/_constraint_validator.py +401 -0
- app/shared/services/llm/actions/_regex_validator.py +85 -0
- app/shared/services/llm/actions/_schema_validator.py +82 -0
- app/shared/services/llm/actions/_settings_validator.py +76 -0
- app/shared/services/llm/actions/_transform_validator.py +78 -0
- app/shared/services/llm/actions/action_handlers.py +400 -0
- app/shared/services/llm/actions/action_parser.py +63 -0
- app/shared/services/llm/actions/action_processor.py +468 -0
- app/shared/services/llm/actions/action_validator.py +311 -0
- app/shared/services/llm/actions/diff_compute.py +195 -0
- app/shared/services/llm/actions/regex_handlers.py +284 -0
- app/shared/services/llm/actions/registry.py +336 -0
- app/shared/services/llm/actions/schema_handlers.py +335 -0
- app/shared/services/llm/actions/settings_handlers.py +180 -0
- app/shared/services/llm/actions/specs.py +259 -0
- app/shared/services/llm/actions/transform_handlers.py +279 -0
- app/shared/services/llm/actions/validation_types.py +147 -0
- app/shared/services/llm/cache/__init__.py +18 -0
- app/shared/services/llm/cache/response_cache.py +80 -0
- app/shared/services/llm/chat/__init__.py +35 -0
- app/shared/services/llm/chat/chat_system_prompt.py +561 -0
- app/shared/services/llm/chat/response_parser.py +296 -0
- app/shared/services/llm/config/__init__.py +45 -0
- app/shared/services/llm/config/crypto.py +135 -0
- app/shared/services/llm/config/loader.py +258 -0
- app/shared/services/llm/config/models.py +202 -0
- app/shared/services/llm/config/presets.py +128 -0
- app/shared/services/llm/config_generator.py +163 -0
- app/shared/services/llm/constraints/__init__.py +41 -0
- app/shared/services/llm/constraints/constraint_builder.py +177 -0
- app/shared/services/llm/constraints/constraint_deletion.py +84 -0
- app/shared/services/llm/constraints/constraint_id.py +174 -0
- app/shared/services/llm/constraints/frontend_instructions.py +333 -0
- app/shared/services/llm/constraints/inline_batch.py +279 -0
- app/shared/services/llm/discovery/__init__.py +35 -0
- app/shared/services/llm/discovery/scanner.py +181 -0
- app/shared/services/llm/generation/__init__.py +42 -0
- app/shared/services/llm/generation/agent_wiring.py +156 -0
- app/shared/services/llm/generation/config_builder.py +386 -0
- app/shared/services/llm/generation/errors.py +36 -0
- app/shared/services/llm/generation/existing_config.py +99 -0
- app/shared/services/llm/generation/profiler.py +279 -0
- app/shared/services/llm/generation/prompt_builder.py +199 -0
- app/shared/services/llm/generation/response_parser.py +144 -0
- app/shared/services/llm/generation/service.py +622 -0
- app/shared/services/llm/models.py +104 -0
- app/shared/services/llm/providers/__init__.py +48 -0
- app/shared/services/llm/providers/base.py +310 -0
- app/shared/services/llm/providers/cached_provider.py +90 -0
- app/shared/services/llm/providers/ollama.py +498 -0
- app/shared/services/llm/providers/openai.py +334 -0
- app/shared/services/llm/providers/registry.py +108 -0
- app/shared/services/llm/schema_resolver.py +141 -0
- app/shared/services/llm/suggestion_utils.py +219 -0
- app/shared/services/llm/validate_executor.py +163 -0
- app/shared/services/llm/yaml_io.py +406 -0
- app/shared/services/preview/__init__.py +18 -0
- app/shared/services/preview/loader.py +145 -0
- app/shared/services/preview/path_validation.py +138 -0
- app/shared/services/project_loader.py +57 -0
- app/shared/services/schema_inference.py +260 -0
- app/shared/services/schema_runtime_builder.py +120 -0
- app/shared/services/validation/__init__.py +52 -0
- app/shared/services/validation/chunked_loader.py +509 -0
- app/shared/services/validation/dag/__init__.py +35 -0
- app/shared/services/validation/dag/builder.py +141 -0
- app/shared/services/validation/dag/executor.py +225 -0
- app/shared/services/validation/dag/sorter.py +77 -0
- app/shared/services/validation/data_loader.py +231 -0
- app/shared/services/validation/engine.py +446 -0
- app/shared/services/validation/executor.py +1030 -0
- app/shared/services/validation/extractors.py +266 -0
- app/shared/services/validation/history.py +209 -0
- app/shared/services/validation/json_payload.py +136 -0
- app/shared/services/validation/loader.py +152 -0
- app/shared/services/validation/memory_monitor.py +179 -0
- app/shared/services/validation/postprocess.py +208 -0
- app/shared/services/validation/progress.py +64 -0
- app/shared/services/validation/report_export.py +222 -0
- app/shared/services/validation/resolver.py +180 -0
- app/shared/services/validation/service.py +418 -0
- app/shared/services/validation/types.py +238 -0
- app/shared/services/validation/validators/__init__.py +36 -0
- app/shared/services/validation/validators/adapter.py +182 -0
- app/shared/services/validation/validators/base.py +332 -0
- app/shared/services/validation/validators/composite.py +233 -0
- app/shared/services/validation/validators/date_logic.py +276 -0
- app/start_server.py +133 -0
- precis_cli-0.1.3.dist-info/METADATA +180 -0
- precis_cli-0.1.3.dist-info/RECORD +442 -0
- precis_cli-0.1.3.dist-info/WHEEL +5 -0
- precis_cli-0.1.3.dist-info/entry_points.txt +4 -0
- precis_cli-0.1.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2026 Precis Team
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
"""
|
|
17
|
+
@fileoverview 约束规则配置模块
|
|
18
|
+
|
|
19
|
+
功能概述:
|
|
20
|
+
- 定义 V2 约束规则的数据类型(使用 Pydantic BaseModel)
|
|
21
|
+
- 定义各种约束类型的引用结构(Refs)
|
|
22
|
+
- 提供约束配置文件的数据模型
|
|
23
|
+
|
|
24
|
+
架构设计:
|
|
25
|
+
- 类型定义层: Constraint 系统的核心数据模型层
|
|
26
|
+
- 分离设计: refs(引用区)定义约束目标,params(参数区)定义约束参数
|
|
27
|
+
- Pydantic 验证: 使用 BaseModel 进行数据验证和序列化
|
|
28
|
+
|
|
29
|
+
输入示例:
|
|
30
|
+
refs = UniqueRefs(table_id="users", column_ids=["email"])
|
|
31
|
+
constraint = ConstraintFile(
|
|
32
|
+
version=2,
|
|
33
|
+
id="unique_email",
|
|
34
|
+
type="Unique",
|
|
35
|
+
refs=refs,
|
|
36
|
+
params={},
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
输出示例:
|
|
40
|
+
yaml_data = constraint.model_dump(exclude_none=True)
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
from __future__ import annotations
|
|
44
|
+
|
|
45
|
+
from typing import Any, Literal
|
|
46
|
+
|
|
47
|
+
from pydantic import BaseModel, Field
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class UniqueRefs(BaseModel):
|
|
51
|
+
"""
|
|
52
|
+
@classdesc 唯一性约束引用结构。
|
|
53
|
+
|
|
54
|
+
============================================================================
|
|
55
|
+
配置文件对应关系 (这个类对应哪种配置文件)
|
|
56
|
+
============================================================================
|
|
57
|
+
本类对应 *.constraint.yaml 中 Unique 约束的 refs 字段:
|
|
58
|
+
|
|
59
|
+
```yaml
|
|
60
|
+
# *.constraint.yaml - Unique 约束示例
|
|
61
|
+
refs:
|
|
62
|
+
table_id: users # 目标表的 ID
|
|
63
|
+
column_ids: [email] # 目标列 ID 列表(单列)
|
|
64
|
+
# 或多列组合唯一:
|
|
65
|
+
refs:
|
|
66
|
+
table_id: orders
|
|
67
|
+
column_ids: [user_id, order_date] # 组合唯一
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
============================================================================
|
|
71
|
+
字段映射说明 (YAML 如何变成 Python 对象)
|
|
72
|
+
============================================================================
|
|
73
|
+
| YAML 字段 | Python 属性 | 数据类型 | 说明 | 示例值 |
|
|
74
|
+
|----------|------------|---------|------|-------|
|
|
75
|
+
| table_id | table_id | str | 目标表的 ID | "users" |
|
|
76
|
+
| column_ids | column_ids | List[str] | 目标列 ID 列表 | ["email"] |
|
|
77
|
+
|
|
78
|
+
============================================================================
|
|
79
|
+
业务场景 (这个类在什么地方被使用)
|
|
80
|
+
============================================================================
|
|
81
|
+
- 场景1: 确保邮箱唯一
|
|
82
|
+
约束类型为 Unique,refs 指定表和列,系统校验时检查该列是否有重复值。
|
|
83
|
+
|
|
84
|
+
- 场景2: 组合唯一
|
|
85
|
+
如订单表中 (user_id, order_date) 组合必须唯一。
|
|
86
|
+
|
|
87
|
+
============================================================================
|
|
88
|
+
使用示例
|
|
89
|
+
============================================================================
|
|
90
|
+
【创建实例】
|
|
91
|
+
```python
|
|
92
|
+
from app.shared.core.project.constraint.types import UniqueRefs
|
|
93
|
+
|
|
94
|
+
refs = UniqueRefs(
|
|
95
|
+
table_id="users",
|
|
96
|
+
column_ids=["email"]
|
|
97
|
+
)
|
|
98
|
+
```
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
# 目标表的 ID(对应 schema 文件中的 table.id)
|
|
102
|
+
table_id: str
|
|
103
|
+
# 目标列 ID 列表(对应 schema 文件中的 column.id)
|
|
104
|
+
# 多列组合表示这些列的组合值必须唯一
|
|
105
|
+
column_ids: list[str]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class NotNullRefs(BaseModel):
|
|
109
|
+
"""
|
|
110
|
+
@classdesc 非空约束引用结构。
|
|
111
|
+
|
|
112
|
+
============================================================================
|
|
113
|
+
配置文件对应关系 (这个类对应哪种配置文件)
|
|
114
|
+
============================================================================
|
|
115
|
+
本类对应 *.constraint.yaml 中 NotNull 约束的 refs 字段:
|
|
116
|
+
|
|
117
|
+
```yaml
|
|
118
|
+
# *.constraint.yaml - NotNull 约束示例
|
|
119
|
+
refs:
|
|
120
|
+
table_id: users # 目标表的 ID
|
|
121
|
+
column_id: email # 目标列 ID
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
============================================================================
|
|
125
|
+
字段映射说明
|
|
126
|
+
============================================================================
|
|
127
|
+
| YAML 字段 | Python 属性 | 数据类型 | 说明 |
|
|
128
|
+
|----------|------------|---------|------|
|
|
129
|
+
| table_id | table_id | str | 目标表的 ID |
|
|
130
|
+
| column_id | column_id | str | 目标列 ID |
|
|
131
|
+
|
|
132
|
+
============================================================================
|
|
133
|
+
业务场景
|
|
134
|
+
============================================================================
|
|
135
|
+
- 场景1: 确保必填字段有值
|
|
136
|
+
用户注册时,邮箱和手机号必须填写,不能为空。
|
|
137
|
+
|
|
138
|
+
:param table_id: 目标表的 ID
|
|
139
|
+
:param column_id: 目标列 ID
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
# 目标表的 ID
|
|
143
|
+
table_id: str
|
|
144
|
+
# 目标列 ID
|
|
145
|
+
column_id: str
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class AllowedValuesRefs(BaseModel):
|
|
149
|
+
"""
|
|
150
|
+
@classdesc 允许值约束引用结构。
|
|
151
|
+
|
|
152
|
+
============================================================================
|
|
153
|
+
配置文件对应关系 (这个类对应哪种配置文件)
|
|
154
|
+
============================================================================
|
|
155
|
+
本类对应 *.constraint.yaml 中 AllowedValues 约束的 refs 字段:
|
|
156
|
+
|
|
157
|
+
```yaml
|
|
158
|
+
# *.constraint.yaml - AllowedValues 约束示例
|
|
159
|
+
refs:
|
|
160
|
+
table_id: users # 目标表的 ID
|
|
161
|
+
column_id: gender # 目标列 ID
|
|
162
|
+
params:
|
|
163
|
+
allowed_values: [男, 女, 未知] # 允许值列表(在 params 中)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
============================================================================
|
|
167
|
+
业务场景
|
|
168
|
+
============================================================================
|
|
169
|
+
- 场景1: 限制枚举值
|
|
170
|
+
性别字段只能是"男"、"女"、"未知"。
|
|
171
|
+
|
|
172
|
+
:param table_id: 目标表的 ID
|
|
173
|
+
:param column_id: 目标列 ID
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
# 目标表的 ID
|
|
177
|
+
table_id: str
|
|
178
|
+
# 目标列 ID
|
|
179
|
+
column_id: str
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class ForeignKeyRefs(BaseModel):
|
|
183
|
+
"""
|
|
184
|
+
@classdesc 外键约束引用结构。
|
|
185
|
+
|
|
186
|
+
============================================================================
|
|
187
|
+
配置文件对应关系 (这个类对应哪种配置文件)
|
|
188
|
+
============================================================================
|
|
189
|
+
本类对应 *.constraint.yaml 中 ForeignKey 约束的 refs 字段:
|
|
190
|
+
|
|
191
|
+
```yaml
|
|
192
|
+
# *.constraint.yaml - ForeignKey 约束示例
|
|
193
|
+
refs:
|
|
194
|
+
from_table_id: orders # 源表 ID(包含外键的表)
|
|
195
|
+
from_column_id: user_id # 源列 ID(外键列)
|
|
196
|
+
to_table_id: users # 目标表 ID(被参照的表)
|
|
197
|
+
to_column_id: user_id # 目标列 ID(被参照的列,通常为主键)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
============================================================================
|
|
201
|
+
业务场景
|
|
202
|
+
============================================================================
|
|
203
|
+
- 场景1: 订单关联用户
|
|
204
|
+
订单表中的 user_id 必须在用户表中存在。
|
|
205
|
+
|
|
206
|
+
:param from_table_id: 源表 ID
|
|
207
|
+
:param from_column_id: 源列 ID
|
|
208
|
+
:param to_table_id: 目标表 ID
|
|
209
|
+
:param to_column_id: 目标列 ID
|
|
210
|
+
"""
|
|
211
|
+
|
|
212
|
+
# 源表 ID(包含外键的表)
|
|
213
|
+
from_table_id: str
|
|
214
|
+
# 源列 ID(外键列)
|
|
215
|
+
from_column_id: str
|
|
216
|
+
# 目标表 ID(被参照的表)
|
|
217
|
+
to_table_id: str
|
|
218
|
+
# 目标列 ID(被参照的列,通常为主键)
|
|
219
|
+
to_column_id: str
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class ConditionalIfCondition(BaseModel):
|
|
223
|
+
"""
|
|
224
|
+
@classdesc 条件约束的 IF 条件结构。
|
|
225
|
+
|
|
226
|
+
============================================================================
|
|
227
|
+
配置文件对应关系 (这个类对应哪种配置文件)
|
|
228
|
+
============================================================================
|
|
229
|
+
本类对应 *.constraint.yaml 中 Conditional 约束的 if 条件:
|
|
230
|
+
|
|
231
|
+
```yaml
|
|
232
|
+
# *.constraint.yaml - Conditional 约束示例
|
|
233
|
+
refs:
|
|
234
|
+
table_id: users
|
|
235
|
+
then_column_id: status
|
|
236
|
+
if_conditions:
|
|
237
|
+
- if_column_id: age
|
|
238
|
+
operator: ">"
|
|
239
|
+
value: 18
|
|
240
|
+
if_logic: and
|
|
241
|
+
params:
|
|
242
|
+
then_value: "成人"
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
============================================================================
|
|
246
|
+
字段映射说明
|
|
247
|
+
============================================================================
|
|
248
|
+
| YAML 字段 | Python 属性 | 数据类型 | 说明 |
|
|
249
|
+
|----------|------------|---------|------|
|
|
250
|
+
| if_column_id | if_column_id | str | 条件判断依据的列 ID |
|
|
251
|
+
| operator | operator | str | 比较运算符 |
|
|
252
|
+
| value | value | Any | 单值比较时的值 |
|
|
253
|
+
| values | values | Optional[List[Any]] | 多值比较时的值列表 |
|
|
254
|
+
|
|
255
|
+
============================================================================
|
|
256
|
+
业务场景
|
|
257
|
+
============================================================================
|
|
258
|
+
- 场景1: 条件赋值
|
|
259
|
+
如果年龄 > 18,则状态为"成人"。
|
|
260
|
+
|
|
261
|
+
:param if_column_id: 条件判断依据的列 ID
|
|
262
|
+
:param operator: 比较运算符
|
|
263
|
+
:param value: 单值比较时的值
|
|
264
|
+
:param values: 多值比较时的值列表
|
|
265
|
+
"""
|
|
266
|
+
|
|
267
|
+
# 条件判断依据的列 ID
|
|
268
|
+
if_column_id: str
|
|
269
|
+
# 比较运算符,支持 eq/ne/in/greater_than/less_than 等
|
|
270
|
+
operator: str = Field(..., description="eq/ne/in/greater_than 等")
|
|
271
|
+
# 单值比较时的值
|
|
272
|
+
value: Any = None
|
|
273
|
+
# 多值比较时的值列表(用于 in 运算符)
|
|
274
|
+
values: list[Any] | None = None
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class ConditionalRefs(BaseModel):
|
|
278
|
+
"""
|
|
279
|
+
@classdesc 条件约束引用结构。
|
|
280
|
+
|
|
281
|
+
============================================================================
|
|
282
|
+
配置文件对应关系 (这个类对应哪种配置文件)
|
|
283
|
+
============================================================================
|
|
284
|
+
本类对应 *.constraint.yaml 中 Conditional 约束的 refs 字段:
|
|
285
|
+
|
|
286
|
+
```yaml
|
|
287
|
+
# *.constraint.yaml - Conditional 约束示例
|
|
288
|
+
refs:
|
|
289
|
+
table_id: users
|
|
290
|
+
then_column_id: status
|
|
291
|
+
if_conditions:
|
|
292
|
+
- if_column_id: age
|
|
293
|
+
operator: ">"
|
|
294
|
+
value: 18
|
|
295
|
+
if_logic: and
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
============================================================================
|
|
299
|
+
业务场景
|
|
300
|
+
============================================================================
|
|
301
|
+
- 场景1: 条件更新
|
|
302
|
+
当满足某些条件时,对特定列应用约束规则。
|
|
303
|
+
|
|
304
|
+
:param table_id: 目标表 ID
|
|
305
|
+
:param then_column_id: 需要应用约束的列 ID
|
|
306
|
+
:param if_conditions: IF 条件列表
|
|
307
|
+
:param if_logic: 条件逻辑:and/or
|
|
308
|
+
"""
|
|
309
|
+
|
|
310
|
+
# 目标表 ID
|
|
311
|
+
table_id: str
|
|
312
|
+
# THEN 部分的列 ID(需要应用约束的列)
|
|
313
|
+
then_column_id: str
|
|
314
|
+
# IF 条件列表(支持多个条件组合)
|
|
315
|
+
if_conditions: list[ConditionalIfCondition] = Field(default_factory=list)
|
|
316
|
+
# 条件逻辑:and(全部满足)/ or(任一满足)
|
|
317
|
+
if_logic: Literal["and", "or"] = "and"
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
class ScriptedRefs(BaseModel):
|
|
321
|
+
"""
|
|
322
|
+
@classdesc 脚本约束引用结构。
|
|
323
|
+
|
|
324
|
+
============================================================================
|
|
325
|
+
配置文件对应关系 (这个类对应哪种配置文件)
|
|
326
|
+
============================================================================
|
|
327
|
+
本类对应 *.constraint.yaml 中 Scripted 约束的 refs 字段:
|
|
328
|
+
|
|
329
|
+
```yaml
|
|
330
|
+
# *.constraint.yaml - Scripted 约束示例
|
|
331
|
+
refs:
|
|
332
|
+
table_id: users
|
|
333
|
+
column_id: email
|
|
334
|
+
params:
|
|
335
|
+
expression: "validate_email(value)" # 脚本表达式
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
============================================================================
|
|
339
|
+
业务场景
|
|
340
|
+
============================================================================
|
|
341
|
+
- 场景1: 自定义验证逻辑
|
|
342
|
+
使用脚本表达式进行复杂的数据验证。
|
|
343
|
+
|
|
344
|
+
:param table_id: 目标表 ID
|
|
345
|
+
:param column_id: 目标列 ID(可选,若不指定则对整行数据执行脚本)
|
|
346
|
+
"""
|
|
347
|
+
|
|
348
|
+
# 目标表 ID
|
|
349
|
+
table_id: str
|
|
350
|
+
# 目标列 ID(可选,若不指定则对整行数据执行脚本)
|
|
351
|
+
column_id: str | None = None
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
class RangeRefs(BaseModel):
|
|
355
|
+
"""
|
|
356
|
+
@classdesc 区间约束引用结构。
|
|
357
|
+
|
|
358
|
+
============================================================================
|
|
359
|
+
配置文件对应关系 (这个类对应哪种配置文件)
|
|
360
|
+
============================================================================
|
|
361
|
+
本类对应 *.constraint.yaml 中 Range 约束的 refs 字段:
|
|
362
|
+
|
|
363
|
+
```yaml
|
|
364
|
+
# *.constraint.yaml - Range 约束示例
|
|
365
|
+
refs:
|
|
366
|
+
table_id: products
|
|
367
|
+
column_id: price
|
|
368
|
+
params:
|
|
369
|
+
min: 0
|
|
370
|
+
max: 10000
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
============================================================================
|
|
374
|
+
业务场景
|
|
375
|
+
============================================================================
|
|
376
|
+
- 场景1: 数值范围校验
|
|
377
|
+
商品价格必须在 0-10000 之间。
|
|
378
|
+
|
|
379
|
+
:param table_id: 目标表 ID
|
|
380
|
+
:param column_id: 目标列 ID
|
|
381
|
+
"""
|
|
382
|
+
|
|
383
|
+
# 目标表 ID
|
|
384
|
+
table_id: str
|
|
385
|
+
# 目标列 ID
|
|
386
|
+
column_id: str
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class DateLogicRefs(BaseModel):
|
|
390
|
+
"""
|
|
391
|
+
@classdesc 日期逻辑约束引用结构。
|
|
392
|
+
|
|
393
|
+
============================================================================
|
|
394
|
+
配置文件对应关系 (这个类对应哪种配置文件)
|
|
395
|
+
============================================================================
|
|
396
|
+
本类对应 *.constraint.yaml 中 DateLogic 约束的 refs 字段:
|
|
397
|
+
|
|
398
|
+
```yaml
|
|
399
|
+
# *.constraint.yaml - DateLogic 约束示例
|
|
400
|
+
refs:
|
|
401
|
+
table_id: users
|
|
402
|
+
column_id: birth_date
|
|
403
|
+
params:
|
|
404
|
+
logic_mode: compare # compare=比较模式, calculation=计算模式
|
|
405
|
+
compare_op: gt # gt/lt/eq/gte/lte/range
|
|
406
|
+
reference_date: "2000-01-01"
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
============================================================================
|
|
410
|
+
业务场景
|
|
411
|
+
============================================================================
|
|
412
|
+
- 场景1: 日期比较
|
|
413
|
+
出生日期必须晚于 2000-01-01。
|
|
414
|
+
|
|
415
|
+
- 场景2: 日期范围
|
|
416
|
+
订单日期必须在 2024-01-01 到 2024-12-31 之间。
|
|
417
|
+
此时 params 中 compare_op 为 "range",并需要 reference_date_end / reference_column_end。
|
|
418
|
+
|
|
419
|
+
:param table_id: 目标表 ID
|
|
420
|
+
:param column_id: 目标列 ID
|
|
421
|
+
"""
|
|
422
|
+
|
|
423
|
+
# 目标表 ID
|
|
424
|
+
table_id: str
|
|
425
|
+
# 目标列 ID
|
|
426
|
+
column_id: str
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
class CharsetRefs(BaseModel):
|
|
430
|
+
"""
|
|
431
|
+
@classdesc 字符集约束引用结构。
|
|
432
|
+
|
|
433
|
+
============================================================================
|
|
434
|
+
配置文件对应关系 (这个类对应哪种配置文件)
|
|
435
|
+
============================================================================
|
|
436
|
+
本类对应 *.constraint.yaml 中 Charset 约束的 refs 字段:
|
|
437
|
+
|
|
438
|
+
```yaml
|
|
439
|
+
# *.constraint.yaml - Charset 约束示例
|
|
440
|
+
refs:
|
|
441
|
+
table_id: users
|
|
442
|
+
column_id: name
|
|
443
|
+
params:
|
|
444
|
+
encoding: utf-8
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
============================================================================
|
|
448
|
+
业务场景
|
|
449
|
+
============================================================================
|
|
450
|
+
- 场景1: 字符集检测
|
|
451
|
+
检测列数据是否符合指定字符集编码。
|
|
452
|
+
|
|
453
|
+
:param table_id: 目标表 ID
|
|
454
|
+
:param column_id: 目标列 ID
|
|
455
|
+
"""
|
|
456
|
+
|
|
457
|
+
# 目标表 ID
|
|
458
|
+
table_id: str
|
|
459
|
+
# 目标列 ID
|
|
460
|
+
column_id: str
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2026 Precis Team
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
"""
|
|
17
|
+
@fileoverview 约束类型兼容层模块
|
|
18
|
+
|
|
19
|
+
功能概述:
|
|
20
|
+
- 向后兼容的重新导出入口
|
|
21
|
+
- 将 constraint.types 子包的内容暴露到 app.shared.core.project.constraint.types 命名空间
|
|
22
|
+
|
|
23
|
+
架构设计:
|
|
24
|
+
- 兼容层模式:保持历史导入路径稳定
|
|
25
|
+
- 无逻辑:仅重新导出,不定义新类型
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from .types import * # noqa: F401,F403
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2026 Precis Team
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
"""
|
|
17
|
+
@fileoverview 约束规则写入模块
|
|
18
|
+
|
|
19
|
+
功能概述:
|
|
20
|
+
- 写入 *.constraint.yaml 文件
|
|
21
|
+
- 将程序中的 ConstraintFile 对象持久化为 YAML 格式配置文件
|
|
22
|
+
|
|
23
|
+
架构设计:
|
|
24
|
+
- 写入层: 负责将配置对象序列化并写入文件系统
|
|
25
|
+
- 简洁输出: 使用 model_dump(exclude_none=True) 排除 None 字段
|
|
26
|
+
- 读写配合: 与 reader.py 配合实现约束配置全生命周期管理
|
|
27
|
+
|
|
28
|
+
输入示例:
|
|
29
|
+
constraint = ConstraintFile(
|
|
30
|
+
version=2,
|
|
31
|
+
id="not_null_email",
|
|
32
|
+
type="NotNull",
|
|
33
|
+
enabled=True,
|
|
34
|
+
refs={"table_id": "users", "column_id": "email"},
|
|
35
|
+
params={},
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
输出示例:
|
|
39
|
+
save_constraint(constraint, "constraints/email.constraint.yaml")
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from __future__ import annotations
|
|
43
|
+
|
|
44
|
+
from pathlib import Path
|
|
45
|
+
from typing import TYPE_CHECKING
|
|
46
|
+
|
|
47
|
+
# 导入底层的 YAML 写入工具
|
|
48
|
+
from app.shared.core.io.yaml import write_yaml_atomic
|
|
49
|
+
|
|
50
|
+
# 导入约束配置的数据模型
|
|
51
|
+
from .types import ConstraintFile
|
|
52
|
+
|
|
53
|
+
if TYPE_CHECKING:
|
|
54
|
+
# 仅在类型检查阶段导入,避免运行时循环依赖
|
|
55
|
+
from .types import ConstraintFile
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def save_constraint(constraint: ConstraintFile, constraint_path: str | Path) -> None:
|
|
59
|
+
"""
|
|
60
|
+
@methoddesc 保存约束到 YAML 文件。
|
|
61
|
+
|
|
62
|
+
============================================================================
|
|
63
|
+
输入数据示例 (这个函数接收什么数据)
|
|
64
|
+
============================================================================
|
|
65
|
+
本函数接收 ConstraintFile 对象:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
# 创建 Unique 约束对象
|
|
69
|
+
constraint = ConstraintFile(
|
|
70
|
+
version=2,
|
|
71
|
+
id="unique_user_email",
|
|
72
|
+
type="Unique",
|
|
73
|
+
enabled=True,
|
|
74
|
+
description="用户邮箱必须唯一",
|
|
75
|
+
refs={
|
|
76
|
+
"table_id": "users",
|
|
77
|
+
"column_ids": ["email"]
|
|
78
|
+
},
|
|
79
|
+
params={}
|
|
80
|
+
)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
写入后的 YAML 文件内容:
|
|
84
|
+
|
|
85
|
+
```yaml
|
|
86
|
+
# 约束规则配置文件 (*.constraint.yaml)
|
|
87
|
+
version: 2
|
|
88
|
+
|
|
89
|
+
id: unique_user_email
|
|
90
|
+
|
|
91
|
+
type: Unique
|
|
92
|
+
|
|
93
|
+
enabled: true
|
|
94
|
+
|
|
95
|
+
description: "用户邮箱必须唯一"
|
|
96
|
+
|
|
97
|
+
refs:
|
|
98
|
+
table_id: users
|
|
99
|
+
column_ids:
|
|
100
|
+
- email
|
|
101
|
+
|
|
102
|
+
params: {}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
============================================================================
|
|
106
|
+
业务场景 (什么情况下会调用这个函数)
|
|
107
|
+
============================================================================
|
|
108
|
+
- 场景1: 用户通过 UI 创建约束
|
|
109
|
+
用户在约束创建页面填写完配置表单后,点击保存按钮。
|
|
110
|
+
系统将表单数据构建为 ConstraintFile 对象,然后调用 save_constraint
|
|
111
|
+
将其写入到 constraints/ 目录下的 *.constraint.yaml 文件。
|
|
112
|
+
|
|
113
|
+
- 场景2: 用户通过 UI 编辑约束
|
|
114
|
+
用户修改现有约束的配置后,点击保存按钮。
|
|
115
|
+
系统更新 ConstraintFile 对象并写回原文件。
|
|
116
|
+
|
|
117
|
+
- 场景3: 导入约束配置
|
|
118
|
+
用户导入约束配置文件时,系统将其写入项目目录。
|
|
119
|
+
|
|
120
|
+
============================================================================
|
|
121
|
+
数据流 (输入如何变成输出)
|
|
122
|
+
============================================================================
|
|
123
|
+
输入参数:
|
|
124
|
+
- constraint: ConstraintFile,约束配置对象
|
|
125
|
+
示例值: ConstraintFile(id="unique_user_email", type="Unique", ...)
|
|
126
|
+
- constraint_path: str | Path,目标文件路径
|
|
127
|
+
示例值: "constraints/unique_user_email.constraint.yaml"
|
|
128
|
+
|
|
129
|
+
处理步骤:
|
|
130
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
131
|
+
│ Step 1: 路径标准化 │
|
|
132
|
+
├─────────────────────────────────────────────────────────────┤
|
|
133
|
+
│ 输入: 字符串或 Path 对象 │
|
|
134
|
+
│ 操作: 转换为 Path 对象 │
|
|
135
|
+
│ 输出: Path 对象 │
|
|
136
|
+
└─────────────────────────────────────────────────────────────┘
|
|
137
|
+
|
|
138
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
139
|
+
│ Step 2: 对象序列化为字典 │
|
|
140
|
+
├─────────────────────────────────────────────────────────────┤
|
|
141
|
+
│ 输入: ConstraintFile 对象 │
|
|
142
|
+
│ 操作: 使用 model_dump(exclude_none=True) 转换为字典 │
|
|
143
|
+
│ exclude_none=True 排除值为 None 的可选字段 │
|
|
144
|
+
│ 输出: Dict 对象 │
|
|
145
|
+
└─────────────────────────────────────────────────────────────┘
|
|
146
|
+
|
|
147
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
148
|
+
│ Step 3: 写入 YAML 文件 │
|
|
149
|
+
├─────────────────────────────────────────────────────────────┤
|
|
150
|
+
│ 输入: 字典对象 │
|
|
151
|
+
│ 操作: 调用 write_yaml 写入文件 │
|
|
152
|
+
│ 输出: (无返回值,文件已写入) │
|
|
153
|
+
└─────────────────────────────────────────────────────────────┘
|
|
154
|
+
|
|
155
|
+
最终输出: None (无返回值,写入成功后直接返回)
|
|
156
|
+
|
|
157
|
+
============================================================================
|
|
158
|
+
异常处理 (可能出什么问题)
|
|
159
|
+
============================================================================
|
|
160
|
+
| 异常类型 | 触发条件 | 处理方式 |
|
|
161
|
+
|---------|---------|---------|
|
|
162
|
+
| PermissionError | 没有写入权限 | 检查文件/目录权限 |
|
|
163
|
+
| FileNotFoundError | 父目录不存在 | 检查路径有效性 |
|
|
164
|
+
|
|
165
|
+
:param constraint: ConstraintFile 对象,包含完整的约束配置数据
|
|
166
|
+
:param constraint_path: 保存路径,可以是字符串或 Path 对象
|
|
167
|
+
:return: 无返回值(写入文件后直接返回)
|
|
168
|
+
:raises IOError: 文件写入失败时由 write_yaml 抛出
|
|
169
|
+
|
|
170
|
+
示例:
|
|
171
|
+
constraint = ConstraintFile(
|
|
172
|
+
id="unique_user_email",
|
|
173
|
+
type="Unique",
|
|
174
|
+
refs={"table_id": "users", "column_ids": ["email"]},
|
|
175
|
+
params={}
|
|
176
|
+
)
|
|
177
|
+
save_constraint(constraint, "/project/constraints/unique_user_email.constraint.yaml")
|
|
178
|
+
"""
|
|
179
|
+
# 步骤1:将 ConstraintFile 对象转换为字典,exclude_none=True 排除空值字段
|
|
180
|
+
# 步骤2:调用底层 YAML 写入函数将数据持久化到文件(write_yaml_atomic 内部处理字符串/Path 路径)
|
|
181
|
+
write_yaml_atomic(Path(constraint_path), constraint.model_dump(exclude_none=True))
|