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,247 @@
1
+ """
2
+ Documentation Renderers - Output format implementations.
3
+
4
+ Provides Protocol definition and concrete implementations for
5
+ rendering documentation in various formats.
6
+ """
7
+
8
+ from typing import Any, Dict, List, Optional, Protocol
9
+
10
+
11
+ class DocsRenderer(Protocol):
12
+ """Protocol for documentation renderers."""
13
+
14
+ def render_header(self, title: str, version: Optional[str] = None) -> str:
15
+ """Render document header with title and optional version."""
16
+ ...
17
+
18
+ def render_section(self, title: str, content: str) -> str:
19
+ """Render a section with title and content."""
20
+ ...
21
+
22
+ def render_table(
23
+ self, headers: List[str], rows: List[List[str]], caption: Optional[str] = None
24
+ ) -> str:
25
+ """Render a table with headers and rows."""
26
+ ...
27
+
28
+ def render_code_block(self, code: str, language: str = "") -> str:
29
+ """Render a code block."""
30
+ ...
31
+
32
+ def render_list(self, items: List[str], ordered: bool = False) -> str:
33
+ """Render a list of items."""
34
+ ...
35
+
36
+ def render_description(self, text: str) -> str:
37
+ """Render description/paragraph text."""
38
+ ...
39
+
40
+ def render_badge(self, label: str, value: str) -> str:
41
+ """Render a badge/tag (e.g., required, optional)."""
42
+ ...
43
+
44
+
45
+ class MarkdownRenderer:
46
+ """Render documentation as Markdown."""
47
+
48
+ def render_header(self, title: str, version: Optional[str] = None) -> str:
49
+ """Render document header with title and optional version."""
50
+ header = f"# {title}\n\n"
51
+ if version:
52
+ header += f"**Version:** `{version}`\n\n"
53
+ return header
54
+
55
+ def render_section(self, title: str, content: str) -> str:
56
+ """Render a section with title and content."""
57
+ return f"## {title}\n\n{content}\n\n"
58
+
59
+ def render_subsection(self, title: str, content: str) -> str:
60
+ """Render a subsection with title and content."""
61
+ return f"### {title}\n\n{content}\n\n"
62
+
63
+ def render_table(
64
+ self, headers: List[str], rows: List[List[str]], caption: Optional[str] = None
65
+ ) -> str:
66
+ """Render a Markdown table."""
67
+ if not headers or not rows:
68
+ return ""
69
+
70
+ lines = []
71
+ if caption:
72
+ lines.append(f"*{caption}*\n")
73
+
74
+ # Header row
75
+ lines.append("| " + " | ".join(headers) + " |")
76
+ # Separator
77
+ lines.append("| " + " | ".join(["---"] * len(headers)) + " |")
78
+ # Data rows
79
+ for row in rows:
80
+ # Ensure row has same length as headers
81
+ padded_row = row + [""] * (len(headers) - len(row))
82
+ lines.append("| " + " | ".join(padded_row[: len(headers)]) + " |")
83
+
84
+ return "\n".join(lines) + "\n"
85
+
86
+ def render_code_block(self, code: str, language: str = "") -> str:
87
+ """Render a fenced code block."""
88
+ return f"```{language}\n{code}\n```\n"
89
+
90
+ def render_list(self, items: List[str], ordered: bool = False) -> str:
91
+ """Render a list of items."""
92
+ if not items:
93
+ return ""
94
+ if ordered:
95
+ return "\n".join(f"{i+1}. {item}" for i, item in enumerate(items)) + "\n"
96
+ return "\n".join(f"- {item}" for item in items) + "\n"
97
+
98
+ def render_description(self, text: str) -> str:
99
+ """Render description/paragraph text."""
100
+ return f"{text}\n\n"
101
+
102
+ def render_badge(self, label: str, value: str) -> str:
103
+ """Render a badge as inline code."""
104
+ return f"`{label}: {value}`"
105
+
106
+ def render_key_value(self, key: str, value: Any) -> str:
107
+ """Render a key-value pair."""
108
+ if isinstance(value, (dict, list)):
109
+ return f"**{key}:**\n{self.render_code_block(str(value), 'json')}"
110
+ return f"**{key}:** {value}\n"
111
+
112
+
113
+ class HTMLRenderer:
114
+ """Render documentation as HTML."""
115
+
116
+ def __init__(self, include_styles: bool = True):
117
+ """
118
+ Initialize HTML renderer.
119
+
120
+ Args:
121
+ include_styles: If True, include inline CSS styles
122
+ """
123
+ self.include_styles = include_styles
124
+
125
+ def _get_styles(self) -> str:
126
+ """Get CSS styles for HTML output."""
127
+ return """
128
+ <style>
129
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
130
+ line-height: 1.6; max-width: 900px; margin: 0 auto; padding: 20px; }
131
+ h1 { border-bottom: 2px solid #333; padding-bottom: 10px; }
132
+ h2 { border-bottom: 1px solid #ddd; padding-bottom: 5px; margin-top: 30px; }
133
+ table { border-collapse: collapse; width: 100%; margin: 15px 0; }
134
+ th, td { border: 1px solid #ddd; padding: 10px; text-align: left; }
135
+ th { background: #f5f5f5; font-weight: 600; }
136
+ tr:nth-child(even) { background: #fafafa; }
137
+ code { background: #f4f4f4; padding: 2px 6px; border-radius: 3px; font-size: 0.9em; }
138
+ pre { background: #f4f4f4; padding: 15px; border-radius: 5px; overflow-x: auto; }
139
+ pre code { background: none; padding: 0; }
140
+ .badge { display: inline-block; padding: 2px 8px; border-radius: 3px;
141
+ font-size: 0.8em; font-weight: 500; }
142
+ .badge-required { background: #fee2e2; color: #991b1b; }
143
+ .badge-optional { background: #e0f2fe; color: #0369a1; }
144
+ .version { color: #666; font-size: 0.9em; }
145
+ </style>
146
+ """
147
+
148
+ def render_header(self, title: str, version: Optional[str] = None) -> str:
149
+ """Render document header with title and optional version."""
150
+ styles = self._get_styles() if self.include_styles else ""
151
+ version_html = (
152
+ f'<p class="version">Version: <code>{version}</code></p>'
153
+ if version
154
+ else ""
155
+ )
156
+ return f"""<!DOCTYPE html>
157
+ <html>
158
+ <head>
159
+ <meta charset="UTF-8">
160
+ <title>{title}</title>
161
+ {styles}
162
+ </head>
163
+ <body>
164
+ <h1>{title}</h1>
165
+ {version_html}
166
+ """
167
+
168
+ def render_footer(self) -> str:
169
+ """Render document footer."""
170
+ return "</body>\n</html>"
171
+
172
+ def render_section(self, title: str, content: str) -> str:
173
+ """Render a section with title and content."""
174
+ return f"<h2>{title}</h2>\n{content}\n"
175
+
176
+ def render_subsection(self, title: str, content: str) -> str:
177
+ """Render a subsection with title and content."""
178
+ return f"<h3>{title}</h3>\n{content}\n"
179
+
180
+ def render_table(
181
+ self, headers: List[str], rows: List[List[str]], caption: Optional[str] = None
182
+ ) -> str:
183
+ """Render an HTML table."""
184
+ if not headers or not rows:
185
+ return ""
186
+
187
+ lines = ["<table>"]
188
+ if caption:
189
+ lines.append(f"<caption>{caption}</caption>")
190
+
191
+ # Header
192
+ lines.append("<thead><tr>")
193
+ for h in headers:
194
+ lines.append(f"<th>{h}</th>")
195
+ lines.append("</tr></thead>")
196
+
197
+ # Body
198
+ lines.append("<tbody>")
199
+ for row in rows:
200
+ lines.append("<tr>")
201
+ for i, cell in enumerate(row):
202
+ if i < len(headers):
203
+ lines.append(f"<td>{cell}</td>")
204
+ # Pad if needed
205
+ for _ in range(len(headers) - len(row)):
206
+ lines.append("<td></td>")
207
+ lines.append("</tr>")
208
+ lines.append("</tbody>")
209
+ lines.append("</table>")
210
+
211
+ return "\n".join(lines) + "\n"
212
+
213
+ def render_code_block(self, code: str, language: str = "") -> str:
214
+ """Render a code block."""
215
+ return f"<pre><code>{self._escape_html(code)}</code></pre>\n"
216
+
217
+ def render_list(self, items: List[str], ordered: bool = False) -> str:
218
+ """Render a list of items."""
219
+ if not items:
220
+ return ""
221
+ tag = "ol" if ordered else "ul"
222
+ items_html = "\n".join(f"<li>{item}</li>" for item in items)
223
+ return f"<{tag}>\n{items_html}\n</{tag}>\n"
224
+
225
+ def render_description(self, text: str) -> str:
226
+ """Render description/paragraph text."""
227
+ return f"<p>{text}</p>\n"
228
+
229
+ def render_badge(self, label: str, value: str) -> str:
230
+ """Render a badge span."""
231
+ css_class = "badge-required" if value.lower() == "required" else "badge-optional"
232
+ return f'<span class="badge {css_class}">{label}: {value}</span>'
233
+
234
+ def render_key_value(self, key: str, value: Any) -> str:
235
+ """Render a key-value pair."""
236
+ if isinstance(value, (dict, list)):
237
+ return f"<p><strong>{key}:</strong></p>\n{self.render_code_block(str(value), 'json')}"
238
+ return f"<p><strong>{key}:</strong> {value}</p>\n"
239
+
240
+ def _escape_html(self, text: str) -> str:
241
+ """Escape HTML special characters."""
242
+ return (
243
+ text.replace("&", "&amp;")
244
+ .replace("<", "&lt;")
245
+ .replace(">", "&gt;")
246
+ .replace('"', "&quot;")
247
+ )
@@ -1,94 +1,182 @@
1
1
  """
2
- ETL Orchestrator - Runtime ETL pipeline execution from contract artifacts.
2
+ ETL Generator - Modern ETL pipeline framework.
3
3
 
4
- PRIMARY INTERFACE: ETLOrchestrator Class
5
- ========================================
4
+ Two APIs:
5
+ =========
6
+ 1. Programmatic API (Pipeline with | operator):
7
+ >>> pipeline = Pipeline(HTTPExtractor(url="...")) | Rename(...) | PostgresLoader(...)
8
+ >>> result = await pipeline.run()
6
9
 
7
- The ETLOrchestrator class is the recommended and primary way to execute ETL pipelines
8
- in pycharter. It can be instantiated with contract artifacts from various sources:
10
+ 2. Config-driven API (YAML files):
11
+ >>> # From explicit files (most flexible)
12
+ >>> pipeline = Pipeline.from_config_files(
13
+ ... extract="configs/extract.yaml",
14
+ ... load="configs/load.yaml",
15
+ ... variables={"API_KEY": "secret"}
16
+ ... )
17
+ >>>
18
+ >>> # From directory (expects extract.yaml, transform.yaml, load.yaml)
19
+ >>> pipeline = Pipeline.from_config_dir("pipelines/users/")
20
+ >>>
21
+ >>> # From single file (pipeline.yaml)
22
+ >>> pipeline = Pipeline.from_config_file("pipelines/users/pipeline.yaml")
9
23
 
10
- >>> from pycharter.etl_generator import ETLOrchestrator
11
- >>>
12
- >>> # From contract directory (contains schema, coercion_rules, validation_rules, extract, transform, load)
13
- >>> orchestrator = ETLOrchestrator(contract_dir="data/contracts/user")
14
- >>> await orchestrator.run()
15
- >>>
16
- >>> # From contract file
17
- >>> orchestrator = ETLOrchestrator(contract_file="contracts/user.yaml")
18
- >>> await orchestrator.run(dry_run=False)
19
- >>>
20
- >>> # With direct ETL config dictionaries (overrides directory configs)
21
- >>> orchestrator = ETLOrchestrator(
22
- ... contract_dir="data/contracts/user",
23
- ... extract_config={"base_url": "https://api.example.com", "api_endpoint": "/v1/data"},
24
- ... transform_config={"rename": {"oldName": "new_name"}},
25
- ... load_config={"target_table": "users"}
26
- ... )
27
- >>> await orchestrator.run()
28
- >>>
29
- >>> # With separate ETL config file paths (overrides directory configs)
30
- >>> orchestrator = ETLOrchestrator(
31
- ... contract_dir="data/contracts/user",
32
- ... extract_file="custom/extract.yaml",
33
- ... transform_file="custom/transform.yaml",
34
- ... load_file="custom/load.yaml"
35
- ... )
36
- >>> await orchestrator.run()
37
- >>>
38
- >>> # Mixed approach - override specific configs
39
- >>> orchestrator = ETLOrchestrator(
40
- ... contract_dir="data/contracts/user",
41
- ... extract_config={"base_url": "https://custom.api.com"} # Override extract only
42
- ... # transform.yaml and load.yaml loaded from contract_dir
43
- ... )
44
- >>> await orchestrator.run()
24
+ Quick Start:
25
+ ============
26
+ from pycharter.etl_generator import (
27
+ Pipeline, HTTPExtractor, PostgresLoader,
28
+ Rename, AddField, Filter
29
+ )
30
+
31
+ # Programmatic
32
+ pipeline = (
33
+ Pipeline(HTTPExtractor(url="https://api.example.com/users"))
34
+ | Rename({"user_name": "name"})
35
+ | AddField("full_name", "${first_name} ${last_name}")
36
+ | PostgresLoader(connection_string="...", table="users")
37
+ )
38
+ result = await pipeline.run()
39
+
40
+ # Config-driven (explicit files)
41
+ pipeline = Pipeline.from_config_files(
42
+ extract="pipelines/users/extract.yaml",
43
+ load="pipelines/users/load.yaml",
44
+ variables={"DATA_DIR": "./data"}
45
+ )
46
+ result = await pipeline.run()
45
47
 
46
- Core Components:
47
- - orchestrator: ETLOrchestrator class (PRIMARY INTERFACE - use this for all ETL execution)
48
- - config_generator: Utility functions for generating ETL configs from contracts (helper utilities)
48
+ Config Format:
49
+ ==============
50
+ Single-file (pipeline.yaml):
51
+ name: users_pipeline
52
+ extract:
53
+ type: http # Required: http | file | database | cloud_storage
54
+ url: https://api.example.com/users
55
+ transform:
56
+ - rename: {userId: user_id}
57
+ - add:
58
+ full_name: "${first_name} ${last_name}"
59
+ load:
60
+ type: postgres # Required: postgres | sqlite | file | cloud_storage
61
+ table: users
62
+ database:
63
+ url: ${DATABASE_URL}
49
64
  """
50
65
 
51
- from pycharter.etl_generator.checkpoint import (
52
- CheckpointManager,
53
- CheckpointState,
66
+ # Core pipeline classes
67
+ from pycharter.etl_generator.pipeline import Pipeline
68
+ from pycharter.etl_generator.builder import PipelineBuilder
69
+ from pycharter.etl_generator.context import PipelineContext
70
+ from pycharter.etl_generator.result import PipelineResult, BatchResult, LoadResult
71
+ from pycharter.etl_generator.protocols import Extractor, Transformer, Loader
72
+
73
+ # Config loading and validation
74
+ from pycharter.etl_generator.config_loader import (
75
+ ConfigLoader,
76
+ PipelineConfig,
77
+ load_pipeline_config,
78
+ ConfigLoadError,
54
79
  )
55
- from pycharter.etl_generator.config_generator import (
56
- generate_etl_config,
57
- generate_etl_config_from_contract,
58
- generate_etl_config_from_store,
80
+ from pycharter.etl_generator.config_validator import (
81
+ ConfigValidator,
82
+ ConfigValidationError,
83
+ validate_config,
59
84
  )
60
- from pycharter.etl_generator.dlq import (
61
- DeadLetterQueue,
62
- DeadLetterRecord,
63
- DLQReason,
85
+
86
+ # Expression evaluation
87
+ from pycharter.etl_generator.expression import (
88
+ ExpressionEvaluator,
89
+ ExpressionError,
90
+ evaluate_expression,
91
+ is_expression,
64
92
  )
65
- from pycharter.etl_generator.orchestrator import (
66
- ETLOrchestrator,
67
- create_orchestrator,
93
+
94
+ # Extractors
95
+ from pycharter.etl_generator.extractors import (
96
+ BaseExtractor,
97
+ HTTPExtractor,
98
+ FileExtractor,
99
+ DatabaseExtractor,
100
+ CloudStorageExtractor,
101
+ ExtractorFactory,
68
102
  )
69
- from pycharter.etl_generator.factory import PipelineFactory
70
- from pycharter.etl_generator.progress import (
71
- ETLProgress,
72
- ProgressTracker,
103
+
104
+ # Transformers
105
+ from pycharter.etl_generator.transformers import (
106
+ BaseTransformer,
107
+ TransformerChain,
108
+ Rename,
109
+ AddField,
110
+ Drop,
111
+ Select,
112
+ Filter,
113
+ Convert,
114
+ Default,
115
+ Map,
116
+ FlatMap,
117
+ CustomFunction,
73
118
  )
119
+
120
+ # Loaders
121
+ from pycharter.etl_generator.loaders import (
122
+ BaseLoader,
123
+ PostgresLoader,
124
+ DatabaseLoader,
125
+ FileLoader,
126
+ CloudStorageLoader,
127
+ LoaderFactory,
128
+ )
129
+
74
130
  __all__ = [
75
- # PRIMARY INTERFACE: ETLOrchestrator class (use this for all ETL execution)
76
- "ETLOrchestrator",
77
- "create_orchestrator",
78
- # Pipeline factory (auto-discovery of config directories)
79
- "PipelineFactory",
80
- # Config generation utilities (helper functions for generating ETL configs)
81
- "generate_etl_config",
82
- "generate_etl_config_from_contract",
83
- "generate_etl_config_from_store",
84
- # Progress tracking
85
- "ETLProgress",
86
- "ProgressTracker",
87
- # Checkpoint/resume
88
- "CheckpointManager",
89
- "CheckpointState",
90
- # Dead Letter Queue
91
- "DeadLetterQueue",
92
- "DeadLetterRecord",
93
- "DLQReason",
131
+ # Core
132
+ "Pipeline",
133
+ "PipelineBuilder",
134
+ "PipelineContext",
135
+ "PipelineResult",
136
+ "BatchResult",
137
+ "LoadResult",
138
+ # Protocols
139
+ "Extractor",
140
+ "Transformer",
141
+ "Loader",
142
+ # Config loading
143
+ "ConfigLoader",
144
+ "PipelineConfig",
145
+ "load_pipeline_config",
146
+ "ConfigLoadError",
147
+ "ConfigValidator",
148
+ "ConfigValidationError",
149
+ "validate_config",
150
+ # Expressions
151
+ "ExpressionEvaluator",
152
+ "ExpressionError",
153
+ "evaluate_expression",
154
+ "is_expression",
155
+ # Extractors
156
+ "BaseExtractor",
157
+ "HTTPExtractor",
158
+ "FileExtractor",
159
+ "DatabaseExtractor",
160
+ "CloudStorageExtractor",
161
+ "ExtractorFactory",
162
+ # Transformers
163
+ "BaseTransformer",
164
+ "TransformerChain",
165
+ "Rename",
166
+ "AddField",
167
+ "Drop",
168
+ "Select",
169
+ "Filter",
170
+ "Convert",
171
+ "Default",
172
+ "Map",
173
+ "FlatMap",
174
+ "CustomFunction",
175
+ # Loaders
176
+ "BaseLoader",
177
+ "PostgresLoader",
178
+ "DatabaseLoader",
179
+ "FileLoader",
180
+ "CloudStorageLoader",
181
+ "LoaderFactory",
94
182
  ]
@@ -0,0 +1,121 @@
1
+ """
2
+ Fluent PipelineBuilder API.
3
+ """
4
+
5
+ from typing import Any, Dict, List, Optional
6
+
7
+ from pycharter.etl_generator.context import PipelineContext
8
+ from pycharter.etl_generator.pipeline import Pipeline
9
+ from pycharter.etl_generator.protocols import Extractor, Transformer, Loader
10
+
11
+
12
+ class PipelineBuilder:
13
+ """
14
+ Fluent API for building ETL pipelines.
15
+
16
+ Example:
17
+ >>> pipeline = (
18
+ ... PipelineBuilder()
19
+ ... .name("user_pipeline")
20
+ ... .extract_from("http", url="https://api.example.com/users")
21
+ ... .transform(Rename({"old": "new"}))
22
+ ... .load_to("postgres", table="users", connection_string="...")
23
+ ... .build()
24
+ ... )
25
+ """
26
+
27
+ def __init__(self):
28
+ self._name: Optional[str] = None
29
+ self._extractor: Optional[Extractor] = None
30
+ self._transformers: List[Transformer] = []
31
+ self._loader: Optional[Loader] = None
32
+ self._context: Optional[PipelineContext] = None
33
+
34
+ def name(self, name: str) -> "PipelineBuilder":
35
+ """Set pipeline name."""
36
+ self._name = name
37
+ return self
38
+
39
+ def with_context(self, context: PipelineContext) -> "PipelineBuilder":
40
+ """Set pipeline context."""
41
+ self._context = context
42
+ return self
43
+
44
+ def extract_from(self, source_type: str, **config) -> "PipelineBuilder":
45
+ """Configure extractor by type."""
46
+ self._extractor = self._create_extractor(source_type, config)
47
+ return self
48
+
49
+ def extractor(self, extractor: Extractor) -> "PipelineBuilder":
50
+ """Set extractor directly."""
51
+ self._extractor = extractor
52
+ return self
53
+
54
+ def transform(self, transformer: Transformer) -> "PipelineBuilder":
55
+ """Add transformer to pipeline."""
56
+ self._transformers.append(transformer)
57
+ return self
58
+
59
+ def load_to(self, target_type: str, **config) -> "PipelineBuilder":
60
+ """Configure loader by type."""
61
+ self._loader = self._create_loader(target_type, config)
62
+ return self
63
+
64
+ def loader(self, loader: Loader) -> "PipelineBuilder":
65
+ """Set loader directly."""
66
+ self._loader = loader
67
+ return self
68
+
69
+ def build(self) -> Pipeline:
70
+ """Build the pipeline."""
71
+ if not self._extractor:
72
+ raise ValueError("Extractor required. Call extract_from() or extractor()")
73
+
74
+ return Pipeline(
75
+ extractor=self._extractor,
76
+ transformers=self._transformers,
77
+ loader=self._loader,
78
+ context=self._context or PipelineContext(),
79
+ name=self._name,
80
+ )
81
+
82
+ def _create_extractor(self, source_type: str, config: Dict[str, Any]) -> Extractor:
83
+ """Create extractor from type and config."""
84
+ from pycharter.etl_generator.extractors import (
85
+ HTTPExtractor,
86
+ FileExtractor,
87
+ DatabaseExtractor,
88
+ CloudStorageExtractor,
89
+ )
90
+
91
+ source_type = source_type.lower()
92
+
93
+ if source_type == "http":
94
+ return HTTPExtractor(**config)
95
+ elif source_type == "file":
96
+ return FileExtractor(**config)
97
+ elif source_type in ("database", "db", "sql"):
98
+ return DatabaseExtractor(**config)
99
+ elif source_type in ("s3", "cloud", "cloud_storage"):
100
+ return CloudStorageExtractor(**config)
101
+ else:
102
+ raise ValueError(f"Unknown extractor type: {source_type}")
103
+
104
+ def _create_loader(self, target_type: str, config: Dict[str, Any]) -> Loader:
105
+ """Create loader from type and config."""
106
+ from pycharter.etl_generator.loaders import (
107
+ PostgresLoader,
108
+ FileLoader,
109
+ CloudStorageLoader,
110
+ )
111
+
112
+ target_type = target_type.lower()
113
+
114
+ if target_type in ("postgres", "postgresql", "database", "db"):
115
+ return PostgresLoader(**config)
116
+ elif target_type == "file":
117
+ return FileLoader(**config)
118
+ elif target_type in ("s3", "cloud", "cloud_storage"):
119
+ return CloudStorageLoader(**config)
120
+ else:
121
+ raise ValueError(f"Unknown loader type: {target_type}")