pycharter 0.0.24__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 (567) hide show
  1. pycharter/__init__.py +6 -0
  2. pycharter/api/README.md +340 -0
  3. {api → pycharter/api}/__init__.py +1 -1
  4. {api → pycharter/api}/dependencies/__init__.py +2 -2
  5. pycharter/api/dependencies/auth.py +158 -0
  6. {api → pycharter/api}/main.py +32 -4
  7. {api → pycharter/api}/models/__init__.py +4 -4
  8. pycharter/api/models/etl.py +66 -0
  9. {api → pycharter/api}/routes/v1/__init__.py +5 -1
  10. pycharter/api/routes/v1/auth.py +97 -0
  11. {api → pycharter/api}/routes/v1/contracts.py +14 -12
  12. {api → pycharter/api}/routes/v1/docs.py +2 -2
  13. pycharter/api/routes/v1/etl.py +131 -0
  14. {api → pycharter/api}/routes/v1/evolution.py +2 -2
  15. {api → pycharter/api}/routes/v1/metadata.py +5 -5
  16. {api → pycharter/api}/routes/v1/quality.py +3 -3
  17. {api → pycharter/api}/routes/v1/schemas.py +1 -1
  18. {api → pycharter/api}/routes/v1/settings.py +1 -1
  19. {api → pycharter/api}/routes/v1/tracking.py +1 -1
  20. {api → pycharter/api}/routes/v1/validation.py +2 -2
  21. {api → pycharter/api}/routes/v1/validation_jobs.py +3 -3
  22. pycharter/cli.py +9 -11
  23. pycharter/config.py +69 -0
  24. pycharter/contract_builder/builder.py +32 -37
  25. pycharter/data/seed/compliance_frameworks.yaml +22 -0
  26. pycharter/data/seed/contracts.yaml +130 -0
  27. pycharter/data/seed/data_feeds.yaml +22 -0
  28. pycharter/data/seed/domains.yaml +13 -0
  29. pycharter/data/seed/environments.yaml +19 -0
  30. pycharter/data/seed/owners.yaml +21 -0
  31. pycharter/data/seed/systems.yaml +13 -0
  32. pycharter/data/seed/tags.yaml +25 -0
  33. pycharter/data/templates/contract/README.md +161 -0
  34. pycharter/data/templates/contract/template_contract.yaml +37 -0
  35. pycharter/data/templates/etl/README.md +1 -1
  36. pycharter/data/templates/etl/extract_with_validation.yaml +86 -0
  37. pycharter/data/templates/etl/load_with_validation.yaml +111 -0
  38. pycharter/data/templates/etl/settings.yaml +55 -0
  39. pycharter/db/README.md +179 -0
  40. pycharter/db/cli.py +126 -4
  41. pycharter/db/migrations/versions/20260122000000_change_artifact_unique_constraints_to_title_version.py +2 -2
  42. pycharter/db/schemas/README.md +96 -0
  43. pycharter/etl_generator/ASYNC_AND_EXECUTION.md +91 -0
  44. pycharter/etl_generator/INTERFACES.md +142 -0
  45. pycharter/etl_generator/README.md +271 -0
  46. pycharter/etl_generator/TRANSFORMATION_GUIDE.md +452 -0
  47. pycharter/etl_generator/__init__.py +47 -11
  48. pycharter/etl_generator/config_models.py +673 -0
  49. pycharter/etl_generator/config_validator.py +133 -157
  50. pycharter/etl_generator/context.py +3 -0
  51. pycharter/etl_generator/database.py +5 -1
  52. pycharter/etl_generator/extractors/__init__.py +4 -2
  53. pycharter/etl_generator/extractors/cloud_storage.py +9 -9
  54. pycharter/etl_generator/extractors/database.py +2 -2
  55. pycharter/etl_generator/extractors/factory.py +15 -33
  56. pycharter/etl_generator/extractors/file.py +2 -2
  57. pycharter/etl_generator/extractors/http.py +2 -2
  58. pycharter/etl_generator/extractors/mongodb.py +393 -0
  59. pycharter/etl_generator/extractors/streaming.py +2 -2
  60. pycharter/etl_generator/loaders/__init__.py +15 -9
  61. pycharter/etl_generator/loaders/{cloud_storage_loader.py → cloud_storage.py} +95 -2
  62. pycharter/etl_generator/loaders/factory.py +16 -29
  63. pycharter/etl_generator/loaders/file.py +135 -1
  64. pycharter/etl_generator/loaders/mongodb.py +416 -0
  65. pycharter/etl_generator/pipeline.py +283 -164
  66. pycharter/etl_generator/result.py +16 -0
  67. pycharter/etl_generator/schemas/__init__.py +71 -42
  68. pycharter/etl_generator/transformers/config.py +3 -2
  69. pycharter/etl_generator/transformers/simple_operations.py +57 -4
  70. pycharter/etl_generator/validation.py +551 -0
  71. pycharter/metadata_store/README.md +229 -0
  72. pycharter/quality/README.md +235 -0
  73. pycharter/runtime_validator/__init__.py +7 -0
  74. pycharter/runtime_validator/utils.py +33 -0
  75. pycharter/runtime_validator/validator.py +13 -10
  76. pycharter/ui/.eslintrc.json +4 -0
  77. pycharter/ui/README.md +186 -0
  78. {ui → pycharter/ui}/__init__.py +3 -3
  79. pycharter/ui/components.json +17 -0
  80. pycharter/ui/package-lock.json +6617 -0
  81. pycharter/ui/package.json +37 -0
  82. {ui → pycharter/ui}/server.py +7 -8
  83. pycharter/ui/static/404/index.html +1 -0
  84. pycharter/ui/static/404.html +1 -0
  85. pycharter/ui/static/__next.__PAGE__.txt +10 -0
  86. pycharter/ui/static/__next._full.txt +30 -0
  87. pycharter/ui/static/__next._head.txt +7 -0
  88. pycharter/ui/static/__next._index.txt +9 -0
  89. pycharter/ui/static/__next._tree.txt +2 -0
  90. pycharter/ui/static/_next/static/YCnlK66gA7FV5vvcixspB/_clientMiddlewareManifest.json +1 -0
  91. pycharter/ui/static/_next/static/chunks/0fc1f70b787b8845.js +1 -0
  92. pycharter/ui/static/_next/static/chunks/17bb8075d7b75663.css +1 -0
  93. pycharter/ui/static/_next/static/chunks/381932864dcbfdb8.js +1 -0
  94. pycharter/ui/static/_next/static/chunks/4c951b8e4507e2b3.js +1 -0
  95. pycharter/ui/static/_next/static/chunks/68b87a6f65abd3ed.js +1 -0
  96. pycharter/ui/static/_next/static/chunks/78572617b8fae189.js +1 -0
  97. pycharter/ui/static/_next/static/chunks/8b7be2803e3fe184.js +1 -0
  98. pycharter/ui/static/_next/static/chunks/a8e529fd1e67f121.js +1 -0
  99. pycharter/ui/static/_next/static/chunks/c35d998f80be3ff5.js +1 -0
  100. pycharter/ui/static/_next/static/chunks/e453aa5d01c32c17.js +1 -0
  101. pycharter/ui/static/_next/static/chunks/f2d240eb057f898a.js +970 -0
  102. pycharter/ui/static/_next/static/chunks/f7722448f6040846.js +1 -0
  103. pycharter/ui/static/_not-found/__next._full.txt +17 -0
  104. pycharter/ui/static/_not-found/__next._head.txt +7 -0
  105. pycharter/ui/static/_not-found/__next._index.txt +9 -0
  106. pycharter/ui/static/_not-found/__next._not-found.__PAGE__.txt +5 -0
  107. pycharter/ui/static/_not-found/__next._not-found.txt +4 -0
  108. pycharter/ui/static/_not-found/__next._tree.txt +2 -0
  109. pycharter/ui/static/_not-found/index.html +1 -0
  110. pycharter/ui/static/_not-found/index.txt +17 -0
  111. pycharter/ui/static/contracts/__next._full.txt +21 -0
  112. pycharter/ui/static/contracts/__next._head.txt +7 -0
  113. pycharter/ui/static/contracts/__next._index.txt +9 -0
  114. pycharter/ui/static/contracts/__next._tree.txt +2 -0
  115. pycharter/ui/static/contracts/__next.contracts.__PAGE__.txt +9 -0
  116. pycharter/ui/static/contracts/__next.contracts.txt +4 -0
  117. pycharter/ui/static/contracts/index.html +1 -0
  118. pycharter/ui/static/contracts/index.txt +21 -0
  119. pycharter/ui/static/documentation/__next._full.txt +21 -0
  120. pycharter/ui/static/documentation/__next._head.txt +7 -0
  121. pycharter/ui/static/documentation/__next._index.txt +9 -0
  122. pycharter/ui/static/documentation/__next._tree.txt +2 -0
  123. pycharter/ui/static/documentation/__next.documentation.__PAGE__.txt +9 -0
  124. pycharter/ui/static/documentation/__next.documentation.txt +4 -0
  125. pycharter/ui/static/documentation/index.html +93 -0
  126. pycharter/ui/static/documentation/index.txt +21 -0
  127. pycharter/ui/static/etl/__next._full.txt +21 -0
  128. pycharter/ui/static/etl/__next._head.txt +7 -0
  129. pycharter/ui/static/etl/__next._index.txt +9 -0
  130. pycharter/ui/static/etl/__next._tree.txt +2 -0
  131. pycharter/ui/static/etl/__next.etl.__PAGE__.txt +9 -0
  132. pycharter/ui/static/etl/__next.etl.txt +4 -0
  133. pycharter/ui/static/etl/index.html +2 -0
  134. pycharter/ui/static/etl/index.txt +21 -0
  135. pycharter/ui/static/index.html +1 -0
  136. pycharter/ui/static/index.txt +30 -0
  137. pycharter/ui/static/metadata/__next._full.txt +21 -0
  138. pycharter/ui/static/metadata/__next._head.txt +7 -0
  139. pycharter/ui/static/metadata/__next._index.txt +9 -0
  140. pycharter/ui/static/metadata/__next._tree.txt +2 -0
  141. pycharter/ui/static/metadata/__next.metadata.__PAGE__.txt +9 -0
  142. pycharter/ui/static/metadata/__next.metadata.txt +4 -0
  143. pycharter/ui/static/metadata/index.html +1 -0
  144. pycharter/ui/static/metadata/index.txt +21 -0
  145. pycharter/ui/static/quality/__next._full.txt +21 -0
  146. pycharter/ui/static/quality/__next._head.txt +7 -0
  147. pycharter/ui/static/quality/__next._index.txt +9 -0
  148. pycharter/ui/static/quality/__next._tree.txt +2 -0
  149. pycharter/ui/static/quality/__next.quality.__PAGE__.txt +9 -0
  150. pycharter/ui/static/quality/__next.quality.txt +4 -0
  151. pycharter/ui/static/quality/index.html +2 -0
  152. pycharter/ui/static/quality/index.txt +21 -0
  153. pycharter/ui/static/rules/__next._full.txt +21 -0
  154. pycharter/ui/static/rules/__next._head.txt +7 -0
  155. pycharter/ui/static/rules/__next._index.txt +9 -0
  156. pycharter/ui/static/rules/__next._tree.txt +2 -0
  157. pycharter/ui/static/rules/__next.rules.__PAGE__.txt +9 -0
  158. pycharter/ui/static/rules/__next.rules.txt +4 -0
  159. pycharter/ui/static/rules/index.html +1 -0
  160. pycharter/ui/static/rules/index.txt +21 -0
  161. pycharter/ui/static/schemas/__next._full.txt +21 -0
  162. pycharter/ui/static/schemas/__next._head.txt +7 -0
  163. pycharter/ui/static/schemas/__next._index.txt +9 -0
  164. pycharter/ui/static/schemas/__next._tree.txt +2 -0
  165. pycharter/ui/static/schemas/__next.schemas.__PAGE__.txt +9 -0
  166. pycharter/ui/static/schemas/__next.schemas.txt +4 -0
  167. pycharter/ui/static/schemas/index.html +1 -0
  168. pycharter/ui/static/schemas/index.txt +21 -0
  169. pycharter/ui/static/settings/__next._full.txt +21 -0
  170. pycharter/ui/static/settings/__next._head.txt +7 -0
  171. pycharter/ui/static/settings/__next._index.txt +9 -0
  172. pycharter/ui/static/settings/__next._tree.txt +2 -0
  173. pycharter/ui/static/settings/__next.settings.__PAGE__.txt +9 -0
  174. pycharter/ui/static/settings/__next.settings.txt +4 -0
  175. pycharter/ui/static/settings/index.html +1 -0
  176. pycharter/ui/static/settings/index.txt +21 -0
  177. pycharter/ui/static/static/_next/static/2gKjNv6YvE6BcIdFthBLs/_clientMiddlewareManifest.json +1 -0
  178. pycharter/ui/static/static/static/_next/static/0rYA78L88aUyD2Uh38hhX/_clientMiddlewareManifest.json +1 -0
  179. pycharter/ui/static/static/static/_next/static/chunks/f7d1a90dd75d2572.js +1 -0
  180. pycharter/ui/static/static/static/static/.gitkeep +0 -0
  181. pycharter/ui/static/static/static/static/_next/static/chunks/222442f6da32302a.js +1 -0
  182. pycharter/ui/static/static/static/static/_next/static/chunks/247eb132b7f7b574.js +1 -0
  183. pycharter/ui/static/static/static/static/_next/static/chunks/297d55555b71baba.js +1 -0
  184. pycharter/ui/static/static/static/static/_next/static/chunks/414e77373f8ff61c.js +1 -0
  185. pycharter/ui/static/static/static/static/_next/static/chunks/652ad0aa26265c47.js +2 -0
  186. pycharter/ui/static/static/static/static/_next/static/chunks/9c23f44fff36548a.js +1 -0
  187. pycharter/ui/static/static/static/static/_next/static/chunks/a6dad97d9634a72d.js +1 -0
  188. pycharter/ui/static/static/static/static/_next/static/chunks/b32a0963684b9933.js +4 -0
  189. pycharter/ui/static/static/static/static/_next/static/chunks/db913959c675cea6.js +1 -0
  190. pycharter/ui/static/static/static/static/_next/static/chunks/f2e7afeab1178138.js +1 -0
  191. pycharter/ui/static/static/static/static/_next/static/chunks/ff1a16fafef87110.js +1 -0
  192. pycharter/ui/static/static/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +3 -0
  193. pycharter/ui/static/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_buildManifest.js +11 -0
  194. pycharter/ui/static/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_clientMiddlewareManifest.json +1 -0
  195. pycharter/ui/static/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_ssgManifest.js +1 -0
  196. pycharter/ui/static/validation/__next._full.txt +21 -0
  197. pycharter/ui/static/validation/__next._head.txt +7 -0
  198. pycharter/ui/static/validation/__next._index.txt +9 -0
  199. pycharter/ui/static/validation/__next._tree.txt +2 -0
  200. pycharter/ui/static/validation/__next.validation.__PAGE__.txt +9 -0
  201. pycharter/ui/static/validation/__next.validation.txt +4 -0
  202. pycharter/ui/static/validation/index.html +1 -0
  203. pycharter/ui/static/validation/index.txt +21 -0
  204. pycharter/ui/tsconfig.json +42 -0
  205. pycharter/worker/README.md +187 -0
  206. pycharter/worker/backends/__init__.py +8 -0
  207. pycharter/worker/backends/base.py +46 -0
  208. pycharter/worker/backends/spark.py +233 -0
  209. {worker → pycharter/worker}/cli.py +1 -1
  210. {worker → pycharter/worker}/processor.py +2 -2
  211. pycharter/worker/queue/__init__.py +8 -0
  212. pycharter/worker/queue/redis_queue.py +147 -0
  213. {pycharter-0.0.24.dist-info → pycharter-0.0.26.dist-info}/METADATA +57 -26
  214. pycharter-0.0.26.dist-info/RECORD +702 -0
  215. pycharter-0.0.26.dist-info/top_level.txt +1 -0
  216. pycharter/etl_generator/config_loader.py +0 -394
  217. pycharter/etl_generator/loaders/cloud.py +0 -87
  218. pycharter/etl_generator/loaders/file_loader.py +0 -130
  219. pycharter-0.0.24.dist-info/RECORD +0 -543
  220. pycharter-0.0.24.dist-info/top_level.txt +0 -4
  221. {api → pycharter/api}/dependencies/database.py +0 -0
  222. {api → pycharter/api}/dependencies/store.py +0 -0
  223. {api → pycharter/api}/models/contracts.py +0 -0
  224. {api → pycharter/api}/models/docs.py +0 -0
  225. {api → pycharter/api}/models/evolution.py +0 -0
  226. {api → pycharter/api}/models/metadata.py +0 -0
  227. {api → pycharter/api}/models/metadata_entities.py +0 -0
  228. {api → pycharter/api}/models/quality.py +0 -0
  229. {api → pycharter/api}/models/schemas.py +0 -0
  230. {api → pycharter/api}/models/tracking.py +0 -0
  231. {api → pycharter/api}/models/validation.py +0 -0
  232. {api → pycharter/api}/routes/__init__.py +0 -0
  233. {api → pycharter/api}/routes/v1/templates.py +0 -0
  234. {api → pycharter/api}/utils.py +0 -0
  235. {ui → pycharter/ui}/build.py +0 -0
  236. {ui → pycharter/ui}/dev.py +0 -0
  237. {ui → pycharter/ui}/static/.gitkeep +0 -0
  238. {ui/static/_next/static/2gKjNv6YvE6BcIdFthBLs → pycharter/ui/static/_next/static/YCnlK66gA7FV5vvcixspB}/_buildManifest.js +0 -0
  239. {ui/static/_next/static/2gKjNv6YvE6BcIdFthBLs → pycharter/ui/static/_next/static/YCnlK66gA7FV5vvcixspB}/_ssgManifest.js +0 -0
  240. {ui → pycharter/ui}/static/_next/static/chunks/222442f6da32302a.js +0 -0
  241. {ui → pycharter/ui}/static/_next/static/chunks/247eb132b7f7b574.js +0 -0
  242. {ui → pycharter/ui}/static/_next/static/chunks/297d55555b71baba.js +0 -0
  243. {ui → pycharter/ui}/static/_next/static/chunks/414e77373f8ff61c.js +0 -0
  244. {ui → pycharter/ui}/static/_next/static/chunks/652ad0aa26265c47.js +0 -0
  245. {ui → pycharter/ui}/static/_next/static/chunks/9c23f44fff36548a.js +0 -0
  246. {ui → pycharter/ui}/static/_next/static/chunks/a6dad97d9634a72d.js +0 -0
  247. {ui → pycharter/ui}/static/_next/static/chunks/b32a0963684b9933.js +0 -0
  248. {ui → pycharter/ui}/static/_next/static/chunks/db913959c675cea6.js +0 -0
  249. {ui → pycharter/ui}/static/_next/static/chunks/f2e7afeab1178138.js +0 -0
  250. {ui → pycharter/ui}/static/_next/static/chunks/f7d1a90dd75d2572.js +0 -0
  251. {ui → pycharter/ui}/static/_next/static/chunks/ff1a16fafef87110.js +0 -0
  252. {ui → pycharter/ui}/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +0 -0
  253. {ui → pycharter/ui}/static/static/.gitkeep +0 -0
  254. {ui → pycharter/ui/static}/static/404/index.html +0 -0
  255. {ui → pycharter/ui/static}/static/404.html +0 -0
  256. {ui → pycharter/ui/static}/static/__next.__PAGE__.txt +0 -0
  257. {ui → pycharter/ui/static}/static/__next._full.txt +0 -0
  258. {ui → pycharter/ui/static}/static/__next._head.txt +0 -0
  259. {ui → pycharter/ui/static}/static/__next._index.txt +0 -0
  260. {ui → pycharter/ui/static}/static/__next._tree.txt +0 -0
  261. {ui/static/static/_next/static/0rYA78L88aUyD2Uh38hhX → pycharter/ui/static/static/_next/static/2gKjNv6YvE6BcIdFthBLs}/_buildManifest.js +0 -0
  262. {ui/static/static/_next/static/0rYA78L88aUyD2Uh38hhX → pycharter/ui/static/static/_next/static/2gKjNv6YvE6BcIdFthBLs}/_ssgManifest.js +0 -0
  263. {ui → pycharter/ui/static}/static/_next/static/chunks/13d4a0fbd74c1ee4.js +0 -0
  264. {ui → pycharter/ui}/static/static/_next/static/chunks/222442f6da32302a.js +0 -0
  265. {ui → pycharter/ui}/static/static/_next/static/chunks/247eb132b7f7b574.js +0 -0
  266. {ui → pycharter/ui/static}/static/_next/static/chunks/26dfc590f7714c03.js +0 -0
  267. {ui → pycharter/ui}/static/static/_next/static/chunks/297d55555b71baba.js +0 -0
  268. {ui → pycharter/ui/static}/static/_next/static/chunks/2ab439ce003cd691.js +0 -0
  269. {ui → pycharter/ui/static}/static/_next/static/chunks/2edb43b48432ac04.js +0 -0
  270. {ui → pycharter/ui/static}/static/_next/static/chunks/34d289e6db2ef551.js +0 -0
  271. {ui → pycharter/ui}/static/static/_next/static/chunks/414e77373f8ff61c.js +0 -0
  272. {ui → pycharter/ui/static}/static/_next/static/chunks/49ca65abd26ae49e.js +0 -0
  273. {ui → pycharter/ui}/static/static/_next/static/chunks/652ad0aa26265c47.js +0 -0
  274. {ui → pycharter/ui/static}/static/_next/static/chunks/9667e7a3d359eb39.js +0 -0
  275. {ui → pycharter/ui/static}/static/_next/static/chunks/99508d9d5869cc27.js +0 -0
  276. {ui → pycharter/ui}/static/static/_next/static/chunks/9c23f44fff36548a.js +0 -0
  277. {ui → pycharter/ui}/static/static/_next/static/chunks/a6dad97d9634a72d.js +0 -0
  278. {ui → pycharter/ui/static}/static/_next/static/chunks/b313c35a6ba76574.js +0 -0
  279. {ui → pycharter/ui}/static/static/_next/static/chunks/b32a0963684b9933.js +0 -0
  280. {ui → pycharter/ui/static}/static/_next/static/chunks/c69f6cba366bd988.js +0 -0
  281. {ui → pycharter/ui/static}/static/_next/static/chunks/d2363397e1b2bcab.css +0 -0
  282. {ui → pycharter/ui}/static/static/_next/static/chunks/db913959c675cea6.js +0 -0
  283. {ui → pycharter/ui/static}/static/_next/static/chunks/f061a4be97bfc3b3.js +0 -0
  284. {ui → pycharter/ui}/static/static/_next/static/chunks/f2e7afeab1178138.js +0 -0
  285. {ui → pycharter/ui}/static/static/_next/static/chunks/f7d1a90dd75d2572.js +0 -0
  286. {ui → pycharter/ui}/static/static/_next/static/chunks/ff1a16fafef87110.js +0 -0
  287. {ui → pycharter/ui}/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +0 -0
  288. {ui → pycharter/ui/static}/static/_not-found/__next._full.txt +0 -0
  289. {ui → pycharter/ui/static}/static/_not-found/__next._head.txt +0 -0
  290. {ui → pycharter/ui/static}/static/_not-found/__next._index.txt +0 -0
  291. {ui → pycharter/ui/static}/static/_not-found/__next._not-found.__PAGE__.txt +0 -0
  292. {ui → pycharter/ui/static}/static/_not-found/__next._not-found.txt +0 -0
  293. {ui → pycharter/ui/static}/static/_not-found/__next._tree.txt +0 -0
  294. {ui → pycharter/ui/static}/static/_not-found/index.html +0 -0
  295. {ui → pycharter/ui/static}/static/_not-found/index.txt +0 -0
  296. {ui → pycharter/ui/static}/static/contracts/__next._full.txt +0 -0
  297. {ui → pycharter/ui/static}/static/contracts/__next._head.txt +0 -0
  298. {ui → pycharter/ui/static}/static/contracts/__next._index.txt +0 -0
  299. {ui → pycharter/ui/static}/static/contracts/__next._tree.txt +0 -0
  300. {ui → pycharter/ui/static}/static/contracts/__next.contracts.__PAGE__.txt +0 -0
  301. {ui → pycharter/ui/static}/static/contracts/__next.contracts.txt +0 -0
  302. {ui → pycharter/ui/static}/static/contracts/index.html +0 -0
  303. {ui → pycharter/ui/static}/static/contracts/index.txt +0 -0
  304. {ui → pycharter/ui/static}/static/documentation/__next._full.txt +0 -0
  305. {ui → pycharter/ui/static}/static/documentation/__next._head.txt +0 -0
  306. {ui → pycharter/ui/static}/static/documentation/__next._index.txt +0 -0
  307. {ui → pycharter/ui/static}/static/documentation/__next._tree.txt +0 -0
  308. {ui → pycharter/ui/static}/static/documentation/__next.documentation.__PAGE__.txt +0 -0
  309. {ui → pycharter/ui/static}/static/documentation/__next.documentation.txt +0 -0
  310. {ui → pycharter/ui/static}/static/documentation/index.html +0 -0
  311. {ui → pycharter/ui/static}/static/documentation/index.txt +0 -0
  312. {ui → pycharter/ui/static}/static/index.html +0 -0
  313. {ui → pycharter/ui/static}/static/index.txt +0 -0
  314. {ui → pycharter/ui/static}/static/metadata/__next._full.txt +0 -0
  315. {ui → pycharter/ui/static}/static/metadata/__next._head.txt +0 -0
  316. {ui → pycharter/ui/static}/static/metadata/__next._index.txt +0 -0
  317. {ui → pycharter/ui/static}/static/metadata/__next._tree.txt +0 -0
  318. {ui → pycharter/ui/static}/static/metadata/__next.metadata.__PAGE__.txt +0 -0
  319. {ui → pycharter/ui/static}/static/metadata/__next.metadata.txt +0 -0
  320. {ui → pycharter/ui/static}/static/metadata/index.html +0 -0
  321. {ui → pycharter/ui/static}/static/metadata/index.txt +0 -0
  322. {ui → pycharter/ui/static}/static/quality/__next._full.txt +0 -0
  323. {ui → pycharter/ui/static}/static/quality/__next._head.txt +0 -0
  324. {ui → pycharter/ui/static}/static/quality/__next._index.txt +0 -0
  325. {ui → pycharter/ui/static}/static/quality/__next._tree.txt +0 -0
  326. {ui → pycharter/ui/static}/static/quality/__next.quality.__PAGE__.txt +0 -0
  327. {ui → pycharter/ui/static}/static/quality/__next.quality.txt +0 -0
  328. {ui → pycharter/ui/static}/static/quality/index.html +0 -0
  329. {ui → pycharter/ui/static}/static/quality/index.txt +0 -0
  330. {ui → pycharter/ui/static}/static/rules/__next._full.txt +0 -0
  331. {ui → pycharter/ui/static}/static/rules/__next._head.txt +0 -0
  332. {ui → pycharter/ui/static}/static/rules/__next._index.txt +0 -0
  333. {ui → pycharter/ui/static}/static/rules/__next._tree.txt +0 -0
  334. {ui → pycharter/ui/static}/static/rules/__next.rules.__PAGE__.txt +0 -0
  335. {ui → pycharter/ui/static}/static/rules/__next.rules.txt +0 -0
  336. {ui → pycharter/ui/static}/static/rules/index.html +0 -0
  337. {ui → pycharter/ui/static}/static/rules/index.txt +0 -0
  338. {ui → pycharter/ui/static}/static/schemas/__next._full.txt +0 -0
  339. {ui → pycharter/ui/static}/static/schemas/__next._head.txt +0 -0
  340. {ui → pycharter/ui/static}/static/schemas/__next._index.txt +0 -0
  341. {ui → pycharter/ui/static}/static/schemas/__next._tree.txt +0 -0
  342. {ui → pycharter/ui/static}/static/schemas/__next.schemas.__PAGE__.txt +0 -0
  343. {ui → pycharter/ui/static}/static/schemas/__next.schemas.txt +0 -0
  344. {ui → pycharter/ui/static}/static/schemas/index.html +0 -0
  345. {ui → pycharter/ui/static}/static/schemas/index.txt +0 -0
  346. {ui → pycharter/ui/static}/static/settings/__next._full.txt +0 -0
  347. {ui → pycharter/ui/static}/static/settings/__next._head.txt +0 -0
  348. {ui → pycharter/ui/static}/static/settings/__next._index.txt +0 -0
  349. {ui → pycharter/ui/static}/static/settings/__next._tree.txt +0 -0
  350. {ui → pycharter/ui/static}/static/settings/__next.settings.__PAGE__.txt +0 -0
  351. {ui → pycharter/ui/static}/static/settings/__next.settings.txt +0 -0
  352. {ui → pycharter/ui/static}/static/settings/index.html +0 -0
  353. {ui → pycharter/ui/static}/static/settings/index.txt +0 -0
  354. {ui → pycharter/ui}/static/static/static/.gitkeep +0 -0
  355. {ui → pycharter/ui/static}/static/static/404/index.html +0 -0
  356. {ui → pycharter/ui/static}/static/static/404.html +0 -0
  357. {ui → pycharter/ui/static}/static/static/__next.__PAGE__.txt +0 -0
  358. {ui → pycharter/ui/static}/static/static/__next._full.txt +0 -0
  359. {ui → pycharter/ui/static}/static/static/__next._head.txt +0 -0
  360. {ui → pycharter/ui/static}/static/static/__next._index.txt +0 -0
  361. {ui → pycharter/ui/static}/static/static/__next._tree.txt +0 -0
  362. {ui/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl → pycharter/ui/static/static/static/_next/static/0rYA78L88aUyD2Uh38hhX}/_buildManifest.js +0 -0
  363. {ui/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl → pycharter/ui/static/static/static/_next/static/0rYA78L88aUyD2Uh38hhX}/_ssgManifest.js +0 -0
  364. {ui → pycharter/ui/static}/static/static/_next/static/chunks/13d4a0fbd74c1ee4.js +0 -0
  365. {ui → pycharter/ui}/static/static/static/_next/static/chunks/222442f6da32302a.js +0 -0
  366. {ui → pycharter/ui}/static/static/static/_next/static/chunks/247eb132b7f7b574.js +0 -0
  367. {ui → pycharter/ui}/static/static/static/_next/static/chunks/297d55555b71baba.js +0 -0
  368. {ui → pycharter/ui/static}/static/static/_next/static/chunks/2ab439ce003cd691.js +0 -0
  369. {ui → pycharter/ui/static}/static/static/_next/static/chunks/2edb43b48432ac04.js +0 -0
  370. {ui → pycharter/ui}/static/static/static/_next/static/chunks/414e77373f8ff61c.js +0 -0
  371. {ui → pycharter/ui/static}/static/static/_next/static/chunks/49ca65abd26ae49e.js +0 -0
  372. {ui → pycharter/ui/static}/static/static/_next/static/chunks/5e04d10c4a7b58a3.js +0 -0
  373. {ui → pycharter/ui}/static/static/static/_next/static/chunks/652ad0aa26265c47.js +0 -0
  374. {ui → pycharter/ui/static}/static/static/_next/static/chunks/75d88a058d8ffaa6.js +0 -0
  375. {ui → pycharter/ui/static}/static/static/_next/static/chunks/8c89634cf6bad76f.js +0 -0
  376. {ui → pycharter/ui/static}/static/static/_next/static/chunks/9667e7a3d359eb39.js +0 -0
  377. {ui → pycharter/ui}/static/static/static/_next/static/chunks/9c23f44fff36548a.js +0 -0
  378. {ui → pycharter/ui}/static/static/static/_next/static/chunks/a6dad97d9634a72d.js +0 -0
  379. {ui → pycharter/ui}/static/static/static/_next/static/chunks/b32a0963684b9933.js +0 -0
  380. {ui → pycharter/ui/static}/static/static/_next/static/chunks/c4fa4f4114b7c352.js +0 -0
  381. {ui → pycharter/ui/static}/static/static/_next/static/chunks/c69f6cba366bd988.js +0 -0
  382. {ui → pycharter/ui/static}/static/static/_next/static/chunks/d2363397e1b2bcab.css +0 -0
  383. {ui → pycharter/ui}/static/static/static/_next/static/chunks/db913959c675cea6.js +0 -0
  384. {ui → pycharter/ui/static}/static/static/_next/static/chunks/f061a4be97bfc3b3.js +0 -0
  385. {ui → pycharter/ui}/static/static/static/_next/static/chunks/f2e7afeab1178138.js +0 -0
  386. {ui → pycharter/ui}/static/static/static/_next/static/chunks/ff1a16fafef87110.js +0 -0
  387. {ui → pycharter/ui}/static/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +0 -0
  388. {ui → pycharter/ui/static}/static/static/_not-found/__next._full.txt +0 -0
  389. {ui → pycharter/ui/static}/static/static/_not-found/__next._head.txt +0 -0
  390. {ui → pycharter/ui/static}/static/static/_not-found/__next._index.txt +0 -0
  391. {ui → pycharter/ui/static}/static/static/_not-found/__next._not-found.__PAGE__.txt +0 -0
  392. {ui → pycharter/ui/static}/static/static/_not-found/__next._not-found.txt +0 -0
  393. {ui → pycharter/ui/static}/static/static/_not-found/__next._tree.txt +0 -0
  394. {ui → pycharter/ui/static}/static/static/_not-found/index.html +0 -0
  395. {ui → pycharter/ui/static}/static/static/_not-found/index.txt +0 -0
  396. {ui → pycharter/ui/static}/static/static/contracts/__next._full.txt +0 -0
  397. {ui → pycharter/ui/static}/static/static/contracts/__next._head.txt +0 -0
  398. {ui → pycharter/ui/static}/static/static/contracts/__next._index.txt +0 -0
  399. {ui → pycharter/ui/static}/static/static/contracts/__next._tree.txt +0 -0
  400. {ui → pycharter/ui/static}/static/static/contracts/__next.contracts.__PAGE__.txt +0 -0
  401. {ui → pycharter/ui/static}/static/static/contracts/__next.contracts.txt +0 -0
  402. {ui → pycharter/ui/static}/static/static/contracts/index.html +0 -0
  403. {ui → pycharter/ui/static}/static/static/contracts/index.txt +0 -0
  404. {ui → pycharter/ui/static}/static/static/documentation/__next._full.txt +0 -0
  405. {ui → pycharter/ui/static}/static/static/documentation/__next._head.txt +0 -0
  406. {ui → pycharter/ui/static}/static/static/documentation/__next._index.txt +0 -0
  407. {ui → pycharter/ui/static}/static/static/documentation/__next._tree.txt +0 -0
  408. {ui → pycharter/ui/static}/static/static/documentation/__next.documentation.__PAGE__.txt +0 -0
  409. {ui → pycharter/ui/static}/static/static/documentation/__next.documentation.txt +0 -0
  410. {ui → pycharter/ui/static}/static/static/documentation/index.html +0 -0
  411. {ui → pycharter/ui/static}/static/static/documentation/index.txt +0 -0
  412. {ui → pycharter/ui/static}/static/static/index.html +0 -0
  413. {ui → pycharter/ui/static}/static/static/index.txt +0 -0
  414. {ui → pycharter/ui/static}/static/static/metadata/__next._full.txt +0 -0
  415. {ui → pycharter/ui/static}/static/static/metadata/__next._head.txt +0 -0
  416. {ui → pycharter/ui/static}/static/static/metadata/__next._index.txt +0 -0
  417. {ui → pycharter/ui/static}/static/static/metadata/__next._tree.txt +0 -0
  418. {ui → pycharter/ui/static}/static/static/metadata/__next.metadata.__PAGE__.txt +0 -0
  419. {ui → pycharter/ui/static}/static/static/metadata/__next.metadata.txt +0 -0
  420. {ui → pycharter/ui/static}/static/static/metadata/index.html +0 -0
  421. {ui → pycharter/ui/static}/static/static/metadata/index.txt +0 -0
  422. {ui → pycharter/ui/static}/static/static/quality/__next._full.txt +0 -0
  423. {ui → pycharter/ui/static}/static/static/quality/__next._head.txt +0 -0
  424. {ui → pycharter/ui/static}/static/static/quality/__next._index.txt +0 -0
  425. {ui → pycharter/ui/static}/static/static/quality/__next._tree.txt +0 -0
  426. {ui → pycharter/ui/static}/static/static/quality/__next.quality.__PAGE__.txt +0 -0
  427. {ui → pycharter/ui/static}/static/static/quality/__next.quality.txt +0 -0
  428. {ui → pycharter/ui/static}/static/static/quality/index.html +0 -0
  429. {ui → pycharter/ui/static}/static/static/quality/index.txt +0 -0
  430. {ui → pycharter/ui/static}/static/static/rules/__next._full.txt +0 -0
  431. {ui → pycharter/ui/static}/static/static/rules/__next._head.txt +0 -0
  432. {ui → pycharter/ui/static}/static/static/rules/__next._index.txt +0 -0
  433. {ui → pycharter/ui/static}/static/static/rules/__next._tree.txt +0 -0
  434. {ui → pycharter/ui/static}/static/static/rules/__next.rules.__PAGE__.txt +0 -0
  435. {ui → pycharter/ui/static}/static/static/rules/__next.rules.txt +0 -0
  436. {ui → pycharter/ui/static}/static/static/rules/index.html +0 -0
  437. {ui → pycharter/ui/static}/static/static/rules/index.txt +0 -0
  438. {ui → pycharter/ui/static}/static/static/schemas/__next._full.txt +0 -0
  439. {ui → pycharter/ui/static}/static/static/schemas/__next._head.txt +0 -0
  440. {ui → pycharter/ui/static}/static/static/schemas/__next._index.txt +0 -0
  441. {ui → pycharter/ui/static}/static/static/schemas/__next._tree.txt +0 -0
  442. {ui → pycharter/ui/static}/static/static/schemas/__next.schemas.__PAGE__.txt +0 -0
  443. {ui → pycharter/ui/static}/static/static/schemas/__next.schemas.txt +0 -0
  444. {ui → pycharter/ui/static}/static/static/schemas/index.html +0 -0
  445. {ui → pycharter/ui/static}/static/static/schemas/index.txt +0 -0
  446. {ui → pycharter/ui/static}/static/static/settings/__next._full.txt +0 -0
  447. {ui → pycharter/ui/static}/static/static/settings/__next._head.txt +0 -0
  448. {ui → pycharter/ui/static}/static/static/settings/__next._index.txt +0 -0
  449. {ui → pycharter/ui/static}/static/static/settings/__next._tree.txt +0 -0
  450. {ui → pycharter/ui/static}/static/static/settings/__next.settings.__PAGE__.txt +0 -0
  451. {ui → pycharter/ui/static}/static/static/settings/__next.settings.txt +0 -0
  452. {ui → pycharter/ui/static}/static/static/settings/index.html +0 -0
  453. {ui → pycharter/ui/static}/static/static/settings/index.txt +0 -0
  454. {ui → pycharter/ui/static}/static/static/static/404/index.html +0 -0
  455. {ui → pycharter/ui/static}/static/static/static/404.html +0 -0
  456. {ui → pycharter/ui/static}/static/static/static/__next.__PAGE__.txt +0 -0
  457. {ui → pycharter/ui/static}/static/static/static/__next._full.txt +0 -0
  458. {ui → pycharter/ui/static}/static/static/static/__next._head.txt +0 -0
  459. {ui → pycharter/ui/static}/static/static/static/__next._index.txt +0 -0
  460. {ui → pycharter/ui/static}/static/static/static/__next._tree.txt +0 -0
  461. {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/2ab439ce003cd691.js +0 -0
  462. {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/49ca65abd26ae49e.js +0 -0
  463. {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/4e310fe5005770a3.css +0 -0
  464. {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/5e04d10c4a7b58a3.js +0 -0
  465. {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/5fc14c00a2779dc5.js +0 -0
  466. {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/75d88a058d8ffaa6.js +0 -0
  467. {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/8c89634cf6bad76f.js +0 -0
  468. {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/9667e7a3d359eb39.js +0 -0
  469. {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/b584574fdc8ab13e.js +0 -0
  470. {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/c69f6cba366bd988.js +0 -0
  471. {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/d5989c94d3614b3a.js +0 -0
  472. {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/f061a4be97bfc3b3.js +0 -0
  473. {ui → pycharter/ui/static}/static/static/static/_not-found/__next._full.txt +0 -0
  474. {ui → pycharter/ui/static}/static/static/static/_not-found/__next._head.txt +0 -0
  475. {ui → pycharter/ui/static}/static/static/static/_not-found/__next._index.txt +0 -0
  476. {ui → pycharter/ui/static}/static/static/static/_not-found/__next._not-found.__PAGE__.txt +0 -0
  477. {ui → pycharter/ui/static}/static/static/static/_not-found/__next._not-found.txt +0 -0
  478. {ui → pycharter/ui/static}/static/static/static/_not-found/__next._tree.txt +0 -0
  479. {ui → pycharter/ui/static}/static/static/static/_not-found/index.html +0 -0
  480. {ui → pycharter/ui/static}/static/static/static/_not-found/index.txt +0 -0
  481. {ui → pycharter/ui/static}/static/static/static/contracts/__next._full.txt +0 -0
  482. {ui → pycharter/ui/static}/static/static/static/contracts/__next._head.txt +0 -0
  483. {ui → pycharter/ui/static}/static/static/static/contracts/__next._index.txt +0 -0
  484. {ui → pycharter/ui/static}/static/static/static/contracts/__next._tree.txt +0 -0
  485. {ui → pycharter/ui/static}/static/static/static/contracts/__next.contracts.__PAGE__.txt +0 -0
  486. {ui → pycharter/ui/static}/static/static/static/contracts/__next.contracts.txt +0 -0
  487. {ui → pycharter/ui/static}/static/static/static/contracts/index.html +0 -0
  488. {ui → pycharter/ui/static}/static/static/static/contracts/index.txt +0 -0
  489. {ui → pycharter/ui/static}/static/static/static/documentation/__next._full.txt +0 -0
  490. {ui → pycharter/ui/static}/static/static/static/documentation/__next._head.txt +0 -0
  491. {ui → pycharter/ui/static}/static/static/static/documentation/__next._index.txt +0 -0
  492. {ui → pycharter/ui/static}/static/static/static/documentation/__next._tree.txt +0 -0
  493. {ui → pycharter/ui/static}/static/static/static/documentation/__next.documentation.__PAGE__.txt +0 -0
  494. {ui → pycharter/ui/static}/static/static/static/documentation/__next.documentation.txt +0 -0
  495. {ui → pycharter/ui/static}/static/static/static/documentation/index.html +0 -0
  496. {ui → pycharter/ui/static}/static/static/static/documentation/index.txt +0 -0
  497. {ui → pycharter/ui/static}/static/static/static/index.html +0 -0
  498. {ui → pycharter/ui/static}/static/static/static/index.txt +0 -0
  499. {ui → pycharter/ui/static}/static/static/static/metadata/__next._full.txt +0 -0
  500. {ui → pycharter/ui/static}/static/static/static/metadata/__next._head.txt +0 -0
  501. {ui → pycharter/ui/static}/static/static/static/metadata/__next._index.txt +0 -0
  502. {ui → pycharter/ui/static}/static/static/static/metadata/__next._tree.txt +0 -0
  503. {ui → pycharter/ui/static}/static/static/static/metadata/__next.metadata.__PAGE__.txt +0 -0
  504. {ui → pycharter/ui/static}/static/static/static/metadata/__next.metadata.txt +0 -0
  505. {ui → pycharter/ui/static}/static/static/static/metadata/index.html +0 -0
  506. {ui → pycharter/ui/static}/static/static/static/metadata/index.txt +0 -0
  507. {ui → pycharter/ui/static}/static/static/static/quality/__next._full.txt +0 -0
  508. {ui → pycharter/ui/static}/static/static/static/quality/__next._head.txt +0 -0
  509. {ui → pycharter/ui/static}/static/static/static/quality/__next._index.txt +0 -0
  510. {ui → pycharter/ui/static}/static/static/static/quality/__next._tree.txt +0 -0
  511. {ui → pycharter/ui/static}/static/static/static/quality/__next.quality.__PAGE__.txt +0 -0
  512. {ui → pycharter/ui/static}/static/static/static/quality/__next.quality.txt +0 -0
  513. {ui → pycharter/ui/static}/static/static/static/quality/index.html +0 -0
  514. {ui → pycharter/ui/static}/static/static/static/quality/index.txt +0 -0
  515. {ui → pycharter/ui/static}/static/static/static/rules/__next._full.txt +0 -0
  516. {ui → pycharter/ui/static}/static/static/static/rules/__next._head.txt +0 -0
  517. {ui → pycharter/ui/static}/static/static/static/rules/__next._index.txt +0 -0
  518. {ui → pycharter/ui/static}/static/static/static/rules/__next._tree.txt +0 -0
  519. {ui → pycharter/ui/static}/static/static/static/rules/__next.rules.__PAGE__.txt +0 -0
  520. {ui → pycharter/ui/static}/static/static/static/rules/__next.rules.txt +0 -0
  521. {ui → pycharter/ui/static}/static/static/static/rules/index.html +0 -0
  522. {ui → pycharter/ui/static}/static/static/static/rules/index.txt +0 -0
  523. {ui → pycharter/ui/static}/static/static/static/schemas/__next._full.txt +0 -0
  524. {ui → pycharter/ui/static}/static/static/static/schemas/__next._head.txt +0 -0
  525. {ui → pycharter/ui/static}/static/static/static/schemas/__next._index.txt +0 -0
  526. {ui → pycharter/ui/static}/static/static/static/schemas/__next._tree.txt +0 -0
  527. {ui → pycharter/ui/static}/static/static/static/schemas/__next.schemas.__PAGE__.txt +0 -0
  528. {ui → pycharter/ui/static}/static/static/static/schemas/__next.schemas.txt +0 -0
  529. {ui → pycharter/ui/static}/static/static/static/schemas/index.html +0 -0
  530. {ui → pycharter/ui/static}/static/static/static/schemas/index.txt +0 -0
  531. {ui → pycharter/ui/static}/static/static/static/settings/__next._full.txt +0 -0
  532. {ui → pycharter/ui/static}/static/static/static/settings/__next._head.txt +0 -0
  533. {ui → pycharter/ui/static}/static/static/static/settings/__next._index.txt +0 -0
  534. {ui → pycharter/ui/static}/static/static/static/settings/__next._tree.txt +0 -0
  535. {ui → pycharter/ui/static}/static/static/static/settings/__next.settings.__PAGE__.txt +0 -0
  536. {ui → pycharter/ui/static}/static/static/static/settings/__next.settings.txt +0 -0
  537. {ui → pycharter/ui/static}/static/static/static/settings/index.html +0 -0
  538. {ui → pycharter/ui/static}/static/static/static/settings/index.txt +0 -0
  539. {ui → pycharter/ui/static}/static/static/static/validation/__next._full.txt +0 -0
  540. {ui → pycharter/ui/static}/static/static/static/validation/__next._head.txt +0 -0
  541. {ui → pycharter/ui/static}/static/static/static/validation/__next._index.txt +0 -0
  542. {ui → pycharter/ui/static}/static/static/static/validation/__next._tree.txt +0 -0
  543. {ui → pycharter/ui/static}/static/static/static/validation/__next.validation.__PAGE__.txt +0 -0
  544. {ui → pycharter/ui/static}/static/static/static/validation/__next.validation.txt +0 -0
  545. {ui → pycharter/ui/static}/static/static/static/validation/index.html +0 -0
  546. {ui → pycharter/ui/static}/static/static/static/validation/index.txt +0 -0
  547. {ui → pycharter/ui/static}/static/static/validation/__next._full.txt +0 -0
  548. {ui → pycharter/ui/static}/static/static/validation/__next._head.txt +0 -0
  549. {ui → pycharter/ui/static}/static/static/validation/__next._index.txt +0 -0
  550. {ui → pycharter/ui/static}/static/static/validation/__next._tree.txt +0 -0
  551. {ui → pycharter/ui/static}/static/static/validation/__next.validation.__PAGE__.txt +0 -0
  552. {ui → pycharter/ui/static}/static/static/validation/__next.validation.txt +0 -0
  553. {ui → pycharter/ui/static}/static/static/validation/index.html +0 -0
  554. {ui → pycharter/ui/static}/static/static/validation/index.txt +0 -0
  555. {ui → pycharter/ui/static}/static/validation/__next._full.txt +0 -0
  556. {ui → pycharter/ui/static}/static/validation/__next._head.txt +0 -0
  557. {ui → pycharter/ui/static}/static/validation/__next._index.txt +0 -0
  558. {ui → pycharter/ui/static}/static/validation/__next._tree.txt +0 -0
  559. {ui → pycharter/ui/static}/static/validation/__next.validation.__PAGE__.txt +0 -0
  560. {ui → pycharter/ui/static}/static/validation/__next.validation.txt +0 -0
  561. {ui → pycharter/ui/static}/static/validation/index.html +0 -0
  562. {ui → pycharter/ui/static}/static/validation/index.txt +0 -0
  563. {worker → pycharter/worker}/__init__.py +0 -0
  564. {worker → pycharter/worker}/models.py +0 -0
  565. {pycharter-0.0.24.dist-info → pycharter-0.0.26.dist-info}/WHEEL +0 -0
  566. {pycharter-0.0.24.dist-info → pycharter-0.0.26.dist-info}/entry_points.txt +0 -0
  567. {pycharter-0.0.24.dist-info → pycharter-0.0.26.dist-info}/licenses/LICENSE +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/README.md ADDED
@@ -0,0 +1,179 @@
1
+ # Database Management with Alembic
2
+
3
+ PyCharter uses **Alembic** for database schema migrations, following the same pattern as Apache Airflow.
4
+
5
+ ## Overview
6
+
7
+ - **SQLAlchemy Models**: Database schema defined as Python models in `pycharter/db/models/`
8
+ - **Alembic Migrations**: Version-controlled schema changes in `pycharter/db/migrations/`
9
+ - **CLI Commands**: `pycharter db init`, `pycharter db upgrade`, etc.
10
+
11
+ ## Quick Start
12
+
13
+ ### Initialize Database
14
+
15
+ ```bash
16
+ # Initialize a fresh database
17
+ pycharter db init postgresql://user:pass@localhost/pycharter
18
+ ```
19
+
20
+ This will:
21
+ 1. Create all tables defined in SQLAlchemy models
22
+ 2. Create the initial Alembic migration
23
+ 3. Stamp the database with the current revision
24
+
25
+ ### Upgrade Database
26
+
27
+ ```bash
28
+ # Upgrade to latest version
29
+ pycharter db upgrade postgresql://user:pass@localhost/pycharter
30
+
31
+ # Or use environment variable
32
+ export PYCHARTER_DATABASE_URL=postgresql://user:pass@localhost/pycharter
33
+ pycharter db upgrade
34
+ ```
35
+
36
+ ### Check Current Version
37
+
38
+ ```bash
39
+ pycharter db current postgresql://user:pass@localhost/pycharter
40
+ ```
41
+
42
+ ### View Migration History
43
+
44
+ ```bash
45
+ pycharter db history
46
+ ```
47
+
48
+ ## Commands
49
+
50
+ | Command | Description | Example |
51
+ |---------|-------------|---------|
52
+ | `pycharter db init` | Initialize database from scratch | `pycharter db init postgresql://...` |
53
+ | `pycharter db upgrade` | Upgrade to latest revision | `pycharter db upgrade postgresql://...` |
54
+ | `pycharter db downgrade` | Downgrade to previous revision | `pycharter db downgrade postgresql://... --revision -1` |
55
+ | `pycharter db current` | Show current revision | `pycharter db current postgresql://...` |
56
+ | `pycharter db history` | Show migration history | `pycharter db history` |
57
+
58
+ ## Creating New Migrations
59
+
60
+ When you modify SQLAlchemy models, create a new migration:
61
+
62
+ ```bash
63
+ export PYCHARTER_DATABASE_URL=postgresql://user:pass@localhost/pycharter
64
+ alembic revision --autogenerate -m "Description of changes"
65
+ ```
66
+
67
+ Then review and edit the generated migration file in `pycharter/db/migrations/versions/`.
68
+
69
+ ## Database Models
70
+
71
+ All models are defined in `pycharter/db/models/`:
72
+
73
+ - `SchemaModel` - JSON Schema definitions
74
+ - `CoercionRuleModel` - Coercion rules
75
+ - `ValidationRuleModel` - Validation rules
76
+ - `MetadataRecordModel` - Comprehensive metadata storage (includes governance rules and ownership fields)
77
+ - `OwnerModel` - Direct ownership information (owner/team)
78
+ - `DataContractModel` - Central table linking all components
79
+ - `SystemModel` - System information
80
+ - `DomainModel` - Domain information
81
+
82
+ ## Comparison with Airflow
83
+
84
+ | Feature | Airflow | PyCharter |
85
+ |---------|---------|-----------|
86
+ | **CLI Command** | `airflow db init` | `pycharter db init` |
87
+ | **Upgrade** | `airflow db upgrade` | `pycharter db upgrade` |
88
+ | **Migration Tool** | Alembic | Alembic |
89
+ | **Models** | SQLAlchemy | SQLAlchemy |
90
+ | **Auto-generate** | Yes | Yes |
91
+
92
+ ## Best Practices
93
+
94
+ 1. **Always create migrations** when changing models
95
+ 2. **Test migrations** on a development database first
96
+ 3. **Review autogenerated migrations** before applying
97
+ 4. **Use version control** for all migration files
98
+ 5. **Backup database** before running migrations in production
99
+
100
+ ## Configuration
101
+
102
+ PyCharter follows Airflow's configuration pattern. Database connection can be configured via:
103
+
104
+ ### 1. Environment Variables (Recommended)
105
+
106
+ **Airflow-style:**
107
+ ```bash
108
+ export PYCHARTER__DATABASE__SQL_ALCHEMY_CONN=postgresql://user:password@localhost:5432/pycharter
109
+ ```
110
+
111
+ **Simpler alternative:**
112
+ ```bash
113
+ export PYCHARTER_DATABASE_URL=postgresql://user:password@localhost:5432/pycharter
114
+ ```
115
+
116
+ ### 2. Config File
117
+
118
+ Create `pycharter.cfg` in your project root or `~/.pycharter/`:
119
+
120
+ ```ini
121
+ [database]
122
+ sql_alchemy_conn = postgresql://user:password@localhost:5432/pycharter
123
+ ```
124
+
125
+ ### 3. Alembic Config
126
+
127
+ Set in `alembic.ini`:
128
+
129
+ ```ini
130
+ [alembic]
131
+ sqlalchemy.url = postgresql://user:password@localhost:5432/pycharter
132
+ ```
133
+
134
+ ### Priority Order
135
+
136
+ 1. Command-line argument (if provided)
137
+ 2. `PYCHARTER__DATABASE__SQL_ALCHEMY_CONN` environment variable
138
+ 3. `PYCHARTER_DATABASE_URL` environment variable
139
+ 4. `pycharter.cfg` config file
140
+ 5. `alembic.ini` config file
141
+
142
+ ### Usage
143
+
144
+ Once configured, you can run commands without passing the database URL:
145
+
146
+ ```bash
147
+ # Database URL from configuration
148
+ pycharter db init
149
+ pycharter db upgrade
150
+ pycharter db current
151
+
152
+ # Or override with command-line argument
153
+ pycharter db init postgresql://other:db@localhost:5432/other_db
154
+ ```
155
+
156
+ ## Troubleshooting
157
+
158
+ ### Tables Already Exist
159
+
160
+ If tables already exist (from old migration system), stamp the database:
161
+
162
+ ```bash
163
+ alembic stamp head
164
+ ```
165
+
166
+ ### Migration Conflicts
167
+
168
+ If you have conflicts, you can:
169
+ 1. Review the migration file
170
+ 2. Manually edit if needed
171
+ 3. Test on a development database first
172
+
173
+ ### Connection Issues
174
+
175
+ Make sure:
176
+ - Database is running
177
+ - Connection string is correct
178
+ - User has proper permissions
179
+
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
@@ -0,0 +1,96 @@
1
+ # Data Contract Validation Schemas
2
+
3
+ This directory contains Pydantic models for validating data contract structure.
4
+
5
+ ## Purpose
6
+
7
+ The Pydantic models in this directory ensure that all data contracts parsed by `parse_contract()` and `parse_contract_file()` strictly adhere to the database table design.
8
+
9
+ ## Files
10
+
11
+ - `data_contract.py` - Main Pydantic models for data contract validation
12
+ - `DataContractSchema` - Main model for validating complete contracts
13
+ - `SchemaComponent` - Validates the schema component (required)
14
+ - `MetadataComponent` - Validates metadata component (optional)
15
+ - `OwnershipComponent` - Validates ownership component (optional)
16
+ - `GovernanceRulesComponent` - Validates governance rules (optional)
17
+ - `CoercionRulesComponent` - Validates coercion rules (optional)
18
+ - `ValidationRulesComponent` - Validates validation rules (optional)
19
+ - `VersionsComponent` - Validates version tracking (optional)
20
+
21
+ ## Database Alignment
22
+
23
+ All models are designed to match the database table structure exactly:
24
+
25
+ - **Field constraints** (max_length) match database column sizes
26
+ - **Required fields** match database NOT NULL constraints
27
+ - **Optional fields** match database nullable columns
28
+ - **Enum values** match database CHECK constraints
29
+ - **Field types** match database column types
30
+
31
+ ## Usage
32
+
33
+ The models are automatically used by the contract parser:
34
+
35
+ ```python
36
+ from pycharter import parse_contract, parse_contract_file
37
+
38
+ # Validation is enabled by default
39
+ contract_metadata = parse_contract_file("contract.yaml") # Validates automatically
40
+
41
+ # You can disable validation if needed (not recommended)
42
+ contract_metadata = parse_contract_file("contract.yaml", validate=False)
43
+ ```
44
+
45
+ ## Validation Behavior
46
+
47
+ - **Valid contracts**: Pass validation and parse successfully
48
+ - **Invalid contracts**: Raise `ValueError` with detailed Pydantic error messages
49
+ - **Missing required fields**: Validation fails with clear error messages
50
+ - **Extra fields**: Rejected (except in flexible components like metadata)
51
+
52
+ ## Model Design Principles
53
+
54
+ 1. **Strict Top-Level**: `DataContractSchema` uses `extra = "forbid"` to prevent unexpected fields
55
+ 2. **Flexible Components**: Metadata, governance_rules allow `extra = "allow"` for flexible JSON storage
56
+ 3. **Database Constraints**: All field constraints match database column definitions
57
+ 4. **Type Safety**: Full Python type hints for IDE support and static analysis
58
+
59
+ ## Updating Models
60
+
61
+ When the database schema changes:
62
+
63
+ 1. Update the corresponding Pydantic model in `data_contract.py`
64
+ 2. Update field constraints to match new database columns
65
+ 3. Add/remove fields as needed
66
+ 4. Test with existing contracts to ensure backward compatibility
67
+ 5. Update this README if model structure changes significantly
68
+
69
+ ## Testing
70
+
71
+ To test the validation models:
72
+
73
+ ```python
74
+ from pycharter.db.schemas.data_contract import DataContractSchema
75
+
76
+ # Valid contract
77
+ valid_contract = {
78
+ "schema": {"type": "object", "properties": {"name": {"type": "string"}}}
79
+ }
80
+ result = DataContractSchema.model_validate(valid_contract) # ✓ Passes
81
+
82
+ # Invalid contract (missing required schema)
83
+ invalid_contract = {
84
+ "metadata": {"version": "1.0.0"}
85
+ }
86
+ result = DataContractSchema.model_validate(invalid_contract) # ✗ Raises ValidationError
87
+ ```
88
+
89
+
90
+
91
+
92
+
93
+
94
+
95
+
96
+