pycharter 0.0.25__py3-none-any.whl → 0.0.26__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 (462) hide show
  1. pycharter/__init__.py +6 -0
  2. pycharter/api/README.md +1 -1
  3. pycharter/api/dependencies/auth.py +158 -0
  4. pycharter/api/main.py +30 -2
  5. pycharter/api/models/etl.py +66 -0
  6. pycharter/api/routes/v1/__init__.py +4 -0
  7. pycharter/api/routes/v1/auth.py +97 -0
  8. pycharter/api/routes/v1/contracts.py +10 -8
  9. pycharter/api/routes/v1/etl.py +131 -0
  10. pycharter/cli.py +1 -1
  11. pycharter/config.py +69 -0
  12. pycharter/contract_builder/builder.py +32 -37
  13. pycharter/data/seed/compliance_frameworks.yaml +22 -0
  14. pycharter/data/seed/contracts.yaml +130 -0
  15. pycharter/data/seed/data_feeds.yaml +22 -0
  16. pycharter/data/seed/domains.yaml +13 -0
  17. pycharter/data/seed/environments.yaml +19 -0
  18. pycharter/data/seed/owners.yaml +21 -0
  19. pycharter/data/seed/systems.yaml +13 -0
  20. pycharter/data/seed/tags.yaml +25 -0
  21. pycharter/data/templates/contract/README.md +31 -14
  22. pycharter/data/templates/contract/template_contract.yaml +37 -0
  23. pycharter/data/templates/etl/README.md +1 -1
  24. pycharter/data/templates/etl/extract_with_validation.yaml +86 -0
  25. pycharter/data/templates/etl/load_with_validation.yaml +111 -0
  26. pycharter/data/templates/etl/settings.yaml +55 -0
  27. pycharter/db/cli.py +126 -4
  28. pycharter/db/migrations/versions/20260122000000_change_artifact_unique_constraints_to_title_version.py +2 -2
  29. pycharter/etl_generator/INTERFACES.md +6 -7
  30. pycharter/etl_generator/__init__.py +47 -11
  31. pycharter/etl_generator/config_models.py +673 -0
  32. pycharter/etl_generator/config_validator.py +133 -157
  33. pycharter/etl_generator/context.py +3 -0
  34. pycharter/etl_generator/database.py +5 -1
  35. pycharter/etl_generator/extractors/__init__.py +4 -2
  36. pycharter/etl_generator/extractors/cloud_storage.py +9 -9
  37. pycharter/etl_generator/extractors/database.py +2 -2
  38. pycharter/etl_generator/extractors/factory.py +15 -33
  39. pycharter/etl_generator/extractors/file.py +2 -2
  40. pycharter/etl_generator/extractors/http.py +2 -2
  41. pycharter/etl_generator/extractors/mongodb.py +393 -0
  42. pycharter/etl_generator/extractors/streaming.py +2 -2
  43. pycharter/etl_generator/loaders/__init__.py +15 -9
  44. pycharter/etl_generator/loaders/{cloud_storage_loader.py → cloud_storage.py} +95 -2
  45. pycharter/etl_generator/loaders/factory.py +16 -29
  46. pycharter/etl_generator/loaders/file.py +135 -1
  47. pycharter/etl_generator/loaders/mongodb.py +416 -0
  48. pycharter/etl_generator/pipeline.py +283 -164
  49. pycharter/etl_generator/result.py +16 -0
  50. pycharter/etl_generator/schemas/__init__.py +71 -42
  51. pycharter/etl_generator/transformers/config.py +3 -2
  52. pycharter/etl_generator/transformers/simple_operations.py +57 -4
  53. pycharter/etl_generator/validation.py +551 -0
  54. pycharter/runtime_validator/__init__.py +7 -0
  55. pycharter/runtime_validator/utils.py +33 -0
  56. pycharter/runtime_validator/validator.py +13 -10
  57. pycharter/ui/package-lock.json +50 -41
  58. pycharter/ui/package.json +2 -1
  59. pycharter/ui/static/404/index.html +1 -1
  60. pycharter/ui/static/404.html +1 -1
  61. pycharter/ui/static/__next.__PAGE__.txt +2 -2
  62. pycharter/ui/static/__next._full.txt +7 -7
  63. pycharter/ui/static/__next._head.txt +1 -1
  64. pycharter/ui/static/__next._index.txt +6 -6
  65. pycharter/ui/static/__next._tree.txt +2 -2
  66. pycharter/ui/static/_next/static/chunks/0fc1f70b787b8845.js +1 -0
  67. pycharter/ui/static/_next/static/chunks/17bb8075d7b75663.css +1 -0
  68. pycharter/ui/static/_next/static/chunks/381932864dcbfdb8.js +1 -0
  69. pycharter/ui/static/_next/static/chunks/4c951b8e4507e2b3.js +1 -0
  70. pycharter/ui/static/_next/static/chunks/68b87a6f65abd3ed.js +1 -0
  71. pycharter/ui/static/_next/static/chunks/78572617b8fae189.js +1 -0
  72. pycharter/ui/static/_next/static/chunks/8b7be2803e3fe184.js +1 -0
  73. pycharter/ui/static/_next/static/chunks/a8e529fd1e67f121.js +1 -0
  74. pycharter/ui/static/_next/static/chunks/c35d998f80be3ff5.js +1 -0
  75. pycharter/ui/static/_next/static/chunks/e453aa5d01c32c17.js +1 -0
  76. pycharter/ui/static/_next/static/chunks/f2d240eb057f898a.js +970 -0
  77. pycharter/ui/static/_next/static/chunks/f7722448f6040846.js +1 -0
  78. pycharter/ui/static/_not-found/__next._full.txt +12 -12
  79. pycharter/ui/static/_not-found/__next._head.txt +3 -3
  80. pycharter/ui/static/_not-found/__next._index.txt +8 -8
  81. pycharter/ui/static/_not-found/__next._not-found.__PAGE__.txt +2 -2
  82. pycharter/ui/static/_not-found/__next._not-found.txt +3 -3
  83. pycharter/ui/static/_not-found/__next._tree.txt +2 -2
  84. pycharter/ui/static/_not-found/index.html +1 -1
  85. pycharter/ui/static/_not-found/index.txt +12 -12
  86. pycharter/ui/static/contracts/__next._full.txt +7 -7
  87. pycharter/ui/static/contracts/__next._head.txt +1 -1
  88. pycharter/ui/static/contracts/__next._index.txt +6 -6
  89. pycharter/ui/static/contracts/__next._tree.txt +2 -2
  90. pycharter/ui/static/contracts/__next.contracts.__PAGE__.txt +2 -2
  91. pycharter/ui/static/contracts/__next.contracts.txt +1 -1
  92. pycharter/ui/static/contracts/index.html +1 -1
  93. pycharter/ui/static/contracts/index.txt +7 -7
  94. pycharter/ui/static/documentation/__next._full.txt +7 -7
  95. pycharter/ui/static/documentation/__next._head.txt +1 -1
  96. pycharter/ui/static/documentation/__next._index.txt +6 -6
  97. pycharter/ui/static/documentation/__next._tree.txt +2 -2
  98. pycharter/ui/static/documentation/__next.documentation.__PAGE__.txt +2 -2
  99. pycharter/ui/static/documentation/__next.documentation.txt +1 -1
  100. pycharter/ui/static/documentation/index.html +3 -3
  101. pycharter/ui/static/documentation/index.txt +7 -7
  102. pycharter/ui/static/etl/__next._full.txt +21 -0
  103. pycharter/ui/static/etl/__next._head.txt +7 -0
  104. pycharter/ui/static/etl/__next._index.txt +9 -0
  105. pycharter/ui/static/etl/__next._tree.txt +2 -0
  106. pycharter/ui/static/etl/__next.etl.__PAGE__.txt +9 -0
  107. pycharter/ui/static/etl/__next.etl.txt +4 -0
  108. pycharter/ui/static/etl/index.html +2 -0
  109. pycharter/ui/static/etl/index.txt +21 -0
  110. pycharter/ui/static/index.html +1 -1
  111. pycharter/ui/static/index.txt +7 -7
  112. pycharter/ui/static/metadata/__next._full.txt +7 -7
  113. pycharter/ui/static/metadata/__next._head.txt +1 -1
  114. pycharter/ui/static/metadata/__next._index.txt +6 -6
  115. pycharter/ui/static/metadata/__next._tree.txt +2 -2
  116. pycharter/ui/static/metadata/__next.metadata.__PAGE__.txt +2 -2
  117. pycharter/ui/static/metadata/__next.metadata.txt +1 -1
  118. pycharter/ui/static/metadata/index.html +1 -1
  119. pycharter/ui/static/metadata/index.txt +7 -7
  120. pycharter/ui/static/quality/__next._full.txt +7 -7
  121. pycharter/ui/static/quality/__next._head.txt +1 -1
  122. pycharter/ui/static/quality/__next._index.txt +6 -6
  123. pycharter/ui/static/quality/__next._tree.txt +2 -2
  124. pycharter/ui/static/quality/__next.quality.__PAGE__.txt +2 -2
  125. pycharter/ui/static/quality/__next.quality.txt +1 -1
  126. pycharter/ui/static/quality/index.html +2 -2
  127. pycharter/ui/static/quality/index.txt +7 -7
  128. pycharter/ui/static/rules/__next._full.txt +7 -7
  129. pycharter/ui/static/rules/__next._head.txt +1 -1
  130. pycharter/ui/static/rules/__next._index.txt +6 -6
  131. pycharter/ui/static/rules/__next._tree.txt +2 -2
  132. pycharter/ui/static/rules/__next.rules.__PAGE__.txt +2 -2
  133. pycharter/ui/static/rules/__next.rules.txt +1 -1
  134. pycharter/ui/static/rules/index.html +1 -1
  135. pycharter/ui/static/rules/index.txt +7 -7
  136. pycharter/ui/static/schemas/__next._full.txt +7 -7
  137. pycharter/ui/static/schemas/__next._head.txt +1 -1
  138. pycharter/ui/static/schemas/__next._index.txt +6 -6
  139. pycharter/ui/static/schemas/__next._tree.txt +2 -2
  140. pycharter/ui/static/schemas/__next.schemas.__PAGE__.txt +2 -2
  141. pycharter/ui/static/schemas/__next.schemas.txt +1 -1
  142. pycharter/ui/static/schemas/index.html +1 -1
  143. pycharter/ui/static/schemas/index.txt +7 -7
  144. pycharter/ui/static/settings/__next._full.txt +7 -7
  145. pycharter/ui/static/settings/__next._head.txt +1 -1
  146. pycharter/ui/static/settings/__next._index.txt +6 -6
  147. pycharter/ui/static/settings/__next._tree.txt +2 -2
  148. pycharter/ui/static/settings/__next.settings.__PAGE__.txt +2 -2
  149. pycharter/ui/static/settings/__next.settings.txt +1 -1
  150. pycharter/ui/static/settings/index.html +1 -1
  151. pycharter/ui/static/settings/index.txt +7 -7
  152. pycharter/ui/static/static/404/index.html +1 -1
  153. pycharter/ui/static/static/404.html +1 -1
  154. pycharter/ui/static/static/__next.__PAGE__.txt +1 -1
  155. pycharter/ui/static/static/__next._full.txt +1 -1
  156. pycharter/ui/static/static/__next._head.txt +1 -1
  157. pycharter/ui/static/static/__next._index.txt +1 -1
  158. pycharter/ui/static/static/__next._tree.txt +1 -1
  159. pycharter/ui/static/static/_not-found/__next._full.txt +1 -1
  160. pycharter/ui/static/static/_not-found/__next._head.txt +1 -1
  161. pycharter/ui/static/static/_not-found/__next._index.txt +1 -1
  162. pycharter/ui/static/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
  163. pycharter/ui/static/static/_not-found/__next._not-found.txt +1 -1
  164. pycharter/ui/static/static/_not-found/__next._tree.txt +1 -1
  165. pycharter/ui/static/static/_not-found/index.html +1 -1
  166. pycharter/ui/static/static/_not-found/index.txt +1 -1
  167. pycharter/ui/static/static/contracts/__next._full.txt +2 -2
  168. pycharter/ui/static/static/contracts/__next._head.txt +1 -1
  169. pycharter/ui/static/static/contracts/__next._index.txt +1 -1
  170. pycharter/ui/static/static/contracts/__next._tree.txt +1 -1
  171. pycharter/ui/static/static/contracts/__next.contracts.__PAGE__.txt +2 -2
  172. pycharter/ui/static/static/contracts/__next.contracts.txt +1 -1
  173. pycharter/ui/static/static/contracts/index.html +1 -1
  174. pycharter/ui/static/static/contracts/index.txt +2 -2
  175. pycharter/ui/static/static/documentation/__next._full.txt +1 -1
  176. pycharter/ui/static/static/documentation/__next._head.txt +1 -1
  177. pycharter/ui/static/static/documentation/__next._index.txt +1 -1
  178. pycharter/ui/static/static/documentation/__next._tree.txt +1 -1
  179. pycharter/ui/static/static/documentation/__next.documentation.__PAGE__.txt +1 -1
  180. pycharter/ui/static/static/documentation/__next.documentation.txt +1 -1
  181. pycharter/ui/static/static/documentation/index.html +2 -2
  182. pycharter/ui/static/static/documentation/index.txt +1 -1
  183. pycharter/ui/static/static/index.html +1 -1
  184. pycharter/ui/static/static/index.txt +1 -1
  185. pycharter/ui/static/static/metadata/__next._full.txt +1 -1
  186. pycharter/ui/static/static/metadata/__next._head.txt +1 -1
  187. pycharter/ui/static/static/metadata/__next._index.txt +1 -1
  188. pycharter/ui/static/static/metadata/__next._tree.txt +1 -1
  189. pycharter/ui/static/static/metadata/__next.metadata.__PAGE__.txt +1 -1
  190. pycharter/ui/static/static/metadata/__next.metadata.txt +1 -1
  191. pycharter/ui/static/static/metadata/index.html +1 -1
  192. pycharter/ui/static/static/metadata/index.txt +1 -1
  193. pycharter/ui/static/static/quality/__next._full.txt +2 -2
  194. pycharter/ui/static/static/quality/__next._head.txt +1 -1
  195. pycharter/ui/static/static/quality/__next._index.txt +1 -1
  196. pycharter/ui/static/static/quality/__next._tree.txt +1 -1
  197. pycharter/ui/static/static/quality/__next.quality.__PAGE__.txt +2 -2
  198. pycharter/ui/static/static/quality/__next.quality.txt +1 -1
  199. pycharter/ui/static/static/quality/index.html +2 -2
  200. pycharter/ui/static/static/quality/index.txt +2 -2
  201. pycharter/ui/static/static/rules/__next._full.txt +1 -1
  202. pycharter/ui/static/static/rules/__next._head.txt +1 -1
  203. pycharter/ui/static/static/rules/__next._index.txt +1 -1
  204. pycharter/ui/static/static/rules/__next._tree.txt +1 -1
  205. pycharter/ui/static/static/rules/__next.rules.__PAGE__.txt +1 -1
  206. pycharter/ui/static/static/rules/__next.rules.txt +1 -1
  207. pycharter/ui/static/static/rules/index.html +1 -1
  208. pycharter/ui/static/static/rules/index.txt +1 -1
  209. pycharter/ui/static/static/schemas/__next._full.txt +1 -1
  210. pycharter/ui/static/static/schemas/__next._head.txt +1 -1
  211. pycharter/ui/static/static/schemas/__next._index.txt +1 -1
  212. pycharter/ui/static/static/schemas/__next._tree.txt +1 -1
  213. pycharter/ui/static/static/schemas/__next.schemas.__PAGE__.txt +1 -1
  214. pycharter/ui/static/static/schemas/__next.schemas.txt +1 -1
  215. pycharter/ui/static/static/schemas/index.html +1 -1
  216. pycharter/ui/static/static/schemas/index.txt +1 -1
  217. pycharter/ui/static/static/settings/__next._full.txt +1 -1
  218. pycharter/ui/static/static/settings/__next._head.txt +1 -1
  219. pycharter/ui/static/static/settings/__next._index.txt +1 -1
  220. pycharter/ui/static/static/settings/__next._tree.txt +1 -1
  221. pycharter/ui/static/static/settings/__next.settings.__PAGE__.txt +1 -1
  222. pycharter/ui/static/static/settings/__next.settings.txt +1 -1
  223. pycharter/ui/static/static/settings/index.html +1 -1
  224. pycharter/ui/static/static/settings/index.txt +1 -1
  225. pycharter/ui/static/static/static/404/index.html +1 -1
  226. pycharter/ui/static/static/static/404.html +1 -1
  227. pycharter/ui/static/static/static/__next.__PAGE__.txt +1 -1
  228. pycharter/ui/static/static/static/__next._full.txt +2 -2
  229. pycharter/ui/static/static/static/__next._head.txt +1 -1
  230. pycharter/ui/static/static/static/__next._index.txt +2 -2
  231. pycharter/ui/static/static/static/__next._tree.txt +2 -2
  232. pycharter/ui/static/static/static/_next/static/chunks/f7d1a90dd75d2572.js +1 -0
  233. pycharter/ui/static/static/static/_not-found/__next._full.txt +2 -2
  234. pycharter/ui/static/static/static/_not-found/__next._head.txt +1 -1
  235. pycharter/ui/static/static/static/_not-found/__next._index.txt +2 -2
  236. pycharter/ui/static/static/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
  237. pycharter/ui/static/static/static/_not-found/__next._not-found.txt +1 -1
  238. pycharter/ui/static/static/static/_not-found/__next._tree.txt +2 -2
  239. pycharter/ui/static/static/static/_not-found/index.html +1 -1
  240. pycharter/ui/static/static/static/_not-found/index.txt +2 -2
  241. pycharter/ui/static/static/static/contracts/__next._full.txt +3 -3
  242. pycharter/ui/static/static/static/contracts/__next._head.txt +1 -1
  243. pycharter/ui/static/static/static/contracts/__next._index.txt +2 -2
  244. pycharter/ui/static/static/static/contracts/__next._tree.txt +2 -2
  245. pycharter/ui/static/static/static/contracts/__next.contracts.__PAGE__.txt +2 -2
  246. pycharter/ui/static/static/static/contracts/__next.contracts.txt +1 -1
  247. pycharter/ui/static/static/static/contracts/index.html +1 -1
  248. pycharter/ui/static/static/static/contracts/index.txt +3 -3
  249. pycharter/ui/static/static/static/documentation/__next._full.txt +3 -3
  250. pycharter/ui/static/static/static/documentation/__next._head.txt +1 -1
  251. pycharter/ui/static/static/static/documentation/__next._index.txt +2 -2
  252. pycharter/ui/static/static/static/documentation/__next._tree.txt +2 -2
  253. pycharter/ui/static/static/static/documentation/__next.documentation.__PAGE__.txt +2 -2
  254. pycharter/ui/static/static/static/documentation/__next.documentation.txt +1 -1
  255. pycharter/ui/static/static/static/documentation/index.html +2 -2
  256. pycharter/ui/static/static/static/documentation/index.txt +3 -3
  257. pycharter/ui/static/static/static/index.html +1 -1
  258. pycharter/ui/static/static/static/index.txt +2 -2
  259. pycharter/ui/static/static/static/metadata/__next._full.txt +2 -2
  260. pycharter/ui/static/static/static/metadata/__next._head.txt +1 -1
  261. pycharter/ui/static/static/static/metadata/__next._index.txt +2 -2
  262. pycharter/ui/static/static/static/metadata/__next._tree.txt +2 -2
  263. pycharter/ui/static/static/static/metadata/__next.metadata.__PAGE__.txt +1 -1
  264. pycharter/ui/static/static/static/metadata/__next.metadata.txt +1 -1
  265. pycharter/ui/static/static/static/metadata/index.html +1 -1
  266. pycharter/ui/static/static/static/metadata/index.txt +2 -2
  267. pycharter/ui/static/static/static/quality/__next._full.txt +2 -2
  268. pycharter/ui/static/static/static/quality/__next._head.txt +1 -1
  269. pycharter/ui/static/static/static/quality/__next._index.txt +2 -2
  270. pycharter/ui/static/static/static/quality/__next._tree.txt +2 -2
  271. pycharter/ui/static/static/static/quality/__next.quality.__PAGE__.txt +1 -1
  272. pycharter/ui/static/static/static/quality/__next.quality.txt +1 -1
  273. pycharter/ui/static/static/static/quality/index.html +2 -2
  274. pycharter/ui/static/static/static/quality/index.txt +2 -2
  275. pycharter/ui/static/static/static/rules/__next._full.txt +2 -2
  276. pycharter/ui/static/static/static/rules/__next._head.txt +1 -1
  277. pycharter/ui/static/static/static/rules/__next._index.txt +2 -2
  278. pycharter/ui/static/static/static/rules/__next._tree.txt +2 -2
  279. pycharter/ui/static/static/static/rules/__next.rules.__PAGE__.txt +1 -1
  280. pycharter/ui/static/static/static/rules/__next.rules.txt +1 -1
  281. pycharter/ui/static/static/static/rules/index.html +1 -1
  282. pycharter/ui/static/static/static/rules/index.txt +2 -2
  283. pycharter/ui/static/static/static/schemas/__next._full.txt +2 -2
  284. pycharter/ui/static/static/static/schemas/__next._head.txt +1 -1
  285. pycharter/ui/static/static/static/schemas/__next._index.txt +2 -2
  286. pycharter/ui/static/static/static/schemas/__next._tree.txt +2 -2
  287. pycharter/ui/static/static/static/schemas/__next.schemas.__PAGE__.txt +1 -1
  288. pycharter/ui/static/static/static/schemas/__next.schemas.txt +1 -1
  289. pycharter/ui/static/static/static/schemas/index.html +1 -1
  290. pycharter/ui/static/static/static/schemas/index.txt +2 -2
  291. pycharter/ui/static/static/static/settings/__next._full.txt +2 -2
  292. pycharter/ui/static/static/static/settings/__next._head.txt +1 -1
  293. pycharter/ui/static/static/static/settings/__next._index.txt +2 -2
  294. pycharter/ui/static/static/static/settings/__next._tree.txt +2 -2
  295. pycharter/ui/static/static/static/settings/__next.settings.__PAGE__.txt +1 -1
  296. pycharter/ui/static/static/static/settings/__next.settings.txt +1 -1
  297. pycharter/ui/static/static/static/settings/index.html +1 -1
  298. pycharter/ui/static/static/static/settings/index.txt +2 -2
  299. pycharter/ui/static/static/static/static/.gitkeep +0 -0
  300. pycharter/ui/static/static/static/static/404/index.html +1 -0
  301. pycharter/ui/static/static/static/static/404.html +1 -0
  302. pycharter/ui/static/static/static/static/__next.__PAGE__.txt +10 -0
  303. pycharter/ui/static/static/static/static/__next._full.txt +30 -0
  304. pycharter/ui/static/static/static/static/__next._head.txt +7 -0
  305. pycharter/ui/static/static/static/static/__next._index.txt +9 -0
  306. pycharter/ui/static/static/static/static/__next._tree.txt +2 -0
  307. pycharter/ui/static/static/static/static/_next/static/chunks/222442f6da32302a.js +1 -0
  308. pycharter/ui/static/static/static/static/_next/static/chunks/247eb132b7f7b574.js +1 -0
  309. pycharter/ui/static/static/static/static/_next/static/chunks/297d55555b71baba.js +1 -0
  310. pycharter/ui/static/static/static/static/_next/static/chunks/414e77373f8ff61c.js +1 -0
  311. pycharter/ui/static/static/static/static/_next/static/chunks/652ad0aa26265c47.js +2 -0
  312. pycharter/ui/static/static/static/static/_next/static/chunks/9c23f44fff36548a.js +1 -0
  313. pycharter/ui/static/static/static/static/_next/static/chunks/a6dad97d9634a72d.js +1 -0
  314. pycharter/ui/static/static/static/static/_next/static/chunks/b32a0963684b9933.js +4 -0
  315. pycharter/ui/static/static/static/static/_next/static/chunks/db913959c675cea6.js +1 -0
  316. pycharter/ui/static/static/static/static/_next/static/chunks/f2e7afeab1178138.js +1 -0
  317. pycharter/ui/static/static/static/static/_next/static/chunks/ff1a16fafef87110.js +1 -0
  318. pycharter/ui/static/static/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +3 -0
  319. pycharter/ui/static/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_buildManifest.js +11 -0
  320. pycharter/ui/static/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_clientMiddlewareManifest.json +1 -0
  321. pycharter/ui/static/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_ssgManifest.js +1 -0
  322. pycharter/ui/static/static/static/static/_not-found/__next._full.txt +17 -0
  323. pycharter/ui/static/static/static/static/_not-found/__next._head.txt +7 -0
  324. pycharter/ui/static/static/static/static/_not-found/__next._index.txt +9 -0
  325. pycharter/ui/static/static/static/static/_not-found/__next._not-found.__PAGE__.txt +5 -0
  326. pycharter/ui/static/static/static/static/_not-found/__next._not-found.txt +4 -0
  327. pycharter/ui/static/static/static/static/_not-found/__next._tree.txt +2 -0
  328. pycharter/ui/static/static/static/static/_not-found/index.html +1 -0
  329. pycharter/ui/static/static/static/static/_not-found/index.txt +17 -0
  330. pycharter/ui/static/static/static/static/contracts/__next._full.txt +21 -0
  331. pycharter/ui/static/static/static/static/contracts/__next._head.txt +7 -0
  332. pycharter/ui/static/static/static/static/contracts/__next._index.txt +9 -0
  333. pycharter/ui/static/static/static/static/contracts/__next._tree.txt +2 -0
  334. pycharter/ui/static/static/static/static/contracts/__next.contracts.__PAGE__.txt +9 -0
  335. pycharter/ui/static/static/static/static/contracts/__next.contracts.txt +4 -0
  336. pycharter/ui/static/static/static/static/contracts/index.html +1 -0
  337. pycharter/ui/static/static/static/static/contracts/index.txt +21 -0
  338. pycharter/ui/static/static/static/static/documentation/__next._full.txt +21 -0
  339. pycharter/ui/static/static/static/static/documentation/__next._head.txt +7 -0
  340. pycharter/ui/static/static/static/static/documentation/__next._index.txt +9 -0
  341. pycharter/ui/static/static/static/static/documentation/__next._tree.txt +2 -0
  342. pycharter/ui/static/static/static/static/documentation/__next.documentation.__PAGE__.txt +9 -0
  343. pycharter/ui/static/static/static/static/documentation/__next.documentation.txt +4 -0
  344. pycharter/ui/static/static/static/static/documentation/index.html +93 -0
  345. pycharter/ui/static/static/static/static/documentation/index.txt +21 -0
  346. pycharter/ui/static/static/static/static/index.html +1 -0
  347. pycharter/ui/static/static/static/static/index.txt +30 -0
  348. pycharter/ui/static/static/static/static/metadata/__next._full.txt +21 -0
  349. pycharter/ui/static/static/static/static/metadata/__next._head.txt +7 -0
  350. pycharter/ui/static/static/static/static/metadata/__next._index.txt +9 -0
  351. pycharter/ui/static/static/static/static/metadata/__next._tree.txt +2 -0
  352. pycharter/ui/static/static/static/static/metadata/__next.metadata.__PAGE__.txt +9 -0
  353. pycharter/ui/static/static/static/static/metadata/__next.metadata.txt +4 -0
  354. pycharter/ui/static/static/static/static/metadata/index.html +1 -0
  355. pycharter/ui/static/static/static/static/metadata/index.txt +21 -0
  356. pycharter/ui/static/static/static/static/quality/__next._full.txt +21 -0
  357. pycharter/ui/static/static/static/static/quality/__next._head.txt +7 -0
  358. pycharter/ui/static/static/static/static/quality/__next._index.txt +9 -0
  359. pycharter/ui/static/static/static/static/quality/__next._tree.txt +2 -0
  360. pycharter/ui/static/static/static/static/quality/__next.quality.__PAGE__.txt +9 -0
  361. pycharter/ui/static/static/static/static/quality/__next.quality.txt +4 -0
  362. pycharter/ui/static/static/static/static/quality/index.html +2 -0
  363. pycharter/ui/static/static/static/static/quality/index.txt +21 -0
  364. pycharter/ui/static/static/static/static/rules/__next._full.txt +21 -0
  365. pycharter/ui/static/static/static/static/rules/__next._head.txt +7 -0
  366. pycharter/ui/static/static/static/static/rules/__next._index.txt +9 -0
  367. pycharter/ui/static/static/static/static/rules/__next._tree.txt +2 -0
  368. pycharter/ui/static/static/static/static/rules/__next.rules.__PAGE__.txt +9 -0
  369. pycharter/ui/static/static/static/static/rules/__next.rules.txt +4 -0
  370. pycharter/ui/static/static/static/static/rules/index.html +1 -0
  371. pycharter/ui/static/static/static/static/rules/index.txt +21 -0
  372. pycharter/ui/static/static/static/static/schemas/__next._full.txt +21 -0
  373. pycharter/ui/static/static/static/static/schemas/__next._head.txt +7 -0
  374. pycharter/ui/static/static/static/static/schemas/__next._index.txt +9 -0
  375. pycharter/ui/static/static/static/static/schemas/__next._tree.txt +2 -0
  376. pycharter/ui/static/static/static/static/schemas/__next.schemas.__PAGE__.txt +9 -0
  377. pycharter/ui/static/static/static/static/schemas/__next.schemas.txt +4 -0
  378. pycharter/ui/static/static/static/static/schemas/index.html +1 -0
  379. pycharter/ui/static/static/static/static/schemas/index.txt +21 -0
  380. pycharter/ui/static/static/static/static/settings/__next._full.txt +21 -0
  381. pycharter/ui/static/static/static/static/settings/__next._head.txt +7 -0
  382. pycharter/ui/static/static/static/static/settings/__next._index.txt +9 -0
  383. pycharter/ui/static/static/static/static/settings/__next._tree.txt +2 -0
  384. pycharter/ui/static/static/static/static/settings/__next.settings.__PAGE__.txt +9 -0
  385. pycharter/ui/static/static/static/static/settings/__next.settings.txt +4 -0
  386. pycharter/ui/static/static/static/static/settings/index.html +1 -0
  387. pycharter/ui/static/static/static/static/settings/index.txt +21 -0
  388. pycharter/ui/static/static/static/static/validation/__next._full.txt +21 -0
  389. pycharter/ui/static/static/static/static/validation/__next._head.txt +7 -0
  390. pycharter/ui/static/static/static/static/validation/__next._index.txt +9 -0
  391. pycharter/ui/static/static/static/static/validation/__next._tree.txt +2 -0
  392. pycharter/ui/static/static/static/static/validation/__next.validation.__PAGE__.txt +9 -0
  393. pycharter/ui/static/static/static/static/validation/__next.validation.txt +4 -0
  394. pycharter/ui/static/static/static/static/validation/index.html +1 -0
  395. pycharter/ui/static/static/static/static/validation/index.txt +21 -0
  396. pycharter/ui/static/static/static/validation/__next._full.txt +2 -2
  397. pycharter/ui/static/static/static/validation/__next._head.txt +1 -1
  398. pycharter/ui/static/static/static/validation/__next._index.txt +2 -2
  399. pycharter/ui/static/static/static/validation/__next._tree.txt +2 -2
  400. pycharter/ui/static/static/static/validation/__next.validation.__PAGE__.txt +1 -1
  401. pycharter/ui/static/static/static/validation/__next.validation.txt +1 -1
  402. pycharter/ui/static/static/static/validation/index.html +1 -1
  403. pycharter/ui/static/static/static/validation/index.txt +2 -2
  404. pycharter/ui/static/static/validation/__next._full.txt +2 -2
  405. pycharter/ui/static/static/validation/__next._head.txt +1 -1
  406. pycharter/ui/static/static/validation/__next._index.txt +1 -1
  407. pycharter/ui/static/static/validation/__next._tree.txt +1 -1
  408. pycharter/ui/static/static/validation/__next.validation.__PAGE__.txt +2 -2
  409. pycharter/ui/static/static/validation/__next.validation.txt +1 -1
  410. pycharter/ui/static/static/validation/index.html +1 -1
  411. pycharter/ui/static/static/validation/index.txt +2 -2
  412. pycharter/ui/static/validation/__next._full.txt +7 -7
  413. pycharter/ui/static/validation/__next._head.txt +1 -1
  414. pycharter/ui/static/validation/__next._index.txt +6 -6
  415. pycharter/ui/static/validation/__next._tree.txt +2 -2
  416. pycharter/ui/static/validation/__next.validation.__PAGE__.txt +2 -2
  417. pycharter/ui/static/validation/__next.validation.txt +1 -1
  418. pycharter/ui/static/validation/index.html +1 -1
  419. pycharter/ui/static/validation/index.txt +7 -7
  420. {pycharter-0.0.25.dist-info → pycharter-0.0.26.dist-info}/METADATA +57 -26
  421. pycharter-0.0.26.dist-info/RECORD +702 -0
  422. pycharter/etl_generator/config_loader.py +0 -394
  423. pycharter/etl_generator/loaders/cloud.py +0 -87
  424. pycharter/etl_generator/loaders/file_loader.py +0 -130
  425. pycharter/etl_generator/schemas/extract.json +0 -234
  426. pycharter/etl_generator/schemas/load.json +0 -202
  427. pycharter/etl_generator/schemas/pipeline.json +0 -94
  428. pycharter/etl_generator/schemas/transform.json +0 -171
  429. pycharter-0.0.25.dist-info/RECORD +0 -572
  430. /pycharter/ui/static/_next/static/{2gKjNv6YvE6BcIdFthBLs → YCnlK66gA7FV5vvcixspB}/_buildManifest.js +0 -0
  431. /pycharter/ui/static/_next/static/{2gKjNv6YvE6BcIdFthBLs → YCnlK66gA7FV5vvcixspB}/_clientMiddlewareManifest.json +0 -0
  432. /pycharter/ui/static/_next/static/{2gKjNv6YvE6BcIdFthBLs → YCnlK66gA7FV5vvcixspB}/_ssgManifest.js +0 -0
  433. /pycharter/ui/static/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_buildManifest.js +0 -0
  434. /pycharter/ui/static/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_clientMiddlewareManifest.json +0 -0
  435. /pycharter/ui/static/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_ssgManifest.js +0 -0
  436. /pycharter/ui/static/{_next → static/_next}/static/chunks/26dfc590f7714c03.js +0 -0
  437. /pycharter/ui/static/{_next → static/_next}/static/chunks/34d289e6db2ef551.js +0 -0
  438. /pycharter/ui/static/{_next → static/_next}/static/chunks/99508d9d5869cc27.js +0 -0
  439. /pycharter/ui/static/{_next → static/_next}/static/chunks/b313c35a6ba76574.js +0 -0
  440. /pycharter/ui/static/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_buildManifest.js +0 -0
  441. /pycharter/ui/static/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_clientMiddlewareManifest.json +0 -0
  442. /pycharter/ui/static/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_ssgManifest.js +0 -0
  443. /pycharter/ui/static/{_next → static/static/_next}/static/chunks/13d4a0fbd74c1ee4.js +0 -0
  444. /pycharter/ui/static/{_next → static/static/_next}/static/chunks/2edb43b48432ac04.js +0 -0
  445. /pycharter/ui/static/static/{_next → static/_next}/static/chunks/c4fa4f4114b7c352.js +0 -0
  446. /pycharter/ui/static/{_next → static/static/_next}/static/chunks/d2363397e1b2bcab.css +0 -0
  447. /pycharter/ui/static/{_next → static/static/static/_next}/static/chunks/2ab439ce003cd691.js +0 -0
  448. /pycharter/ui/static/{_next → static/static/static/_next}/static/chunks/49ca65abd26ae49e.js +0 -0
  449. /pycharter/ui/static/static/static/{_next → static/_next}/static/chunks/4e310fe5005770a3.css +0 -0
  450. /pycharter/ui/static/static/{_next → static/static/_next}/static/chunks/5e04d10c4a7b58a3.js +0 -0
  451. /pycharter/ui/static/static/static/{_next → static/_next}/static/chunks/5fc14c00a2779dc5.js +0 -0
  452. /pycharter/ui/static/static/{_next → static/static/_next}/static/chunks/75d88a058d8ffaa6.js +0 -0
  453. /pycharter/ui/static/static/{_next → static/static/_next}/static/chunks/8c89634cf6bad76f.js +0 -0
  454. /pycharter/ui/static/{_next → static/static/static/_next}/static/chunks/9667e7a3d359eb39.js +0 -0
  455. /pycharter/ui/static/static/static/{_next → static/_next}/static/chunks/b584574fdc8ab13e.js +0 -0
  456. /pycharter/ui/static/{_next → static/static/static/_next}/static/chunks/c69f6cba366bd988.js +0 -0
  457. /pycharter/ui/static/static/static/{_next → static/_next}/static/chunks/d5989c94d3614b3a.js +0 -0
  458. /pycharter/ui/static/{_next → static/static/static/_next}/static/chunks/f061a4be97bfc3b3.js +0 -0
  459. {pycharter-0.0.25.dist-info → pycharter-0.0.26.dist-info}/WHEEL +0 -0
  460. {pycharter-0.0.25.dist-info → pycharter-0.0.26.dist-info}/entry_points.txt +0 -0
  461. {pycharter-0.0.25.dist-info → pycharter-0.0.26.dist-info}/licenses/LICENSE +0 -0
  462. {pycharter-0.0.25.dist-info → pycharter-0.0.26.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,111 @@
1
+ # Template: PostgreSQL Load with Target Validation
2
+ # Copy to your pipeline directory as load.yaml
3
+ #
4
+ # This template demonstrates target contract validation before loading.
5
+ # Data is validated against a contract (schema + coercion + validation rules)
6
+ # to ensure it meets the target system's requirements.
7
+ #
8
+ # Usage:
9
+ # pipeline = Pipeline.from_config_dir("path/to/pipeline")
10
+ # result = await pipeline.run()
11
+ # print(f"Records loaded: {result.rows_loaded}")
12
+ # print(f"Quarantined at load: {result.rows_quarantined_load}")
13
+
14
+ title: postgres_load_with_validation
15
+ description: Load to PostgreSQL with target contract validation
16
+ version: "1.0.0"
17
+
18
+ # Destination type
19
+ type: postgres
20
+
21
+ # Database connection
22
+ connection_string: ${DATABASE_URL}
23
+ # Or use database object:
24
+ # database:
25
+ # url: ${DATABASE_URL}
26
+
27
+ # Target table
28
+ schema: public
29
+ table: orders
30
+
31
+ # Write method
32
+ write_method: upsert # insert, upsert, replace, truncate_and_load
33
+ primary_key: order_id
34
+
35
+ # Batch size
36
+ batch_size: 500
37
+
38
+ # ============================================================================
39
+ # Target Contract Validation (Optional)
40
+ # ============================================================================
41
+ # Validate transformed data against a contract before loading.
42
+ # This ensures data conforms to the target table schema and business rules.
43
+
44
+ validation:
45
+ # Option 1: Path to contract directory or file
46
+ # Contract directory should contain: schema.yaml, coercion_rules.yaml (optional),
47
+ # validation_rules.yaml (optional)
48
+ contract: ./contracts/orders
49
+
50
+ # Option 2: Reference contract from metadata store (requires settings.yaml metadata_store)
51
+ # contract:
52
+ # type: database
53
+ # name: orders_contract
54
+ # version: "2.0" # Optional, defaults to latest
55
+
56
+ # Option 3: Use default contract from settings.yaml
57
+ # use_contract: true
58
+
59
+ # Action when validation fails:
60
+ # - fail: Stop pipeline immediately (default)
61
+ # - warn: Log warning but continue with all records
62
+ # - skip: Skip invalid records (don't load them)
63
+ # - quarantine: Send invalid records to DLQ
64
+ on_error: quarantine
65
+
66
+ # DLQ configuration (optional, overrides settings.yaml)
67
+ dlq: true # Use default DLQ from settings.yaml
68
+
69
+ # ============================================================================
70
+ # Example Contract Structure (create in ./contracts/orders/)
71
+ # ============================================================================
72
+ #
73
+ # contracts/orders/schema.yaml:
74
+ # ---
75
+ # version: "1.0.0"
76
+ # type: object
77
+ # properties:
78
+ # order_id:
79
+ # type: string
80
+ # maxLength: 20
81
+ # customer_id:
82
+ # type: integer
83
+ # status:
84
+ # type: string
85
+ # enum: ["pending", "processing", "shipped", "delivered", "cancelled"]
86
+ # total_amount:
87
+ # type: number
88
+ # minimum: 0
89
+ # created_at:
90
+ # type: string
91
+ # format: date-time
92
+ # required:
93
+ # - order_id
94
+ # - customer_id
95
+ # - status
96
+ #
97
+ # contracts/orders/coercion_rules.yaml:
98
+ # ---
99
+ # order_id: string
100
+ # customer_id: int
101
+ # total_amount: float
102
+ # created_at: datetime
103
+ #
104
+ # contracts/orders/validation_rules.yaml:
105
+ # ---
106
+ # order_id:
107
+ # - not_empty
108
+ # - max_length: 20
109
+ # total_amount:
110
+ # - min_value: 0
111
+ # - max_value: 1000000
@@ -0,0 +1,55 @@
1
+ # Template: Pipeline Settings
2
+ # Copy to your pipeline directory as settings.yaml
3
+ #
4
+ # This file provides shared configuration for the pipeline:
5
+ # - Dead Letter Queue (DLQ) settings for quarantining invalid records
6
+ # - Metadata store connection for database-backed contracts
7
+ # - Default contract name for load validation
8
+ #
9
+ # Usage:
10
+ # pipeline = Pipeline.from_config_dir("path/to/pipeline")
11
+ # result = await pipeline.run()
12
+
13
+ title: pipeline_settings
14
+ description: Shared pipeline configuration
15
+ version: "1.0.0"
16
+
17
+ # ============================================================================
18
+ # Dead Letter Queue (DLQ) Configuration
19
+ # ============================================================================
20
+ # Records that fail validation can be sent to a DLQ for later review.
21
+ # Configure this at the settings level to share across extract and load stages.
22
+
23
+ dlq:
24
+ # Enable/disable DLQ
25
+ enabled: true
26
+
27
+ # Storage backend: "file", "database", or "memory"
28
+ backend: file
29
+
30
+ # File backend options
31
+ path: ./dlq # Directory to store quarantined records
32
+
33
+ # Database backend options (uncomment to use)
34
+ # backend: database
35
+ # connection_string: ${DATABASE_URL}
36
+ # schema: pycharter # Database schema for DLQ table
37
+ # table: dead_letter_queue # Optional custom table name
38
+
39
+ # ============================================================================
40
+ # Metadata Store Configuration (Optional)
41
+ # ============================================================================
42
+ # If you store contracts in a database (using MetadataStoreClient), configure
43
+ # the connection here. This allows load validation to reference contracts by name.
44
+
45
+ # metadata_store:
46
+ # type: postgres # or sqlite, mongodb, redis, memory
47
+ # connection_string: ${DATABASE_URL}
48
+
49
+ # ============================================================================
50
+ # Default Contract (Optional)
51
+ # ============================================================================
52
+ # Specify a default contract name for load validation when using `use_contract: true`
53
+ # in your load config. The contract must exist in the metadata_store.
54
+
55
+ # contract: orders_v2
pycharter/db/cli.py CHANGED
@@ -27,15 +27,20 @@ except ImportError:
27
27
  from pycharter.config import get_database_url, set_database_url
28
28
  from pycharter.db.models import (
29
29
  APIEndpointModel,
30
+ CoercionRuleModel,
30
31
  ComplianceFrameworkModel,
32
+ DataContractModel,
31
33
  DataFeedModel,
32
34
  DomainModel,
33
35
  EnvironmentModel,
36
+ MetadataRecordModel,
34
37
  OwnerModel,
38
+ SchemaModel,
35
39
  SystemModel,
36
40
  TagModel,
41
+ ValidationRuleModel,
37
42
  )
38
- from pycharter.db.models.base import Base, get_session
43
+ from pycharter.db.models.base import Base, get_engine, get_session
39
44
 
40
45
 
41
46
  # Helper functions
@@ -268,7 +273,7 @@ def _init_sqlite(database_url: str, force: bool = False) -> int:
268
273
  if db_path != ":memory:":
269
274
  Path(db_path).parent.mkdir(parents=True, exist_ok=True)
270
275
 
271
- engine = create_engine(database_url)
276
+ engine = get_engine(database_url)
272
277
  existing_tables = inspect(engine).get_table_names()
273
278
 
274
279
  if existing_tables and not force:
@@ -276,6 +281,15 @@ def _init_sqlite(database_url: str, force: bool = False) -> int:
276
281
  print(" Use --force to reinitialize, or run 'pycharter db upgrade' to apply migrations.")
277
282
  return 1
278
283
 
284
+ if existing_tables and force:
285
+ with engine.connect() as conn:
286
+ conn.execute(text("PRAGMA foreign_keys=OFF"))
287
+ for name in existing_tables:
288
+ conn.execute(text(f'DROP TABLE IF EXISTS "{name}"'))
289
+ conn.execute(text("PRAGMA foreign_keys=ON"))
290
+ conn.commit()
291
+ print("✓ Dropped existing tables")
292
+
279
293
  Base.metadata.create_all(engine)
280
294
  print("✓ Created tables using SQLAlchemy models")
281
295
 
@@ -502,8 +516,9 @@ def cmd_seed(seed_dir: Optional[str] = None, database_url: Optional[str] = None)
502
516
  if seed_dir:
503
517
  seed_path = Path(seed_dir)
504
518
  else:
505
- project_root = Path(__file__).resolve().parent.parent.parent
506
- seed_path = project_root / "data" / "seed"
519
+ # Default: package-bundled seed under src/pycharter/data/seed
520
+ pkg_root = Path(__file__).resolve().parent.parent
521
+ seed_path = pkg_root / "data" / "seed"
507
522
 
508
523
  if not seed_path.exists():
509
524
  print(f"❌ Error: Seed directory not found: {seed_path}")
@@ -522,6 +537,112 @@ def cmd_seed(seed_dir: Optional[str] = None, database_url: Optional[str] = None)
522
537
  return 1
523
538
 
524
539
 
540
+ def _seed_contract_artifacts(seed_path: Path, session: Any) -> int:
541
+ """Seed contract artifacts from contracts.yaml (data contract + schema, coercion, validation, metadata)."""
542
+ contracts_file = seed_path / "contracts.yaml"
543
+ if not contracts_file.exists():
544
+ print("⚠ No contracts.yaml found, skipping contract artifacts")
545
+ return 0
546
+ with open(contracts_file, "r") as f:
547
+ contracts = yaml.safe_load(f) or []
548
+ if not contracts:
549
+ return 0
550
+ print("Loading contract artifacts...")
551
+ count = 0
552
+ for entry in contracts:
553
+ name = entry.get("name")
554
+ version = entry.get("version")
555
+ if not name or not version:
556
+ print("⚠ Skipping contract entry missing name or version")
557
+ continue
558
+ existing = session.query(DataContractModel).filter(
559
+ DataContractModel.name == name,
560
+ DataContractModel.version == version,
561
+ ).first()
562
+ if existing:
563
+ print(f" Skipped (exists): {name}@{version}")
564
+ continue
565
+ # Create data contract with null FKs
566
+ contract = DataContractModel(
567
+ name=name,
568
+ version=version,
569
+ status=entry.get("status") or "active",
570
+ description=entry.get("description"),
571
+ )
572
+ session.add(contract)
573
+ session.flush()
574
+ data_contract_id = contract.id
575
+ # Schema
576
+ schema_def = entry.get("schema") or {}
577
+ schema_title = schema_def.get("title") or f"{name}_schema"
578
+ schema_version = schema_def.get("version") or version
579
+ schema_data = {k: v for k, v in schema_def.items() if k not in ("title", "version")}
580
+ schema_data["title"] = schema_def.get("title", schema_title)
581
+ schema_data["version"] = schema_version
582
+ schema_row = SchemaModel(
583
+ title=schema_title,
584
+ data_contract_id=data_contract_id,
585
+ version=schema_version,
586
+ schema_data=schema_data,
587
+ )
588
+ session.add(schema_row)
589
+ session.flush()
590
+ schema_id = schema_row.id
591
+ # Coercion rules
592
+ cr_def = entry.get("coercion_rules") or {}
593
+ cr_title = cr_def.get("title") or f"{name}_coercion"
594
+ cr_version = cr_def.get("version") or version
595
+ cr_row = CoercionRuleModel(
596
+ title=cr_title,
597
+ data_contract_id=data_contract_id,
598
+ version=cr_version,
599
+ rules=cr_def.get("rules") or {},
600
+ description=cr_def.get("description"),
601
+ schema_id=schema_id,
602
+ )
603
+ session.add(cr_row)
604
+ session.flush()
605
+ # Validation rules
606
+ vr_def = entry.get("validation_rules") or {}
607
+ vr_title = vr_def.get("title") or f"{name}_validation"
608
+ vr_version = vr_def.get("version") or version
609
+ vr_row = ValidationRuleModel(
610
+ title=vr_title,
611
+ data_contract_id=data_contract_id,
612
+ version=vr_version,
613
+ rules=vr_def.get("rules") or {},
614
+ description=vr_def.get("description"),
615
+ schema_id=schema_id,
616
+ )
617
+ session.add(vr_row)
618
+ session.flush()
619
+ # Metadata record
620
+ meta_def = entry.get("metadata") or {}
621
+ meta_title = meta_def.get("title") or f"{name}_metadata"
622
+ meta_version = meta_def.get("version") or version
623
+ meta_row = MetadataRecordModel(
624
+ title=meta_title,
625
+ data_contract_id=data_contract_id,
626
+ version=meta_version,
627
+ status=meta_def.get("status"),
628
+ description=meta_def.get("description"),
629
+ governance_rules=meta_def.get("governance_rules"),
630
+ )
631
+ session.add(meta_row)
632
+ session.flush()
633
+ # Link contract to artifacts
634
+ contract.schema_id = schema_id
635
+ contract.coercion_rules_id = cr_row.id
636
+ contract.validation_rules_id = vr_row.id
637
+ contract.metadata_record_id = meta_row.id
638
+ session.commit()
639
+ print(f" Created contract: {name}@{version}")
640
+ count += 1
641
+ if count:
642
+ print(f"✓ Loaded {count} contract(s)")
643
+ return count
644
+
645
+
525
646
  def _seed_postgresql(seed_path: Path, db_url: str) -> int:
526
647
  """Seed PostgreSQL database with data from YAML files."""
527
648
  _require_alembic()
@@ -535,6 +656,7 @@ def _seed_postgresql(seed_path: Path, db_url: str) -> int:
535
656
  _seed_model(session, DataFeedModel, seed_path / "data_feeds.yaml", "data_feeds", "name")
536
657
  _seed_model(session, ComplianceFrameworkModel, seed_path / "compliance_frameworks.yaml", "compliance_frameworks", "name")
537
658
  _seed_model(session, TagModel, seed_path / "tags.yaml", "tags", "name")
659
+ _seed_contract_artifacts(seed_path, session)
538
660
  print("\n✓ Seed data loaded successfully!")
539
661
  return 0
540
662
  except Exception as e:
@@ -1,6 +1,6 @@
1
1
  """Change artifact unique constraints from (data_contract_id, version) to (title, version)
2
2
 
3
- Revision ID: change_artifact_unique_constraints
3
+ Revision ID: change_artifact_uniq_constraints
4
4
  Revises: remove_artifact_versions
5
5
  Create Date: 2026-01-22 00:00:00.000000
6
6
 
@@ -20,7 +20,7 @@ from alembic import op
20
20
  import sqlalchemy as sa
21
21
 
22
22
  # revision identifiers, used by Alembic.
23
- revision: str = 'change_artifact_unique_constraints'
23
+ revision: str = 'change_artifact_uniq_constraints'
24
24
  down_revision: Union[str, None] = 'remove_artifact_versions'
25
25
  branch_labels: Union[str, Sequence[str], None] = None
26
26
  depends_on: Union[str, Sequence[str], None] = None
@@ -49,16 +49,15 @@ Discovers pipelines from a root directory: each subdir that contains `extract.ya
49
49
 
50
50
  **Import:** `from pycharter.etl_generator.extractors import extract_with_pagination_streaming`
51
51
 
52
- Async generator that yields batches of records. Dispatches to the right extractor via `ExtractorFactory` using `source_type` (or auto-detection from `extract_config`).
52
+ Async generator that yields batches of records. Dispatches to the right extractor via `ExtractorFactory` using `type` (or auto-detection from `extract_config`).
53
53
 
54
54
  - **Args:** `extract_config`, `params`, `headers`, `contract_dir=None`, `batch_size=1000`, `max_records=None`, `config_context=None`
55
55
 
56
- ### `ExtractorFactory` / `get_extractor(extract_config) -> BaseExtractor`
56
+ ### `ExtractorFactory.create(extract_config) -> BaseExtractor`
57
57
 
58
- **Import:** `from pycharter.etl_generator.extractors import ExtractorFactory, get_extractor`
58
+ **Import:** `from pycharter.etl_generator.extractors import ExtractorFactory`
59
59
 
60
- - `ExtractorFactory.get_extractor(extract_config)` — Returns an extractor instance for the given config.
61
- - `get_extractor(extract_config)` — Same, module-level helper.
60
+ - `ExtractorFactory.create(extract_config)` — Returns an extractor instance for the given config.
62
61
  - **Auto-detection:** `base_url`/`api_endpoint` → http; `file_path` → file; `database` → database; `storage` → cloud_storage.
63
62
 
64
63
  ### Extractors (implementations of `BaseExtractor`)
@@ -71,7 +70,7 @@ Async generator that yields batches of records. Dispatches to the right extracto
71
70
  - **DatabaseExtractor** — SQL over PostgreSQL, MySQL, SQLite, MSSQL, Oracle.
72
71
  - **CloudStorageExtractor** — S3, GCS, Azure Blob.
73
72
 
74
- Custom extractors: `ExtractorFactory.register_extractor(source_type, extractor_class)`.
73
+ Custom extractors: `ExtractorFactory.register(type_name, extractor_class)`.
75
74
 
76
75
  ---
77
76
 
@@ -136,7 +135,7 @@ Helpers to produce ETL config dicts (extract/transform/load) from contracts or f
136
135
  |--------------------------|--------|
137
136
  | Run ETL | `from pycharter.etl_generator import ETLOrchestrator, create_orchestrator` |
138
137
  | Discover pipelines | `from pycharter.etl_generator import PipelineFactory` |
139
- | Extract (streaming) | `from pycharter.etl_generator.extractors import extract_with_pagination_streaming, ExtractorFactory, get_extractor` |
138
+ | Extract (streaming) | `from pycharter.etl_generator.extractors import extract_with_pagination_streaming, ExtractorFactory` |
140
139
  | Transform | `from pycharter.etl_generator.transformers import apply_transforms` |
141
140
  | Load to file/cloud | `from pycharter.etl_generator.loaders import load_to_file, load_to_cloud_storage` |
142
141
  | Config generation | `from pycharter.etl_generator import generate_etl_config, generate_etl_config_from_contract, generate_etl_config_from_store` |
@@ -70,19 +70,39 @@ from pycharter.etl_generator.context import PipelineContext
70
70
  from pycharter.etl_generator.result import PipelineResult, BatchResult, LoadResult
71
71
  from pycharter.etl_generator.protocols import Extractor, Transformer, Loader
72
72
 
73
- # Config loading and validation
74
- from pycharter.etl_generator.config_loader import (
75
- ConfigLoader,
76
- PipelineConfig,
77
- load_pipeline_config,
78
- ConfigLoadError,
79
- )
73
+ # Config validation and errors
74
+ from pycharter.shared.errors import ConfigLoadError
80
75
  from pycharter.etl_generator.config_validator import (
81
76
  ConfigValidator,
82
77
  ConfigValidationError,
83
78
  validate_config,
84
79
  )
85
80
 
81
+ # Config models (Pydantic)
82
+ from pycharter.etl_generator.config_models import (
83
+ ExtractConfig,
84
+ LoadConfig,
85
+ TransformConfig,
86
+ SettingsConfig,
87
+ ExtractValidationConfig,
88
+ LoadValidationConfig,
89
+ DLQConfig,
90
+ parse_extract_config,
91
+ parse_load_config,
92
+ parse_transform_config,
93
+ parse_settings_config,
94
+ )
95
+
96
+ # ETL Validation
97
+ from pycharter.etl_generator.validation import (
98
+ ETLValidator,
99
+ ETLValidationError,
100
+ resolve_schema,
101
+ resolve_contract,
102
+ create_dlq,
103
+ create_etl_validator,
104
+ )
105
+
86
106
  # Expression evaluation
87
107
  from pycharter.etl_generator.expression import (
88
108
  ExpressionEvaluator,
@@ -139,14 +159,30 @@ __all__ = [
139
159
  "Extractor",
140
160
  "Transformer",
141
161
  "Loader",
142
- # Config loading
143
- "ConfigLoader",
144
- "PipelineConfig",
145
- "load_pipeline_config",
162
+ # Config validation
146
163
  "ConfigLoadError",
147
164
  "ConfigValidator",
148
165
  "ConfigValidationError",
149
166
  "validate_config",
167
+ # Config models
168
+ "ExtractConfig",
169
+ "LoadConfig",
170
+ "TransformConfig",
171
+ "SettingsConfig",
172
+ "ExtractValidationConfig",
173
+ "LoadValidationConfig",
174
+ "DLQConfig",
175
+ "parse_extract_config",
176
+ "parse_load_config",
177
+ "parse_transform_config",
178
+ "parse_settings_config",
179
+ # ETL Validation
180
+ "ETLValidator",
181
+ "ETLValidationError",
182
+ "resolve_schema",
183
+ "resolve_contract",
184
+ "create_dlq",
185
+ "create_etl_validator",
150
186
  # Expressions
151
187
  "ExpressionEvaluator",
152
188
  "ExpressionError",