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.
Files changed (442) hide show
  1. app/__init__.py +16 -0
  2. app/api/__init__.py +32 -0
  3. app/api/dependencies.py +185 -0
  4. app/api/main.py +301 -0
  5. app/api/middleware/__init__.py +34 -0
  6. app/api/middleware/exception_handler.py +126 -0
  7. app/api/middleware/request_logging.py +97 -0
  8. app/api/middleware/token_auth.py +215 -0
  9. app/api/models/__init__.py +151 -0
  10. app/api/models/connection_rules.py +98 -0
  11. app/api/models/files.py +99 -0
  12. app/api/models/full_validation.py +554 -0
  13. app/api/models/project.py +117 -0
  14. app/api/models/projects.py +80 -0
  15. app/api/models/schema.py +121 -0
  16. app/api/models/v2_responses.py +46 -0
  17. app/api/models/validation.py +303 -0
  18. app/api/models/workspace.py +118 -0
  19. app/api/routers/__init__.py +56 -0
  20. app/api/routers/ai/__init__.py +36 -0
  21. app/api/routers/ai/chat.py +243 -0
  22. app/api/routers/ai/generate.py +156 -0
  23. app/api/routers/ai/hardware.py +199 -0
  24. app/api/routers/ai/jobs.py +544 -0
  25. app/api/routers/ai/migrate.py +471 -0
  26. app/api/routers/ai/models.py +352 -0
  27. app/api/routers/ai/ollama.py +108 -0
  28. app/api/routers/ai/providers.py +491 -0
  29. app/api/routers/ai/router.py +44 -0
  30. app/api/routers/ai/stream.py +281 -0
  31. app/api/routers/ai/utils.py +182 -0
  32. app/api/routers/core/__init__.py +34 -0
  33. app/api/routers/core/connection_rules.py +188 -0
  34. app/api/routers/core/data_sources.py +374 -0
  35. app/api/routers/core/regex.py +333 -0
  36. app/api/routers/core/reporting.py +263 -0
  37. app/api/routers/files/__init__.py +24 -0
  38. app/api/routers/files/ops.py +179 -0
  39. app/api/routers/files/transfer.py +101 -0
  40. app/api/routers/preview/__init__.py +35 -0
  41. app/api/routers/preview/content_mode.py +264 -0
  42. app/api/routers/preview/header_row.py +154 -0
  43. app/api/routers/preview/models.py +81 -0
  44. app/api/routers/preview/path_mode.py +440 -0
  45. app/api/routers/preview/router.py +30 -0
  46. app/api/routers/project/__init__.py +61 -0
  47. app/api/routers/project/base.py +111 -0
  48. app/api/routers/project/constraint.py +345 -0
  49. app/api/routers/project/full_config.py +411 -0
  50. app/api/routers/project/full_config_writer.py +327 -0
  51. app/api/routers/project/helpers.py +167 -0
  52. app/api/routers/project/inspection_fix.py +330 -0
  53. app/api/routers/project/manifest.py +605 -0
  54. app/api/routers/project/models.py +177 -0
  55. app/api/routers/project/pattern.py +268 -0
  56. app/api/routers/project/regex.py +358 -0
  57. app/api/routers/project/scanner.py +157 -0
  58. app/api/routers/project/schema.py +567 -0
  59. app/api/routers/project/schema_helpers.py +149 -0
  60. app/api/routers/project/settings.py +399 -0
  61. app/api/routers/project/template.py +325 -0
  62. app/api/routers/project/validation.py +227 -0
  63. app/api/routers/project/view.py +177 -0
  64. app/api/routers/project/workspaces.py +167 -0
  65. app/api/routers/projects/__init__.py +25 -0
  66. app/api/routers/projects/create.py +121 -0
  67. app/api/routers/projects/open.py +103 -0
  68. app/api/routers/projects/scan.py +100 -0
  69. app/api/routers/validation/__init__.py +39 -0
  70. app/api/routers/validation/common.py +263 -0
  71. app/api/routers/validation/content_mode.py +401 -0
  72. app/api/routers/validation/history.py +235 -0
  73. app/api/routers/validation/inline_mode.py +196 -0
  74. app/api/routers/validation/path_mode.py +314 -0
  75. app/api/routers/validation/router.py +51 -0
  76. app/api/services/full_validation_response_builder.py +412 -0
  77. app/api/services/io_error_messages.py +41 -0
  78. app/api/services/preview_service.py +248 -0
  79. app/cli/__init__.py +18 -0
  80. app/cli/__main__.py +30 -0
  81. app/cli/shared_services/__init__.py +42 -0
  82. app/cli/shared_services/config_ops.py +581 -0
  83. app/cli/shared_services/generation_ops.py +160 -0
  84. app/cli/shared_services/project_ops.py +288 -0
  85. app/cli/shell/__init__.py +48 -0
  86. app/cli/shell/commands/__init__.py +63 -0
  87. app/cli/shell/commands/ai/__init__.py +260 -0
  88. app/cli/shell/commands/ai/chat.py +331 -0
  89. app/cli/shell/commands/ai/delete.py +165 -0
  90. app/cli/shell/commands/ai/diff.py +70 -0
  91. app/cli/shell/commands/ai/display.py +428 -0
  92. app/cli/shell/commands/ai/executor.py +225 -0
  93. app/cli/shell/commands/ai/executor_utils.py +271 -0
  94. app/cli/shell/commands/ai/generate.py +285 -0
  95. app/cli/shell/commands/ai/interaction.py +262 -0
  96. app/cli/shell/commands/ai/migrate.py +281 -0
  97. app/cli/shell/commands/ai/resolver.py +224 -0
  98. app/cli/shell/commands/ai/status.py +101 -0
  99. app/cli/shell/commands/ai/switch.py +158 -0
  100. app/cli/shell/commands/ai/utils.py +52 -0
  101. app/cli/shell/commands/base.py +343 -0
  102. app/cli/shell/commands/config/__init__.py +131 -0
  103. app/cli/shell/commands/config/base.py +80 -0
  104. app/cli/shell/commands/config/check.py +246 -0
  105. app/cli/shell/commands/config/edit.py +148 -0
  106. app/cli/shell/commands/config/get.py +111 -0
  107. app/cli/shell/commands/config/init.py +123 -0
  108. app/cli/shell/commands/config/inspect.py +164 -0
  109. app/cli/shell/commands/config/list.py +104 -0
  110. app/cli/shell/commands/config/set.py +119 -0
  111. app/cli/shell/commands/config/show.py +150 -0
  112. app/cli/shell/commands/exit.py +67 -0
  113. app/cli/shell/commands/help.py +104 -0
  114. app/cli/shell/commands/infer_schema.py +156 -0
  115. app/cli/shell/commands/open.py +201 -0
  116. app/cli/shell/commands/project.py +215 -0
  117. app/cli/shell/commands/provider.py +619 -0
  118. app/cli/shell/commands/system.py +170 -0
  119. app/cli/shell/commands/validate.py +455 -0
  120. app/cli/shell/completer.py +231 -0
  121. app/cli/shell/config_storage.py +191 -0
  122. app/cli/shell/exceptions.py +90 -0
  123. app/cli/shell/formatter.py +371 -0
  124. app/cli/shell/interactive_menu.py +348 -0
  125. app/cli/shell/main.py +286 -0
  126. app/cli/shell/parser.py +266 -0
  127. app/cli/start.py +180 -0
  128. app/cli_main.py +52 -0
  129. app/mcp_server.py +311 -0
  130. app/shared/core/__init__.py +18 -0
  131. app/shared/core/app_version.py +59 -0
  132. app/shared/core/config/__init__.py +119 -0
  133. app/shared/core/config/server.py +193 -0
  134. app/shared/core/data_source/__init__.py +106 -0
  135. app/shared/core/data_source/loader.py +348 -0
  136. app/shared/core/data_source/loaders/__init__.py +213 -0
  137. app/shared/core/data_source/loaders/base.py +215 -0
  138. app/shared/core/data_source/loaders/converter.py +337 -0
  139. app/shared/core/data_source/loaders/csv_loader.py +252 -0
  140. app/shared/core/data_source/loaders/excel_loader.py +666 -0
  141. app/shared/core/data_source/loaders/extractor.py +268 -0
  142. app/shared/core/data_source/loaders/json_loader.py +354 -0
  143. app/shared/core/data_source/loaders/registry.py +88 -0
  144. app/shared/core/data_source/loaders/sql_loader.py +313 -0
  145. app/shared/core/data_source/loaders/strategies/__init__.py +100 -0
  146. app/shared/core/data_source/loaders/strategies/array_parser.py +277 -0
  147. app/shared/core/data_source/loaders/strategies/lines_parser.py +355 -0
  148. app/shared/core/data_source/loaders/strategies/object_parser.py +278 -0
  149. app/shared/core/data_source/schema_info.py +50 -0
  150. app/shared/core/data_source/specs/__init__.py +128 -0
  151. app/shared/core/data_source/specs/base.py +258 -0
  152. app/shared/core/data_source/specs/csv_source.py +137 -0
  153. app/shared/core/data_source/specs/excel_source.py +141 -0
  154. app/shared/core/data_source/specs/file_base.py +252 -0
  155. app/shared/core/data_source/specs/json_source.py +226 -0
  156. app/shared/core/data_source/specs/sql_source.py +161 -0
  157. app/shared/core/encoding.py +36 -0
  158. app/shared/core/io/__init__.py +24 -0
  159. app/shared/core/io/yaml.py +379 -0
  160. app/shared/core/manifest_schema/__init__.py +52 -0
  161. app/shared/core/manifest_schema/types.py +99 -0
  162. app/shared/core/manifest_schema/version.py +175 -0
  163. app/shared/core/patterns/__init__.py +24 -0
  164. app/shared/core/patterns/loader.py +134 -0
  165. app/shared/core/patterns/writer.py +244 -0
  166. app/shared/core/project/__init__.py +47 -0
  167. app/shared/core/project/constraint/builders/__init__.py +50 -0
  168. app/shared/core/project/constraint/builders/base.py +100 -0
  169. app/shared/core/project/constraint/builders/composite.py +77 -0
  170. app/shared/core/project/constraint/builders/conditional.py +67 -0
  171. app/shared/core/project/constraint/builders/foreign_key.py +53 -0
  172. app/shared/core/project/constraint/builders/registry.py +64 -0
  173. app/shared/core/project/constraint/builders/scripted.py +51 -0
  174. app/shared/core/project/constraint/builders/single_column.py +86 -0
  175. app/shared/core/project/constraint/builders/unique.py +53 -0
  176. app/shared/core/project/constraint/factory.py +170 -0
  177. app/shared/core/project/constraint/reader.py +214 -0
  178. app/shared/core/project/constraint/registry.py +233 -0
  179. app/shared/core/project/constraint/types/__init__.py +63 -0
  180. app/shared/core/project/constraint/types/constraint_file.py +261 -0
  181. app/shared/core/project/constraint/types/refs.py +460 -0
  182. app/shared/core/project/constraint/types.py +28 -0
  183. app/shared/core/project/constraint/writer.py +181 -0
  184. app/shared/core/project/loader/__init__.py +56 -0
  185. app/shared/core/project/loader/loader.py +30 -0
  186. app/shared/core/project/loader/loader_parts/config_inspector.py +137 -0
  187. app/shared/core/project/loader/loader_parts/embedded_constraints.py +224 -0
  188. app/shared/core/project/loader/loader_parts/file_loaders.py +58 -0
  189. app/shared/core/project/loader/loader_parts/inspection_ids.py +85 -0
  190. app/shared/core/project/loader/loader_parts/inspector_helpers.py +312 -0
  191. app/shared/core/project/loader/loader_parts/inspector_id_checks.py +274 -0
  192. app/shared/core/project/loader/loader_parts/inspector_reference_checks.py +690 -0
  193. app/shared/core/project/loader/loader_parts/inspector_uniqueness_checks.py +286 -0
  194. app/shared/core/project/loader/loader_parts/loading_error_messages.py +298 -0
  195. app/shared/core/project/loader/loader_parts/main.py +432 -0
  196. app/shared/core/project/loader/loader_parts/path_validation.py +117 -0
  197. app/shared/core/project/loader/loader_parts/runtime.py +125 -0
  198. app/shared/core/project/loader/types.py +246 -0
  199. app/shared/core/project/manifest/coverage.py +391 -0
  200. app/shared/core/project/manifest/reader.py +260 -0
  201. app/shared/core/project/manifest/types.py +91 -0
  202. app/shared/core/project/manifest/types_parts/__init__.py +60 -0
  203. app/shared/core/project/manifest/types_parts/constants.py +40 -0
  204. app/shared/core/project/manifest/types_parts/data_source.py +68 -0
  205. app/shared/core/project/manifest/types_parts/info.py +59 -0
  206. app/shared/core/project/manifest/types_parts/manifest.py +294 -0
  207. app/shared/core/project/manifest/types_parts/refs.py +153 -0
  208. app/shared/core/project/manifest/types_parts/settings.py +92 -0
  209. app/shared/core/project/manifest/types_parts/settings_file_processing.py +64 -0
  210. app/shared/core/project/manifest/types_parts/settings_script_security.py +78 -0
  211. app/shared/core/project/manifest/types_parts/settings_validation.py +83 -0
  212. app/shared/core/project/manifest/types_parts/template.py +66 -0
  213. app/shared/core/project/manifest/writer.py +262 -0
  214. app/shared/core/project/manual_data/__init__.py +27 -0
  215. app/shared/core/project/manual_data/types.py +75 -0
  216. app/shared/core/project/regex/reader.py +197 -0
  217. app/shared/core/project/regex/types.py +405 -0
  218. app/shared/core/project/regex/writer.py +123 -0
  219. app/shared/core/project/schema/reader.py +170 -0
  220. app/shared/core/project/schema/types.py +47 -0
  221. app/shared/core/project/schema/types_parts/__init__.py +36 -0
  222. app/shared/core/project/schema/types_parts/column.py +174 -0
  223. app/shared/core/project/schema/types_parts/column_utils.py +72 -0
  224. app/shared/core/project/schema/types_parts/constraint.py +165 -0
  225. app/shared/core/project/schema/types_parts/schema_id.py +66 -0
  226. app/shared/core/project/schema/types_parts/source.py +255 -0
  227. app/shared/core/project/schema/types_parts/source_options.py +347 -0
  228. app/shared/core/project/schema/types_parts/table.py +230 -0
  229. app/shared/core/project/schema/writer.py +139 -0
  230. app/shared/core/project/schema_ref_check.py +95 -0
  231. app/shared/core/project/template/__init__.py +27 -0
  232. app/shared/core/project/template/expander.py +263 -0
  233. app/shared/core/project/template/reader.py +120 -0
  234. app/shared/core/project/template/types.py +114 -0
  235. app/shared/core/project/transform/reader.py +76 -0
  236. app/shared/core/project/transform/types.py +116 -0
  237. app/shared/core/project/transform/writer.py +84 -0
  238. app/shared/core/pydantic_messages.py +59 -0
  239. app/shared/core/reporter/__init__.py +46 -0
  240. app/shared/core/reporter/reporter.py +220 -0
  241. app/shared/core/reporter/reporters/__init__.py +65 -0
  242. app/shared/core/reporter/reporters/base.py +188 -0
  243. app/shared/core/reporter/reporters/dingtalk_app_reporter.py +274 -0
  244. app/shared/core/reporter/reporters/email_reporter.py +271 -0
  245. app/shared/core/reporter/reporters/feishu_app_reporter.py +467 -0
  246. app/shared/core/reporter/reporters/local_file_reporter.py +208 -0
  247. app/shared/core/reporter/reporters/wecom_app_reporter.py +268 -0
  248. app/shared/core/utils/__init__.py +18 -0
  249. app/shared/core/utils/path_utils.py +60 -0
  250. app/shared/core/utils/regex_extract.py +113 -0
  251. app/shared/domain/__init__.py +114 -0
  252. app/shared/domain/constraints/__init__.py +76 -0
  253. app/shared/domain/constraints/allowed_values.py +211 -0
  254. app/shared/domain/constraints/base.py +141 -0
  255. app/shared/domain/constraints/charset.py +337 -0
  256. app/shared/domain/constraints/composite.py +174 -0
  257. app/shared/domain/constraints/condition_registry.py +130 -0
  258. app/shared/domain/constraints/conditional.py +629 -0
  259. app/shared/domain/constraints/date_logic.py +731 -0
  260. app/shared/domain/constraints/foreign_key.py +261 -0
  261. app/shared/domain/constraints/key_normalization.py +56 -0
  262. app/shared/domain/constraints/not_null.py +185 -0
  263. app/shared/domain/constraints/range.py +360 -0
  264. app/shared/domain/constraints/regex.py +218 -0
  265. app/shared/domain/constraints/scripted.py +276 -0
  266. app/shared/domain/constraints/unique.py +233 -0
  267. app/shared/domain/data_engine.py +384 -0
  268. app/shared/domain/data_types.py +113 -0
  269. app/shared/domain/data_types_parts/__init__.py +67 -0
  270. app/shared/domain/data_types_parts/base.py +204 -0
  271. app/shared/domain/data_types_parts/composite.py +250 -0
  272. app/shared/domain/data_types_parts/expression.py +202 -0
  273. app/shared/domain/data_types_parts/extracted.py +106 -0
  274. app/shared/domain/data_types_parts/json_types.py +234 -0
  275. app/shared/domain/data_types_parts/scalars.py +719 -0
  276. app/shared/domain/data_types_parts/sequence.py +129 -0
  277. app/shared/domain/dataset_schema.py +48 -0
  278. app/shared/domain/eval_sandbox.py +63 -0
  279. app/shared/domain/expression_system.py +366 -0
  280. app/shared/domain/regex_flags.py +53 -0
  281. app/shared/domain/schema/builder.py +223 -0
  282. app/shared/domain/schema/models.py +339 -0
  283. app/shared/domain/transforms/__init__.py +28 -0
  284. app/shared/domain/transforms/aggregate.py +141 -0
  285. app/shared/domain/transforms/base.py +171 -0
  286. app/shared/domain/transforms/cast_type.py +120 -0
  287. app/shared/domain/transforms/concat.py +103 -0
  288. app/shared/domain/transforms/conditional_assign.py +114 -0
  289. app/shared/domain/transforms/date_format.py +81 -0
  290. app/shared/domain/transforms/digits.py +77 -0
  291. app/shared/domain/transforms/drop_duplicates.py +94 -0
  292. app/shared/domain/transforms/fill_na.py +94 -0
  293. app/shared/domain/transforms/filter_rows.py +97 -0
  294. app/shared/domain/transforms/lookup.py +78 -0
  295. app/shared/domain/transforms/lower_case.py +70 -0
  296. app/shared/domain/transforms/map_value.py +90 -0
  297. app/shared/domain/transforms/math_expr.py +112 -0
  298. app/shared/domain/transforms/modulo.py +82 -0
  299. app/shared/domain/transforms/regex_extract.py +103 -0
  300. app/shared/domain/transforms/registry.py +95 -0
  301. app/shared/domain/transforms/replace.py +88 -0
  302. app/shared/domain/transforms/sort_rows.py +94 -0
  303. app/shared/domain/transforms/string_split.py +84 -0
  304. app/shared/domain/transforms/strip.py +73 -0
  305. app/shared/domain/transforms/substring.py +92 -0
  306. app/shared/domain/transforms/upper_case.py +70 -0
  307. app/shared/domain/transforms/weighted_sum.py +104 -0
  308. app/shared/domain/validation_constraints.py +69 -0
  309. app/shared/services/__init__.py +62 -0
  310. app/shared/services/ai/__init__.py +48 -0
  311. app/shared/services/ai/agent/__init__.py +40 -0
  312. app/shared/services/ai/agent/chat_tools/__init__.py +47 -0
  313. app/shared/services/ai/agent/chat_tools/apply_actions.py +592 -0
  314. app/shared/services/ai/agent/chat_tools/ask_user.py +237 -0
  315. app/shared/services/ai/agent/chat_tools/read_canvas.py +140 -0
  316. app/shared/services/ai/agent/chat_tools/read_project.py +160 -0
  317. app/shared/services/ai/agent/chat_tools/read_table.py +301 -0
  318. app/shared/services/ai/agent/chat_tools/schemas.py +105 -0
  319. app/shared/services/ai/agent/chat_tools/validate_table.py +131 -0
  320. app/shared/services/ai/agent/executor.py +554 -0
  321. app/shared/services/ai/agent/memory.py +256 -0
  322. app/shared/services/ai/agent/planner.py +282 -0
  323. app/shared/services/ai/agent/tool_registry.py +296 -0
  324. app/shared/services/ai/agent/tools/__init__.py +32 -0
  325. app/shared/services/ai/agent/tools/config_generate.py +133 -0
  326. app/shared/services/ai/agent/tools/config_refine.py +109 -0
  327. app/shared/services/ai/agent/tools/config_validate.py +403 -0
  328. app/shared/services/ai/agent/tools/merge_results.py +136 -0
  329. app/shared/services/ai/agent/tools/plan_chunks.py +79 -0
  330. app/shared/services/ai/agent/tools/script_parse.py +359 -0
  331. app/shared/services/ai/agent/types.py +138 -0
  332. app/shared/services/ai/chat_agent_runner.py +596 -0
  333. app/shared/services/ai/chat_orchestrator.py +725 -0
  334. app/shared/services/ai/failure_messages.py +65 -0
  335. app/shared/services/ai/job_storage.py +274 -0
  336. app/shared/services/ai/migrate_service.py +550 -0
  337. app/shared/services/ai/streaming/__init__.py +65 -0
  338. app/shared/services/ai/streaming/event_journal.py +197 -0
  339. app/shared/services/ai/streaming/orchestrator.py +262 -0
  340. app/shared/services/ai/streaming/pending_interaction_store.py +227 -0
  341. app/shared/services/ai/streaming/sse_response.py +148 -0
  342. app/shared/services/ai/streaming/types.py +73 -0
  343. app/shared/services/ai/types.py +213 -0
  344. app/shared/services/ai/utils.py +322 -0
  345. app/shared/services/diff/config_diff.py +320 -0
  346. app/shared/services/hardware.py +237 -0
  347. app/shared/services/llm/__init__.py +62 -0
  348. app/shared/services/llm/actions/__init__.py +42 -0
  349. app/shared/services/llm/actions/_canvas_validator.py +147 -0
  350. app/shared/services/llm/actions/_constraint_validator.py +401 -0
  351. app/shared/services/llm/actions/_regex_validator.py +85 -0
  352. app/shared/services/llm/actions/_schema_validator.py +82 -0
  353. app/shared/services/llm/actions/_settings_validator.py +76 -0
  354. app/shared/services/llm/actions/_transform_validator.py +78 -0
  355. app/shared/services/llm/actions/action_handlers.py +400 -0
  356. app/shared/services/llm/actions/action_parser.py +63 -0
  357. app/shared/services/llm/actions/action_processor.py +468 -0
  358. app/shared/services/llm/actions/action_validator.py +311 -0
  359. app/shared/services/llm/actions/diff_compute.py +195 -0
  360. app/shared/services/llm/actions/regex_handlers.py +284 -0
  361. app/shared/services/llm/actions/registry.py +336 -0
  362. app/shared/services/llm/actions/schema_handlers.py +335 -0
  363. app/shared/services/llm/actions/settings_handlers.py +180 -0
  364. app/shared/services/llm/actions/specs.py +259 -0
  365. app/shared/services/llm/actions/transform_handlers.py +279 -0
  366. app/shared/services/llm/actions/validation_types.py +147 -0
  367. app/shared/services/llm/cache/__init__.py +18 -0
  368. app/shared/services/llm/cache/response_cache.py +80 -0
  369. app/shared/services/llm/chat/__init__.py +35 -0
  370. app/shared/services/llm/chat/chat_system_prompt.py +561 -0
  371. app/shared/services/llm/chat/response_parser.py +296 -0
  372. app/shared/services/llm/config/__init__.py +45 -0
  373. app/shared/services/llm/config/crypto.py +135 -0
  374. app/shared/services/llm/config/loader.py +258 -0
  375. app/shared/services/llm/config/models.py +202 -0
  376. app/shared/services/llm/config/presets.py +128 -0
  377. app/shared/services/llm/config_generator.py +163 -0
  378. app/shared/services/llm/constraints/__init__.py +41 -0
  379. app/shared/services/llm/constraints/constraint_builder.py +177 -0
  380. app/shared/services/llm/constraints/constraint_deletion.py +84 -0
  381. app/shared/services/llm/constraints/constraint_id.py +174 -0
  382. app/shared/services/llm/constraints/frontend_instructions.py +333 -0
  383. app/shared/services/llm/constraints/inline_batch.py +279 -0
  384. app/shared/services/llm/discovery/__init__.py +35 -0
  385. app/shared/services/llm/discovery/scanner.py +181 -0
  386. app/shared/services/llm/generation/__init__.py +42 -0
  387. app/shared/services/llm/generation/agent_wiring.py +156 -0
  388. app/shared/services/llm/generation/config_builder.py +386 -0
  389. app/shared/services/llm/generation/errors.py +36 -0
  390. app/shared/services/llm/generation/existing_config.py +99 -0
  391. app/shared/services/llm/generation/profiler.py +279 -0
  392. app/shared/services/llm/generation/prompt_builder.py +199 -0
  393. app/shared/services/llm/generation/response_parser.py +144 -0
  394. app/shared/services/llm/generation/service.py +622 -0
  395. app/shared/services/llm/models.py +104 -0
  396. app/shared/services/llm/providers/__init__.py +48 -0
  397. app/shared/services/llm/providers/base.py +310 -0
  398. app/shared/services/llm/providers/cached_provider.py +90 -0
  399. app/shared/services/llm/providers/ollama.py +498 -0
  400. app/shared/services/llm/providers/openai.py +334 -0
  401. app/shared/services/llm/providers/registry.py +108 -0
  402. app/shared/services/llm/schema_resolver.py +141 -0
  403. app/shared/services/llm/suggestion_utils.py +219 -0
  404. app/shared/services/llm/validate_executor.py +163 -0
  405. app/shared/services/llm/yaml_io.py +406 -0
  406. app/shared/services/preview/__init__.py +18 -0
  407. app/shared/services/preview/loader.py +145 -0
  408. app/shared/services/preview/path_validation.py +138 -0
  409. app/shared/services/project_loader.py +57 -0
  410. app/shared/services/schema_inference.py +260 -0
  411. app/shared/services/schema_runtime_builder.py +120 -0
  412. app/shared/services/validation/__init__.py +52 -0
  413. app/shared/services/validation/chunked_loader.py +509 -0
  414. app/shared/services/validation/dag/__init__.py +35 -0
  415. app/shared/services/validation/dag/builder.py +141 -0
  416. app/shared/services/validation/dag/executor.py +225 -0
  417. app/shared/services/validation/dag/sorter.py +77 -0
  418. app/shared/services/validation/data_loader.py +231 -0
  419. app/shared/services/validation/engine.py +446 -0
  420. app/shared/services/validation/executor.py +1030 -0
  421. app/shared/services/validation/extractors.py +266 -0
  422. app/shared/services/validation/history.py +209 -0
  423. app/shared/services/validation/json_payload.py +136 -0
  424. app/shared/services/validation/loader.py +152 -0
  425. app/shared/services/validation/memory_monitor.py +179 -0
  426. app/shared/services/validation/postprocess.py +208 -0
  427. app/shared/services/validation/progress.py +64 -0
  428. app/shared/services/validation/report_export.py +222 -0
  429. app/shared/services/validation/resolver.py +180 -0
  430. app/shared/services/validation/service.py +418 -0
  431. app/shared/services/validation/types.py +238 -0
  432. app/shared/services/validation/validators/__init__.py +36 -0
  433. app/shared/services/validation/validators/adapter.py +182 -0
  434. app/shared/services/validation/validators/base.py +332 -0
  435. app/shared/services/validation/validators/composite.py +233 -0
  436. app/shared/services/validation/validators/date_logic.py +276 -0
  437. app/start_server.py +133 -0
  438. precis_cli-0.1.3.dist-info/METADATA +180 -0
  439. precis_cli-0.1.3.dist-info/RECORD +442 -0
  440. precis_cli-0.1.3.dist-info/WHEEL +5 -0
  441. precis_cli-0.1.3.dist-info/entry_points.txt +4 -0
  442. precis_cli-0.1.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,418 @@
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
+ - 提供所有数据校验的统一入口 UnifiedValidationService
21
+ - 采用注册表模式管理校验器,支持动态注册和获取
22
+ - 模块加载时自动注册所有内置校验器
23
+ - 支持 REGEX、UNIQUE、NOT_NULL、ALLOWED_VALUES、RANGE、FOREIGN_KEY 等校验类型
24
+ - 统一的 ValidationResult 返回格式简化调用方处理
25
+
26
+ 架构设计:
27
+ - 注册表模式(Registry Pattern): 解耦校验器与调用方
28
+ - 类方法实现: register_validator、get_validator、validate 均为类方法
29
+ - 校验器在模块导入时自动注册,服务立即可用
30
+ - 新增校验器只需注册,无需修改核心服务代码
31
+
32
+ 输入示例:
33
+ import pandas as pd
34
+ df = pd.DataFrame({
35
+ "email": ["test@example.com", "invalid-email", "user@domain.com"]
36
+ })
37
+ result = UnifiedValidationService.validate(
38
+ validation_type="regex",
39
+ df=df,
40
+ column="email",
41
+ regex_pattern=r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
42
+ )
43
+
44
+ 输出示例:
45
+ ValidationResult(
46
+ is_valid=False,
47
+ error_count=1,
48
+ total_rows=3,
49
+ error_rows=[
50
+ {"row_index": 1, "cell_value": "invalid-email",
51
+ "error_message": "值不符合正则表达式模式"}
52
+ ],
53
+ validation_time="0.015s"
54
+ )
55
+ """
56
+
57
+ import logging
58
+ from typing import Any
59
+
60
+ import pandas as pd
61
+
62
+ from .types import ValidationResult, ValidationType
63
+
64
+ logger = logging.getLogger(__name__)
65
+
66
+ from app.shared.domain.constraints.allowed_values import AllowedValuesConstraint
67
+ from app.shared.domain.constraints.charset import CharsetConstraint
68
+ from app.shared.domain.constraints.conditional import ConditionalConstraint
69
+ from app.shared.domain.constraints.foreign_key import ForeignKeyConstraints
70
+ from app.shared.domain.constraints.not_null import NotNullConstraint
71
+ from app.shared.domain.constraints.range import RangeConstraint
72
+ from app.shared.domain.constraints.regex import RegexConstraint
73
+ from app.shared.domain.constraints.scripted import ScriptedConstraint
74
+ from app.shared.domain.constraints.unique import UniqueConstraint
75
+
76
+ from .validators.adapter import ConstraintAdapter, PreCheck
77
+ from .validators.base import BaseValidator
78
+ from .validators.composite import CompositeValidator
79
+ from .validators.date_logic import DateLogicValidator
80
+
81
+
82
+ class UnifiedValidationService:
83
+ r"""
84
+ @classdesc 统一校验服务类 - 提供所有数据校验的统一入口
85
+
86
+ 本服务采用注册表模式(Registry Pattern):
87
+ - _validators: 校验器注册表,键为校验类型,值为校验器实例
88
+ - register_validator: 动态注册新的校验器
89
+ - get_validator: 根据校验类型获取对应的校验器
90
+ - validate: 执行校验的核心方法
91
+
92
+ 为什么采用注册表模式?
93
+ 1. 解耦:新增校验器不需要修改核心服务代码
94
+ 2. 可扩展:运行时可以动态注册新的校验器
95
+ 3. 可测试:可以轻松替换为 Mock 对象进行单元测试
96
+
97
+ 使用示例:
98
+ # 正则表达式校验
99
+ result = UnifiedValidationService.validate(
100
+ validation_type="regex",
101
+ df=dataframe,
102
+ column="email",
103
+ regex_pattern=r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
104
+ )
105
+
106
+ # 唯一性校验
107
+ result = UnifiedValidationService.validate(
108
+ validation_type="unique",
109
+ df=dataframe,
110
+ column="user_id"
111
+ )
112
+
113
+ # 非空校验
114
+ result = UnifiedValidationService.validate(
115
+ validation_type="not_null",
116
+ df=dataframe,
117
+ column="name"
118
+ )
119
+ """
120
+
121
+ # _validators: 校验器注册表
122
+ # 键:校验类型字符串(如 "regex", "unique")
123
+ # 值:对应的校验器实例(BaseValidator 子类)
124
+ # 生命周期:应用启动时注册(模块底部),验证时查询
125
+ # 【关键数据流】所有校验请求都通过此字典路由到具体校验器
126
+ _validators: dict[str, BaseValidator] = {}
127
+
128
+ @classmethod
129
+ def register_validator(cls, validation_type: str, validator: BaseValidator) -> None:
130
+ """
131
+ @methoddesc 注册校验器到统一服务
132
+
133
+ 允许在运行时动态添加新的校验器,支持插件化扩展,
134
+ 便于测试时替换为 Mock 对象。
135
+
136
+ 参数:
137
+ validation_type: 校验类型标识符(对应 ValidationType 常量)
138
+ validator: 校验器实例(需继承自 BaseValidator)
139
+
140
+ 副作用:
141
+ 将校验器添加到 _validators 字典
142
+ """
143
+ cls._validators[validation_type] = validator
144
+
145
+ @classmethod
146
+ def get_validator(cls, validation_type: str) -> BaseValidator | None:
147
+ """
148
+ @methoddesc 获取指定类型的校验器
149
+
150
+ 本方法是校验流程的路由核心,根据 validation_type 查找对应校验器。
151
+
152
+ 参数:
153
+ validation_type: 校验类型标识符
154
+
155
+ 返回:
156
+ 校验器实例,未找到返回 None
157
+ """
158
+ return cls._validators.get(validation_type)
159
+
160
+ @classmethod
161
+ def validate(cls, validation_type: str, df: pd.DataFrame, column: str, **kwargs: Any) -> ValidationResult:
162
+ """
163
+ @methoddesc 执行数据校验 - 统一入口方法
164
+
165
+ 调用方无需关心具体校验器的实现细节,只需传入校验类型和相关参数,
166
+ 返回结果格式统一,便于处理。
167
+
168
+ 参数:
169
+ validation_type: 校验类型(如 "regex", "unique", "range")
170
+ df: 待校验的 DataFrame
171
+ column: 待校验的列名
172
+ **kwargs: 校验器所需的额外参数
173
+ - regex: regex_pattern, regex_flags, match_mode
174
+ - range: min_value, max_value, boundary_mode
175
+ - allowed_values: allowed_values
176
+ - foreign_key: target_table, target_column, target_values
177
+ - 等等...
178
+
179
+ 返回:
180
+ ValidationResult: 包含校验是否通过、错误数量、错误详情等信息
181
+
182
+ 错误处理:
183
+ 如果校验器不存在,返回 is_valid=False 的结果
184
+
185
+ 性能考虑:
186
+ 校验器从注册表获取是 O(1) 操作
187
+ """
188
+ # Step 1: 获取校验器
189
+ # 【关键路由】根据 validation_type 从注册表获取对应校验器
190
+ validator = cls.get_validator(validation_type)
191
+
192
+ # Step 2: 校验器不存在时返回错误结果
193
+ # 【副作用】返回错误结果,调用方可通过 is_valid=False 判断
194
+ if not validator:
195
+ logger.warning(f"不支持的校验类型: {validation_type}")
196
+ return ValidationResult(
197
+ is_valid=False,
198
+ error_count=1,
199
+ total_rows=len(df),
200
+ error_rows=[
201
+ {"row_index": 0, "cell_value": None, "error_message": f"不支持的校验类型: {validation_type}"}
202
+ ],
203
+ validation_time="0.000s",
204
+ )
205
+
206
+ # Step 3: 执行实际校验
207
+ # 【核心逻辑】调用具体校验器的 validate 方法
208
+ # 传入 df, column 和 kwargs(包含各类校验参数)
209
+ logger.debug(f"执行校验: type={validation_type}, column={column}, rows={len(df)}")
210
+ result = validator.validate_with_error_handling(df, column, **kwargs)
211
+ logger.debug(f"校验完成: is_valid={result.is_valid}, errors={result.error_count}")
212
+ return result
213
+
214
+
215
+ # 模块初始化时注册所有内置校验器
216
+ # 【副作用】模块导入时自动执行,完成后服务即可用
217
+ #
218
+ # 注册策略:
219
+ # - ConstraintAdapter: 通过适配器配置消除独立 Validator 文件
220
+ # 支持预检、参数映射、自定义 datasets 和错误格式化
221
+ # - DateLogicValidator: 保留独立实现(日期预扫描逻辑过于特殊)
222
+ # - CompositeValidator: 元验证器,递归调用 UnifiedValidationService.validate
223
+
224
+
225
+ def _not_null_formatter(err: dict) -> dict:
226
+ return {
227
+ "row_index": err.get("row_index"),
228
+ "cell_value": None,
229
+ "error_message": err.get("message"),
230
+ }
231
+
232
+
233
+ def _conditional_kwargs_builder(column: str, kwargs: dict) -> dict:
234
+ if_conditions = kwargs.get("if_conditions") or []
235
+ then_condition = kwargs.get("then_condition") or kwargs.get("then_condition_config")
236
+ if if_conditions:
237
+ return {
238
+ "table": "temp",
239
+ "if_conditions": if_conditions,
240
+ "if_logic": kwargs.get("if_logic", "and"),
241
+ "then_column": column,
242
+ "then_condition": then_condition,
243
+ }
244
+ return {
245
+ "table": "temp",
246
+ "if_column": kwargs.get("if_column", ""),
247
+ "if_value": kwargs.get("if_value"),
248
+ "then_column": column,
249
+ "then_condition": then_condition,
250
+ }
251
+
252
+
253
+ def _conditional_pre_check(df: pd.DataFrame, column: str, kwargs: dict) -> str | None:
254
+ then_condition = kwargs.get("then_condition") or kwargs.get("then_condition_config")
255
+ if not then_condition:
256
+ return "条件校验配置不完整"
257
+ if_conditions = kwargs.get("if_conditions") or []
258
+ if not if_conditions and not kwargs.get("if_column"):
259
+ return "条件校验配置不完整"
260
+ return None
261
+
262
+
263
+ def _conditional_error_formatter(err: dict) -> dict:
264
+ return {
265
+ "row_index": err.get("row_index"),
266
+ "cell_value": err.get("value"),
267
+ "error_message": err.get("message"),
268
+ }
269
+
270
+
271
+ def _fk_kwargs_builder(column: str, kwargs: dict) -> dict:
272
+ return {
273
+ "from_table": "temp",
274
+ "from_column": column,
275
+ "to_table": "target",
276
+ "to_column": kwargs.get("target_column"),
277
+ }
278
+
279
+
280
+ def _fk_datasets_builder(df: pd.DataFrame, column: str, kwargs: dict) -> dict:
281
+ target_column = kwargs.get("target_column")
282
+ target_df = pd.DataFrame({target_column: kwargs.get("target_values", [])})
283
+ return {"temp": df, "target": target_df}
284
+
285
+
286
+ def _fk_pre_check(df: pd.DataFrame, column: str, kwargs: dict) -> str | None:
287
+ if not kwargs.get("target_table") or not kwargs.get("target_column"):
288
+ return "外键校验缺少目标表或目标列配置"
289
+ # target_values 缺失(键不存在或为 None)属于配置不完整:若按空目标表处理,
290
+ # 所有行都会被误报为外键冲突(数据错误),误导用户去修数据而非修配置。
291
+ # 注意区分:显式传入空列表 [] 是合法语义(目标表确认为空 → 全部冲突)。
292
+ if kwargs.get("target_values") is None:
293
+ return "外键校验缺少目标值列表(target_values)"
294
+ if column not in df.columns:
295
+ return f"列 '{column}' 不存在"
296
+ return None
297
+
298
+
299
+ def _scripted_kwargs_builder(column: str, kwargs: dict) -> dict:
300
+ return {
301
+ "table": "temp",
302
+ "name": kwargs.get("script_name", "custom_script"),
303
+ "expression": kwargs.get("script", ""),
304
+ "column": column,
305
+ }
306
+
307
+
308
+ def _scripted_error_formatter(err: dict) -> dict:
309
+ row_index = err.get("row_index")
310
+ if row_index is None:
311
+ row_index = 0
312
+ try:
313
+ row_index = int(row_index)
314
+ except (TypeError, ValueError):
315
+ row_index = 0
316
+ return {
317
+ "row_index": row_index,
318
+ "cell_value": str(err.get("value", "")),
319
+ "error_message": err.get("message", ""),
320
+ }
321
+
322
+
323
+ # --- ConstraintAdapter 注册 ---
324
+
325
+ UnifiedValidationService.register_validator(
326
+ ValidationType.UNIQUE,
327
+ ConstraintAdapter(UniqueConstraint, column_param="column"),
328
+ )
329
+
330
+ UnifiedValidationService.register_validator(
331
+ ValidationType.NOT_NULL,
332
+ ConstraintAdapter(NotNullConstraint, column_param="column", error_formatter=_not_null_formatter),
333
+ )
334
+
335
+ UnifiedValidationService.register_validator(
336
+ ValidationType.ALLOWED_VALUES,
337
+ ConstraintAdapter(
338
+ AllowedValuesConstraint,
339
+ column_param="column",
340
+ kwargs_mapping={"allowed_values": "allowed_values"},
341
+ ),
342
+ )
343
+
344
+ UnifiedValidationService.register_validator(
345
+ ValidationType.RANGE,
346
+ ConstraintAdapter(
347
+ RangeConstraint,
348
+ column_param="column",
349
+ kwargs_mapping={"min_value": "min_value", "max_value": "max_value", "boundary_mode": "boundary_mode"},
350
+ ),
351
+ )
352
+
353
+ UnifiedValidationService.register_validator(
354
+ ValidationType.REGEX,
355
+ ConstraintAdapter(
356
+ RegexConstraint,
357
+ column_param="column",
358
+ kwargs_mapping={
359
+ "regex_pattern": "pattern",
360
+ "regex_flags": "flags",
361
+ "match_mode": "match_mode",
362
+ "case_sensitive": "case_sensitive",
363
+ },
364
+ pre_checks=[PreCheck.column_exists(), PreCheck.param_required("regex_pattern")],
365
+ ),
366
+ )
367
+
368
+ UnifiedValidationService.register_validator(
369
+ ValidationType.FOREIGN_KEY,
370
+ ConstraintAdapter(
371
+ ForeignKeyConstraints,
372
+ column_param="from_column",
373
+ kwargs_builder=_fk_kwargs_builder,
374
+ datasets_builder=_fk_datasets_builder,
375
+ pre_checks=[_fk_pre_check],
376
+ ),
377
+ )
378
+
379
+ UnifiedValidationService.register_validator(
380
+ ValidationType.CONDITIONAL,
381
+ ConstraintAdapter(
382
+ ConditionalConstraint,
383
+ column_param="then_column",
384
+ kwargs_builder=_conditional_kwargs_builder,
385
+ pre_checks=[_conditional_pre_check],
386
+ error_formatter=_conditional_error_formatter,
387
+ ),
388
+ )
389
+
390
+ UnifiedValidationService.register_validator(
391
+ ValidationType.SCRIPTED,
392
+ ConstraintAdapter(
393
+ ScriptedConstraint,
394
+ column_param="column",
395
+ kwargs_builder=_scripted_kwargs_builder,
396
+ pre_checks=[PreCheck.param_required("script")],
397
+ error_formatter=_scripted_error_formatter,
398
+ constraint_kwargs_keys=["allow_unsafe_eval"],
399
+ ),
400
+ )
401
+
402
+ UnifiedValidationService.register_validator(
403
+ ValidationType.CHARSET,
404
+ ConstraintAdapter(
405
+ CharsetConstraint,
406
+ column_param="column",
407
+ kwargs_mapping={"charset_mode": "charset_mode"},
408
+ pre_checks=[PreCheck.column_exists()],
409
+ ),
410
+ )
411
+
412
+ # --- 独立 Validator 注册 ---
413
+
414
+ UnifiedValidationService.register_validator(ValidationType.DATE_LOGIC, DateLogicValidator())
415
+
416
+ UnifiedValidationService.register_validator(
417
+ ValidationType.COMPOSITE, CompositeValidator(validate_fn=UnifiedValidationService.validate)
418
+ )
@@ -0,0 +1,238 @@
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
+ - 定义 ValidationType 校验类型常量类,涵盖所有支持的校验规则
21
+ - 定义 ValidationResult 校验结果数据类,统一校验返回格式
22
+ - 支持 REGEX、UNIQUE、NOT_NULL、ALLOWED_VALUES、RANGE、FOREIGN_KEY 等类型
23
+ - 所有校验结果字段均为可选,支持渐进式填充
24
+
25
+ 架构设计:
26
+ - ValidationType 采用字符串常量类模式,便于 JSON 序列化和前端使用
27
+ - ValidationResult 采用数据类模式,提供清晰的校验结果封装
28
+ - 常量类设计便于扩展新类型,无需修改枚举定义
29
+
30
+ 输入示例:
31
+ # ValidationType 常量
32
+ validation_type = ValidationType.REGEX # "regex"
33
+
34
+ # ValidationResult 对象
35
+ result = ValidationResult(
36
+ is_valid=True,
37
+ error_count=0,
38
+ total_rows=100,
39
+ match_count=100,
40
+ error_rows=[],
41
+ validation_time="0.023s"
42
+ )
43
+
44
+ 输出示例:
45
+ # 校验失败时的错误详情
46
+ error_rows = [
47
+ {
48
+ "row_index": 5,
49
+ "cell_value": "invalid",
50
+ "error_message": "值不符合正则表达式模式"
51
+ }
52
+ ]
53
+ """
54
+
55
+
56
+ class ValidationType:
57
+ """
58
+ @classdesc 校验类型枚举类
59
+
60
+ 定义系统支持的所有数据校验规则类型。每个类型对应一种特定的数据验证逻辑,
61
+ 可通过 UnifiedValidationService 注册相应的验证器实现。
62
+
63
+ 为什么要定义这些类型?
64
+ - 提供类型安全的校验类型标识
65
+ - 集中管理所有支持的校验类型,便于扩展
66
+ - 前端可通过字符串类型选择校验方式
67
+
68
+ 扩展方式:
69
+ 1. 在此类中添加新的类型常量
70
+ 2. 在 service.py 中实现对应的验证器类
71
+ 3. 在验证器工厂中注册新验证器
72
+ """
73
+
74
+ # 正则表达式校验:验证数据是否符合指定的正则表达式模式
75
+ # 适用场景:邮箱、手机号、身份证号等格式验证
76
+ # 示例:验证 email 列是否符合邮箱格式 r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
77
+ REGEX = "regex"
78
+
79
+ # 唯一性校验:验证列中是否存在重复值
80
+ # 适用场景:主键、用户名、订单号等必须唯一的字段
81
+ # 示例:验证 user_id 列的所有值是否唯一
82
+ UNIQUE = "unique"
83
+
84
+ # 非空校验:验证单元格是否为必填项
85
+ # 适用场景:必填字段的强制性检查
86
+ # 示例:验证 name 列是否存在空值
87
+ NOT_NULL = "not_null"
88
+
89
+ # 允许值校验:验证数据是否在预定义的允许值列表中
90
+ # 适用场景:状态字段、类型字段等有限取值范围的验证
91
+ # 示例:验证 status 列是否在 ["active", "inactive", "pending"] 中
92
+ ALLOWED_VALUES = "allowed_values"
93
+
94
+ # 范围校验:验证数值或日期是否在指定范围内
95
+ # 适用场景:年龄、分数、日期区间等边界值验证
96
+ # 示例:验证 age 列是否在 0-150 范围内
97
+ RANGE = "range"
98
+
99
+ # 外键校验:验证数据是否引用了另一个表中的有效记录
100
+ # 适用场景:关联数据完整性验证
101
+ # 示例:验证 user_id 列的值是否在 users 表的 id 列中存在
102
+ FOREIGN_KEY = "foreign_key"
103
+
104
+ # 条件校验:根据条件表达式动态决定校验规则
105
+ # 适用场景:复杂业务规则验证
106
+ # 示例:当 status="vip" 时,discount 必须 <= 500
107
+ CONDITIONAL = "conditional"
108
+
109
+ # 脚本校验:使用自定义脚本进行复杂业务逻辑验证
110
+ # 适用场景:标准校验器无法满足的自定义规则
111
+ # 示例:验证 total == price * quantity
112
+ SCRIPTED = "scripted"
113
+
114
+ # 字符集校验:验证字符串是否符合特定字符集要求
115
+ # 适用场景:纯中文名、纯 ASCII 用户名等
116
+ # 示例:验证 name 列是否只包含中文字符
117
+ CHARSET = "charset"
118
+
119
+ # 日期逻辑校验:验证日期之间的逻辑关系(如开始日期早于结束日期)
120
+ # 适用场景:日期大小关系、年龄计算等
121
+ # 示例:验证 birth_date 是否在 2000-01-01 之前
122
+ DATE_LOGIC = "date_logic"
123
+
124
+ # 复合约束校验:将多个子约束按逻辑策略聚合校验
125
+ # 适用场景:需要同时满足多个条件的复杂业务规则
126
+ # 示例:验证 email 既非空又唯一
127
+ COMPOSITE = "composite"
128
+
129
+
130
+ class ValidationResult:
131
+ """
132
+ @classdesc 校验结果数据类
133
+
134
+ 封装数据校验的执行结果,包含校验状态、错误统计和详细信息。
135
+ 该类用于统一各类型校验器的返回格式,便于调用方统一处理。
136
+
137
+ 数据流说明:
138
+ 调用方接收 ValidationResult 后,通常执行以下判断:
139
+ 1. 检查 is_valid 布尔值(推荐)
140
+ 2. 检查 error_count 数量
141
+ 3. 遍历 error_rows 获取详细错误信息
142
+
143
+ Attributes:
144
+ is_valid: 校验是否通过,True 表示所有数据均符合规则
145
+ error_count: 错误行数,即不符合校验规则的记录数量
146
+ total_rows: 总行数,即被校验的数据总行数
147
+ match_count: 匹配行数,适用于唯一性等需要统计匹配数量的校验
148
+ error_rows: 错误详情列表,每项包含行索引、单元格值和错误信息
149
+ validation_time: 校验耗时,格式为秒(如 "0.123s")
150
+ """
151
+
152
+ def __init__(
153
+ self,
154
+ is_valid: bool,
155
+ error_count: int,
156
+ total_rows: int,
157
+ match_count: int | None = None,
158
+ error_rows: list[dict] | None = None,
159
+ validation_time: str = "0.000s",
160
+ ):
161
+ """
162
+ @methoddesc 初始化校验结果实例
163
+
164
+ Args:
165
+ is_valid: 校验是否通过
166
+ - True: 所有数据符合规则,error_count 应为 0
167
+ - False: 存在不符合规则的数据
168
+ error_count: 错误行数统计
169
+ - 为 0 时表示校验通过
170
+ - 大于 0 时表示存在错误
171
+ total_rows: 被校验数据的总行数
172
+ - 用于计算错误率
173
+ - 前端可显示 "错误行数/总行数"
174
+ match_count: 匹配行数(可选)
175
+ - 仅在唯一性校验时有意义
176
+ - 表示符合规则的数据行数
177
+ - 计算公式:total_rows - error_count
178
+ error_rows: 错误详情列表(可选)
179
+ - 每个元素包含 row_index、cell_value、error_message
180
+ - 默认为空列表,避免空引用异常
181
+ validation_time: 校验耗时字符串(可选)
182
+ - 格式为 "0.123s"(3位小数)
183
+ - 用于性能监控和用户等待提示
184
+ """
185
+ # 校验是否通过标记
186
+ # 【副作用】调用方通常首先检查此字段判断校验状态
187
+ self.is_valid = is_valid
188
+
189
+ # 错误行数统计
190
+ # 【关键数据流】error_count > 0 时,is_valid 应为 False
191
+ self.error_count = error_count
192
+
193
+ # 总行数
194
+ # 【用途】前端显示进度、计算错误率
195
+ self.total_rows = total_rows
196
+
197
+ # 匹配行数(可选)
198
+ # 【用途】唯一性校验时显示唯一值数量
199
+ self.match_count = match_count
200
+
201
+ # 错误行详情列表,使用 or [] 处理 None 传入,避免后续遍历时空引用异常
202
+ # 【关键数据流】包含错误位置的详细信息,供前端定位问题
203
+ self.error_rows = error_rows or []
204
+
205
+ # 校验耗时
206
+ # 【副作用】可能用于性能监控和 UI 加载提示
207
+ self.validation_time = validation_time
208
+
209
+
210
+ # ==============================================================================
211
+ # ValidationResult 使用示例
212
+ # ==============================================================================
213
+ # 示例 1: 校验通过
214
+ # result = ValidationResult(
215
+ # is_valid=True,
216
+ # error_count=0,
217
+ # total_rows=100,
218
+ # match_count=100,
219
+ # error_rows=[],
220
+ # validation_time="0.023s"
221
+ # )
222
+ # 解读:100 行数据全部通过校验,耗时 23 毫秒
223
+ #
224
+ # 示例 2: 校验失败
225
+ # result = ValidationResult(
226
+ # is_valid=False,
227
+ # error_count=3,
228
+ # total_rows=100,
229
+ # match_count=97,
230
+ # error_rows=[
231
+ # {"row_index": 5, "cell_value": "invalid_email", "error_message": "值 'invalid_email' 不符合正则表达式模式"},
232
+ # {"row_index": 15, "cell_value": "bad@format", "error_message": "值 'bad@format' 不符合正则表达式模式"},
233
+ # {"row_index": 88, "cell_value": "no_at_symbol", "error_message": "值 'no_at_symbol' 不符合正则表达式模式"}
234
+ # ],
235
+ # validation_time="0.015s"
236
+ # )
237
+ # 解读:100 行数据中有 3 行校验失败,分别在第 5、15、88 行
238
+ # ==============================================================================
@@ -0,0 +1,36 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ #
3
+ # Copyright 2026 Precis Team
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ """
17
+ @fileoverview 校验器模块入口
18
+
19
+ 功能概述:
20
+ - 导出所有内置数据校验器实现
21
+ - ConstraintAdapter 通用适配器替代大部分独立 Validator 文件
22
+ - DateLogicValidator 和 CompositeValidator 保留独立实现
23
+ """
24
+
25
+ from .adapter import ConstraintAdapter, PreCheck
26
+ from .base import BaseValidator
27
+ from .composite import CompositeValidator
28
+ from .date_logic import DateLogicValidator
29
+
30
+ __all__ = [
31
+ "BaseValidator",
32
+ "ConstraintAdapter",
33
+ "PreCheck",
34
+ "CompositeValidator",
35
+ "DateLogicValidator",
36
+ ]