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,81 @@
|
|
|
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 DateFormat 转换运行器
|
|
18
|
+
|
|
19
|
+
功能概述:
|
|
20
|
+
- 将日期字符串从一种格式转换为另一种格式
|
|
21
|
+
- 支持解析失败时的容错处理
|
|
22
|
+
|
|
23
|
+
参数:
|
|
24
|
+
input_format: 输入日期格式(默认 "%Y-%m-%d")
|
|
25
|
+
output_format: 输出日期格式(默认 "%Y/%m/%d")
|
|
26
|
+
errors: 错误处理策略("coerce" 转为 NaT, "raise" 抛出异常, "ignore" 保留原值)
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
from typing import Any
|
|
32
|
+
|
|
33
|
+
import pandas as pd
|
|
34
|
+
|
|
35
|
+
from .base import TransformRunner
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class DateFormatRunner(TransformRunner):
|
|
39
|
+
"""@classdesc 日期格式转换运行器"""
|
|
40
|
+
|
|
41
|
+
def execute(
|
|
42
|
+
self,
|
|
43
|
+
df: pd.DataFrame,
|
|
44
|
+
input_column: str,
|
|
45
|
+
params: dict[str, Any],
|
|
46
|
+
output_columns: list[str],
|
|
47
|
+
) -> pd.DataFrame:
|
|
48
|
+
"""
|
|
49
|
+
@methoddesc 执行 日期格式化转换
|
|
50
|
+
|
|
51
|
+
业务用途:
|
|
52
|
+
- TransformRunner 协议的标准入口,由 transform 节点调用
|
|
53
|
+
- 读取 params 中的转换参数,对 input_column 应用转换,输出到 output_columns
|
|
54
|
+
|
|
55
|
+
参数:
|
|
56
|
+
df: 源 DataFrame
|
|
57
|
+
input_column: 输入列名
|
|
58
|
+
params: 转换参数字典
|
|
59
|
+
output_columns: 目标输出列名列表
|
|
60
|
+
|
|
61
|
+
返回:
|
|
62
|
+
转换后的 DataFrame
|
|
63
|
+
"""
|
|
64
|
+
input_format = params.get("input_format", "%Y-%m-%d")
|
|
65
|
+
output_format = params.get("output_format", "%Y/%m/%d")
|
|
66
|
+
errors = params.get("errors", "coerce")
|
|
67
|
+
|
|
68
|
+
if input_column not in df.columns:
|
|
69
|
+
raise ValueError(f"输入列不存在: {input_column}")
|
|
70
|
+
|
|
71
|
+
if not output_columns:
|
|
72
|
+
raise ValueError("DateFormat 需要至少一个 output_columns")
|
|
73
|
+
|
|
74
|
+
output_col = output_columns[0]
|
|
75
|
+
|
|
76
|
+
parsed = pd.to_datetime(df[input_column], format=input_format, errors=errors)
|
|
77
|
+
formatted = parsed.dt.strftime(output_format)
|
|
78
|
+
formatted = formatted.where(parsed.notna(), None)
|
|
79
|
+
df[output_col] = formatted
|
|
80
|
+
|
|
81
|
+
return df
|
|
@@ -0,0 +1,77 @@
|
|
|
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 Digits 转换运行器
|
|
18
|
+
|
|
19
|
+
功能概述:
|
|
20
|
+
- 将字符串的每个字符拆分为逗号分隔的数字字符串
|
|
21
|
+
- 保持行数不变,输出单列字符串(如 "1,1,0,1,0,1")
|
|
22
|
+
|
|
23
|
+
参数:
|
|
24
|
+
无
|
|
25
|
+
|
|
26
|
+
输入输出:
|
|
27
|
+
输入: "11010119900307889"(字符串)
|
|
28
|
+
输出: "1,1,0,1,0,1,1,9,9,0,0,3,0,7,8,8,9"(逗号分隔)
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
from typing import Any
|
|
34
|
+
|
|
35
|
+
import pandas as pd
|
|
36
|
+
|
|
37
|
+
from .base import TransformRunner, stringify_preserve_null
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class DigitsRunner(TransformRunner):
|
|
41
|
+
"""@classdesc 逐位分解转换运行器"""
|
|
42
|
+
|
|
43
|
+
def execute(
|
|
44
|
+
self,
|
|
45
|
+
df: pd.DataFrame,
|
|
46
|
+
input_column: str,
|
|
47
|
+
params: dict[str, Any],
|
|
48
|
+
output_columns: list[str],
|
|
49
|
+
) -> pd.DataFrame:
|
|
50
|
+
"""
|
|
51
|
+
@methoddesc 执行 数字提取转换
|
|
52
|
+
|
|
53
|
+
业务用途:
|
|
54
|
+
- TransformRunner 协议的标准入口,由 transform 节点调用
|
|
55
|
+
- 读取 params 中的转换参数,对 input_column 应用转换,输出到 output_columns
|
|
56
|
+
|
|
57
|
+
参数:
|
|
58
|
+
df: 源 DataFrame
|
|
59
|
+
input_column: 输入列名
|
|
60
|
+
params: 转换参数字典
|
|
61
|
+
output_columns: 目标输出列名列表
|
|
62
|
+
|
|
63
|
+
返回:
|
|
64
|
+
转换后的 DataFrame
|
|
65
|
+
"""
|
|
66
|
+
if input_column not in df.columns:
|
|
67
|
+
raise ValueError(f"输入列不存在: {input_column}")
|
|
68
|
+
|
|
69
|
+
output_col = output_columns[0] if output_columns else "digits"
|
|
70
|
+
|
|
71
|
+
# 将每个字符用逗号连接,保持行数不变
|
|
72
|
+
# 空值透传为 None,不产生 "nan" 拆位
|
|
73
|
+
df[output_col] = stringify_preserve_null(df[input_column]).apply(
|
|
74
|
+
lambda s: None if s is None else ",".join(list(s))
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
return df
|
|
@@ -0,0 +1,94 @@
|
|
|
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 DropDuplicates 转换运行器
|
|
18
|
+
|
|
19
|
+
功能概述:
|
|
20
|
+
- 对 DataFrame 进行去重
|
|
21
|
+
- 支持指定子集列(逗号分隔)或全列去重
|
|
22
|
+
- 支持保留策略:first(保留第一条)、last(保留最后一条)、false(全部删除)
|
|
23
|
+
|
|
24
|
+
参数:
|
|
25
|
+
subset: 去重列名,支持逗号分隔字符串或列表,留空则基于全部列去重
|
|
26
|
+
keep: 保留策略 ("first"|"last"|"false")
|
|
27
|
+
|
|
28
|
+
说明:
|
|
29
|
+
- input_column 被忽略(操作整行而非单列)
|
|
30
|
+
- output_columns 被忽略(不产生新列,保留所有原始列)
|
|
31
|
+
- 输出 DataFrame 索引重置为连续 0-based
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
from typing import Any
|
|
37
|
+
|
|
38
|
+
import pandas as pd
|
|
39
|
+
|
|
40
|
+
from .base import TransformRunner
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class DropDuplicatesRunner(TransformRunner):
|
|
44
|
+
"""@classdesc 去重转换运行器"""
|
|
45
|
+
|
|
46
|
+
def execute(
|
|
47
|
+
self,
|
|
48
|
+
df: pd.DataFrame,
|
|
49
|
+
input_column: str,
|
|
50
|
+
params: dict[str, Any],
|
|
51
|
+
output_columns: list[str],
|
|
52
|
+
) -> pd.DataFrame:
|
|
53
|
+
"""
|
|
54
|
+
@methoddesc 执行 去重转换
|
|
55
|
+
|
|
56
|
+
业务用途:
|
|
57
|
+
- TransformRunner 协议的标准入口,由 transform 节点调用
|
|
58
|
+
- 读取 params 中的转换参数,对 input_column 应用转换,输出到 output_columns
|
|
59
|
+
|
|
60
|
+
参数:
|
|
61
|
+
df: 源 DataFrame
|
|
62
|
+
input_column: 输入列名
|
|
63
|
+
params: 转换参数字典
|
|
64
|
+
output_columns: 目标输出列名列表
|
|
65
|
+
|
|
66
|
+
返回:
|
|
67
|
+
转换后的 DataFrame
|
|
68
|
+
"""
|
|
69
|
+
keep = params.get("keep", "first")
|
|
70
|
+
subset_raw = params.get("subset", "")
|
|
71
|
+
|
|
72
|
+
# 解析 subset:兼容逗号分隔字符串与列表两种格式
|
|
73
|
+
# 前端 tags 产出的是数组,旧配置/手写 YAML 可能是逗号分隔字符串
|
|
74
|
+
subset = None
|
|
75
|
+
if subset_raw:
|
|
76
|
+
if isinstance(subset_raw, list):
|
|
77
|
+
parsed = [str(col).strip() for col in subset_raw if str(col).strip()]
|
|
78
|
+
else:
|
|
79
|
+
parsed = [col.strip() for col in str(subset_raw).split(",") if col.strip()]
|
|
80
|
+
# §1.16: 拼错列名报配置错误——原实现静默剔除后走全列去重,
|
|
81
|
+
# 去重结果比预期多、下游统计偏大且无提示。空 subset(未配置)保持全列去重。
|
|
82
|
+
missing = [col for col in parsed if col not in df.columns]
|
|
83
|
+
if missing:
|
|
84
|
+
raise ValueError(f"subset 列不存在: {missing}(表列: {list(df.columns)})")
|
|
85
|
+
if parsed:
|
|
86
|
+
subset = parsed
|
|
87
|
+
|
|
88
|
+
# pandas 的 keep 参数:keep=False 表示全部删除
|
|
89
|
+
keep_param: str | bool = keep
|
|
90
|
+
if keep == "false":
|
|
91
|
+
keep_param = False
|
|
92
|
+
|
|
93
|
+
result = df.drop_duplicates(subset=subset, keep=keep_param).reset_index(drop=True)
|
|
94
|
+
return result
|
|
@@ -0,0 +1,94 @@
|
|
|
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 FillNA 转换运行器
|
|
18
|
+
|
|
19
|
+
功能概述:
|
|
20
|
+
- 填充空值(NaN/None)
|
|
21
|
+
- 支持多种填充策略:固定值、前向填充、后向填充、均值、中位数
|
|
22
|
+
|
|
23
|
+
参数:
|
|
24
|
+
strategy: 填充策略 ("value"|"ffill"|"bfill"|"mean"|"median")
|
|
25
|
+
value: 当 strategy=="value" 时的填充值
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
from typing import Any
|
|
31
|
+
|
|
32
|
+
import pandas as pd
|
|
33
|
+
|
|
34
|
+
from .base import TransformRunner
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class FillNARunner(TransformRunner):
|
|
38
|
+
"""@classdesc 空值填充转换运行器"""
|
|
39
|
+
|
|
40
|
+
def execute(
|
|
41
|
+
self,
|
|
42
|
+
df: pd.DataFrame,
|
|
43
|
+
input_column: str,
|
|
44
|
+
params: dict[str, Any],
|
|
45
|
+
output_columns: list[str],
|
|
46
|
+
) -> pd.DataFrame:
|
|
47
|
+
"""
|
|
48
|
+
@methoddesc 执行 空值填充转换
|
|
49
|
+
|
|
50
|
+
业务用途:
|
|
51
|
+
- TransformRunner 协议的标准入口,由 transform 节点调用
|
|
52
|
+
- 读取 params 中的转换参数,对 input_column 应用转换,输出到 output_columns
|
|
53
|
+
|
|
54
|
+
参数:
|
|
55
|
+
df: 源 DataFrame
|
|
56
|
+
input_column: 输入列名
|
|
57
|
+
params: 转换参数字典
|
|
58
|
+
output_columns: 目标输出列名列表
|
|
59
|
+
|
|
60
|
+
返回:
|
|
61
|
+
转换后的 DataFrame
|
|
62
|
+
"""
|
|
63
|
+
strategy = params.get("strategy", "value")
|
|
64
|
+
|
|
65
|
+
if input_column not in df.columns:
|
|
66
|
+
raise ValueError(f"输入列不存在: {input_column}")
|
|
67
|
+
|
|
68
|
+
if not output_columns:
|
|
69
|
+
raise ValueError("FillNA 需要至少一个 output_columns")
|
|
70
|
+
|
|
71
|
+
output_col = output_columns[0]
|
|
72
|
+
series = df[input_column].copy()
|
|
73
|
+
|
|
74
|
+
if strategy == "value":
|
|
75
|
+
fill_value = params.get("value", "")
|
|
76
|
+
series = series.fillna(fill_value)
|
|
77
|
+
elif strategy == "ffill":
|
|
78
|
+
series = series.ffill()
|
|
79
|
+
elif strategy == "bfill":
|
|
80
|
+
series = series.bfill()
|
|
81
|
+
elif strategy in ("mean", "median"):
|
|
82
|
+
# §1.6: 只填真正的空值,非空非数值值保留原样——原实现整列 coerce 后 fillna,
|
|
83
|
+
# "abc" 这类非空脏值被顺带清洗成均值(用户没要求清洗,原始信息无声丢失)。
|
|
84
|
+
# 均值只对"能转数值的非空值"计算;填充后输出列为 object(混合类型)是预期。
|
|
85
|
+
numeric_series = pd.to_numeric(series, errors="coerce")
|
|
86
|
+
stat = numeric_series.mean() if strategy == "mean" else numeric_series.median()
|
|
87
|
+
filled = series.copy()
|
|
88
|
+
filled[series.isna()] = stat
|
|
89
|
+
series = filled
|
|
90
|
+
else:
|
|
91
|
+
raise ValueError(f"不支持的 FillNA 策略: {strategy}")
|
|
92
|
+
|
|
93
|
+
df[output_col] = series
|
|
94
|
+
return df
|
|
@@ -0,0 +1,97 @@
|
|
|
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 FilterRows 转换运行器
|
|
18
|
+
|
|
19
|
+
功能概述:
|
|
20
|
+
- 按条件过滤行,返回满足条件的行子集
|
|
21
|
+
- 支持多条件组合(AND/OR 逻辑)
|
|
22
|
+
- 支持 eq/ne/gt/gte/lt/lte/contains/startswith/endswith/in/not_in/is_null/is_not_null 操作符
|
|
23
|
+
|
|
24
|
+
参数:
|
|
25
|
+
conditions: 条件列表 [{column, op, value}]
|
|
26
|
+
logic: 条件组合逻辑 ("and"|"or")
|
|
27
|
+
|
|
28
|
+
说明:
|
|
29
|
+
- input_column 被忽略(操作整行而非单列)
|
|
30
|
+
- output_columns 被忽略(不产生新列,保留所有原始列)
|
|
31
|
+
- 输出 DataFrame 索引重置为连续 0-based
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
from typing import Any
|
|
37
|
+
|
|
38
|
+
import pandas as pd
|
|
39
|
+
|
|
40
|
+
from .base import TransformRunner, evaluate_condition
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class FilterRowsRunner(TransformRunner):
|
|
44
|
+
"""@classdesc 行过滤转换运行器"""
|
|
45
|
+
|
|
46
|
+
def execute(
|
|
47
|
+
self,
|
|
48
|
+
df: pd.DataFrame,
|
|
49
|
+
input_column: str,
|
|
50
|
+
params: dict[str, Any],
|
|
51
|
+
output_columns: list[str],
|
|
52
|
+
) -> pd.DataFrame:
|
|
53
|
+
"""
|
|
54
|
+
@methoddesc 执行 行过滤转换
|
|
55
|
+
|
|
56
|
+
业务用途:
|
|
57
|
+
- TransformRunner 协议的标准入口,由 transform 节点调用
|
|
58
|
+
- 读取 params 中的转换参数,对 input_column 应用转换,输出到 output_columns
|
|
59
|
+
|
|
60
|
+
参数:
|
|
61
|
+
df: 源 DataFrame
|
|
62
|
+
input_column: 输入列名
|
|
63
|
+
params: 转换参数字典
|
|
64
|
+
output_columns: 目标输出列名列表
|
|
65
|
+
|
|
66
|
+
返回:
|
|
67
|
+
转换后的 DataFrame
|
|
68
|
+
"""
|
|
69
|
+
conditions = params.get("conditions", [])
|
|
70
|
+
# logic 归一大小写不敏感("OR"/"And" 均合法),未知值 fail-fast——不再静默按 AND 组合
|
|
71
|
+
raw_logic = params.get("logic", "and")
|
|
72
|
+
logic = str(raw_logic).lower()
|
|
73
|
+
if logic not in ("and", "or"):
|
|
74
|
+
raise ValueError(f"未知的 logic '{raw_logic}',支持的值为: and, or")
|
|
75
|
+
|
|
76
|
+
if not conditions:
|
|
77
|
+
# 无条件时保留所有行
|
|
78
|
+
return df.reset_index(drop=True)
|
|
79
|
+
|
|
80
|
+
# 逐个评估条件,生成布尔掩码
|
|
81
|
+
masks = []
|
|
82
|
+
for cond in conditions:
|
|
83
|
+
mask = evaluate_condition(df, cond)
|
|
84
|
+
masks.append(mask)
|
|
85
|
+
|
|
86
|
+
# 组合条件
|
|
87
|
+
if logic == "or":
|
|
88
|
+
combined = masks[0]
|
|
89
|
+
for m in masks[1:]:
|
|
90
|
+
combined = combined | m
|
|
91
|
+
else:
|
|
92
|
+
combined = masks[0]
|
|
93
|
+
for m in masks[1:]:
|
|
94
|
+
combined = combined & m
|
|
95
|
+
|
|
96
|
+
result = df.loc[combined].reset_index(drop=True)
|
|
97
|
+
return result
|
|
@@ -0,0 +1,78 @@
|
|
|
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 Lookup 转换运行器
|
|
18
|
+
|
|
19
|
+
功能概述:
|
|
20
|
+
- 根据映射表精确替换列值
|
|
21
|
+
- 支持默认值回退
|
|
22
|
+
|
|
23
|
+
参数:
|
|
24
|
+
mapping: 字典映射 {旧值: 新值}
|
|
25
|
+
default: 未匹配时的默认值(默认 None)
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
from typing import Any
|
|
31
|
+
|
|
32
|
+
import pandas as pd
|
|
33
|
+
|
|
34
|
+
from .base import TransformRunner
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class LookupRunner(TransformRunner):
|
|
38
|
+
"""@classdesc 字典映射转换运行器"""
|
|
39
|
+
|
|
40
|
+
def execute(
|
|
41
|
+
self,
|
|
42
|
+
df: pd.DataFrame,
|
|
43
|
+
input_column: str,
|
|
44
|
+
params: dict[str, Any],
|
|
45
|
+
output_columns: list[str],
|
|
46
|
+
) -> pd.DataFrame:
|
|
47
|
+
"""
|
|
48
|
+
@methoddesc 执行 字典查找/映射转换
|
|
49
|
+
|
|
50
|
+
业务用途:
|
|
51
|
+
- TransformRunner 协议的标准入口,由 transform 节点调用
|
|
52
|
+
- 读取 params 中的转换参数,对 input_column 应用转换,输出到 output_columns
|
|
53
|
+
|
|
54
|
+
参数:
|
|
55
|
+
df: 源 DataFrame
|
|
56
|
+
input_column: 输入列名
|
|
57
|
+
params: 转换参数字典
|
|
58
|
+
output_columns: 目标输出列名列表
|
|
59
|
+
|
|
60
|
+
返回:
|
|
61
|
+
转换后的 DataFrame
|
|
62
|
+
"""
|
|
63
|
+
mapping = params.get("mapping", {})
|
|
64
|
+
default = params.get("default", None)
|
|
65
|
+
|
|
66
|
+
if input_column not in df.columns:
|
|
67
|
+
raise ValueError(f"输入列不存在: {input_column}")
|
|
68
|
+
|
|
69
|
+
if not output_columns:
|
|
70
|
+
raise ValueError("Lookup 需要至少一个 output_columns")
|
|
71
|
+
|
|
72
|
+
output_col = output_columns[0]
|
|
73
|
+
|
|
74
|
+
df[output_col] = df[input_column].map(mapping)
|
|
75
|
+
if default is not None:
|
|
76
|
+
df[output_col] = df[output_col].fillna(default)
|
|
77
|
+
|
|
78
|
+
return df
|
|
@@ -0,0 +1,70 @@
|
|
|
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 LowerCase 转换运行器
|
|
18
|
+
|
|
19
|
+
功能概述:
|
|
20
|
+
- 将字符串转换为小写
|
|
21
|
+
|
|
22
|
+
参数:
|
|
23
|
+
无
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
from typing import Any
|
|
29
|
+
|
|
30
|
+
import pandas as pd
|
|
31
|
+
|
|
32
|
+
from .base import TransformRunner, stringify_preserve_null
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class LowerCaseRunner(TransformRunner):
|
|
36
|
+
"""@classdesc 字符串转小写转换运行器"""
|
|
37
|
+
|
|
38
|
+
def execute(
|
|
39
|
+
self,
|
|
40
|
+
df: pd.DataFrame,
|
|
41
|
+
input_column: str,
|
|
42
|
+
params: dict[str, Any],
|
|
43
|
+
output_columns: list[str],
|
|
44
|
+
) -> pd.DataFrame:
|
|
45
|
+
"""
|
|
46
|
+
@methoddesc 执行 转小写转换
|
|
47
|
+
|
|
48
|
+
业务用途:
|
|
49
|
+
- TransformRunner 协议的标准入口,由 transform 节点调用
|
|
50
|
+
- 读取 params 中的转换参数,对 input_column 应用转换,输出到 output_columns
|
|
51
|
+
|
|
52
|
+
参数:
|
|
53
|
+
df: 源 DataFrame
|
|
54
|
+
input_column: 输入列名
|
|
55
|
+
params: 转换参数字典
|
|
56
|
+
output_columns: 目标输出列名列表
|
|
57
|
+
|
|
58
|
+
返回:
|
|
59
|
+
转换后的 DataFrame
|
|
60
|
+
"""
|
|
61
|
+
if input_column not in df.columns:
|
|
62
|
+
raise ValueError(f"输入列不存在: {input_column}")
|
|
63
|
+
|
|
64
|
+
if not output_columns:
|
|
65
|
+
raise ValueError("LowerCase 需要至少一个 output_columns")
|
|
66
|
+
|
|
67
|
+
output_col = output_columns[0]
|
|
68
|
+
df[output_col] = stringify_preserve_null(df[input_column]).str.lower()
|
|
69
|
+
|
|
70
|
+
return df
|
|
@@ -0,0 +1,90 @@
|
|
|
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 MapValue 转换运行器
|
|
18
|
+
|
|
19
|
+
功能概述:
|
|
20
|
+
- 以上游值作为索引,查表映射到对应元素
|
|
21
|
+
- 索引越界或无法解析时保留原值
|
|
22
|
+
|
|
23
|
+
参数:
|
|
24
|
+
mapping: 映射数组(如 ["1","0","X","9","8","7","6","5","4","3","2"])
|
|
25
|
+
|
|
26
|
+
输入输出:
|
|
27
|
+
输入: 2
|
|
28
|
+
输出: "X"(mapping[2])
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
from typing import Any
|
|
34
|
+
|
|
35
|
+
import pandas as pd
|
|
36
|
+
|
|
37
|
+
from .base import TransformRunner
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class MapValueRunner(TransformRunner):
|
|
41
|
+
"""@classdesc 查表映射转换运行器"""
|
|
42
|
+
|
|
43
|
+
def execute(
|
|
44
|
+
self,
|
|
45
|
+
df: pd.DataFrame,
|
|
46
|
+
input_column: str,
|
|
47
|
+
params: dict[str, Any],
|
|
48
|
+
output_columns: list[str],
|
|
49
|
+
) -> pd.DataFrame:
|
|
50
|
+
"""
|
|
51
|
+
@methoddesc 执行 值映射转换
|
|
52
|
+
|
|
53
|
+
业务用途:
|
|
54
|
+
- TransformRunner 协议的标准入口,由 transform 节点调用
|
|
55
|
+
- 读取 params 中的转换参数,对 input_column 应用转换,输出到 output_columns
|
|
56
|
+
|
|
57
|
+
参数:
|
|
58
|
+
df: 源 DataFrame
|
|
59
|
+
input_column: 输入列名
|
|
60
|
+
params: 转换参数字典
|
|
61
|
+
output_columns: 目标输出列名列表
|
|
62
|
+
|
|
63
|
+
返回:
|
|
64
|
+
转换后的 DataFrame
|
|
65
|
+
"""
|
|
66
|
+
mapping = params.get("mapping", [])
|
|
67
|
+
if not mapping:
|
|
68
|
+
raise ValueError("MapValue 需要 mapping 参数")
|
|
69
|
+
|
|
70
|
+
if input_column not in df.columns:
|
|
71
|
+
raise ValueError(f"输入列不存在: {input_column}")
|
|
72
|
+
|
|
73
|
+
output_col = output_columns[0] if output_columns else "mapped"
|
|
74
|
+
|
|
75
|
+
mapping_arr = list(mapping)
|
|
76
|
+
numeric_idx = pd.to_numeric(df[input_column], errors="coerce")
|
|
77
|
+
# §1.24: 非整数索引报错——原实现 int(1.9) 向下截断当 1 查表,
|
|
78
|
+
# 用户预期"非整数索引无效"却拿到 mapping[1] 的值,错得毫无迹象
|
|
79
|
+
non_integer_mask = numeric_idx.notna() & (numeric_idx != numeric_idx.round())
|
|
80
|
+
if non_integer_mask.any():
|
|
81
|
+
first_idx = numeric_idx.index[non_integer_mask][0]
|
|
82
|
+
raise ValueError(f"map_value 索引 '{df[input_column][first_idx]}'(第 {first_idx} 行)不是整数,无法查表")
|
|
83
|
+
in_range = numeric_idx.notna() & (numeric_idx >= 0) & (numeric_idx < len(mapping_arr))
|
|
84
|
+
idx_int = numeric_idx.where(in_range)
|
|
85
|
+
mapped = idx_int.apply(
|
|
86
|
+
lambda x: mapping_arr[int(x)] if pd.notna(x) and 0 <= int(x) < len(mapping_arr) else None
|
|
87
|
+
)
|
|
88
|
+
df[output_col] = mapped.where(in_range, df[input_column])
|
|
89
|
+
|
|
90
|
+
return df
|