pycharter 0.0.22__py3-none-any.whl → 0.0.24__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 (404) hide show
  1. api/main.py +27 -1
  2. api/models/docs.py +68 -0
  3. api/models/evolution.py +117 -0
  4. api/models/tracking.py +111 -0
  5. api/models/validation.py +46 -6
  6. api/routes/v1/__init__.py +14 -1
  7. api/routes/v1/docs.py +187 -0
  8. api/routes/v1/evolution.py +337 -0
  9. api/routes/v1/templates.py +211 -27
  10. api/routes/v1/tracking.py +301 -0
  11. api/routes/v1/validation.py +68 -31
  12. pycharter/__init__.py +268 -58
  13. pycharter/data/templates/contract/template_coercion_rules.yaml +57 -0
  14. pycharter/data/templates/contract/template_contract.yaml +122 -0
  15. pycharter/data/templates/contract/template_metadata.yaml +68 -0
  16. pycharter/data/templates/contract/template_schema.yaml +100 -0
  17. pycharter/data/templates/contract/template_validation_rules.yaml +75 -0
  18. pycharter/data/templates/etl/README.md +224 -0
  19. pycharter/data/templates/etl/extract_cloud_azure.yaml +24 -0
  20. pycharter/data/templates/etl/extract_cloud_gcs.yaml +25 -0
  21. pycharter/data/templates/etl/extract_cloud_s3.yaml +30 -0
  22. pycharter/data/templates/etl/extract_database.yaml +34 -0
  23. pycharter/data/templates/etl/extract_database_ssh.yaml +40 -0
  24. pycharter/data/templates/etl/extract_file_csv.yaml +21 -0
  25. pycharter/data/templates/etl/extract_file_glob.yaml +25 -0
  26. pycharter/data/templates/etl/extract_file_json.yaml +24 -0
  27. pycharter/data/templates/etl/extract_file_parquet.yaml +20 -0
  28. pycharter/data/templates/etl/extract_http_paginated.yaml +79 -0
  29. pycharter/data/templates/etl/extract_http_path_params.yaml +38 -0
  30. pycharter/data/templates/etl/extract_http_simple.yaml +62 -0
  31. pycharter/data/templates/etl/load_cloud_azure.yaml +24 -0
  32. pycharter/data/templates/etl/load_cloud_gcs.yaml +22 -0
  33. pycharter/data/templates/etl/load_cloud_s3.yaml +27 -0
  34. pycharter/data/templates/etl/load_file.yaml +34 -0
  35. pycharter/data/templates/etl/load_insert.yaml +18 -0
  36. pycharter/data/templates/etl/load_postgresql.yaml +39 -0
  37. pycharter/data/templates/etl/load_sqlite.yaml +21 -0
  38. pycharter/data/templates/etl/load_truncate_and_load.yaml +20 -0
  39. pycharter/data/templates/etl/load_upsert.yaml +25 -0
  40. pycharter/data/templates/etl/load_with_dlq.yaml +34 -0
  41. pycharter/data/templates/etl/load_with_ssh_tunnel.yaml +35 -0
  42. pycharter/data/templates/etl/pipeline_http_to_db.yaml +75 -0
  43. pycharter/data/templates/etl/transform_combined.yaml +48 -0
  44. pycharter/data/templates/etl/transform_custom_function.yaml +58 -0
  45. pycharter/data/templates/etl/transform_jsonata.yaml +51 -0
  46. pycharter/data/templates/etl/transform_simple.yaml +59 -0
  47. pycharter/db/schemas/.ipynb_checkpoints/data_contract-checkpoint.py +160 -0
  48. pycharter/docs_generator/__init__.py +43 -0
  49. pycharter/docs_generator/generator.py +465 -0
  50. pycharter/docs_generator/renderers.py +247 -0
  51. pycharter/etl_generator/__init__.py +168 -80
  52. pycharter/etl_generator/builder.py +121 -0
  53. pycharter/etl_generator/config_loader.py +394 -0
  54. pycharter/etl_generator/config_validator.py +418 -0
  55. pycharter/etl_generator/context.py +132 -0
  56. pycharter/etl_generator/expression.py +499 -0
  57. pycharter/etl_generator/extractors/__init__.py +30 -0
  58. pycharter/etl_generator/extractors/base.py +70 -0
  59. pycharter/etl_generator/extractors/cloud_storage.py +530 -0
  60. pycharter/etl_generator/extractors/database.py +221 -0
  61. pycharter/etl_generator/extractors/factory.py +185 -0
  62. pycharter/etl_generator/extractors/file.py +475 -0
  63. pycharter/etl_generator/extractors/http.py +895 -0
  64. pycharter/etl_generator/extractors/streaming.py +57 -0
  65. pycharter/etl_generator/loaders/__init__.py +41 -0
  66. pycharter/etl_generator/loaders/base.py +35 -0
  67. pycharter/etl_generator/loaders/cloud.py +87 -0
  68. pycharter/etl_generator/loaders/cloud_storage_loader.py +275 -0
  69. pycharter/etl_generator/loaders/database.py +274 -0
  70. pycharter/etl_generator/loaders/factory.py +180 -0
  71. pycharter/etl_generator/loaders/file.py +72 -0
  72. pycharter/etl_generator/loaders/file_loader.py +130 -0
  73. pycharter/etl_generator/pipeline.py +743 -0
  74. pycharter/etl_generator/protocols.py +54 -0
  75. pycharter/etl_generator/result.py +63 -0
  76. pycharter/etl_generator/schemas/__init__.py +49 -0
  77. pycharter/etl_generator/transformers/__init__.py +49 -0
  78. pycharter/etl_generator/transformers/base.py +63 -0
  79. pycharter/etl_generator/transformers/config.py +45 -0
  80. pycharter/etl_generator/transformers/custom_function.py +101 -0
  81. pycharter/etl_generator/transformers/jsonata_transformer.py +56 -0
  82. pycharter/etl_generator/transformers/operations.py +218 -0
  83. pycharter/etl_generator/transformers/pipeline.py +54 -0
  84. pycharter/etl_generator/transformers/simple_operations.py +131 -0
  85. pycharter/quality/__init__.py +25 -0
  86. pycharter/quality/tracking/__init__.py +64 -0
  87. pycharter/quality/tracking/collector.py +318 -0
  88. pycharter/quality/tracking/exporters.py +238 -0
  89. pycharter/quality/tracking/models.py +194 -0
  90. pycharter/quality/tracking/store.py +385 -0
  91. pycharter/runtime_validator/__init__.py +20 -7
  92. pycharter/runtime_validator/builder.py +328 -0
  93. pycharter/runtime_validator/validator.py +311 -7
  94. pycharter/runtime_validator/validator_core.py +61 -0
  95. pycharter/schema_evolution/__init__.py +61 -0
  96. pycharter/schema_evolution/compatibility.py +270 -0
  97. pycharter/schema_evolution/diff.py +496 -0
  98. pycharter/schema_evolution/models.py +201 -0
  99. pycharter/shared/__init__.py +56 -0
  100. pycharter/shared/errors.py +296 -0
  101. pycharter/shared/protocols.py +234 -0
  102. {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/METADATA +146 -26
  103. pycharter-0.0.24.dist-info/RECORD +543 -0
  104. {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/WHEEL +1 -1
  105. ui/static/404/index.html +1 -1
  106. ui/static/404.html +1 -1
  107. ui/static/__next.__PAGE__.txt +1 -1
  108. ui/static/__next._full.txt +1 -1
  109. ui/static/__next._head.txt +1 -1
  110. ui/static/__next._index.txt +1 -1
  111. ui/static/__next._tree.txt +1 -1
  112. ui/static/_next/static/chunks/26dfc590f7714c03.js +1 -0
  113. ui/static/_next/static/chunks/34d289e6db2ef551.js +1 -0
  114. ui/static/_next/static/chunks/99508d9d5869cc27.js +1 -0
  115. ui/static/_next/static/chunks/b313c35a6ba76574.js +1 -0
  116. ui/static/_not-found/__next._full.txt +1 -1
  117. ui/static/_not-found/__next._head.txt +1 -1
  118. ui/static/_not-found/__next._index.txt +1 -1
  119. ui/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
  120. ui/static/_not-found/__next._not-found.txt +1 -1
  121. ui/static/_not-found/__next._tree.txt +1 -1
  122. ui/static/_not-found/index.html +1 -1
  123. ui/static/_not-found/index.txt +1 -1
  124. ui/static/contracts/__next._full.txt +2 -2
  125. ui/static/contracts/__next._head.txt +1 -1
  126. ui/static/contracts/__next._index.txt +1 -1
  127. ui/static/contracts/__next._tree.txt +1 -1
  128. ui/static/contracts/__next.contracts.__PAGE__.txt +2 -2
  129. ui/static/contracts/__next.contracts.txt +1 -1
  130. ui/static/contracts/index.html +1 -1
  131. ui/static/contracts/index.txt +2 -2
  132. ui/static/documentation/__next._full.txt +1 -1
  133. ui/static/documentation/__next._head.txt +1 -1
  134. ui/static/documentation/__next._index.txt +1 -1
  135. ui/static/documentation/__next._tree.txt +1 -1
  136. ui/static/documentation/__next.documentation.__PAGE__.txt +1 -1
  137. ui/static/documentation/__next.documentation.txt +1 -1
  138. ui/static/documentation/index.html +2 -2
  139. ui/static/documentation/index.txt +1 -1
  140. ui/static/index.html +1 -1
  141. ui/static/index.txt +1 -1
  142. ui/static/metadata/__next._full.txt +1 -1
  143. ui/static/metadata/__next._head.txt +1 -1
  144. ui/static/metadata/__next._index.txt +1 -1
  145. ui/static/metadata/__next._tree.txt +1 -1
  146. ui/static/metadata/__next.metadata.__PAGE__.txt +1 -1
  147. ui/static/metadata/__next.metadata.txt +1 -1
  148. ui/static/metadata/index.html +1 -1
  149. ui/static/metadata/index.txt +1 -1
  150. ui/static/quality/__next._full.txt +2 -2
  151. ui/static/quality/__next._head.txt +1 -1
  152. ui/static/quality/__next._index.txt +1 -1
  153. ui/static/quality/__next._tree.txt +1 -1
  154. ui/static/quality/__next.quality.__PAGE__.txt +2 -2
  155. ui/static/quality/__next.quality.txt +1 -1
  156. ui/static/quality/index.html +2 -2
  157. ui/static/quality/index.txt +2 -2
  158. ui/static/rules/__next._full.txt +1 -1
  159. ui/static/rules/__next._head.txt +1 -1
  160. ui/static/rules/__next._index.txt +1 -1
  161. ui/static/rules/__next._tree.txt +1 -1
  162. ui/static/rules/__next.rules.__PAGE__.txt +1 -1
  163. ui/static/rules/__next.rules.txt +1 -1
  164. ui/static/rules/index.html +1 -1
  165. ui/static/rules/index.txt +1 -1
  166. ui/static/schemas/__next._full.txt +1 -1
  167. ui/static/schemas/__next._head.txt +1 -1
  168. ui/static/schemas/__next._index.txt +1 -1
  169. ui/static/schemas/__next._tree.txt +1 -1
  170. ui/static/schemas/__next.schemas.__PAGE__.txt +1 -1
  171. ui/static/schemas/__next.schemas.txt +1 -1
  172. ui/static/schemas/index.html +1 -1
  173. ui/static/schemas/index.txt +1 -1
  174. ui/static/settings/__next._full.txt +1 -1
  175. ui/static/settings/__next._head.txt +1 -1
  176. ui/static/settings/__next._index.txt +1 -1
  177. ui/static/settings/__next._tree.txt +1 -1
  178. ui/static/settings/__next.settings.__PAGE__.txt +1 -1
  179. ui/static/settings/__next.settings.txt +1 -1
  180. ui/static/settings/index.html +1 -1
  181. ui/static/settings/index.txt +1 -1
  182. ui/static/static/404/index.html +1 -1
  183. ui/static/static/404.html +1 -1
  184. ui/static/static/__next.__PAGE__.txt +1 -1
  185. ui/static/static/__next._full.txt +2 -2
  186. ui/static/static/__next._head.txt +1 -1
  187. ui/static/static/__next._index.txt +2 -2
  188. ui/static/static/__next._tree.txt +2 -2
  189. ui/static/static/_next/static/chunks/13d4a0fbd74c1ee4.js +1 -0
  190. ui/static/static/_next/static/chunks/2edb43b48432ac04.js +441 -0
  191. ui/static/static/_next/static/chunks/d2363397e1b2bcab.css +1 -0
  192. ui/static/static/_next/static/chunks/f7d1a90dd75d2572.js +1 -0
  193. ui/static/static/_not-found/__next._full.txt +2 -2
  194. ui/static/static/_not-found/__next._head.txt +1 -1
  195. ui/static/static/_not-found/__next._index.txt +2 -2
  196. ui/static/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
  197. ui/static/static/_not-found/__next._not-found.txt +1 -1
  198. ui/static/static/_not-found/__next._tree.txt +2 -2
  199. ui/static/static/_not-found/index.html +1 -1
  200. ui/static/static/_not-found/index.txt +2 -2
  201. ui/static/static/contracts/__next._full.txt +3 -3
  202. ui/static/static/contracts/__next._head.txt +1 -1
  203. ui/static/static/contracts/__next._index.txt +2 -2
  204. ui/static/static/contracts/__next._tree.txt +2 -2
  205. ui/static/static/contracts/__next.contracts.__PAGE__.txt +2 -2
  206. ui/static/static/contracts/__next.contracts.txt +1 -1
  207. ui/static/static/contracts/index.html +1 -1
  208. ui/static/static/contracts/index.txt +3 -3
  209. ui/static/static/documentation/__next._full.txt +3 -3
  210. ui/static/static/documentation/__next._head.txt +1 -1
  211. ui/static/static/documentation/__next._index.txt +2 -2
  212. ui/static/static/documentation/__next._tree.txt +2 -2
  213. ui/static/static/documentation/__next.documentation.__PAGE__.txt +2 -2
  214. ui/static/static/documentation/__next.documentation.txt +1 -1
  215. ui/static/static/documentation/index.html +2 -2
  216. ui/static/static/documentation/index.txt +3 -3
  217. ui/static/static/index.html +1 -1
  218. ui/static/static/index.txt +2 -2
  219. ui/static/static/metadata/__next._full.txt +2 -2
  220. ui/static/static/metadata/__next._head.txt +1 -1
  221. ui/static/static/metadata/__next._index.txt +2 -2
  222. ui/static/static/metadata/__next._tree.txt +2 -2
  223. ui/static/static/metadata/__next.metadata.__PAGE__.txt +1 -1
  224. ui/static/static/metadata/__next.metadata.txt +1 -1
  225. ui/static/static/metadata/index.html +1 -1
  226. ui/static/static/metadata/index.txt +2 -2
  227. ui/static/static/quality/__next._full.txt +2 -2
  228. ui/static/static/quality/__next._head.txt +1 -1
  229. ui/static/static/quality/__next._index.txt +2 -2
  230. ui/static/static/quality/__next._tree.txt +2 -2
  231. ui/static/static/quality/__next.quality.__PAGE__.txt +1 -1
  232. ui/static/static/quality/__next.quality.txt +1 -1
  233. ui/static/static/quality/index.html +2 -2
  234. ui/static/static/quality/index.txt +2 -2
  235. ui/static/static/rules/__next._full.txt +2 -2
  236. ui/static/static/rules/__next._head.txt +1 -1
  237. ui/static/static/rules/__next._index.txt +2 -2
  238. ui/static/static/rules/__next._tree.txt +2 -2
  239. ui/static/static/rules/__next.rules.__PAGE__.txt +1 -1
  240. ui/static/static/rules/__next.rules.txt +1 -1
  241. ui/static/static/rules/index.html +1 -1
  242. ui/static/static/rules/index.txt +2 -2
  243. ui/static/static/schemas/__next._full.txt +2 -2
  244. ui/static/static/schemas/__next._head.txt +1 -1
  245. ui/static/static/schemas/__next._index.txt +2 -2
  246. ui/static/static/schemas/__next._tree.txt +2 -2
  247. ui/static/static/schemas/__next.schemas.__PAGE__.txt +1 -1
  248. ui/static/static/schemas/__next.schemas.txt +1 -1
  249. ui/static/static/schemas/index.html +1 -1
  250. ui/static/static/schemas/index.txt +2 -2
  251. ui/static/static/settings/__next._full.txt +2 -2
  252. ui/static/static/settings/__next._head.txt +1 -1
  253. ui/static/static/settings/__next._index.txt +2 -2
  254. ui/static/static/settings/__next._tree.txt +2 -2
  255. ui/static/static/settings/__next.settings.__PAGE__.txt +1 -1
  256. ui/static/static/settings/__next.settings.txt +1 -1
  257. ui/static/static/settings/index.html +1 -1
  258. ui/static/static/settings/index.txt +2 -2
  259. ui/static/static/static/.gitkeep +0 -0
  260. ui/static/static/static/404/index.html +1 -0
  261. ui/static/static/static/404.html +1 -0
  262. ui/static/static/static/__next.__PAGE__.txt +10 -0
  263. ui/static/static/static/__next._full.txt +30 -0
  264. ui/static/static/static/__next._head.txt +7 -0
  265. ui/static/static/static/__next._index.txt +9 -0
  266. ui/static/static/static/__next._tree.txt +2 -0
  267. ui/static/static/static/_next/static/chunks/222442f6da32302a.js +1 -0
  268. ui/static/static/static/_next/static/chunks/247eb132b7f7b574.js +1 -0
  269. ui/static/static/static/_next/static/chunks/297d55555b71baba.js +1 -0
  270. ui/static/static/static/_next/static/chunks/2ab439ce003cd691.js +1 -0
  271. ui/static/static/static/_next/static/chunks/414e77373f8ff61c.js +1 -0
  272. ui/static/static/static/_next/static/chunks/49ca65abd26ae49e.js +1 -0
  273. ui/static/static/static/_next/static/chunks/652ad0aa26265c47.js +2 -0
  274. ui/static/static/static/_next/static/chunks/9667e7a3d359eb39.js +1 -0
  275. ui/static/static/static/_next/static/chunks/9c23f44fff36548a.js +1 -0
  276. ui/static/static/static/_next/static/chunks/a6dad97d9634a72d.js +1 -0
  277. ui/static/static/static/_next/static/chunks/b32a0963684b9933.js +4 -0
  278. ui/static/static/static/_next/static/chunks/c69f6cba366bd988.js +1 -0
  279. ui/static/static/static/_next/static/chunks/db913959c675cea6.js +1 -0
  280. ui/static/static/static/_next/static/chunks/f061a4be97bfc3b3.js +1 -0
  281. ui/static/static/static/_next/static/chunks/f2e7afeab1178138.js +1 -0
  282. ui/static/static/static/_next/static/chunks/ff1a16fafef87110.js +1 -0
  283. ui/static/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +3 -0
  284. ui/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_buildManifest.js +11 -0
  285. ui/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_ssgManifest.js +1 -0
  286. ui/static/static/static/_not-found/__next._full.txt +17 -0
  287. ui/static/static/static/_not-found/__next._head.txt +7 -0
  288. ui/static/static/static/_not-found/__next._index.txt +9 -0
  289. ui/static/static/static/_not-found/__next._not-found.__PAGE__.txt +5 -0
  290. ui/static/static/static/_not-found/__next._not-found.txt +4 -0
  291. ui/static/static/static/_not-found/__next._tree.txt +2 -0
  292. ui/static/static/static/_not-found/index.html +1 -0
  293. ui/static/static/static/_not-found/index.txt +17 -0
  294. ui/static/static/static/contracts/__next._full.txt +21 -0
  295. ui/static/static/static/contracts/__next._head.txt +7 -0
  296. ui/static/static/static/contracts/__next._index.txt +9 -0
  297. ui/static/static/static/contracts/__next._tree.txt +2 -0
  298. ui/static/static/static/contracts/__next.contracts.__PAGE__.txt +9 -0
  299. ui/static/static/static/contracts/__next.contracts.txt +4 -0
  300. ui/static/static/static/contracts/index.html +1 -0
  301. ui/static/static/static/contracts/index.txt +21 -0
  302. ui/static/static/static/documentation/__next._full.txt +21 -0
  303. ui/static/static/static/documentation/__next._head.txt +7 -0
  304. ui/static/static/static/documentation/__next._index.txt +9 -0
  305. ui/static/static/static/documentation/__next._tree.txt +2 -0
  306. ui/static/static/static/documentation/__next.documentation.__PAGE__.txt +9 -0
  307. ui/static/static/static/documentation/__next.documentation.txt +4 -0
  308. ui/static/static/static/documentation/index.html +93 -0
  309. ui/static/static/static/documentation/index.txt +21 -0
  310. ui/static/static/static/index.html +1 -0
  311. ui/static/static/static/index.txt +30 -0
  312. ui/static/static/static/metadata/__next._full.txt +21 -0
  313. ui/static/static/static/metadata/__next._head.txt +7 -0
  314. ui/static/static/static/metadata/__next._index.txt +9 -0
  315. ui/static/static/static/metadata/__next._tree.txt +2 -0
  316. ui/static/static/static/metadata/__next.metadata.__PAGE__.txt +9 -0
  317. ui/static/static/static/metadata/__next.metadata.txt +4 -0
  318. ui/static/static/static/metadata/index.html +1 -0
  319. ui/static/static/static/metadata/index.txt +21 -0
  320. ui/static/static/static/quality/__next._full.txt +21 -0
  321. ui/static/static/static/quality/__next._head.txt +7 -0
  322. ui/static/static/static/quality/__next._index.txt +9 -0
  323. ui/static/static/static/quality/__next._tree.txt +2 -0
  324. ui/static/static/static/quality/__next.quality.__PAGE__.txt +9 -0
  325. ui/static/static/static/quality/__next.quality.txt +4 -0
  326. ui/static/static/static/quality/index.html +2 -0
  327. ui/static/static/static/quality/index.txt +21 -0
  328. ui/static/static/static/rules/__next._full.txt +21 -0
  329. ui/static/static/static/rules/__next._head.txt +7 -0
  330. ui/static/static/static/rules/__next._index.txt +9 -0
  331. ui/static/static/static/rules/__next._tree.txt +2 -0
  332. ui/static/static/static/rules/__next.rules.__PAGE__.txt +9 -0
  333. ui/static/static/static/rules/__next.rules.txt +4 -0
  334. ui/static/static/static/rules/index.html +1 -0
  335. ui/static/static/static/rules/index.txt +21 -0
  336. ui/static/static/static/schemas/__next._full.txt +21 -0
  337. ui/static/static/static/schemas/__next._head.txt +7 -0
  338. ui/static/static/static/schemas/__next._index.txt +9 -0
  339. ui/static/static/static/schemas/__next._tree.txt +2 -0
  340. ui/static/static/static/schemas/__next.schemas.__PAGE__.txt +9 -0
  341. ui/static/static/static/schemas/__next.schemas.txt +4 -0
  342. ui/static/static/static/schemas/index.html +1 -0
  343. ui/static/static/static/schemas/index.txt +21 -0
  344. ui/static/static/static/settings/__next._full.txt +21 -0
  345. ui/static/static/static/settings/__next._head.txt +7 -0
  346. ui/static/static/static/settings/__next._index.txt +9 -0
  347. ui/static/static/static/settings/__next._tree.txt +2 -0
  348. ui/static/static/static/settings/__next.settings.__PAGE__.txt +9 -0
  349. ui/static/static/static/settings/__next.settings.txt +4 -0
  350. ui/static/static/static/settings/index.html +1 -0
  351. ui/static/static/static/settings/index.txt +21 -0
  352. ui/static/static/static/validation/__next._full.txt +21 -0
  353. ui/static/static/static/validation/__next._head.txt +7 -0
  354. ui/static/static/static/validation/__next._index.txt +9 -0
  355. ui/static/static/static/validation/__next._tree.txt +2 -0
  356. ui/static/static/static/validation/__next.validation.__PAGE__.txt +9 -0
  357. ui/static/static/static/validation/__next.validation.txt +4 -0
  358. ui/static/static/static/validation/index.html +1 -0
  359. ui/static/static/static/validation/index.txt +21 -0
  360. ui/static/static/validation/__next._full.txt +2 -2
  361. ui/static/static/validation/__next._head.txt +1 -1
  362. ui/static/static/validation/__next._index.txt +2 -2
  363. ui/static/static/validation/__next._tree.txt +2 -2
  364. ui/static/static/validation/__next.validation.__PAGE__.txt +1 -1
  365. ui/static/static/validation/__next.validation.txt +1 -1
  366. ui/static/static/validation/index.html +1 -1
  367. ui/static/static/validation/index.txt +2 -2
  368. ui/static/validation/__next._full.txt +2 -2
  369. ui/static/validation/__next._head.txt +1 -1
  370. ui/static/validation/__next._index.txt +1 -1
  371. ui/static/validation/__next._tree.txt +1 -1
  372. ui/static/validation/__next.validation.__PAGE__.txt +2 -2
  373. ui/static/validation/__next.validation.txt +1 -1
  374. ui/static/validation/index.html +1 -1
  375. ui/static/validation/index.txt +2 -2
  376. pycharter/data/templates/template_coercion_rules.yaml +0 -15
  377. pycharter/data/templates/template_contract.yaml +0 -587
  378. pycharter/data/templates/template_metadata.yaml +0 -38
  379. pycharter/data/templates/template_schema.yaml +0 -22
  380. pycharter/data/templates/template_transform_advanced.yaml +0 -50
  381. pycharter/data/templates/template_transform_simple.yaml +0 -59
  382. pycharter/data/templates/template_validation_rules.yaml +0 -29
  383. pycharter/etl_generator/extraction.py +0 -916
  384. pycharter/etl_generator/factory.py +0 -174
  385. pycharter/etl_generator/orchestrator.py +0 -1650
  386. pycharter/integrations/__init__.py +0 -19
  387. pycharter/integrations/kafka.py +0 -178
  388. pycharter/integrations/streaming.py +0 -100
  389. pycharter-0.0.22.dist-info/RECORD +0 -358
  390. {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/entry_points.txt +0 -0
  391. {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/licenses/LICENSE +0 -0
  392. {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/top_level.txt +0 -0
  393. /ui/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_buildManifest.js +0 -0
  394. /ui/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_ssgManifest.js +0 -0
  395. /ui/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_buildManifest.js +0 -0
  396. /ui/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_ssgManifest.js +0 -0
  397. /ui/static/{_next → static/_next}/static/chunks/c4fa4f4114b7c352.js +0 -0
  398. /ui/static/static/{_next → static/_next}/static/chunks/4e310fe5005770a3.css +0 -0
  399. /ui/static/{_next → static/static/_next}/static/chunks/5e04d10c4a7b58a3.js +0 -0
  400. /ui/static/static/{_next → static/_next}/static/chunks/5fc14c00a2779dc5.js +0 -0
  401. /ui/static/{_next → static/static/_next}/static/chunks/75d88a058d8ffaa6.js +0 -0
  402. /ui/static/{_next → static/static/_next}/static/chunks/8c89634cf6bad76f.js +0 -0
  403. /ui/static/static/{_next → static/_next}/static/chunks/b584574fdc8ab13e.js +0 -0
  404. /ui/static/static/{_next → static/_next}/static/chunks/d5989c94d3614b3a.js +0 -0
@@ -0,0 +1,301 @@
1
+ """
2
+ API routes for quality tracking (time-series metrics).
3
+
4
+ Provides endpoints to query, record, and export validation metrics
5
+ for quality monitoring and trend analysis.
6
+ """
7
+
8
+ from datetime import datetime
9
+ from typing import Optional
10
+
11
+ from fastapi import APIRouter, HTTPException, Query
12
+
13
+ from api.models.tracking import (
14
+ ExportFormat,
15
+ ExportResponse,
16
+ MetricsQueryResponse,
17
+ MetricsSummaryResponse,
18
+ RecordMetricRequest,
19
+ SchemaListResponse,
20
+ ValidationMetricResponse,
21
+ )
22
+ from pycharter.quality.tracking import (
23
+ MetricsCollector,
24
+ InMemoryMetricsStore,
25
+ ValidationMetric,
26
+ export_json,
27
+ export_prometheus,
28
+ )
29
+ from pycharter.quality.tracking.exporters import export_csv
30
+
31
+ router = APIRouter(prefix="/quality/tracking", tags=["Quality Tracking"])
32
+
33
+ # Global metrics collector (in production, this would be configured via dependency injection)
34
+ # For now, use in-memory store that persists for the lifetime of the API
35
+ _store = InMemoryMetricsStore(max_metrics=50000)
36
+ _collector = MetricsCollector(_store)
37
+
38
+
39
+ def get_collector() -> MetricsCollector:
40
+ """Get the global metrics collector."""
41
+ return _collector
42
+
43
+
44
+ @router.get(
45
+ "",
46
+ response_model=MetricsQueryResponse,
47
+ summary="Query validation metrics",
48
+ description="Query validation metrics with optional filters for schema, time range, and quality thresholds",
49
+ )
50
+ async def query_metrics(
51
+ schema_name: Optional[str] = Query(default=None, description="Filter by schema name"),
52
+ version: Optional[str] = Query(default=None, description="Filter by schema version"),
53
+ since: Optional[datetime] = Query(default=None, description="Filter metrics after this time"),
54
+ until: Optional[datetime] = Query(default=None, description="Filter metrics before this time"),
55
+ min_validity_rate: Optional[float] = Query(
56
+ default=None, ge=0.0, le=1.0, description="Filter by minimum validity rate"
57
+ ),
58
+ limit: int = Query(default=100, ge=1, le=1000, description="Maximum results to return"),
59
+ offset: int = Query(default=0, ge=0, description="Pagination offset"),
60
+ ) -> MetricsQueryResponse:
61
+ """
62
+ Query validation metrics with filters.
63
+
64
+ Returns metrics ordered by timestamp (most recent first).
65
+ """
66
+ collector = get_collector()
67
+
68
+ metrics = collector.query(
69
+ schema_name=schema_name,
70
+ version=version,
71
+ since=since,
72
+ until=until,
73
+ min_validity_rate=min_validity_rate,
74
+ limit=limit,
75
+ offset=offset,
76
+ )
77
+
78
+ # Get total count (without pagination)
79
+ all_metrics = collector.query(
80
+ schema_name=schema_name,
81
+ version=version,
82
+ since=since,
83
+ until=until,
84
+ min_validity_rate=min_validity_rate,
85
+ limit=0, # No limit to get total count
86
+ )
87
+
88
+ return MetricsQueryResponse(
89
+ metrics=[
90
+ ValidationMetricResponse(
91
+ id=m.id,
92
+ schema_name=m.schema_name,
93
+ version=m.version,
94
+ timestamp=m.timestamp,
95
+ record_count=m.record_count,
96
+ valid_count=m.valid_count,
97
+ error_count=m.error_count,
98
+ validity_rate=m.validity_rate,
99
+ completeness=m.completeness,
100
+ field_completeness=m.field_completeness,
101
+ duration_ms=m.duration_ms,
102
+ errors_by_type=m.errors_by_type,
103
+ metadata=m.metadata,
104
+ )
105
+ for m in metrics
106
+ ],
107
+ total=len(all_metrics),
108
+ limit=limit,
109
+ offset=offset,
110
+ )
111
+
112
+
113
+ @router.get(
114
+ "/schemas",
115
+ response_model=SchemaListResponse,
116
+ summary="List schemas with metrics",
117
+ description="Get list of all schema names that have recorded metrics",
118
+ )
119
+ async def list_schemas() -> SchemaListResponse:
120
+ """Get list of all schemas with recorded metrics."""
121
+ collector = get_collector()
122
+ schemas = collector.get_all_schemas()
123
+ return SchemaListResponse(schemas=sorted(schemas))
124
+
125
+
126
+ @router.get(
127
+ "/{schema_name}/summary",
128
+ response_model=MetricsSummaryResponse,
129
+ summary="Get metrics summary for a schema",
130
+ description="Get aggregated metrics summary for a schema within a time window",
131
+ )
132
+ async def get_summary(
133
+ schema_name: str,
134
+ window_hours: int = Query(default=24, ge=1, le=720, description="Hours to look back"),
135
+ ) -> MetricsSummaryResponse:
136
+ """
137
+ Get aggregated summary for a schema.
138
+
139
+ Returns aggregated statistics for all validation runs
140
+ within the specified time window.
141
+ """
142
+ collector = get_collector()
143
+ summary = collector.get_summary(schema_name, window_hours=window_hours)
144
+
145
+ return MetricsSummaryResponse(
146
+ schema_name=summary.schema_name,
147
+ period_start=summary.period_start,
148
+ period_end=summary.period_end,
149
+ total_validations=summary.total_validations,
150
+ total_records=summary.total_records,
151
+ total_valid=summary.total_valid,
152
+ total_errors=summary.total_errors,
153
+ avg_validity_rate=summary.avg_validity_rate,
154
+ min_validity_rate=summary.min_validity_rate,
155
+ max_validity_rate=summary.max_validity_rate,
156
+ avg_completeness=summary.avg_completeness,
157
+ avg_duration_ms=summary.avg_duration_ms,
158
+ overall_validity_rate=summary.overall_validity_rate,
159
+ top_error_types=summary.top_error_types,
160
+ )
161
+
162
+
163
+ @router.post(
164
+ "",
165
+ response_model=ValidationMetricResponse,
166
+ summary="Record a validation metric",
167
+ description="Manually record a validation metric (typically called by validation services)",
168
+ )
169
+ async def record_metric(request: RecordMetricRequest) -> ValidationMetricResponse:
170
+ """
171
+ Record a validation metric.
172
+
173
+ This endpoint allows external services to record validation metrics.
174
+ Typically used by validation pipelines or batch processes.
175
+ """
176
+ collector = get_collector()
177
+
178
+ # Calculate validity rate if not provided
179
+ validity_rate = request.validity_rate
180
+ if validity_rate is None:
181
+ if request.record_count > 0:
182
+ validity_rate = request.valid_count / request.record_count
183
+ else:
184
+ validity_rate = 1.0
185
+
186
+ # Create and store the metric
187
+ metric = ValidationMetric(
188
+ schema_name=request.schema_name,
189
+ version=request.version,
190
+ record_count=request.record_count,
191
+ valid_count=request.valid_count,
192
+ error_count=request.error_count,
193
+ validity_rate=validity_rate,
194
+ completeness=request.completeness,
195
+ field_completeness=request.field_completeness,
196
+ duration_ms=request.duration_ms,
197
+ errors_by_type=request.errors_by_type,
198
+ metadata=request.metadata,
199
+ )
200
+
201
+ collector.store.store(metric)
202
+
203
+ return ValidationMetricResponse(
204
+ id=metric.id,
205
+ schema_name=metric.schema_name,
206
+ version=metric.version,
207
+ timestamp=metric.timestamp,
208
+ record_count=metric.record_count,
209
+ valid_count=metric.valid_count,
210
+ error_count=metric.error_count,
211
+ validity_rate=metric.validity_rate,
212
+ completeness=metric.completeness,
213
+ field_completeness=metric.field_completeness,
214
+ duration_ms=metric.duration_ms,
215
+ errors_by_type=metric.errors_by_type,
216
+ metadata=metric.metadata,
217
+ )
218
+
219
+
220
+ @router.get(
221
+ "/export",
222
+ response_model=ExportResponse,
223
+ summary="Export metrics",
224
+ description="Export metrics in various formats (JSON, Prometheus, CSV)",
225
+ )
226
+ async def export_metrics(
227
+ format: ExportFormat = Query(default=ExportFormat.JSON, description="Export format"),
228
+ schema_name: Optional[str] = Query(default=None, description="Filter by schema name"),
229
+ since: Optional[datetime] = Query(default=None, description="Filter metrics after this time"),
230
+ limit: int = Query(default=1000, ge=1, le=10000, description="Maximum metrics to export"),
231
+ ) -> ExportResponse:
232
+ """
233
+ Export metrics in the specified format.
234
+
235
+ Supported formats:
236
+ - json: JSON array of metrics
237
+ - prometheus: Prometheus text format for scraping
238
+ - csv: CSV format for spreadsheet analysis
239
+ """
240
+ collector = get_collector()
241
+
242
+ metrics = collector.query(
243
+ schema_name=schema_name,
244
+ since=since,
245
+ limit=limit,
246
+ )
247
+
248
+ if format == ExportFormat.JSON:
249
+ data = export_json(metrics)
250
+ elif format == ExportFormat.PROMETHEUS:
251
+ # Include summaries for Prometheus export
252
+ schemas = collector.get_all_schemas()
253
+ if schema_name:
254
+ schemas = [schema_name]
255
+ summaries = [collector.get_summary(s) for s in schemas]
256
+ data = export_prometheus(metrics, summaries=summaries)
257
+ elif format == ExportFormat.CSV:
258
+ data = export_csv(metrics)
259
+ else:
260
+ raise HTTPException(status_code=400, detail=f"Unsupported format: {format}")
261
+
262
+ return ExportResponse(
263
+ format=format,
264
+ data=data,
265
+ count=len(metrics),
266
+ )
267
+
268
+
269
+ @router.delete(
270
+ "/{metric_id}",
271
+ summary="Delete a metric",
272
+ description="Delete a specific metric by ID",
273
+ )
274
+ async def delete_metric(metric_id: str) -> dict:
275
+ """Delete a metric by ID."""
276
+ collector = get_collector()
277
+ deleted = collector.store.delete(metric_id)
278
+
279
+ if not deleted:
280
+ raise HTTPException(status_code=404, detail=f"Metric not found: {metric_id}")
281
+
282
+ return {"deleted": True, "id": metric_id}
283
+
284
+
285
+ @router.delete(
286
+ "",
287
+ summary="Clear metrics",
288
+ description="Clear all metrics or metrics for a specific schema",
289
+ )
290
+ async def clear_metrics(
291
+ schema_name: Optional[str] = Query(default=None, description="Schema to clear (all if not specified)"),
292
+ ) -> dict:
293
+ """Clear metrics."""
294
+ collector = get_collector()
295
+ count = collector.store.clear(schema_name)
296
+
297
+ return {
298
+ "cleared": True,
299
+ "count": count,
300
+ "schema_name": schema_name or "all",
301
+ }
@@ -1,20 +1,18 @@
1
1
  """
2
2
  Route handlers for runtime validation.
3
+
4
+ Supports the new ValidatorBuilder API with quality metrics.
3
5
  """
4
6
 
5
7
  from fastapi import APIRouter, Depends, HTTPException, status
6
8
 
7
- from pycharter import (
8
- validate_batch_with_contract,
9
- validate_batch_with_store,
10
- validate_with_contract,
11
- validate_with_store,
12
- )
9
+ from pycharter import Validator, ValidatorBuilder
13
10
  from api.dependencies.store import get_metadata_store
14
11
  from api.models.validation import (
15
12
  ValidationBatchRequest,
16
13
  ValidationBatchResponse,
17
14
  ValidationErrorDetail,
15
+ ValidationQualityMetrics,
18
16
  ValidationRequest,
19
17
  ValidationResponse,
20
18
  )
@@ -75,6 +73,20 @@ def _parse_validation_errors(errors: list) -> list:
75
73
  return parsed_errors
76
74
 
77
75
 
76
+ def _build_quality_metrics(quality) -> ValidationQualityMetrics | None:
77
+ """Convert pycharter QualityMetrics to API model."""
78
+ if not quality:
79
+ return None
80
+ return ValidationQualityMetrics(
81
+ completeness=quality.completeness,
82
+ field_completeness=quality.field_completeness,
83
+ record_count=quality.record_count,
84
+ valid_count=quality.valid_count,
85
+ error_count=quality.error_count,
86
+ validity_rate=quality.validity_rate,
87
+ )
88
+
89
+
78
90
  @router.post(
79
91
  "/validation/validate",
80
92
  response_model=ValidationResponse,
@@ -97,6 +109,9 @@ async def validate_data(
97
109
  The validation applies coercion rules (if available) and validation rules
98
110
  (if available) in addition to JSON Schema validation.
99
111
 
112
+ Optionally includes quality metrics (completeness, validity rate) when
113
+ `include_quality=True`.
114
+
100
115
  Args:
101
116
  request: Validation request containing data and either schema_id or contract
102
117
  store: Metadata store dependency
@@ -107,26 +122,32 @@ async def validate_data(
107
122
  Raises:
108
123
  HTTPException: If validation fails or required parameters are missing
109
124
  """
125
+ # Build validator using ValidatorBuilder
126
+ builder = ValidatorBuilder()
127
+
110
128
  if request.schema_id:
111
- result = validate_with_store(
112
- store=store,
113
- schema_id=request.schema_id,
114
- data=request.data,
115
- version=request.version,
116
- strict=request.strict,
117
- )
129
+ builder = builder.from_store(store, request.schema_id, request.version)
118
130
  elif request.contract:
119
- result = validate_with_contract(
120
- contract=request.contract,
121
- data=request.data,
122
- strict=request.strict,
123
- )
131
+ builder = builder.from_dict(request.contract)
124
132
  else:
125
133
  raise HTTPException(
126
134
  status_code=status.HTTP_400_BAD_REQUEST,
127
135
  detail="Either schema_id or contract must be provided",
128
136
  )
129
137
 
138
+ # Configure strict mode and quality checks
139
+ if request.strict:
140
+ builder = builder.strict()
141
+ else:
142
+ builder = builder.lenient()
143
+
144
+ if request.include_quality:
145
+ builder = builder.with_quality_checks()
146
+
147
+ # Build and validate
148
+ validator = builder.build()
149
+ result = validator.validate(request.data, include_quality=request.include_quality)
150
+
130
151
  errors = []
131
152
  if not result.is_valid and result.errors:
132
153
  errors = _parse_validation_errors(result.errors)
@@ -136,6 +157,7 @@ async def validate_data(
136
157
  data=result.data.model_dump() if result.data else None,
137
158
  errors=errors,
138
159
  error_count=len(errors),
160
+ quality=_build_quality_metrics(result.quality),
139
161
  )
140
162
 
141
163
 
@@ -159,6 +181,8 @@ async def validate_batch_data(
159
181
  1. **Store-based**: Use `schema_id` to retrieve schema from metadata store
160
182
  2. **Contract-based**: Use `contract` dictionary directly
161
183
 
184
+ Optionally includes aggregate quality metrics when `include_quality=True`.
185
+
162
186
  Args:
163
187
  request: Batch validation request containing data_list and either schema_id or contract
164
188
  store: Metadata store dependency
@@ -169,31 +193,38 @@ async def validate_batch_data(
169
193
  Raises:
170
194
  HTTPException: If batch validation fails or required parameters are missing
171
195
  """
196
+ # Build validator using ValidatorBuilder
197
+ builder = ValidatorBuilder()
198
+
172
199
  if request.schema_id:
173
- results = validate_batch_with_store(
174
- store=store,
175
- schema_id=request.schema_id,
176
- data_list=request.data_list,
177
- version=request.version,
178
- strict=request.strict,
179
- )
200
+ builder = builder.from_store(store, request.schema_id, request.version)
180
201
  elif request.contract:
181
- results = validate_batch_with_contract(
182
- contract=request.contract,
183
- data_list=request.data_list,
184
- strict=request.strict,
185
- )
202
+ builder = builder.from_dict(request.contract)
186
203
  else:
187
204
  raise HTTPException(
188
205
  status_code=status.HTTP_400_BAD_REQUEST,
189
206
  detail="Either schema_id or contract must be provided",
190
207
  )
191
208
 
209
+ # Configure strict mode and quality checks
210
+ if request.strict:
211
+ builder = builder.strict()
212
+ else:
213
+ builder = builder.lenient()
214
+
215
+ if request.include_quality:
216
+ builder = builder.with_quality_checks()
217
+
218
+ # Build and validate batch
219
+ validator = builder.build()
220
+ results = validator.validate_batch(request.data_list, include_quality=request.include_quality)
221
+
192
222
  response_results = []
193
223
  valid_count = 0
194
224
  invalid_count = 0
225
+ aggregate_quality = None
195
226
 
196
- for result in results:
227
+ for i, result in enumerate(results):
197
228
  errors = []
198
229
  if not result.is_valid and result.errors:
199
230
  errors = _parse_validation_errors(result.errors)
@@ -203,12 +234,17 @@ async def validate_batch_data(
203
234
  else:
204
235
  invalid_count += 1
205
236
 
237
+ # Get aggregate quality from last result
238
+ if i == len(results) - 1 and result.quality:
239
+ aggregate_quality = result.quality
240
+
206
241
  response_results.append(
207
242
  ValidationResponse(
208
243
  is_valid=result.is_valid,
209
244
  data=result.data.model_dump() if result.data else None,
210
245
  errors=errors,
211
246
  error_count=len(errors),
247
+ quality=None, # Individual quality not included to keep response size reasonable
212
248
  )
213
249
  )
214
250
 
@@ -217,4 +253,5 @@ async def validate_batch_data(
217
253
  total_count=len(results),
218
254
  valid_count=valid_count,
219
255
  invalid_count=invalid_count,
256
+ quality=_build_quality_metrics(aggregate_quality),
220
257
  )