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,123 @@
|
|
|
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
|
+
- 写入 *.regex.yaml 文件
|
|
21
|
+
- 优化字段顺序,提升 YAML 可读性
|
|
22
|
+
|
|
23
|
+
架构设计:
|
|
24
|
+
- 唯一入口: 封装 Pydantic 模型到 YAML 文件的转换逻辑
|
|
25
|
+
- 字段排序: 按重要程度分组(核心 / 可选 / 内部)
|
|
26
|
+
- 一致性: 与 schema/writer.py 保持一致的排序策略
|
|
27
|
+
|
|
28
|
+
输入示例:
|
|
29
|
+
node = RegexNodeFile(
|
|
30
|
+
version=2,
|
|
31
|
+
id="email",
|
|
32
|
+
name="邮箱校验",
|
|
33
|
+
pattern="^[\\w\\.-]+@[\\w\\.-]+\\.\\w+$",
|
|
34
|
+
match_mode="full",
|
|
35
|
+
enabled=True,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
输出示例:
|
|
39
|
+
save_regex_node(node, "regex/email.regex.yaml")
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from __future__ import annotations
|
|
43
|
+
|
|
44
|
+
from pathlib import Path
|
|
45
|
+
from typing import TYPE_CHECKING
|
|
46
|
+
|
|
47
|
+
from app.shared.core.io.yaml import write_yaml_atomic
|
|
48
|
+
|
|
49
|
+
from .types import RegexNodeFile
|
|
50
|
+
|
|
51
|
+
if TYPE_CHECKING:
|
|
52
|
+
from .types import RegexNodeFile
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def save_regex_node(regex_node: RegexNodeFile, regex_path: str | Path) -> None:
|
|
56
|
+
"""
|
|
57
|
+
@methoddesc 保存正则节点到 YAML 文件。
|
|
58
|
+
|
|
59
|
+
字段顺序优化(按重要程度分组):
|
|
60
|
+
L1(核心):name, description, pattern/uses_pattern
|
|
61
|
+
L2(可选):enabled, match_mode
|
|
62
|
+
L3(内部):version, id, _internal
|
|
63
|
+
|
|
64
|
+
处理流程:
|
|
65
|
+
1. 将 RegexNodeFile 对象序列化为字典(model_dump)
|
|
66
|
+
2. 按重要程度重新组织字段顺序(其余字段追加在末尾,确保不丢失)
|
|
67
|
+
3. 写入 YAML 文件
|
|
68
|
+
|
|
69
|
+
设计说明:
|
|
70
|
+
历史版本曾用手动白名单挑字段,导致新增字段(如 source_ref、input_from_node、
|
|
71
|
+
rules 等)被静默丢弃。现改为「全量保留 + 顺序优化」:先 dump 全部字段,
|
|
72
|
+
再按指定顺序重排,未在顺序表中的字段自动追加到末尾,从根本上避免丢字段。
|
|
73
|
+
|
|
74
|
+
:param regex_node: RegexNodeFile 对象,包含正则节点的完整配置
|
|
75
|
+
:param regex_path: 保存路径,可以是相对路径或绝对路径
|
|
76
|
+
:raises IOError: 文件写入失败时抛出
|
|
77
|
+
"""
|
|
78
|
+
# 步骤1:将 Pydantic 模型序列化为字典(包含全部字段)
|
|
79
|
+
data = regex_node.model_dump()
|
|
80
|
+
|
|
81
|
+
# 步骤2:按重要程度重排字段顺序
|
|
82
|
+
# 核心字段在前,其余字段追加其后,保证不丢失任何字段
|
|
83
|
+
ordered_keys = [
|
|
84
|
+
# L1 - 核心字段(用户最常编辑)
|
|
85
|
+
"name",
|
|
86
|
+
"description",
|
|
87
|
+
"pattern",
|
|
88
|
+
"uses_pattern",
|
|
89
|
+
"pattern_overrides",
|
|
90
|
+
# L2 - 可选配置(功能开关和模式)
|
|
91
|
+
"enabled",
|
|
92
|
+
"match_mode",
|
|
93
|
+
"case_sensitive",
|
|
94
|
+
"flags",
|
|
95
|
+
# 数据流与上游绑定(重建画布连线必需)
|
|
96
|
+
"source_ref",
|
|
97
|
+
"source_column_name",
|
|
98
|
+
"input_from_node",
|
|
99
|
+
"input_column",
|
|
100
|
+
# Extract 模式专用
|
|
101
|
+
"capture_groups",
|
|
102
|
+
"output_columns",
|
|
103
|
+
# 前端扩展结构
|
|
104
|
+
"parameters",
|
|
105
|
+
"rules",
|
|
106
|
+
# L3 - 内部字段(开发者/系统使用)
|
|
107
|
+
"version",
|
|
108
|
+
"id",
|
|
109
|
+
"_internal",
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
ordered: dict[str, object] = {}
|
|
113
|
+
# 先按指定顺序写入已知字段
|
|
114
|
+
for key in ordered_keys:
|
|
115
|
+
if key in data:
|
|
116
|
+
ordered[key] = data[key]
|
|
117
|
+
# 再追加未在顺序表中的字段(未来新增字段自动保留,防止再次丢字段)
|
|
118
|
+
for key, value in data.items():
|
|
119
|
+
if key not in ordered:
|
|
120
|
+
ordered[key] = value
|
|
121
|
+
|
|
122
|
+
# 步骤3:调用底层 YAML 写入工具
|
|
123
|
+
write_yaml_atomic(Path(regex_path), ordered)
|
|
@@ -0,0 +1,170 @@
|
|
|
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
|
+
- 从 *.schema.yaml 文件读取表结构配置
|
|
21
|
+
- 返回 core 层的 TableSchemaFile 配置对象
|
|
22
|
+
- 运行时 TableSchema 对象的构建已迁移至 services.schema_runtime_builder,
|
|
23
|
+
以避免 core 层反向依赖 domain 层。
|
|
24
|
+
|
|
25
|
+
架构设计:
|
|
26
|
+
- 单一职责: 仅负责 Schema 文件读取和基础配置解析
|
|
27
|
+
- 依赖注入: 使用 YAML 读取工具进行文件解析
|
|
28
|
+
- 类型安全: 使用 Pydantic 模型验证确保配置有效
|
|
29
|
+
|
|
30
|
+
输入示例:
|
|
31
|
+
# users.schema.yaml
|
|
32
|
+
version: 2
|
|
33
|
+
id: users
|
|
34
|
+
name: users
|
|
35
|
+
source:
|
|
36
|
+
mode: relative_file
|
|
37
|
+
path: data/users.xlsx
|
|
38
|
+
columns:
|
|
39
|
+
- id: user_id
|
|
40
|
+
name: user_id
|
|
41
|
+
type: string
|
|
42
|
+
|
|
43
|
+
输出示例:
|
|
44
|
+
schema_file = load_schema("schemas/users.schema.yaml")
|
|
45
|
+
# schema_file 是 TableSchemaFile 对象,可交由 services 层转换为运行时对象
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
from __future__ import annotations
|
|
49
|
+
|
|
50
|
+
from pathlib import Path
|
|
51
|
+
|
|
52
|
+
from pydantic import ValidationError
|
|
53
|
+
|
|
54
|
+
from app.shared.core.io.yaml import read_yaml
|
|
55
|
+
|
|
56
|
+
from .types import TableSchemaFile
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def load_schema(schema_path: str | Path) -> TableSchemaFile:
|
|
60
|
+
"""
|
|
61
|
+
@methoddesc 从 YAML 文件加载表结构配置。
|
|
62
|
+
|
|
63
|
+
============================================================================
|
|
64
|
+
配置文件示例 (本函数处理的配置文件长这样)
|
|
65
|
+
============================================================================
|
|
66
|
+
本函数处理以下格式的 *.schema.yaml 文件:
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
# ============================================================
|
|
70
|
+
# 表结构配置文件 (*.schema.yaml)
|
|
71
|
+
# ============================================================
|
|
72
|
+
|
|
73
|
+
version: 2
|
|
74
|
+
|
|
75
|
+
id: users
|
|
76
|
+
|
|
77
|
+
name: users
|
|
78
|
+
|
|
79
|
+
source:
|
|
80
|
+
mode: relative_file
|
|
81
|
+
path: data/users.xlsx
|
|
82
|
+
sheet: Sheet1
|
|
83
|
+
header_row: 0
|
|
84
|
+
|
|
85
|
+
columns:
|
|
86
|
+
- id: user_id
|
|
87
|
+
name: user_id
|
|
88
|
+
type: string
|
|
89
|
+
primary_key: true
|
|
90
|
+
expand: false
|
|
91
|
+
- id: email
|
|
92
|
+
name: email
|
|
93
|
+
type: string
|
|
94
|
+
primary_key: false
|
|
95
|
+
|
|
96
|
+
constraints:
|
|
97
|
+
- id: email_notnull
|
|
98
|
+
type: NotNull
|
|
99
|
+
column: email
|
|
100
|
+
enabled: true
|
|
101
|
+
|
|
102
|
+
script_checks: []
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
============================================================================
|
|
106
|
+
业务场景 (什么情况下会调用这个函数)
|
|
107
|
+
============================================================================
|
|
108
|
+
- 场景1: 加载表结构进行数据校验
|
|
109
|
+
系统需要读取表结构配置来获取列定义和数据源信息。
|
|
110
|
+
|
|
111
|
+
- 场景2: 构建运行时数据模型
|
|
112
|
+
需要将配置文件转换为运行时对象供校验引擎使用。
|
|
113
|
+
运行时转换请使用 app.shared.services.schema_runtime_builder。
|
|
114
|
+
|
|
115
|
+
============================================================================
|
|
116
|
+
数据流 (输入如何变成输出)
|
|
117
|
+
============================================================================
|
|
118
|
+
输入参数:
|
|
119
|
+
- schema_path: str | Path,schema 文件路径
|
|
120
|
+
示例值: "schemas/users.schema.yaml"
|
|
121
|
+
|
|
122
|
+
处理步骤:
|
|
123
|
+
路径标准化 -> YAML 解析 -> Pydantic 验证 -> TableSchemaFile 对象
|
|
124
|
+
|
|
125
|
+
最终输出: TableSchemaFile 对象
|
|
126
|
+
示例:
|
|
127
|
+
schema = load_schema("schemas/users.schema.yaml")
|
|
128
|
+
print(schema.name) # 输出: users
|
|
129
|
+
print(len(schema.columns)) # 输出: 2
|
|
130
|
+
|
|
131
|
+
============================================================================
|
|
132
|
+
异常处理
|
|
133
|
+
============================================================================
|
|
134
|
+
| 异常类型 | 触发条件 | 处理方式 |
|
|
135
|
+
|---------|---------|---------|
|
|
136
|
+
| ValueError | 文件不存在 | 检查文件路径 |
|
|
137
|
+
| ValueError | YAML 格式错误 | 验证 YAML 文件 |
|
|
138
|
+
| ValueError | 验证失败 | 检查配置格式 |
|
|
139
|
+
|
|
140
|
+
:param schema_path: schema 文件路径
|
|
141
|
+
:return: TableSchemaFile 对象
|
|
142
|
+
:raises ValueError: 文件不存在或格式错误
|
|
143
|
+
"""
|
|
144
|
+
path = Path(schema_path)
|
|
145
|
+
raw = read_yaml(path)
|
|
146
|
+
|
|
147
|
+
try:
|
|
148
|
+
return TableSchemaFile.model_validate(raw)
|
|
149
|
+
except ValidationError as e:
|
|
150
|
+
raise ValueError(f"schema 校验失败: {path}\n{e}") from e
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def schema_column_name_by_id(schema: TableSchemaFile) -> dict[str, str]:
|
|
154
|
+
"""
|
|
155
|
+
@methoddesc 构建列 ID 到列名的映射。
|
|
156
|
+
|
|
157
|
+
:param schema: TableSchemaFile 对象
|
|
158
|
+
:return: Dict[str, str],列 ID -> 列名 映射
|
|
159
|
+
"""
|
|
160
|
+
return {c.id: c.name for c in schema.columns if c.id is not None}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def schema_column_id_by_name(schema: TableSchemaFile) -> dict[str, str]:
|
|
164
|
+
"""
|
|
165
|
+
@methoddesc 构建列名到列 ID 的映射。
|
|
166
|
+
|
|
167
|
+
:param schema: TableSchemaFile 对象
|
|
168
|
+
:return: Dict[str, str],列名 -> 列 ID 映射
|
|
169
|
+
"""
|
|
170
|
+
return {c.name: c.id for c in schema.columns if c.id is not None}
|
|
@@ -0,0 +1,47 @@
|
|
|
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
|
+
"""@fileoverview Schema 类型统一导出模块
|
|
17
|
+
|
|
18
|
+
功能概述:
|
|
19
|
+
- 聚合导出 Schema 相关核心类型(列定义、表结构、数据源等)
|
|
20
|
+
- 提供 V2 版本别名及 source 标准化工具函数
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from app.shared.core.project.schema.types_parts.column import ColumnSpec, ExtractedSpec
|
|
26
|
+
from app.shared.core.project.schema.types_parts.constraint import ConstraintItem
|
|
27
|
+
from app.shared.core.project.schema.types_parts.schema_id import normalize_source_key
|
|
28
|
+
from app.shared.core.project.schema.types_parts.source import SourceSpec
|
|
29
|
+
from app.shared.core.project.schema.types_parts.table import TableSchemaFile
|
|
30
|
+
|
|
31
|
+
SourceSpecV2 = SourceSpec
|
|
32
|
+
ColumnSpecV2 = ColumnSpec
|
|
33
|
+
TableSchemaFileV2 = TableSchemaFile
|
|
34
|
+
ConstraintItemV2 = ConstraintItem
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
"SourceSpec",
|
|
38
|
+
"ExtractedSpec",
|
|
39
|
+
"ColumnSpec",
|
|
40
|
+
"ConstraintItem",
|
|
41
|
+
"TableSchemaFile",
|
|
42
|
+
"SourceSpecV2",
|
|
43
|
+
"ColumnSpecV2",
|
|
44
|
+
"TableSchemaFileV2",
|
|
45
|
+
"ConstraintItemV2",
|
|
46
|
+
"normalize_source_key",
|
|
47
|
+
]
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
"""@fileoverview Schema 子类型聚合导出模块
|
|
17
|
+
|
|
18
|
+
功能概述:
|
|
19
|
+
- 集中导出 Schema 各组成部分的类型定义与工具函数
|
|
20
|
+
- 提供列定义、表结构、数据源、约束项及 source 标准化工具
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from app.shared.core.project.schema.types_parts.column import ColumnSpec, ExtractedSpec
|
|
24
|
+
from app.shared.core.project.schema.types_parts.constraint import ConstraintItem
|
|
25
|
+
from app.shared.core.project.schema.types_parts.schema_id import normalize_source_key
|
|
26
|
+
from app.shared.core.project.schema.types_parts.source import SourceSpec
|
|
27
|
+
from app.shared.core.project.schema.types_parts.table import TableSchemaFile
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"ColumnSpec",
|
|
31
|
+
"ExtractedSpec",
|
|
32
|
+
"ConstraintItem",
|
|
33
|
+
"SourceSpec",
|
|
34
|
+
"TableSchemaFile",
|
|
35
|
+
"normalize_source_key",
|
|
36
|
+
]
|
|
@@ -0,0 +1,174 @@
|
|
|
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
|
+
- 定义 Schema 中的列结构 (ColumnSpec)
|
|
21
|
+
- 支持提取列类型 (ExtractedSpec) 用于正则提取
|
|
22
|
+
|
|
23
|
+
架构设计:
|
|
24
|
+
- 列 ID 自动生成: 如果未指定 id,默认使用 name
|
|
25
|
+
- 类型灵活: type 字段可以是字符串或字典
|
|
26
|
+
|
|
27
|
+
输入示例 (schema.yaml):
|
|
28
|
+
columns:
|
|
29
|
+
- id: col_1
|
|
30
|
+
name: id
|
|
31
|
+
type: integer
|
|
32
|
+
primary_key: true
|
|
33
|
+
- id: col_2
|
|
34
|
+
name: email
|
|
35
|
+
type: string
|
|
36
|
+
- id: col_3
|
|
37
|
+
name: username
|
|
38
|
+
type:
|
|
39
|
+
name: Extracted
|
|
40
|
+
source_column: email
|
|
41
|
+
extract_key: username
|
|
42
|
+
result_type: string
|
|
43
|
+
|
|
44
|
+
输出示例:
|
|
45
|
+
ColumnSpec(
|
|
46
|
+
id="col_1",
|
|
47
|
+
name="id",
|
|
48
|
+
type="integer",
|
|
49
|
+
primary_key=True,
|
|
50
|
+
expand=False
|
|
51
|
+
)
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
from __future__ import annotations
|
|
55
|
+
|
|
56
|
+
from typing import Any, Literal
|
|
57
|
+
|
|
58
|
+
from pydantic import BaseModel, Field, model_validator
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ExtractedSpec(BaseModel):
|
|
62
|
+
"""@classdesc 提取列规范
|
|
63
|
+
|
|
64
|
+
用于定义从源列中提取数据的规则。
|
|
65
|
+
常用于正则提取创建派生列。
|
|
66
|
+
|
|
67
|
+
字段说明:
|
|
68
|
+
- name: 固定为 "Extracted"
|
|
69
|
+
- source_column: 源列名
|
|
70
|
+
- extract_key: 正则表达式中的命名捕获组
|
|
71
|
+
- result_type: 提取结果的数据类型
|
|
72
|
+
|
|
73
|
+
示例:
|
|
74
|
+
# 从 email 列中提取用户名
|
|
75
|
+
name: Extracted
|
|
76
|
+
source_column: email
|
|
77
|
+
extract_key: username
|
|
78
|
+
result_type: string
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
name: Literal["Extracted"] = "Extracted"
|
|
82
|
+
source_column: str = Field(..., description="源列名(从中提取数据)")
|
|
83
|
+
extract_key: str = Field(..., description="提取键(正则命名捕获组)")
|
|
84
|
+
result_type: str | None = Field(None, description="提取结果的数据类型")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class ColumnSpec(BaseModel):
|
|
88
|
+
"""
|
|
89
|
+
@classdesc 列规范
|
|
90
|
+
|
|
91
|
+
定义表中的一列。
|
|
92
|
+
|
|
93
|
+
字段说明:
|
|
94
|
+
- id: 列的唯一标识符 (可选,默认使用 name)
|
|
95
|
+
- name: 列名 (用于显示和校验)
|
|
96
|
+
- type: 数据类型
|
|
97
|
+
- 简单类型: "integer", "string", "boolean" 等
|
|
98
|
+
- 复杂类型: ExtractedSpec 字典
|
|
99
|
+
- primary_key: 是否为主键
|
|
100
|
+
- expand: 是否展开 (用于数组/对象类型)
|
|
101
|
+
|
|
102
|
+
输入示例 (schema.yaml):
|
|
103
|
+
columns:
|
|
104
|
+
- id: col_1
|
|
105
|
+
name: id
|
|
106
|
+
type: integer
|
|
107
|
+
primary_key: true
|
|
108
|
+
- id: col_2
|
|
109
|
+
name: email
|
|
110
|
+
type: string
|
|
111
|
+
- id: col_3
|
|
112
|
+
name: username
|
|
113
|
+
type:
|
|
114
|
+
name: Extracted
|
|
115
|
+
source_column: email
|
|
116
|
+
extract_key: username
|
|
117
|
+
result_type: string
|
|
118
|
+
|
|
119
|
+
输出示例:
|
|
120
|
+
ColumnSpec(
|
|
121
|
+
id="col_1",
|
|
122
|
+
name="id",
|
|
123
|
+
type="integer",
|
|
124
|
+
primary_key=True,
|
|
125
|
+
expand=False
|
|
126
|
+
)
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
id: str | None = Field(None, description="列 ID(稳定标识,缺失时自动使用 name)")
|
|
130
|
+
name: str = Field(..., description="列名(展示/校验使用)")
|
|
131
|
+
type: Any = Field(..., description="数据类型配置:支持字符串或字典(与现有 TYPE_REGISTRY 兼容)")
|
|
132
|
+
primary_key: bool = Field(False, description="是否主键")
|
|
133
|
+
nullable: bool = Field(True, description="是否允许为空")
|
|
134
|
+
expand: bool = Field(False, description="是否展开(与现有 schema 模型保持一致)")
|
|
135
|
+
json_path: str | None = Field(None, description="JSON 特有路径映射")
|
|
136
|
+
children: list[ColumnSpec] | None = Field(None, description="JSON 嵌套子列(树形结构)")
|
|
137
|
+
|
|
138
|
+
@model_validator(mode="before")
|
|
139
|
+
@classmethod
|
|
140
|
+
def _generate_id_from_name(cls, data: Any) -> Any:
|
|
141
|
+
"""@methoddesc 当未提供列 ID 时,自动使用列名作为 ID
|
|
142
|
+
|
|
143
|
+
处理逻辑:
|
|
144
|
+
1. 检查输入数据是否为字典类型(Pydantic 验证器接收原始数据)
|
|
145
|
+
2. 如果是字典,检查是否同时满足以下两个条件:
|
|
146
|
+
- 包含 "name" 字段(有列名可用)
|
|
147
|
+
- 不包含 "id" 字段(用户未手动指定 ID)
|
|
148
|
+
3. 满足条件时,将 name 的值复制给 id,实现 ID 自动补全
|
|
149
|
+
4. 返回处理后的数据(无论是否修改),供后续验证流程继续处理
|
|
150
|
+
|
|
151
|
+
为什么需要这个验证器?
|
|
152
|
+
- 简化配置:用户只需写 name,不必重复写 id
|
|
153
|
+
- 向后兼容:旧配置中 id 和 name 相同的场景无需修改
|
|
154
|
+
- 一致性保证:确保每列都有稳定的 ID 用于内部引用
|
|
155
|
+
|
|
156
|
+
示例:
|
|
157
|
+
输入: {"name": "email", "type": "string"}
|
|
158
|
+
处理: 自动补充 id -> {"name": "email", "type": "string", "id": "email"}
|
|
159
|
+
输出: {"name": "email", "type": "string", "id": "email"}
|
|
160
|
+
|
|
161
|
+
输入: {"id": "col_1", "name": "email", "type": "string"}
|
|
162
|
+
处理: 已有 id,不修改 -> {"id": "col_1", "name": "email", "type": "string"}
|
|
163
|
+
输出: {"id": "col_1", "name": "email", "type": "string"}
|
|
164
|
+
"""
|
|
165
|
+
# 只有字典类型的原始数据才需要处理
|
|
166
|
+
# Pydantic 验证器可能接收到 dict、已实例化的模型对象或其他类型
|
|
167
|
+
if isinstance(data, dict):
|
|
168
|
+
# 条件判断:有 name 但没有 id 时才自动补全
|
|
169
|
+
# 使用 "in" 操作符检查键是否存在(即使值为 None 也算存在)
|
|
170
|
+
if "name" in data and "id" not in data:
|
|
171
|
+
# 将 name 的值赋给 id,实现自动 ID 生成
|
|
172
|
+
data["id"] = data["name"]
|
|
173
|
+
# 返回处理后的数据对象,供 Pydantic 继续后续验证流程
|
|
174
|
+
return data
|
|
@@ -0,0 +1,72 @@
|
|
|
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
|
+
提供递归处理 ColumnSpec(含嵌套 children)的工具函数。
|
|
20
|
+
平面 schema(无 children)行为等价于原顶层遍历,向后兼容。
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from collections.abc import Iterator
|
|
26
|
+
|
|
27
|
+
from app.shared.core.project.schema.types_parts.column import ColumnSpec
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def iter_all_columns(columns: list[ColumnSpec] | None) -> Iterator[ColumnSpec]:
|
|
31
|
+
"""递归遍历列(含嵌套 children),深度优先遍历父节点本身。
|
|
32
|
+
|
|
33
|
+
对无 children 的平面列,等价于直接遍历顶层(行为不变)。
|
|
34
|
+
|
|
35
|
+
:param columns: 顶层列列表,可为 None
|
|
36
|
+
:yield: 每个列(父 + 所有子孙),深度优先
|
|
37
|
+
"""
|
|
38
|
+
for col in columns or []:
|
|
39
|
+
yield col
|
|
40
|
+
if col.children:
|
|
41
|
+
yield from iter_all_columns(col.children)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def build_column_id_to_name_map(columns: list[ColumnSpec] | None) -> dict[str, str]:
|
|
45
|
+
"""递归构建 column_id -> column_name 映射。
|
|
46
|
+
|
|
47
|
+
嵌套子列用「父.子」全限定名(与 json_normalize 展平后的点分列一致),
|
|
48
|
+
使约束能直接 df['profile.name'] 取值。
|
|
49
|
+
平面列(无父)映射为自身名。
|
|
50
|
+
|
|
51
|
+
:param columns: 顶层列列表
|
|
52
|
+
:return: {column_id: 全限定列名},跳过 id 为 None 的列
|
|
53
|
+
"""
|
|
54
|
+
result: dict[str, str] = {}
|
|
55
|
+
_collect_qualified_names(columns or [], "", result)
|
|
56
|
+
return result
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _collect_qualified_names(columns: list[ColumnSpec], parent_path: str, result: dict[str, str]) -> None:
|
|
60
|
+
"""递归收集 column_id -> 全限定列名。
|
|
61
|
+
|
|
62
|
+
:param columns: 当前层级的列列表
|
|
63
|
+
:param parent_path: 父级全限定路径(空串表示顶层)
|
|
64
|
+
:param result: 累积结果的字典
|
|
65
|
+
"""
|
|
66
|
+
prefix = f"{parent_path}." if parent_path else ""
|
|
67
|
+
for col in columns:
|
|
68
|
+
qualified_name = f"{prefix}{col.name}"
|
|
69
|
+
if col.id is not None:
|
|
70
|
+
result[col.id] = qualified_name
|
|
71
|
+
if col.children:
|
|
72
|
+
_collect_qualified_names(col.children, qualified_name, result)
|