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,394 @@
1
+ """
2
+ Unified configuration loader for ETL pipelines.
3
+
4
+ Supports both single-file (pipeline.yaml) and multi-file (extract.yaml, transform.yaml, load.yaml) formats.
5
+ Handles variable resolution and config validation.
6
+ """
7
+
8
+ import os
9
+ import re
10
+ from pathlib import Path
11
+ from typing import Any, Dict, Optional, Tuple, Union
12
+
13
+ import yaml
14
+
15
+ from pycharter.shared.errors import ConfigLoadError
16
+ from pycharter.etl_generator.config_validator import (
17
+ ConfigValidator,
18
+ ConfigValidationError,
19
+ )
20
+
21
+
22
+ # Variable pattern: ${VAR_NAME} or ${VAR_NAME:-default} or ${VAR_NAME:?error}
23
+ VARIABLE_PATTERN = re.compile(r'\$\{([^}:]+)(?::([?-])([^}]*))?\}')
24
+
25
+
26
+ class PipelineConfig:
27
+ """
28
+ Loaded and validated pipeline configuration.
29
+
30
+ Attributes:
31
+ extract: Extract configuration dict
32
+ transform: Transform configuration dict (may be empty)
33
+ load: Load configuration dict
34
+ name: Pipeline name (from config or directory name)
35
+ version: Pipeline version (if specified)
36
+ source_path: Path to the config file or directory
37
+ """
38
+
39
+ def __init__(
40
+ self,
41
+ extract: Dict[str, Any],
42
+ transform: Dict[str, Any],
43
+ load: Dict[str, Any],
44
+ name: Optional[str] = None,
45
+ version: Optional[str] = None,
46
+ source_path: Optional[str] = None,
47
+ ):
48
+ self.extract = extract
49
+ self.transform = transform
50
+ self.load = load
51
+ self.name = name
52
+ self.version = version
53
+ self.source_path = source_path
54
+
55
+ def __repr__(self) -> str:
56
+ return f"PipelineConfig(name={self.name!r}, source={self.source_path!r})"
57
+
58
+
59
+ class ConfigLoader:
60
+ """
61
+ Loads ETL pipeline configurations from files or directories.
62
+
63
+ Supports:
64
+ - Single-file format: pipeline.yaml with extract, transform, load sections
65
+ - Multi-file format: Directory with extract.yaml, transform.yaml, load.yaml
66
+ - Variable resolution: ${VAR}, ${VAR:-default}, ${VAR:?error}
67
+ - Config validation with clear error messages
68
+
69
+ Usage:
70
+ loader = ConfigLoader()
71
+
72
+ # Load from single file
73
+ config = loader.load("pipelines/users/pipeline.yaml")
74
+
75
+ # Load from directory
76
+ config = loader.load("pipelines/users/")
77
+
78
+ # Load with variables
79
+ config = loader.load("pipelines/users/", variables={"API_KEY": "xxx"})
80
+ """
81
+
82
+ def __init__(
83
+ self,
84
+ validate: bool = True,
85
+ strict: bool = True,
86
+ ):
87
+ """
88
+ Initialize the config loader.
89
+
90
+ Args:
91
+ validate: If True, validate configs against schemas
92
+ strict: If True, raise errors on validation failure
93
+ """
94
+ self.validate = validate
95
+ self.strict = strict
96
+ self._validator = ConfigValidator(strict=strict) if validate else None
97
+
98
+ def load(
99
+ self,
100
+ path: Union[str, Path],
101
+ variables: Optional[Dict[str, str]] = None,
102
+ ) -> PipelineConfig:
103
+ """
104
+ Load pipeline configuration from a file or directory.
105
+
106
+ Args:
107
+ path: Path to config file or directory
108
+ variables: Additional variables for resolution (in addition to env vars)
109
+
110
+ Returns:
111
+ PipelineConfig with loaded and validated configuration
112
+
113
+ Raises:
114
+ ConfigLoadError: If config files cannot be found or loaded
115
+ ConfigValidationError: If config validation fails
116
+ """
117
+ path = Path(path).resolve()
118
+
119
+ if path.is_file():
120
+ return self._load_single_file(path, variables)
121
+ elif path.is_dir():
122
+ return self._load_multi_file(path, variables)
123
+ else:
124
+ # Check if it's a path without extension
125
+ for ext in (".yaml", ".yml"):
126
+ single_file = path.with_suffix(ext)
127
+ if single_file.exists():
128
+ return self._load_single_file(single_file, variables)
129
+
130
+ # Check if adding pipeline.yaml works
131
+ pipeline_file = path / "pipeline.yaml"
132
+ if pipeline_file.exists():
133
+ return self._load_single_file(pipeline_file, variables)
134
+
135
+ raise ConfigLoadError(
136
+ f"Config not found: {path}. Expected a YAML file or directory with config files.",
137
+ str(path),
138
+ )
139
+
140
+ def _load_single_file(
141
+ self,
142
+ path: Path,
143
+ variables: Optional[Dict[str, str]] = None,
144
+ ) -> PipelineConfig:
145
+ """Load configuration from a single pipeline.yaml file."""
146
+ config = self._load_yaml(path, variables, str(path.parent))
147
+
148
+ if not isinstance(config, dict):
149
+ raise ConfigLoadError(
150
+ f"Invalid config format: expected a dict, got {type(config).__name__}",
151
+ str(path),
152
+ )
153
+
154
+ # Extract sections
155
+ extract = config.get("extract")
156
+ transform = config.get("transform", {})
157
+ load = config.get("load")
158
+
159
+ if extract is None:
160
+ raise ConfigLoadError(
161
+ "Missing 'extract' section in pipeline config",
162
+ str(path),
163
+ )
164
+
165
+ if load is None:
166
+ raise ConfigLoadError(
167
+ "Missing 'load' section in pipeline config",
168
+ str(path),
169
+ )
170
+
171
+ # Normalize transform config
172
+ transform = self._normalize_transform(transform)
173
+
174
+ # Validate if enabled
175
+ if self._validator:
176
+ self._validator.validate_pipeline(config, str(path))
177
+
178
+ return PipelineConfig(
179
+ extract=extract,
180
+ transform=transform,
181
+ load=load,
182
+ name=config.get("name", path.stem),
183
+ version=config.get("version"),
184
+ source_path=str(path),
185
+ )
186
+
187
+ def _load_multi_file(
188
+ self,
189
+ directory: Path,
190
+ variables: Optional[Dict[str, str]] = None,
191
+ ) -> PipelineConfig:
192
+ """Load configuration from separate extract.yaml, transform.yaml, load.yaml files."""
193
+ contract_dir = str(directory)
194
+
195
+ # Load extract config (required)
196
+ extract_path = self._find_config_file(directory, "extract")
197
+ if extract_path is None:
198
+ raise ConfigLoadError(
199
+ f"Missing extract config in {directory}. Expected extract.yaml or extract.yml",
200
+ str(directory),
201
+ )
202
+ extract = self._load_yaml(extract_path, variables, contract_dir)
203
+
204
+ # Load transform config (optional)
205
+ transform_path = self._find_config_file(directory, "transform")
206
+ if transform_path:
207
+ transform = self._load_yaml(transform_path, variables, contract_dir)
208
+ transform = self._normalize_transform(transform)
209
+ else:
210
+ transform = {}
211
+
212
+ # Load load config (required)
213
+ load_path = self._find_config_file(directory, "load")
214
+ if load_path is None:
215
+ raise ConfigLoadError(
216
+ f"Missing load config in {directory}. Expected load.yaml or load.yml",
217
+ str(directory),
218
+ )
219
+ load_config = self._load_yaml(load_path, variables, contract_dir)
220
+
221
+ # Validate if enabled
222
+ if self._validator:
223
+ self._validator.validate_extract(extract, str(extract_path))
224
+ if transform:
225
+ self._validator.validate_transform(
226
+ {"transform": transform} if isinstance(transform, list) else transform,
227
+ str(transform_path) if transform_path else None,
228
+ )
229
+ self._validator.validate_load(load_config, str(load_path))
230
+
231
+ return PipelineConfig(
232
+ extract=extract,
233
+ transform=transform,
234
+ load=load_config,
235
+ name=directory.name,
236
+ source_path=str(directory),
237
+ )
238
+
239
+ def _find_config_file(self, directory: Path, name: str) -> Optional[Path]:
240
+ """Find a config file by name, checking for .yaml and .yml extensions."""
241
+ for ext in (".yaml", ".yml"):
242
+ path = directory / f"{name}{ext}"
243
+ if path.exists():
244
+ return path
245
+ return None
246
+
247
+ def _load_yaml(
248
+ self,
249
+ path: Path,
250
+ variables: Optional[Dict[str, str]] = None,
251
+ contract_dir: Optional[str] = None,
252
+ ) -> Dict[str, Any]:
253
+ """Load and parse a YAML file with variable resolution."""
254
+ try:
255
+ with open(path) as f:
256
+ content = f.read()
257
+ except FileNotFoundError:
258
+ raise ConfigLoadError(f"Config file not found: {path}", str(path))
259
+ except IOError as e:
260
+ raise ConfigLoadError(f"Error reading config file: {e}", str(path))
261
+
262
+ # Resolve variables in the YAML content
263
+ content = self._resolve_variables(content, variables, contract_dir)
264
+
265
+ try:
266
+ config = yaml.safe_load(content)
267
+ except yaml.YAMLError as e:
268
+ raise ConfigLoadError(f"Invalid YAML: {e}", str(path))
269
+
270
+ return config or {}
271
+
272
+ def _resolve_variables(
273
+ self,
274
+ content: str,
275
+ variables: Optional[Dict[str, str]] = None,
276
+ contract_dir: Optional[str] = None,
277
+ ) -> str:
278
+ """
279
+ Resolve ${VAR} placeholders in content.
280
+
281
+ Supports:
282
+ - ${VAR} - Use variable or env var
283
+ - ${VAR:-default} - Use default if not set
284
+ - ${VAR:?error message} - Raise error if not set
285
+ """
286
+ variables = variables or {}
287
+
288
+ def replace_var(match):
289
+ var_name = match.group(1)
290
+ modifier = match.group(2) # '-' for default, '?' for required
291
+ modifier_value = match.group(3) # default value or error message
292
+
293
+ # Check variables dict first, then environment
294
+ value = variables.get(var_name) or os.environ.get(var_name)
295
+
296
+ if value:
297
+ return value
298
+
299
+ # Handle modifiers
300
+ if modifier == "-":
301
+ return modifier_value if modifier_value is not None else ""
302
+ elif modifier == "?":
303
+ error_msg = modifier_value or f"Required variable {var_name} is not set"
304
+ raise ConfigLoadError(error_msg, path=None)
305
+
306
+ # No modifier - return original placeholder (will be resolved later or cause error)
307
+ return match.group(0)
308
+
309
+ return VARIABLE_PATTERN.sub(replace_var, content)
310
+
311
+ def _normalize_transform(
312
+ self,
313
+ transform: Union[Dict[str, Any], list, None],
314
+ ) -> Union[Dict[str, Any], list]:
315
+ """
316
+ Normalize transform config to consistent format.
317
+
318
+ Handles:
319
+ - Empty/None -> {}
320
+ - List (ordered) -> list (unchanged)
321
+ - Dict with 'transform' key -> unwrap
322
+ - Dict (legacy) -> dict (unchanged)
323
+ """
324
+ if transform is None:
325
+ return {}
326
+
327
+ if isinstance(transform, list):
328
+ return transform
329
+
330
+ if isinstance(transform, dict):
331
+ # Check if wrapped in 'transform' key
332
+ if "transform" in transform and len(transform) == 1:
333
+ return transform["transform"]
334
+ return transform
335
+
336
+ return {}
337
+
338
+
339
+ def load_pipeline_config(
340
+ path: Union[str, Path],
341
+ variables: Optional[Dict[str, str]] = None,
342
+ validate: bool = True,
343
+ ) -> PipelineConfig:
344
+ """
345
+ Convenience function to load a pipeline configuration.
346
+
347
+ Args:
348
+ path: Path to config file or directory
349
+ variables: Additional variables for resolution
350
+ validate: If True, validate configs against schemas
351
+
352
+ Returns:
353
+ PipelineConfig with loaded configuration
354
+ """
355
+ loader = ConfigLoader(validate=validate)
356
+ return loader.load(path, variables)
357
+
358
+
359
+ def detect_config_format(path: Union[str, Path]) -> str:
360
+ """
361
+ Detect whether a path points to single-file or multi-file config.
362
+
363
+ Args:
364
+ path: Path to check
365
+
366
+ Returns:
367
+ "single" for single-file format, "multi" for multi-file format
368
+
369
+ Raises:
370
+ ConfigLoadError: If config format cannot be determined
371
+ """
372
+ path = Path(path)
373
+
374
+ if path.is_file():
375
+ return "single"
376
+
377
+ if path.is_dir():
378
+ # Check for pipeline.yaml (single-file in directory)
379
+ if (path / "pipeline.yaml").exists() or (path / "pipeline.yml").exists():
380
+ return "single"
381
+
382
+ # Check for extract.yaml (multi-file)
383
+ if (path / "extract.yaml").exists() or (path / "extract.yml").exists():
384
+ return "multi"
385
+
386
+ # Check with extensions
387
+ for ext in (".yaml", ".yml"):
388
+ if path.with_suffix(ext).exists():
389
+ return "single"
390
+
391
+ raise ConfigLoadError(
392
+ f"Cannot determine config format for: {path}",
393
+ str(path),
394
+ )