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,384 @@
|
|
|
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
|
+
- 提供 DataFrame 解析和验证的核心逻辑
|
|
21
|
+
- 执行逐单元格的类型检查和格式验证
|
|
22
|
+
- 支持结构化列展开(JSON 展开)
|
|
23
|
+
- 收集并报告验证错误
|
|
24
|
+
|
|
25
|
+
架构设计:
|
|
26
|
+
- process_dataframe 是核心函数,逐行逐列验证数据
|
|
27
|
+
- 使用 DataType 的 validate 方法进行类型验证
|
|
28
|
+
- 使用 DataType 的 parse 方法进行类型转换
|
|
29
|
+
- 支持结构化列展开(ExpressionType 类型)
|
|
30
|
+
|
|
31
|
+
输入示例:
|
|
32
|
+
from app.shared.domain.dataset_schema import TableSchema
|
|
33
|
+
from app.shared.domain.data_engine import process_dataframe
|
|
34
|
+
|
|
35
|
+
schema = TableSchema(name="users", columns=[...])
|
|
36
|
+
parsed_df, errors = process_dataframe(raw_df, schema)
|
|
37
|
+
|
|
38
|
+
输出示例:
|
|
39
|
+
parsed_df: 解析后的 DataFrame(类型已转换)
|
|
40
|
+
errors_df: 错误列表,每项包含 row_index、column、message
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
# 1. 第三方库导入
|
|
44
|
+
import pandas as pd
|
|
45
|
+
|
|
46
|
+
# 2. 项目内部导入
|
|
47
|
+
from app.shared.domain.data_types import ExpressionType
|
|
48
|
+
from app.shared.domain.data_types_parts.json_types import JsonObjectType
|
|
49
|
+
from app.shared.domain.dataset_schema import ColumnSchema, TableSchema
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _reconstruct_expand_columns(df: pd.DataFrame, table_schema: TableSchema) -> pd.DataFrame:
|
|
53
|
+
"""
|
|
54
|
+
将 pd.json_normalize 展平产生的点分列名重构回原始 dict 列。
|
|
55
|
+
|
|
56
|
+
pd.json_normalize 会将 {"specs": {"brand": "X", "model": "Y"}} 展平为
|
|
57
|
+
specs.brand 和 specs.model 两列。本函数对 schema 中 expand=True 且有 children
|
|
58
|
+
的列,将这些点分列重新聚合为 dict 列(如 specs 列的每个单元格是一个 dict)。
|
|
59
|
+
|
|
60
|
+
这样后续 _expand_structured_columns 就能正确处理这些列。
|
|
61
|
+
"""
|
|
62
|
+
df = df.copy()
|
|
63
|
+
for col_name, col_schema in table_schema.columns.items():
|
|
64
|
+
if not col_schema.expand or not col_schema.children:
|
|
65
|
+
continue
|
|
66
|
+
if col_name in df.columns:
|
|
67
|
+
continue
|
|
68
|
+
|
|
69
|
+
prefix = f"{col_name}."
|
|
70
|
+
matching_cols = [c for c in df.columns if c.startswith(prefix)]
|
|
71
|
+
if not matching_cols:
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
# 预筛选存在的子列(保留 children 顺序)
|
|
75
|
+
child_names = []
|
|
76
|
+
child_keys = []
|
|
77
|
+
for child in col_schema.children or []:
|
|
78
|
+
col_key = f"{col_name}.{child.name}"
|
|
79
|
+
if col_key in df.columns:
|
|
80
|
+
child_names.append(child.name)
|
|
81
|
+
child_keys.append(col_key)
|
|
82
|
+
|
|
83
|
+
# 向量化重构:逐行组装 dict cell,NaN 值省略,整行空则为 None
|
|
84
|
+
# (原 iterrows 实现的等价语义,itertuples 性能优于 iterrows)
|
|
85
|
+
sub_df = df[child_keys]
|
|
86
|
+
reconstructed: list[dict | None] = []
|
|
87
|
+
for row_vals in sub_df.itertuples(index=False, name=None):
|
|
88
|
+
cell = {}
|
|
89
|
+
for idx, child_name in enumerate(child_names):
|
|
90
|
+
val = row_vals[idx]
|
|
91
|
+
if pd.notna(val):
|
|
92
|
+
cell[child_name] = val
|
|
93
|
+
reconstructed.append(cell if cell else None)
|
|
94
|
+
|
|
95
|
+
df[col_name] = reconstructed
|
|
96
|
+
df = df.drop(columns=matching_cols)
|
|
97
|
+
|
|
98
|
+
return df
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _map_json_path_columns(df: pd.DataFrame, table_schema: TableSchema) -> pd.DataFrame:
|
|
102
|
+
"""
|
|
103
|
+
使用 json_path 将 DataFrame 中的点分列名映射为 schema 定义的列名。
|
|
104
|
+
|
|
105
|
+
pd.json_normalize 会将 {"location": {"zone": "A"}} 展平为 location.zone 列,
|
|
106
|
+
但 schema 定义的列名可能是 location_zone,通过 json_path("$.location.zone") 关联。
|
|
107
|
+
本函数根据 json_path 将 DataFrame 列名重命名为 schema 列名。
|
|
108
|
+
"""
|
|
109
|
+
rename_map = {}
|
|
110
|
+
for col_name, col_schema in table_schema.columns.items():
|
|
111
|
+
if col_name in df.columns:
|
|
112
|
+
continue
|
|
113
|
+
if not col_schema.json_path:
|
|
114
|
+
continue
|
|
115
|
+
json_path = col_schema.json_path
|
|
116
|
+
if not json_path.startswith("$."):
|
|
117
|
+
continue
|
|
118
|
+
dot_path = json_path[2:]
|
|
119
|
+
if dot_path in df.columns:
|
|
120
|
+
rename_map[dot_path] = col_name
|
|
121
|
+
|
|
122
|
+
if rename_map:
|
|
123
|
+
df = df.rename(columns=rename_map)
|
|
124
|
+
|
|
125
|
+
return df
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _fillna_dict(col: pd.Series) -> pd.Series:
|
|
129
|
+
"""将 JSON 列中的空值(NaN/None)替换为空 dict,供 json_normalize 展开为全空行。
|
|
130
|
+
|
|
131
|
+
说明: 不能用 fillna({})——Series.fillna 把 dict 参数按"索引标签→值"映射解释,
|
|
132
|
+
空 dict 什么都填不上,空值原样残留后进入 json_normalize 会产生垃圾行为。
|
|
133
|
+
"""
|
|
134
|
+
return col.where(col.notna(), {})
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _expand_structured_columns(df: pd.DataFrame, table_schema: TableSchema) -> pd.DataFrame:
|
|
138
|
+
"""
|
|
139
|
+
展开结构化列(JSON 列)。
|
|
140
|
+
|
|
141
|
+
============================================================================
|
|
142
|
+
业务场景 (什么情况下会调用这个函数)
|
|
143
|
+
============================================================================
|
|
144
|
+
- 场景1: 展开 JSON 列
|
|
145
|
+
当列的数据类型是 ExpressionType 且 expand=True 时,
|
|
146
|
+
将 JSON 对象展开为多个独立的列。
|
|
147
|
+
|
|
148
|
+
============================================================================
|
|
149
|
+
数据流 (输入如何变成输出)
|
|
150
|
+
============================================================================
|
|
151
|
+
输入参数:
|
|
152
|
+
- df: pd.DataFrame 待处理的 DataFrame
|
|
153
|
+
- table_schema: TableSchema 表结构定义
|
|
154
|
+
|
|
155
|
+
处理步骤:
|
|
156
|
+
1. 遍历所有列
|
|
157
|
+
2. 检查是否有 ExpressionType 且 expand=True 的列
|
|
158
|
+
3. 使用 pd.json_normalize 展开 JSON
|
|
159
|
+
4. 合并到原 DataFrame
|
|
160
|
+
|
|
161
|
+
最终输出: pd.DataFrame - 展开后的 DataFrame
|
|
162
|
+
"""
|
|
163
|
+
# 复制 DataFrame 避免修改原始数据,保证函数的纯性
|
|
164
|
+
df_to_process = df.copy()
|
|
165
|
+
|
|
166
|
+
# 遍历 schema 中定义的所有列,查找需要展开的结构化列
|
|
167
|
+
for col_name, col_schema in table_schema.columns.items():
|
|
168
|
+
# 条件判断:列必须存在于 DataFrame 中,且 schema 标记为需要展开
|
|
169
|
+
if col_name not in df_to_process.columns or not col_schema.expand:
|
|
170
|
+
continue
|
|
171
|
+
|
|
172
|
+
# 仅 ExpressionType 和 JsonObjectType(有 children)支持展开
|
|
173
|
+
is_expression = isinstance(col_schema.data_type, ExpressionType)
|
|
174
|
+
is_json_object_with_children = isinstance(col_schema.data_type, JsonObjectType) and col_schema.children
|
|
175
|
+
if not (is_expression or is_json_object_with_children):
|
|
176
|
+
continue
|
|
177
|
+
|
|
178
|
+
if is_json_object_with_children:
|
|
179
|
+
if not col_schema.children:
|
|
180
|
+
continue
|
|
181
|
+
child_names = [child.name for child in col_schema.children]
|
|
182
|
+
normalized_data = pd.json_normalize(_fillna_dict(df_to_process[col_name]).tolist())
|
|
183
|
+
normalized_data = normalized_data.reindex(columns=child_names)
|
|
184
|
+
normalized_data = normalized_data.add_prefix(f"{col_name}_")
|
|
185
|
+
else:
|
|
186
|
+
normalized_data = pd.json_normalize(_fillna_dict(df_to_process[col_name]).tolist()).add_prefix(
|
|
187
|
+
f"{col_name}_"
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
# 删除原始的 JSON 列,避免列名冲突
|
|
191
|
+
df_to_process = df_to_process.drop(columns=[col_name])
|
|
192
|
+
|
|
193
|
+
# 将展开后的新列横向合并到原 DataFrame
|
|
194
|
+
df_to_process = pd.concat([df_to_process, normalized_data], axis=1)
|
|
195
|
+
|
|
196
|
+
import logging
|
|
197
|
+
|
|
198
|
+
logger = logging.getLogger(__name__)
|
|
199
|
+
logger.info(f"列 '{col_name}' 已被自动展开为新的列。")
|
|
200
|
+
|
|
201
|
+
return df_to_process
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _process_columns_recursive(
|
|
205
|
+
df: pd.DataFrame,
|
|
206
|
+
schema_columns: dict[str, ColumnSchema],
|
|
207
|
+
parent_path: str,
|
|
208
|
+
parsed_data: dict[str, list],
|
|
209
|
+
errors: list[dict],
|
|
210
|
+
num_rows: int,
|
|
211
|
+
) -> None:
|
|
212
|
+
"""递归处理 schema 列。
|
|
213
|
+
|
|
214
|
+
JsonObject 列(有 children)跳过自身列检查,递归处理叶子子列。
|
|
215
|
+
叶子列以「父.子」全限定名参与校验(对应 json_normalize 点分列)。
|
|
216
|
+
平面列(无父)全限定名即自身名,行为不变。
|
|
217
|
+
|
|
218
|
+
:param df: 待处理的 DataFrame
|
|
219
|
+
:param schema_columns: 当前层级的列字典 {name: ColumnSchema}
|
|
220
|
+
:param parent_path: 父级全限定路径(空串表示顶层)
|
|
221
|
+
:param parsed_data: 累积解析列数据的字典
|
|
222
|
+
:param errors: 累积错误的列表
|
|
223
|
+
:param num_rows: 原始行数(用于缺失列填充)
|
|
224
|
+
"""
|
|
225
|
+
prefix = f"{parent_path}." if parent_path else ""
|
|
226
|
+
for col_name, col_schema in schema_columns.items():
|
|
227
|
+
# 跳过派生列(Extracted 类型),它们不存在于原始数据中
|
|
228
|
+
if hasattr(col_schema.data_type, "name") and col_schema.data_type.name == "Extracted":
|
|
229
|
+
continue
|
|
230
|
+
|
|
231
|
+
qualified = f"{prefix}{col_name}"
|
|
232
|
+
|
|
233
|
+
# JsonObject 列(有 children):递归处理子列,跳过自身列检查
|
|
234
|
+
# children 是 list[ColumnSchema],转为 {name: col} dict 以复用递归
|
|
235
|
+
#
|
|
236
|
+
# 例外:expand=True 时不递归。该路径下 process_dataframe 已先调用
|
|
237
|
+
# _reconstruct_expand_columns 把点分子列(specs.brand)折叠回单列 dict 列
|
|
238
|
+
# (specs),交由后续 _expand_structured_columns 重新展开。若此处仍递归,
|
|
239
|
+
# 子列的全限定名已不在 df.columns 中,会误报 MissingColumn。改为按父列
|
|
240
|
+
# 全限定名做存在性检查(折叠后的 dict 列存在),与历史行为一致。
|
|
241
|
+
if isinstance(col_schema.data_type, JsonObjectType) and col_schema.children and not col_schema.expand:
|
|
242
|
+
child_dict = {c.name: c for c in col_schema.children}
|
|
243
|
+
_process_columns_recursive(df, child_dict, qualified, parsed_data, errors, num_rows)
|
|
244
|
+
continue
|
|
245
|
+
|
|
246
|
+
# 叶子列(含平面列和嵌套叶子):按全限定名检查存在性
|
|
247
|
+
if qualified not in df.columns:
|
|
248
|
+
errors.append(
|
|
249
|
+
{
|
|
250
|
+
"row_index": None,
|
|
251
|
+
"column": qualified,
|
|
252
|
+
"value": None,
|
|
253
|
+
"error_type": "MissingColumn",
|
|
254
|
+
"error_message": f"数据表中缺少必需的列 '{qualified}'",
|
|
255
|
+
}
|
|
256
|
+
)
|
|
257
|
+
parsed_data[qualified] = [None] * num_rows
|
|
258
|
+
continue
|
|
259
|
+
|
|
260
|
+
# 类型校验(用全限定名取列)
|
|
261
|
+
nullable = getattr(col_schema, "nullable", True)
|
|
262
|
+
parsed_series, col_errors = col_schema.data_type.process_column(df[qualified], qualified, nullable=nullable)
|
|
263
|
+
parsed_data[qualified] = parsed_series
|
|
264
|
+
errors.extend(col_errors)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def process_dataframe(df: pd.DataFrame, schema: TableSchema) -> tuple[pd.DataFrame, list[dict]]:
|
|
268
|
+
"""
|
|
269
|
+
处理 DataFrame,执行类型验证和解析。
|
|
270
|
+
|
|
271
|
+
============================================================================
|
|
272
|
+
业务场景 (什么情况下会调用这个函数)
|
|
273
|
+
============================================================================
|
|
274
|
+
- 场景1: 数据校验
|
|
275
|
+
对导入的 Excel/CSV 数据进行类型验证,确保数据符合 schema 定义。
|
|
276
|
+
|
|
277
|
+
- 场景2: 数据清洗
|
|
278
|
+
将字符串类型的数据解析为正确的类型(如 "123" -> 123)。
|
|
279
|
+
|
|
280
|
+
============================================================================
|
|
281
|
+
数据流 (输入如何变成输出)
|
|
282
|
+
============================================================================
|
|
283
|
+
输入参数:
|
|
284
|
+
- df: pd.DataFrame 原始 DataFrame
|
|
285
|
+
示例值: pd.DataFrame({"user_id": ["1", "2"], "name": ["Alice", "Bob"]})
|
|
286
|
+
- schema: TableSchema 表结构定义
|
|
287
|
+
示例值: TableSchema(name="users", columns=[ColumnSchema(...), ...])
|
|
288
|
+
|
|
289
|
+
处理步骤:
|
|
290
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
291
|
+
│ Step 1: 遍历每一列 │
|
|
292
|
+
├─────────────────────────────────────────────────────────────┤
|
|
293
|
+
│ 输入: df, schema │
|
|
294
|
+
│ 操作: for col_name, col_schema in schema.columns.items()│
|
|
295
|
+
│ 输出: 遍历每个定义的列 │
|
|
296
|
+
└─────────────────────────────────────────────────────────────┘
|
|
297
|
+
|
|
298
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
299
|
+
│ Step 2: 检查列是否存在 │
|
|
300
|
+
├─────────────────────────────────────────────────────────────┤
|
|
301
|
+
│ 输入: 列名 │
|
|
302
|
+
│ 操作: if col_name not in df.columns │
|
|
303
|
+
│ 输出: 错误记录(MissingColumn) │
|
|
304
|
+
└─────────────────────────────────────────────────────────────┘
|
|
305
|
+
|
|
306
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
307
|
+
│ Step 3: 遍历每一行的值 │
|
|
308
|
+
├─────────────────────────────────────────────────────────────┤
|
|
309
|
+
│ 输入: 列的所有值 │
|
|
310
|
+
│ 操作: for index, value in df[col_name].items() │
|
|
311
|
+
│ 输出: 逐个验证 │
|
|
312
|
+
└─────────────────────────────────────────────────────────────┘
|
|
313
|
+
|
|
314
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
315
|
+
│ Step 4: 类型验证 │
|
|
316
|
+
├─────────────────────────────────────────────────────────────┤
|
|
317
|
+
│ 输入: cell value │
|
|
318
|
+
│ 操作: col_schema.data_type.validate(value) │
|
|
319
|
+
│ 输出: (is_valid, error_message) │
|
|
320
|
+
└─────────────────────────────────────────────────────────────┘
|
|
321
|
+
|
|
322
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
323
|
+
│ Step 5: 类型解析 │
|
|
324
|
+
├─────────────────────────────────────────────────────────────┤
|
|
325
|
+
│ 输入: 验证通过的值 │
|
|
326
|
+
│ 操作: col_schema.data_type.parse(value) │
|
|
327
|
+
│ 输出: 解析后的值 │
|
|
328
|
+
└─────────────────────────────────────────────────────────────┘
|
|
329
|
+
|
|
330
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
331
|
+
│ Step 6: 展开结构化列 │
|
|
332
|
+
├─────────────────────────────────────────────────────────────┤
|
|
333
|
+
│ 输入: 解析后的 DataFrame │
|
|
334
|
+
│ 操作: _expand_structured_columns │
|
|
335
|
+
│ 输出: 展开后的 DataFrame │
|
|
336
|
+
└─────────────────────────────────────────────────────────────┘
|
|
337
|
+
|
|
338
|
+
最终输出: Tuple[pd.DataFrame, list[dict]]
|
|
339
|
+
- 第一个元素: 解析后的 DataFrame
|
|
340
|
+
- 第二个元素: 错误字典列表,每项包含 row_index、column、value、error_type、error_message
|
|
341
|
+
|
|
342
|
+
============================================================================
|
|
343
|
+
错误格式
|
|
344
|
+
============================================================================
|
|
345
|
+
错误列表中的每个字典包含以下字段:
|
|
346
|
+
- row_index: 行索引(整数或 None,None 表示整列级错误)
|
|
347
|
+
- column: 列名(字符串)
|
|
348
|
+
- value: 错误值(任意类型)
|
|
349
|
+
- error_type: 错误类型(字符串)
|
|
350
|
+
- error_message: 错误消息(字符串)
|
|
351
|
+
|
|
352
|
+
============================================================================
|
|
353
|
+
边界情况处理
|
|
354
|
+
============================================================================
|
|
355
|
+
- 缺少列: 记录 MissingColumn 错误,该列填充 None
|
|
356
|
+
- 空 DataFrame: 返回空的解析 DataFrame 和空的错误 DataFrame
|
|
357
|
+
- 验证失败: 记录错误,该单元格填充 None
|
|
358
|
+
"""
|
|
359
|
+
# 展开 expand 列:将 pd.json_normalize 展平产生的点分列名(如 specs.brand)
|
|
360
|
+
# 重构回原始 dict 列,以便后续 _expand_structured_columns 能正确处理
|
|
361
|
+
df = _reconstruct_expand_columns(df, schema)
|
|
362
|
+
|
|
363
|
+
# 使用 json_path 将 DataFrame 中的点分列名映射为 schema 定义的列名
|
|
364
|
+
df = _map_json_path_columns(df, schema)
|
|
365
|
+
|
|
366
|
+
# 存储解析后的列数据,键为列名,值为解析后的列表
|
|
367
|
+
parsed_data: dict[str, list] = {}
|
|
368
|
+
|
|
369
|
+
# 存储所有验证和解析过程中发现的错误
|
|
370
|
+
errors: list[dict] = []
|
|
371
|
+
|
|
372
|
+
# 记录原始 DataFrame 的行数,用于缺失列时填充空值
|
|
373
|
+
num_rows = len(df)
|
|
374
|
+
|
|
375
|
+
# 递归处理列:JsonObject 列跳过自身检查、递归叶子子列(全限定名校验)
|
|
376
|
+
_process_columns_recursive(df, schema.columns, "", parsed_data, errors, num_rows)
|
|
377
|
+
|
|
378
|
+
# 将解析后的列数据组装为 DataFrame,保留原始索引以维持行号对应关系
|
|
379
|
+
parsed_df = pd.DataFrame(parsed_data, index=df.index)
|
|
380
|
+
|
|
381
|
+
# 展开需要展开的结构化列(JSON 列)
|
|
382
|
+
parsed_df = _expand_structured_columns(parsed_df, schema)
|
|
383
|
+
|
|
384
|
+
return parsed_df, errors
|
|
@@ -0,0 +1,113 @@
|
|
|
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
|
+
- 为校验引擎和 Schema 解析提供统一的数据类型入口
|
|
22
|
+
- 作为 app.shared.domain.data_types 包的门面(Facade),隐藏 data_types_parts 内部结构
|
|
23
|
+
|
|
24
|
+
架构设计:
|
|
25
|
+
- 类型注册表驱动: 各类型继承自 DataType 基类,通过统一的 process_column / validate / parse 接口工作
|
|
26
|
+
- 扁平化导出: 将分散在 data_types_parts 子目录中的类型集中暴露,降低调用方的认知负担
|
|
27
|
+
- 开闭原则: 新增数据类型时只需在对应子模块中实现,并在本模块重导出,不影响现有代码
|
|
28
|
+
|
|
29
|
+
类型分类说明:
|
|
30
|
+
- 标量类型 (scalars): StringType、IntegerType、DecimalType、FloatType、BooleanType、DateType
|
|
31
|
+
用于表示单一值的基本数据类型,是最常用的类型。
|
|
32
|
+
- 复合类型 (composite): CompositeConditionType、SpecificCompositeConditionType
|
|
33
|
+
用于表示由多个条件组合而成的复杂校验规则。
|
|
34
|
+
- 表达式类型 (expression): ExpressionType、SpecificExpressionType
|
|
35
|
+
用于表示需要动态求值的表达式列,支持 JSON 展开。
|
|
36
|
+
- 提取类型 (extracted): ExtractedType
|
|
37
|
+
用于表示从其他列派生计算得到的虚拟列,不存在于原始数据中。
|
|
38
|
+
- 序列类型 (sequence): SequenceType
|
|
39
|
+
用于表示数组或列表类型的数据。
|
|
40
|
+
|
|
41
|
+
输入示例:
|
|
42
|
+
>>> from app.shared.domain.data_types import StringType, IntegerType, ExpressionType
|
|
43
|
+
>>> string_type = StringType()
|
|
44
|
+
>>> int_type = IntegerType()
|
|
45
|
+
|
|
46
|
+
输出示例:
|
|
47
|
+
>>> data_type = StringType() # 或 IntegerType(), DateType() 等
|
|
48
|
+
>>> parsed, errors = data_type.process_column(series, "column_name", nullable=True)
|
|
49
|
+
|
|
50
|
+
注意事项:
|
|
51
|
+
- 所有具体类型必须继承自 DataType 基类,并实现 process_column 方法。
|
|
52
|
+
- 若需新增数据类型,应在 data_types_parts 的对应子模块中实现,
|
|
53
|
+
然后在本模块导入并重导出,同时更新 __all__ 列表。
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
from __future__ import annotations
|
|
57
|
+
|
|
58
|
+
# 1. 项目内部导入 —— 从 data_types_parts 各子模块聚合类型
|
|
59
|
+
# 基类:所有数据类型的抽象基类,定义统一的 process_column / validate / parse 接口
|
|
60
|
+
from app.shared.domain.data_types_parts.base import DataType
|
|
61
|
+
|
|
62
|
+
# 复合类型:支持多条件组合校验的复合条件类型
|
|
63
|
+
from app.shared.domain.data_types_parts.composite import CompositeConditionType, SpecificCompositeConditionType
|
|
64
|
+
|
|
65
|
+
# 表达式类型:支持 JSONPath 等动态表达式求值的列类型,可配置展开为多个子列
|
|
66
|
+
from app.shared.domain.data_types_parts.expression import ExpressionType, SpecificExpressionType
|
|
67
|
+
|
|
68
|
+
# 提取类型:派生列类型,表示由正则提取或其他计算逻辑生成的虚拟列
|
|
69
|
+
from app.shared.domain.data_types_parts.extracted import ExtractedType
|
|
70
|
+
|
|
71
|
+
# JSON 类型:JSON 对象、数组和空值类型
|
|
72
|
+
from app.shared.domain.data_types_parts.json_types import JsonArrayType, JsonNullType, JsonObjectType
|
|
73
|
+
|
|
74
|
+
# 标量类型:基本数据类型,覆盖数值、字符串、布尔、日期等常见类型
|
|
75
|
+
from app.shared.domain.data_types_parts.scalars import (
|
|
76
|
+
BooleanType, # 布尔类型:True / False
|
|
77
|
+
DateType, # 日期类型:datetime.date 或 datetime.datetime
|
|
78
|
+
DecimalType, # 高精度小数类型:Decimal,适用于财务计算
|
|
79
|
+
FloatType, # 浮点类型:float,适用于科学计算
|
|
80
|
+
IntegerType, # 整数类型:int
|
|
81
|
+
StringType, # 字符串类型:str,最通用的数据类型
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# 序列类型:数组/列表类型,表示同一类型的多个值集合
|
|
85
|
+
from app.shared.domain.data_types_parts.sequence import SequenceType
|
|
86
|
+
|
|
87
|
+
# 控制 `from module import *` 时导出的公开名称
|
|
88
|
+
# 按类型类别分组排列,便于开发者快速定位所需类型
|
|
89
|
+
__all__ = [
|
|
90
|
+
# 基类
|
|
91
|
+
"DataType",
|
|
92
|
+
# 标量类型
|
|
93
|
+
"BooleanType",
|
|
94
|
+
"DateType",
|
|
95
|
+
"DecimalType",
|
|
96
|
+
"FloatType",
|
|
97
|
+
"IntegerType",
|
|
98
|
+
"StringType",
|
|
99
|
+
# 复合类型
|
|
100
|
+
"CompositeConditionType",
|
|
101
|
+
"SpecificCompositeConditionType",
|
|
102
|
+
# 表达式类型
|
|
103
|
+
"ExpressionType",
|
|
104
|
+
"SpecificExpressionType",
|
|
105
|
+
# 提取类型
|
|
106
|
+
"ExtractedType",
|
|
107
|
+
# JSON 类型
|
|
108
|
+
"JsonArrayType",
|
|
109
|
+
"JsonNullType",
|
|
110
|
+
"JsonObjectType",
|
|
111
|
+
# 序列类型
|
|
112
|
+
"SequenceType",
|
|
113
|
+
]
|
|
@@ -0,0 +1,67 @@
|
|
|
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
|
+
- 简化上层模块对 data_types_parts 的导入路径
|
|
22
|
+
|
|
23
|
+
架构设计:
|
|
24
|
+
- 扁平化聚合: 将 base、scalars、composite、expression 等子模块统一暴露
|
|
25
|
+
- 显式接口控制: 通过 __all__ 限定公开符号
|
|
26
|
+
|
|
27
|
+
输入示例:
|
|
28
|
+
from app.shared.domain.data_types_parts import StringType, DataType, ExpressionType
|
|
29
|
+
|
|
30
|
+
输出示例:
|
|
31
|
+
dtype = DataType.create("string") # 返回 StringType 实例
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
# 1. 项目内部导入
|
|
35
|
+
from app.shared.domain.data_types_parts.base import DataType
|
|
36
|
+
from app.shared.domain.data_types_parts.composite import CompositeConditionType, SpecificCompositeConditionType
|
|
37
|
+
from app.shared.domain.data_types_parts.expression import ExpressionType, SpecificExpressionType
|
|
38
|
+
from app.shared.domain.data_types_parts.extracted import ExtractedType
|
|
39
|
+
from app.shared.domain.data_types_parts.json_types import JsonArrayType, JsonNullType, JsonObjectType
|
|
40
|
+
from app.shared.domain.data_types_parts.scalars import (
|
|
41
|
+
BooleanType,
|
|
42
|
+
DateType,
|
|
43
|
+
DecimalType,
|
|
44
|
+
FloatType,
|
|
45
|
+
IntegerType,
|
|
46
|
+
StringType,
|
|
47
|
+
)
|
|
48
|
+
from app.shared.domain.data_types_parts.sequence import SequenceType
|
|
49
|
+
|
|
50
|
+
__all__ = [
|
|
51
|
+
"BooleanType",
|
|
52
|
+
"CompositeConditionType",
|
|
53
|
+
"DataType",
|
|
54
|
+
"DateType",
|
|
55
|
+
"DecimalType",
|
|
56
|
+
"ExpressionType",
|
|
57
|
+
"ExtractedType",
|
|
58
|
+
"FloatType",
|
|
59
|
+
"IntegerType",
|
|
60
|
+
"JsonArrayType",
|
|
61
|
+
"JsonNullType",
|
|
62
|
+
"JsonObjectType",
|
|
63
|
+
"SequenceType",
|
|
64
|
+
"SpecificCompositeConditionType",
|
|
65
|
+
"SpecificExpressionType",
|
|
66
|
+
"StringType",
|
|
67
|
+
]
|