pycharter 0.0.22__py3-none-any.whl → 0.0.24__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (404) hide show
  1. api/main.py +27 -1
  2. api/models/docs.py +68 -0
  3. api/models/evolution.py +117 -0
  4. api/models/tracking.py +111 -0
  5. api/models/validation.py +46 -6
  6. api/routes/v1/__init__.py +14 -1
  7. api/routes/v1/docs.py +187 -0
  8. api/routes/v1/evolution.py +337 -0
  9. api/routes/v1/templates.py +211 -27
  10. api/routes/v1/tracking.py +301 -0
  11. api/routes/v1/validation.py +68 -31
  12. pycharter/__init__.py +268 -58
  13. pycharter/data/templates/contract/template_coercion_rules.yaml +57 -0
  14. pycharter/data/templates/contract/template_contract.yaml +122 -0
  15. pycharter/data/templates/contract/template_metadata.yaml +68 -0
  16. pycharter/data/templates/contract/template_schema.yaml +100 -0
  17. pycharter/data/templates/contract/template_validation_rules.yaml +75 -0
  18. pycharter/data/templates/etl/README.md +224 -0
  19. pycharter/data/templates/etl/extract_cloud_azure.yaml +24 -0
  20. pycharter/data/templates/etl/extract_cloud_gcs.yaml +25 -0
  21. pycharter/data/templates/etl/extract_cloud_s3.yaml +30 -0
  22. pycharter/data/templates/etl/extract_database.yaml +34 -0
  23. pycharter/data/templates/etl/extract_database_ssh.yaml +40 -0
  24. pycharter/data/templates/etl/extract_file_csv.yaml +21 -0
  25. pycharter/data/templates/etl/extract_file_glob.yaml +25 -0
  26. pycharter/data/templates/etl/extract_file_json.yaml +24 -0
  27. pycharter/data/templates/etl/extract_file_parquet.yaml +20 -0
  28. pycharter/data/templates/etl/extract_http_paginated.yaml +79 -0
  29. pycharter/data/templates/etl/extract_http_path_params.yaml +38 -0
  30. pycharter/data/templates/etl/extract_http_simple.yaml +62 -0
  31. pycharter/data/templates/etl/load_cloud_azure.yaml +24 -0
  32. pycharter/data/templates/etl/load_cloud_gcs.yaml +22 -0
  33. pycharter/data/templates/etl/load_cloud_s3.yaml +27 -0
  34. pycharter/data/templates/etl/load_file.yaml +34 -0
  35. pycharter/data/templates/etl/load_insert.yaml +18 -0
  36. pycharter/data/templates/etl/load_postgresql.yaml +39 -0
  37. pycharter/data/templates/etl/load_sqlite.yaml +21 -0
  38. pycharter/data/templates/etl/load_truncate_and_load.yaml +20 -0
  39. pycharter/data/templates/etl/load_upsert.yaml +25 -0
  40. pycharter/data/templates/etl/load_with_dlq.yaml +34 -0
  41. pycharter/data/templates/etl/load_with_ssh_tunnel.yaml +35 -0
  42. pycharter/data/templates/etl/pipeline_http_to_db.yaml +75 -0
  43. pycharter/data/templates/etl/transform_combined.yaml +48 -0
  44. pycharter/data/templates/etl/transform_custom_function.yaml +58 -0
  45. pycharter/data/templates/etl/transform_jsonata.yaml +51 -0
  46. pycharter/data/templates/etl/transform_simple.yaml +59 -0
  47. pycharter/db/schemas/.ipynb_checkpoints/data_contract-checkpoint.py +160 -0
  48. pycharter/docs_generator/__init__.py +43 -0
  49. pycharter/docs_generator/generator.py +465 -0
  50. pycharter/docs_generator/renderers.py +247 -0
  51. pycharter/etl_generator/__init__.py +168 -80
  52. pycharter/etl_generator/builder.py +121 -0
  53. pycharter/etl_generator/config_loader.py +394 -0
  54. pycharter/etl_generator/config_validator.py +418 -0
  55. pycharter/etl_generator/context.py +132 -0
  56. pycharter/etl_generator/expression.py +499 -0
  57. pycharter/etl_generator/extractors/__init__.py +30 -0
  58. pycharter/etl_generator/extractors/base.py +70 -0
  59. pycharter/etl_generator/extractors/cloud_storage.py +530 -0
  60. pycharter/etl_generator/extractors/database.py +221 -0
  61. pycharter/etl_generator/extractors/factory.py +185 -0
  62. pycharter/etl_generator/extractors/file.py +475 -0
  63. pycharter/etl_generator/extractors/http.py +895 -0
  64. pycharter/etl_generator/extractors/streaming.py +57 -0
  65. pycharter/etl_generator/loaders/__init__.py +41 -0
  66. pycharter/etl_generator/loaders/base.py +35 -0
  67. pycharter/etl_generator/loaders/cloud.py +87 -0
  68. pycharter/etl_generator/loaders/cloud_storage_loader.py +275 -0
  69. pycharter/etl_generator/loaders/database.py +274 -0
  70. pycharter/etl_generator/loaders/factory.py +180 -0
  71. pycharter/etl_generator/loaders/file.py +72 -0
  72. pycharter/etl_generator/loaders/file_loader.py +130 -0
  73. pycharter/etl_generator/pipeline.py +743 -0
  74. pycharter/etl_generator/protocols.py +54 -0
  75. pycharter/etl_generator/result.py +63 -0
  76. pycharter/etl_generator/schemas/__init__.py +49 -0
  77. pycharter/etl_generator/transformers/__init__.py +49 -0
  78. pycharter/etl_generator/transformers/base.py +63 -0
  79. pycharter/etl_generator/transformers/config.py +45 -0
  80. pycharter/etl_generator/transformers/custom_function.py +101 -0
  81. pycharter/etl_generator/transformers/jsonata_transformer.py +56 -0
  82. pycharter/etl_generator/transformers/operations.py +218 -0
  83. pycharter/etl_generator/transformers/pipeline.py +54 -0
  84. pycharter/etl_generator/transformers/simple_operations.py +131 -0
  85. pycharter/quality/__init__.py +25 -0
  86. pycharter/quality/tracking/__init__.py +64 -0
  87. pycharter/quality/tracking/collector.py +318 -0
  88. pycharter/quality/tracking/exporters.py +238 -0
  89. pycharter/quality/tracking/models.py +194 -0
  90. pycharter/quality/tracking/store.py +385 -0
  91. pycharter/runtime_validator/__init__.py +20 -7
  92. pycharter/runtime_validator/builder.py +328 -0
  93. pycharter/runtime_validator/validator.py +311 -7
  94. pycharter/runtime_validator/validator_core.py +61 -0
  95. pycharter/schema_evolution/__init__.py +61 -0
  96. pycharter/schema_evolution/compatibility.py +270 -0
  97. pycharter/schema_evolution/diff.py +496 -0
  98. pycharter/schema_evolution/models.py +201 -0
  99. pycharter/shared/__init__.py +56 -0
  100. pycharter/shared/errors.py +296 -0
  101. pycharter/shared/protocols.py +234 -0
  102. {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/METADATA +146 -26
  103. pycharter-0.0.24.dist-info/RECORD +543 -0
  104. {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/WHEEL +1 -1
  105. ui/static/404/index.html +1 -1
  106. ui/static/404.html +1 -1
  107. ui/static/__next.__PAGE__.txt +1 -1
  108. ui/static/__next._full.txt +1 -1
  109. ui/static/__next._head.txt +1 -1
  110. ui/static/__next._index.txt +1 -1
  111. ui/static/__next._tree.txt +1 -1
  112. ui/static/_next/static/chunks/26dfc590f7714c03.js +1 -0
  113. ui/static/_next/static/chunks/34d289e6db2ef551.js +1 -0
  114. ui/static/_next/static/chunks/99508d9d5869cc27.js +1 -0
  115. ui/static/_next/static/chunks/b313c35a6ba76574.js +1 -0
  116. ui/static/_not-found/__next._full.txt +1 -1
  117. ui/static/_not-found/__next._head.txt +1 -1
  118. ui/static/_not-found/__next._index.txt +1 -1
  119. ui/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
  120. ui/static/_not-found/__next._not-found.txt +1 -1
  121. ui/static/_not-found/__next._tree.txt +1 -1
  122. ui/static/_not-found/index.html +1 -1
  123. ui/static/_not-found/index.txt +1 -1
  124. ui/static/contracts/__next._full.txt +2 -2
  125. ui/static/contracts/__next._head.txt +1 -1
  126. ui/static/contracts/__next._index.txt +1 -1
  127. ui/static/contracts/__next._tree.txt +1 -1
  128. ui/static/contracts/__next.contracts.__PAGE__.txt +2 -2
  129. ui/static/contracts/__next.contracts.txt +1 -1
  130. ui/static/contracts/index.html +1 -1
  131. ui/static/contracts/index.txt +2 -2
  132. ui/static/documentation/__next._full.txt +1 -1
  133. ui/static/documentation/__next._head.txt +1 -1
  134. ui/static/documentation/__next._index.txt +1 -1
  135. ui/static/documentation/__next._tree.txt +1 -1
  136. ui/static/documentation/__next.documentation.__PAGE__.txt +1 -1
  137. ui/static/documentation/__next.documentation.txt +1 -1
  138. ui/static/documentation/index.html +2 -2
  139. ui/static/documentation/index.txt +1 -1
  140. ui/static/index.html +1 -1
  141. ui/static/index.txt +1 -1
  142. ui/static/metadata/__next._full.txt +1 -1
  143. ui/static/metadata/__next._head.txt +1 -1
  144. ui/static/metadata/__next._index.txt +1 -1
  145. ui/static/metadata/__next._tree.txt +1 -1
  146. ui/static/metadata/__next.metadata.__PAGE__.txt +1 -1
  147. ui/static/metadata/__next.metadata.txt +1 -1
  148. ui/static/metadata/index.html +1 -1
  149. ui/static/metadata/index.txt +1 -1
  150. ui/static/quality/__next._full.txt +2 -2
  151. ui/static/quality/__next._head.txt +1 -1
  152. ui/static/quality/__next._index.txt +1 -1
  153. ui/static/quality/__next._tree.txt +1 -1
  154. ui/static/quality/__next.quality.__PAGE__.txt +2 -2
  155. ui/static/quality/__next.quality.txt +1 -1
  156. ui/static/quality/index.html +2 -2
  157. ui/static/quality/index.txt +2 -2
  158. ui/static/rules/__next._full.txt +1 -1
  159. ui/static/rules/__next._head.txt +1 -1
  160. ui/static/rules/__next._index.txt +1 -1
  161. ui/static/rules/__next._tree.txt +1 -1
  162. ui/static/rules/__next.rules.__PAGE__.txt +1 -1
  163. ui/static/rules/__next.rules.txt +1 -1
  164. ui/static/rules/index.html +1 -1
  165. ui/static/rules/index.txt +1 -1
  166. ui/static/schemas/__next._full.txt +1 -1
  167. ui/static/schemas/__next._head.txt +1 -1
  168. ui/static/schemas/__next._index.txt +1 -1
  169. ui/static/schemas/__next._tree.txt +1 -1
  170. ui/static/schemas/__next.schemas.__PAGE__.txt +1 -1
  171. ui/static/schemas/__next.schemas.txt +1 -1
  172. ui/static/schemas/index.html +1 -1
  173. ui/static/schemas/index.txt +1 -1
  174. ui/static/settings/__next._full.txt +1 -1
  175. ui/static/settings/__next._head.txt +1 -1
  176. ui/static/settings/__next._index.txt +1 -1
  177. ui/static/settings/__next._tree.txt +1 -1
  178. ui/static/settings/__next.settings.__PAGE__.txt +1 -1
  179. ui/static/settings/__next.settings.txt +1 -1
  180. ui/static/settings/index.html +1 -1
  181. ui/static/settings/index.txt +1 -1
  182. ui/static/static/404/index.html +1 -1
  183. ui/static/static/404.html +1 -1
  184. ui/static/static/__next.__PAGE__.txt +1 -1
  185. ui/static/static/__next._full.txt +2 -2
  186. ui/static/static/__next._head.txt +1 -1
  187. ui/static/static/__next._index.txt +2 -2
  188. ui/static/static/__next._tree.txt +2 -2
  189. ui/static/static/_next/static/chunks/13d4a0fbd74c1ee4.js +1 -0
  190. ui/static/static/_next/static/chunks/2edb43b48432ac04.js +441 -0
  191. ui/static/static/_next/static/chunks/d2363397e1b2bcab.css +1 -0
  192. ui/static/static/_next/static/chunks/f7d1a90dd75d2572.js +1 -0
  193. ui/static/static/_not-found/__next._full.txt +2 -2
  194. ui/static/static/_not-found/__next._head.txt +1 -1
  195. ui/static/static/_not-found/__next._index.txt +2 -2
  196. ui/static/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
  197. ui/static/static/_not-found/__next._not-found.txt +1 -1
  198. ui/static/static/_not-found/__next._tree.txt +2 -2
  199. ui/static/static/_not-found/index.html +1 -1
  200. ui/static/static/_not-found/index.txt +2 -2
  201. ui/static/static/contracts/__next._full.txt +3 -3
  202. ui/static/static/contracts/__next._head.txt +1 -1
  203. ui/static/static/contracts/__next._index.txt +2 -2
  204. ui/static/static/contracts/__next._tree.txt +2 -2
  205. ui/static/static/contracts/__next.contracts.__PAGE__.txt +2 -2
  206. ui/static/static/contracts/__next.contracts.txt +1 -1
  207. ui/static/static/contracts/index.html +1 -1
  208. ui/static/static/contracts/index.txt +3 -3
  209. ui/static/static/documentation/__next._full.txt +3 -3
  210. ui/static/static/documentation/__next._head.txt +1 -1
  211. ui/static/static/documentation/__next._index.txt +2 -2
  212. ui/static/static/documentation/__next._tree.txt +2 -2
  213. ui/static/static/documentation/__next.documentation.__PAGE__.txt +2 -2
  214. ui/static/static/documentation/__next.documentation.txt +1 -1
  215. ui/static/static/documentation/index.html +2 -2
  216. ui/static/static/documentation/index.txt +3 -3
  217. ui/static/static/index.html +1 -1
  218. ui/static/static/index.txt +2 -2
  219. ui/static/static/metadata/__next._full.txt +2 -2
  220. ui/static/static/metadata/__next._head.txt +1 -1
  221. ui/static/static/metadata/__next._index.txt +2 -2
  222. ui/static/static/metadata/__next._tree.txt +2 -2
  223. ui/static/static/metadata/__next.metadata.__PAGE__.txt +1 -1
  224. ui/static/static/metadata/__next.metadata.txt +1 -1
  225. ui/static/static/metadata/index.html +1 -1
  226. ui/static/static/metadata/index.txt +2 -2
  227. ui/static/static/quality/__next._full.txt +2 -2
  228. ui/static/static/quality/__next._head.txt +1 -1
  229. ui/static/static/quality/__next._index.txt +2 -2
  230. ui/static/static/quality/__next._tree.txt +2 -2
  231. ui/static/static/quality/__next.quality.__PAGE__.txt +1 -1
  232. ui/static/static/quality/__next.quality.txt +1 -1
  233. ui/static/static/quality/index.html +2 -2
  234. ui/static/static/quality/index.txt +2 -2
  235. ui/static/static/rules/__next._full.txt +2 -2
  236. ui/static/static/rules/__next._head.txt +1 -1
  237. ui/static/static/rules/__next._index.txt +2 -2
  238. ui/static/static/rules/__next._tree.txt +2 -2
  239. ui/static/static/rules/__next.rules.__PAGE__.txt +1 -1
  240. ui/static/static/rules/__next.rules.txt +1 -1
  241. ui/static/static/rules/index.html +1 -1
  242. ui/static/static/rules/index.txt +2 -2
  243. ui/static/static/schemas/__next._full.txt +2 -2
  244. ui/static/static/schemas/__next._head.txt +1 -1
  245. ui/static/static/schemas/__next._index.txt +2 -2
  246. ui/static/static/schemas/__next._tree.txt +2 -2
  247. ui/static/static/schemas/__next.schemas.__PAGE__.txt +1 -1
  248. ui/static/static/schemas/__next.schemas.txt +1 -1
  249. ui/static/static/schemas/index.html +1 -1
  250. ui/static/static/schemas/index.txt +2 -2
  251. ui/static/static/settings/__next._full.txt +2 -2
  252. ui/static/static/settings/__next._head.txt +1 -1
  253. ui/static/static/settings/__next._index.txt +2 -2
  254. ui/static/static/settings/__next._tree.txt +2 -2
  255. ui/static/static/settings/__next.settings.__PAGE__.txt +1 -1
  256. ui/static/static/settings/__next.settings.txt +1 -1
  257. ui/static/static/settings/index.html +1 -1
  258. ui/static/static/settings/index.txt +2 -2
  259. ui/static/static/static/.gitkeep +0 -0
  260. ui/static/static/static/404/index.html +1 -0
  261. ui/static/static/static/404.html +1 -0
  262. ui/static/static/static/__next.__PAGE__.txt +10 -0
  263. ui/static/static/static/__next._full.txt +30 -0
  264. ui/static/static/static/__next._head.txt +7 -0
  265. ui/static/static/static/__next._index.txt +9 -0
  266. ui/static/static/static/__next._tree.txt +2 -0
  267. ui/static/static/static/_next/static/chunks/222442f6da32302a.js +1 -0
  268. ui/static/static/static/_next/static/chunks/247eb132b7f7b574.js +1 -0
  269. ui/static/static/static/_next/static/chunks/297d55555b71baba.js +1 -0
  270. ui/static/static/static/_next/static/chunks/2ab439ce003cd691.js +1 -0
  271. ui/static/static/static/_next/static/chunks/414e77373f8ff61c.js +1 -0
  272. ui/static/static/static/_next/static/chunks/49ca65abd26ae49e.js +1 -0
  273. ui/static/static/static/_next/static/chunks/652ad0aa26265c47.js +2 -0
  274. ui/static/static/static/_next/static/chunks/9667e7a3d359eb39.js +1 -0
  275. ui/static/static/static/_next/static/chunks/9c23f44fff36548a.js +1 -0
  276. ui/static/static/static/_next/static/chunks/a6dad97d9634a72d.js +1 -0
  277. ui/static/static/static/_next/static/chunks/b32a0963684b9933.js +4 -0
  278. ui/static/static/static/_next/static/chunks/c69f6cba366bd988.js +1 -0
  279. ui/static/static/static/_next/static/chunks/db913959c675cea6.js +1 -0
  280. ui/static/static/static/_next/static/chunks/f061a4be97bfc3b3.js +1 -0
  281. ui/static/static/static/_next/static/chunks/f2e7afeab1178138.js +1 -0
  282. ui/static/static/static/_next/static/chunks/ff1a16fafef87110.js +1 -0
  283. ui/static/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +3 -0
  284. ui/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_buildManifest.js +11 -0
  285. ui/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_ssgManifest.js +1 -0
  286. ui/static/static/static/_not-found/__next._full.txt +17 -0
  287. ui/static/static/static/_not-found/__next._head.txt +7 -0
  288. ui/static/static/static/_not-found/__next._index.txt +9 -0
  289. ui/static/static/static/_not-found/__next._not-found.__PAGE__.txt +5 -0
  290. ui/static/static/static/_not-found/__next._not-found.txt +4 -0
  291. ui/static/static/static/_not-found/__next._tree.txt +2 -0
  292. ui/static/static/static/_not-found/index.html +1 -0
  293. ui/static/static/static/_not-found/index.txt +17 -0
  294. ui/static/static/static/contracts/__next._full.txt +21 -0
  295. ui/static/static/static/contracts/__next._head.txt +7 -0
  296. ui/static/static/static/contracts/__next._index.txt +9 -0
  297. ui/static/static/static/contracts/__next._tree.txt +2 -0
  298. ui/static/static/static/contracts/__next.contracts.__PAGE__.txt +9 -0
  299. ui/static/static/static/contracts/__next.contracts.txt +4 -0
  300. ui/static/static/static/contracts/index.html +1 -0
  301. ui/static/static/static/contracts/index.txt +21 -0
  302. ui/static/static/static/documentation/__next._full.txt +21 -0
  303. ui/static/static/static/documentation/__next._head.txt +7 -0
  304. ui/static/static/static/documentation/__next._index.txt +9 -0
  305. ui/static/static/static/documentation/__next._tree.txt +2 -0
  306. ui/static/static/static/documentation/__next.documentation.__PAGE__.txt +9 -0
  307. ui/static/static/static/documentation/__next.documentation.txt +4 -0
  308. ui/static/static/static/documentation/index.html +93 -0
  309. ui/static/static/static/documentation/index.txt +21 -0
  310. ui/static/static/static/index.html +1 -0
  311. ui/static/static/static/index.txt +30 -0
  312. ui/static/static/static/metadata/__next._full.txt +21 -0
  313. ui/static/static/static/metadata/__next._head.txt +7 -0
  314. ui/static/static/static/metadata/__next._index.txt +9 -0
  315. ui/static/static/static/metadata/__next._tree.txt +2 -0
  316. ui/static/static/static/metadata/__next.metadata.__PAGE__.txt +9 -0
  317. ui/static/static/static/metadata/__next.metadata.txt +4 -0
  318. ui/static/static/static/metadata/index.html +1 -0
  319. ui/static/static/static/metadata/index.txt +21 -0
  320. ui/static/static/static/quality/__next._full.txt +21 -0
  321. ui/static/static/static/quality/__next._head.txt +7 -0
  322. ui/static/static/static/quality/__next._index.txt +9 -0
  323. ui/static/static/static/quality/__next._tree.txt +2 -0
  324. ui/static/static/static/quality/__next.quality.__PAGE__.txt +9 -0
  325. ui/static/static/static/quality/__next.quality.txt +4 -0
  326. ui/static/static/static/quality/index.html +2 -0
  327. ui/static/static/static/quality/index.txt +21 -0
  328. ui/static/static/static/rules/__next._full.txt +21 -0
  329. ui/static/static/static/rules/__next._head.txt +7 -0
  330. ui/static/static/static/rules/__next._index.txt +9 -0
  331. ui/static/static/static/rules/__next._tree.txt +2 -0
  332. ui/static/static/static/rules/__next.rules.__PAGE__.txt +9 -0
  333. ui/static/static/static/rules/__next.rules.txt +4 -0
  334. ui/static/static/static/rules/index.html +1 -0
  335. ui/static/static/static/rules/index.txt +21 -0
  336. ui/static/static/static/schemas/__next._full.txt +21 -0
  337. ui/static/static/static/schemas/__next._head.txt +7 -0
  338. ui/static/static/static/schemas/__next._index.txt +9 -0
  339. ui/static/static/static/schemas/__next._tree.txt +2 -0
  340. ui/static/static/static/schemas/__next.schemas.__PAGE__.txt +9 -0
  341. ui/static/static/static/schemas/__next.schemas.txt +4 -0
  342. ui/static/static/static/schemas/index.html +1 -0
  343. ui/static/static/static/schemas/index.txt +21 -0
  344. ui/static/static/static/settings/__next._full.txt +21 -0
  345. ui/static/static/static/settings/__next._head.txt +7 -0
  346. ui/static/static/static/settings/__next._index.txt +9 -0
  347. ui/static/static/static/settings/__next._tree.txt +2 -0
  348. ui/static/static/static/settings/__next.settings.__PAGE__.txt +9 -0
  349. ui/static/static/static/settings/__next.settings.txt +4 -0
  350. ui/static/static/static/settings/index.html +1 -0
  351. ui/static/static/static/settings/index.txt +21 -0
  352. ui/static/static/static/validation/__next._full.txt +21 -0
  353. ui/static/static/static/validation/__next._head.txt +7 -0
  354. ui/static/static/static/validation/__next._index.txt +9 -0
  355. ui/static/static/static/validation/__next._tree.txt +2 -0
  356. ui/static/static/static/validation/__next.validation.__PAGE__.txt +9 -0
  357. ui/static/static/static/validation/__next.validation.txt +4 -0
  358. ui/static/static/static/validation/index.html +1 -0
  359. ui/static/static/static/validation/index.txt +21 -0
  360. ui/static/static/validation/__next._full.txt +2 -2
  361. ui/static/static/validation/__next._head.txt +1 -1
  362. ui/static/static/validation/__next._index.txt +2 -2
  363. ui/static/static/validation/__next._tree.txt +2 -2
  364. ui/static/static/validation/__next.validation.__PAGE__.txt +1 -1
  365. ui/static/static/validation/__next.validation.txt +1 -1
  366. ui/static/static/validation/index.html +1 -1
  367. ui/static/static/validation/index.txt +2 -2
  368. ui/static/validation/__next._full.txt +2 -2
  369. ui/static/validation/__next._head.txt +1 -1
  370. ui/static/validation/__next._index.txt +1 -1
  371. ui/static/validation/__next._tree.txt +1 -1
  372. ui/static/validation/__next.validation.__PAGE__.txt +2 -2
  373. ui/static/validation/__next.validation.txt +1 -1
  374. ui/static/validation/index.html +1 -1
  375. ui/static/validation/index.txt +2 -2
  376. pycharter/data/templates/template_coercion_rules.yaml +0 -15
  377. pycharter/data/templates/template_contract.yaml +0 -587
  378. pycharter/data/templates/template_metadata.yaml +0 -38
  379. pycharter/data/templates/template_schema.yaml +0 -22
  380. pycharter/data/templates/template_transform_advanced.yaml +0 -50
  381. pycharter/data/templates/template_transform_simple.yaml +0 -59
  382. pycharter/data/templates/template_validation_rules.yaml +0 -29
  383. pycharter/etl_generator/extraction.py +0 -916
  384. pycharter/etl_generator/factory.py +0 -174
  385. pycharter/etl_generator/orchestrator.py +0 -1650
  386. pycharter/integrations/__init__.py +0 -19
  387. pycharter/integrations/kafka.py +0 -178
  388. pycharter/integrations/streaming.py +0 -100
  389. pycharter-0.0.22.dist-info/RECORD +0 -358
  390. {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/entry_points.txt +0 -0
  391. {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/licenses/LICENSE +0 -0
  392. {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/top_level.txt +0 -0
  393. /ui/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_buildManifest.js +0 -0
  394. /ui/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_ssgManifest.js +0 -0
  395. /ui/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_buildManifest.js +0 -0
  396. /ui/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_ssgManifest.js +0 -0
  397. /ui/static/{_next → static/_next}/static/chunks/c4fa4f4114b7c352.js +0 -0
  398. /ui/static/static/{_next → static/_next}/static/chunks/4e310fe5005770a3.css +0 -0
  399. /ui/static/{_next → static/static/_next}/static/chunks/5e04d10c4a7b58a3.js +0 -0
  400. /ui/static/static/{_next → static/_next}/static/chunks/5fc14c00a2779dc5.js +0 -0
  401. /ui/static/{_next → static/static/_next}/static/chunks/75d88a058d8ffaa6.js +0 -0
  402. /ui/static/{_next → static/static/_next}/static/chunks/8c89634cf6bad76f.js +0 -0
  403. /ui/static/static/{_next → static/_next}/static/chunks/b584574fdc8ab13e.js +0 -0
  404. /ui/static/static/{_next → static/_next}/static/chunks/d5989c94d3614b3a.js +0 -0
api/main.py CHANGED
@@ -16,7 +16,18 @@ from fastapi.routing import APIRoute
16
16
  from pycharter import __version__ as pycharter_version
17
17
 
18
18
  # Import routers from v1
19
- from api.routes.v1 import contracts, metadata, quality, schemas, templates, validation, settings
19
+ from api.routes.v1 import (
20
+ contracts,
21
+ metadata,
22
+ quality,
23
+ schemas,
24
+ templates,
25
+ validation,
26
+ settings,
27
+ docs,
28
+ tracking,
29
+ evolution,
30
+ )
20
31
 
21
32
  # Try to import validation_jobs router (requires worker component)
22
33
  try:
@@ -128,6 +139,21 @@ def create_application() -> FastAPI:
128
139
  prefix=f"/api/{API_VERSION}",
129
140
  tags=["Settings"],
130
141
  )
142
+ app.include_router(
143
+ docs.router,
144
+ prefix=f"/api/{API_VERSION}",
145
+ tags=["Documentation"],
146
+ )
147
+ app.include_router(
148
+ tracking.router,
149
+ prefix=f"/api/{API_VERSION}",
150
+ tags=["Quality Tracking"],
151
+ )
152
+ app.include_router(
153
+ evolution.router,
154
+ prefix=f"/api/{API_VERSION}",
155
+ tags=["Schema Evolution"],
156
+ )
131
157
 
132
158
  # Include validation_jobs router if worker component is available
133
159
  if VALIDATION_JOBS_AVAILABLE:
api/models/docs.py ADDED
@@ -0,0 +1,68 @@
1
+ """
2
+ API models for documentation generation endpoints.
3
+ """
4
+
5
+ from enum import Enum
6
+ from typing import Optional
7
+
8
+ from pydantic import BaseModel, Field
9
+
10
+
11
+ class DocsFormat(str, Enum):
12
+ """Supported documentation output formats."""
13
+
14
+ MARKDOWN = "markdown"
15
+ HTML = "html"
16
+
17
+
18
+ class DocsRequest(BaseModel):
19
+ """Request model for generating documentation from contract data."""
20
+
21
+ contract: dict = Field(..., description="Contract data to generate documentation for")
22
+ format: DocsFormat = Field(
23
+ default=DocsFormat.MARKDOWN, description="Output format for documentation"
24
+ )
25
+ include_schema: bool = Field(default=True, description="Include schema fields section")
26
+ include_coercions: bool = Field(
27
+ default=True, description="Include coercion rules section"
28
+ )
29
+ include_validations: bool = Field(
30
+ default=True, description="Include validation rules section"
31
+ )
32
+ include_metadata: bool = Field(
33
+ default=True, description="Include metadata/ownership section"
34
+ )
35
+
36
+
37
+ class DocsResponse(BaseModel):
38
+ """Response model for generated documentation."""
39
+
40
+ documentation: str = Field(..., description="Generated documentation content")
41
+ format: DocsFormat = Field(..., description="Format of the generated documentation")
42
+ schema_name: Optional[str] = Field(
43
+ default=None, description="Name of the schema documented"
44
+ )
45
+ version: Optional[str] = Field(
46
+ default=None, description="Version of the schema documented"
47
+ )
48
+
49
+
50
+ class DocsSectionRequest(BaseModel):
51
+ """Request model for generating a specific documentation section."""
52
+
53
+ contract: dict = Field(..., description="Contract data to generate documentation for")
54
+ section: str = Field(
55
+ ...,
56
+ description="Section to generate: 'schema', 'coercions', 'validations', 'metadata'",
57
+ )
58
+ format: DocsFormat = Field(
59
+ default=DocsFormat.MARKDOWN, description="Output format for documentation"
60
+ )
61
+
62
+
63
+ class DocsSectionResponse(BaseModel):
64
+ """Response model for a documentation section."""
65
+
66
+ section: str = Field(..., description="Name of the section generated")
67
+ content: str = Field(..., description="Generated section content")
68
+ format: DocsFormat = Field(..., description="Format of the generated content")
@@ -0,0 +1,117 @@
1
+ """
2
+ API models for schema evolution endpoints.
3
+ """
4
+
5
+ from enum import Enum
6
+ from typing import Any, Dict, List, Optional
7
+
8
+ from pydantic import BaseModel, Field
9
+
10
+
11
+ class CompatibilityModeEnum(str, Enum):
12
+ """Compatibility checking modes."""
13
+
14
+ BACKWARD = "backward"
15
+ FORWARD = "forward"
16
+ FULL = "full"
17
+
18
+
19
+ class ChangeTypeEnum(str, Enum):
20
+ """Types of schema changes."""
21
+
22
+ FIELD_ADDED = "field_added"
23
+ FIELD_REMOVED = "field_removed"
24
+ FIELD_RENAMED = "field_renamed"
25
+ TYPE_CHANGED = "type_changed"
26
+ TYPE_WIDENED = "type_widened"
27
+ TYPE_NARROWED = "type_narrowed"
28
+ CONSTRAINT_ADDED = "constraint_added"
29
+ CONSTRAINT_REMOVED = "constraint_removed"
30
+ CONSTRAINT_MODIFIED = "constraint_modified"
31
+ REQUIRED_ADDED = "required_added"
32
+ REQUIRED_REMOVED = "required_removed"
33
+ ENUM_VALUE_ADDED = "enum_value_added"
34
+ ENUM_VALUE_REMOVED = "enum_value_removed"
35
+ DEFAULT_ADDED = "default_added"
36
+ DEFAULT_REMOVED = "default_removed"
37
+ DEFAULT_CHANGED = "default_changed"
38
+ DESCRIPTION_CHANGED = "description_changed"
39
+ TITLE_CHANGED = "title_changed"
40
+ FORMAT_CHANGED = "format_changed"
41
+ PATTERN_CHANGED = "pattern_changed"
42
+
43
+
44
+ class SchemaChangeResponse(BaseModel):
45
+ """Response model for a single schema change."""
46
+
47
+ path: str = Field(..., description="JSON path to the changed element")
48
+ change_type: ChangeTypeEnum = Field(..., description="Type of change")
49
+ old_value: Optional[Any] = Field(default=None, description="Value in old schema")
50
+ new_value: Optional[Any] = Field(default=None, description="Value in new schema")
51
+ breaking: bool = Field(..., description="Whether this is a breaking change")
52
+ message: str = Field(..., description="Human-readable description")
53
+
54
+
55
+ class SchemaDiffResponse(BaseModel):
56
+ """Response model for schema diff."""
57
+
58
+ changes: List[SchemaChangeResponse] = Field(..., description="All detected changes")
59
+ breaking_changes: List[SchemaChangeResponse] = Field(
60
+ ..., description="Only breaking changes"
61
+ )
62
+ additions: List[SchemaChangeResponse] = Field(..., description="Added elements")
63
+ removals: List[SchemaChangeResponse] = Field(..., description="Removed elements")
64
+ modifications: List[SchemaChangeResponse] = Field(
65
+ ..., description="Modified elements"
66
+ )
67
+ has_breaking_changes: bool = Field(
68
+ ..., description="Whether any breaking changes exist"
69
+ )
70
+ total_changes: int = Field(..., description="Total number of changes")
71
+
72
+
73
+ class CompatibilityCheckRequest(BaseModel):
74
+ """Request model for checking compatibility between two schemas."""
75
+
76
+ old_schema: Dict[str, Any] = Field(..., description="Original/existing schema")
77
+ new_schema: Dict[str, Any] = Field(..., description="New schema to check")
78
+ mode: CompatibilityModeEnum = Field(
79
+ default=CompatibilityModeEnum.BACKWARD,
80
+ description="Compatibility mode to check against",
81
+ )
82
+
83
+
84
+ class CompatibilityCheckResponse(BaseModel):
85
+ """Response model for compatibility check."""
86
+
87
+ compatible: bool = Field(
88
+ ..., description="Whether schemas are compatible in the specified mode"
89
+ )
90
+ mode: CompatibilityModeEnum = Field(..., description="Compatibility mode used")
91
+ diff: Optional[SchemaDiffResponse] = Field(
92
+ default=None, description="Detailed schema diff"
93
+ )
94
+ issues: List[str] = Field(
95
+ default_factory=list, description="Breaking change descriptions"
96
+ )
97
+ warnings: List[str] = Field(
98
+ default_factory=list, description="Non-breaking issues that may need attention"
99
+ )
100
+ breaking_change_count: int = Field(
101
+ ..., description="Number of breaking changes detected"
102
+ )
103
+
104
+
105
+ class DiffRequest(BaseModel):
106
+ """Request model for computing schema diff."""
107
+
108
+ old_schema: Dict[str, Any] = Field(..., description="Original schema")
109
+ new_schema: Dict[str, Any] = Field(..., description="New schema")
110
+
111
+
112
+ class StoredSchemaDiffRequest(BaseModel):
113
+ """Request model for diffing stored schema versions."""
114
+
115
+ schema_name: str = Field(..., description="Name of the schema")
116
+ old_version: str = Field(..., description="Old version to compare")
117
+ new_version: str = Field(..., description="New version to compare")
api/models/tracking.py ADDED
@@ -0,0 +1,111 @@
1
+ """
2
+ API models for quality tracking endpoints.
3
+ """
4
+
5
+ from datetime import datetime
6
+ from enum import Enum
7
+ from typing import Any, Dict, List, Optional
8
+
9
+ from pydantic import BaseModel, Field
10
+
11
+
12
+ class ExportFormat(str, Enum):
13
+ """Supported export formats."""
14
+
15
+ JSON = "json"
16
+ PROMETHEUS = "prometheus"
17
+ CSV = "csv"
18
+
19
+
20
+ class ValidationMetricResponse(BaseModel):
21
+ """Response model for a single validation metric."""
22
+
23
+ id: str = Field(..., description="Unique metric identifier")
24
+ schema_name: str = Field(..., description="Name of the schema validated")
25
+ version: str = Field(..., description="Version of the schema")
26
+ timestamp: datetime = Field(..., description="When the validation occurred")
27
+ record_count: int = Field(..., description="Number of records validated")
28
+ valid_count: int = Field(..., description="Number of valid records")
29
+ error_count: int = Field(..., description="Number of records with errors")
30
+ validity_rate: float = Field(..., description="Ratio of valid records (0.0 to 1.0)")
31
+ completeness: float = Field(..., description="Data completeness ratio (0.0 to 1.0)")
32
+ field_completeness: Dict[str, float] = Field(
33
+ default_factory=dict, description="Per-field completeness ratios"
34
+ )
35
+ duration_ms: float = Field(..., description="Validation duration in milliseconds")
36
+ errors_by_type: Dict[str, int] = Field(
37
+ default_factory=dict, description="Error counts by type"
38
+ )
39
+ metadata: Dict[str, Any] = Field(
40
+ default_factory=dict, description="Additional metadata"
41
+ )
42
+
43
+
44
+ class MetricsSummaryResponse(BaseModel):
45
+ """Response model for aggregated metrics summary."""
46
+
47
+ schema_name: str = Field(..., description="Name of the schema")
48
+ period_start: datetime = Field(..., description="Start of the summary period")
49
+ period_end: datetime = Field(..., description="End of the summary period")
50
+ total_validations: int = Field(..., description="Total validation runs in period")
51
+ total_records: int = Field(..., description="Total records validated")
52
+ total_valid: int = Field(..., description="Total valid records")
53
+ total_errors: int = Field(..., description="Total records with errors")
54
+ avg_validity_rate: float = Field(..., description="Average validity rate")
55
+ min_validity_rate: float = Field(..., description="Minimum validity rate")
56
+ max_validity_rate: float = Field(..., description="Maximum validity rate")
57
+ avg_completeness: float = Field(..., description="Average completeness")
58
+ avg_duration_ms: float = Field(..., description="Average validation duration")
59
+ overall_validity_rate: float = Field(..., description="Overall validity rate from totals")
60
+ top_error_types: Dict[str, int] = Field(
61
+ default_factory=dict, description="Most common error types"
62
+ )
63
+
64
+
65
+ class MetricsQueryResponse(BaseModel):
66
+ """Response model for metrics query."""
67
+
68
+ metrics: List[ValidationMetricResponse] = Field(
69
+ ..., description="List of matching metrics"
70
+ )
71
+ total: int = Field(..., description="Total number of metrics matching filters")
72
+ limit: int = Field(..., description="Maximum results returned")
73
+ offset: int = Field(..., description="Pagination offset")
74
+
75
+
76
+ class RecordMetricRequest(BaseModel):
77
+ """Request model for recording a validation metric."""
78
+
79
+ schema_name: str = Field(..., description="Name of the schema validated")
80
+ version: str = Field(..., description="Version of the schema")
81
+ record_count: int = Field(default=1, description="Number of records validated")
82
+ valid_count: int = Field(default=1, description="Number of valid records")
83
+ error_count: int = Field(default=0, description="Number of records with errors")
84
+ validity_rate: Optional[float] = Field(
85
+ default=None, description="Validity rate (calculated if not provided)"
86
+ )
87
+ completeness: float = Field(default=1.0, description="Data completeness")
88
+ field_completeness: Dict[str, float] = Field(
89
+ default_factory=dict, description="Per-field completeness"
90
+ )
91
+ duration_ms: float = Field(default=0.0, description="Validation duration")
92
+ errors_by_type: Dict[str, int] = Field(
93
+ default_factory=dict, description="Error counts by type"
94
+ )
95
+ metadata: Dict[str, Any] = Field(
96
+ default_factory=dict, description="Additional metadata"
97
+ )
98
+
99
+
100
+ class ExportResponse(BaseModel):
101
+ """Response model for metrics export."""
102
+
103
+ format: ExportFormat = Field(..., description="Export format used")
104
+ data: str = Field(..., description="Exported data as string")
105
+ count: int = Field(..., description="Number of metrics exported")
106
+
107
+
108
+ class SchemaListResponse(BaseModel):
109
+ """Response model for list of schemas with metrics."""
110
+
111
+ schemas: List[str] = Field(..., description="List of schema names with recorded metrics")
api/models/validation.py CHANGED
@@ -15,6 +15,7 @@ class ValidationRequest(BaseModel):
15
15
  data: Dict[str, Any] = Field(..., description="Data to validate")
16
16
  version: Optional[str] = Field(None, description="Schema version (default: latest)")
17
17
  strict: bool = Field(False, description="If True, raise exceptions on validation errors")
18
+ include_quality: bool = Field(False, description="If True, include quality metrics in response")
18
19
 
19
20
  class Config:
20
21
  json_schema_extra = {
@@ -25,7 +26,8 @@ class ValidationRequest(BaseModel):
25
26
  "age": 30
26
27
  },
27
28
  "version": "1.0.0",
28
- "strict": False
29
+ "strict": False,
30
+ "include_quality": False
29
31
  }
30
32
  }
31
33
 
@@ -47,6 +49,29 @@ class ValidationErrorDetail(BaseModel):
47
49
  }
48
50
 
49
51
 
52
+ class ValidationQualityMetrics(BaseModel):
53
+ """Quality metrics for validated data."""
54
+
55
+ completeness: float = Field(..., description="Ratio of non-null fields (0.0 to 1.0)")
56
+ field_completeness: Dict[str, float] = Field(default_factory=dict, description="Per-field completeness ratios")
57
+ record_count: int = Field(0, description="Number of records validated")
58
+ valid_count: int = Field(0, description="Number of valid records")
59
+ error_count: int = Field(0, description="Number of records with errors")
60
+ validity_rate: float = Field(..., description="Ratio of valid records")
61
+
62
+ class Config:
63
+ json_schema_extra = {
64
+ "example": {
65
+ "completeness": 0.95,
66
+ "field_completeness": {"name": 1.0, "age": 0.9, "email": 0.85},
67
+ "record_count": 100,
68
+ "valid_count": 95,
69
+ "error_count": 5,
70
+ "validity_rate": 0.95
71
+ }
72
+ }
73
+
74
+
50
75
  class ValidationResponse(BaseModel):
51
76
  """Response model for validation result."""
52
77
 
@@ -54,6 +79,7 @@ class ValidationResponse(BaseModel):
54
79
  data: Optional[Dict[str, Any]] = Field(None, description="Validated data (if valid)")
55
80
  errors: List[ValidationErrorDetail] = Field(default_factory=list, description="Validation errors (if invalid)")
56
81
  error_count: int = Field(0, description="Number of validation errors")
82
+ quality: Optional[ValidationQualityMetrics] = Field(None, description="Quality metrics (if requested)")
57
83
 
58
84
  class Config:
59
85
  json_schema_extra = {
@@ -64,7 +90,8 @@ class ValidationResponse(BaseModel):
64
90
  "age": 30
65
91
  },
66
92
  "errors": [],
67
- "error_count": 0
93
+ "error_count": 0,
94
+ "quality": None
68
95
  }
69
96
  }
70
97
 
@@ -77,6 +104,7 @@ class ValidationBatchRequest(BaseModel):
77
104
  data_list: List[Dict[str, Any]] = Field(..., description="List of data dictionaries to validate")
78
105
  version: Optional[str] = Field(None, description="Schema version (default: latest)")
79
106
  strict: bool = Field(False, description="If True, raise exceptions on validation errors")
107
+ include_quality: bool = Field(False, description="If True, include quality metrics in response")
80
108
 
81
109
  class Config:
82
110
  json_schema_extra = {
@@ -87,7 +115,8 @@ class ValidationBatchRequest(BaseModel):
87
115
  {"name": "Bob", "age": 25}
88
116
  ],
89
117
  "version": "1.0.0",
90
- "strict": False
118
+ "strict": False,
119
+ "include_quality": False
91
120
  }
92
121
  }
93
122
 
@@ -99,6 +128,7 @@ class ValidationBatchResponse(BaseModel):
99
128
  total_count: int = Field(..., description="Total number of items validated")
100
129
  valid_count: int = Field(..., description="Number of valid items")
101
130
  invalid_count: int = Field(..., description="Number of invalid items")
131
+ quality: Optional[ValidationQualityMetrics] = Field(None, description="Aggregate quality metrics (if requested)")
102
132
 
103
133
  class Config:
104
134
  json_schema_extra = {
@@ -108,7 +138,8 @@ class ValidationBatchResponse(BaseModel):
108
138
  "is_valid": True,
109
139
  "data": {"name": "Alice", "age": 30},
110
140
  "errors": [],
111
- "error_count": 0
141
+ "error_count": 0,
142
+ "quality": None
112
143
  },
113
144
  {
114
145
  "is_valid": False,
@@ -120,12 +151,21 @@ class ValidationBatchResponse(BaseModel):
120
151
  "input_value": -5
121
152
  }
122
153
  ],
123
- "error_count": 1
154
+ "error_count": 1,
155
+ "quality": None
124
156
  }
125
157
  ],
126
158
  "total_count": 2,
127
159
  "valid_count": 1,
128
- "invalid_count": 1
160
+ "invalid_count": 1,
161
+ "quality": {
162
+ "completeness": 0.95,
163
+ "field_completeness": {"name": 1.0, "age": 0.9},
164
+ "record_count": 2,
165
+ "valid_count": 1,
166
+ "error_count": 1,
167
+ "validity_rate": 0.5
168
+ }
129
169
  }
130
170
  }
131
171
 
api/routes/v1/__init__.py CHANGED
@@ -6,7 +6,17 @@ All routers defined here are automatically included in the main application
6
6
  with the `/api/v1` prefix.
7
7
  """
8
8
 
9
- from api.routes.v1 import contracts, metadata, quality, schemas, validation, settings
9
+ from api.routes.v1 import (
10
+ contracts,
11
+ metadata,
12
+ quality,
13
+ schemas,
14
+ validation,
15
+ settings,
16
+ docs,
17
+ tracking,
18
+ evolution,
19
+ )
10
20
 
11
21
  # Export all routers for automatic inclusion in main.py
12
22
  __all__ = [
@@ -16,5 +26,8 @@ __all__ = [
16
26
  "schemas",
17
27
  "validation",
18
28
  "settings",
29
+ "docs",
30
+ "tracking",
31
+ "evolution",
19
32
  ]
20
33
 
api/routes/v1/docs.py ADDED
@@ -0,0 +1,187 @@
1
+ """
2
+ API routes for documentation generation.
3
+
4
+ Provides endpoints to generate human-readable documentation
5
+ from data contracts.
6
+ """
7
+
8
+ from typing import Optional
9
+
10
+ from fastapi import APIRouter, Depends, HTTPException, Query
11
+
12
+ from api.dependencies.store import get_metadata_store
13
+ from api.models.docs import (
14
+ DocsFormat,
15
+ DocsRequest,
16
+ DocsResponse,
17
+ DocsSectionRequest,
18
+ DocsSectionResponse,
19
+ )
20
+ from pycharter.contract_parser import parse_contract
21
+ from pycharter.docs_generator import DocsGenerator, generate_docs
22
+ from pycharter.docs_generator.renderers import HTMLRenderer, MarkdownRenderer
23
+ from pycharter.metadata_store import MetadataStoreClient
24
+
25
+ router = APIRouter(prefix="/docs", tags=["Documentation"])
26
+
27
+
28
+ @router.post(
29
+ "/generate",
30
+ response_model=DocsResponse,
31
+ summary="Generate documentation from contract",
32
+ description="Generate human-readable documentation from contract data",
33
+ )
34
+ async def generate_documentation(request: DocsRequest) -> DocsResponse:
35
+ """
36
+ Generate documentation from a contract.
37
+
38
+ Accepts contract data and generates formatted documentation
39
+ in the specified format (Markdown or HTML).
40
+ """
41
+ try:
42
+ # Parse the contract
43
+ contract = parse_contract(request.contract, validate=False)
44
+
45
+ # Generate documentation
46
+ docs = generate_docs(
47
+ contract,
48
+ format=request.format.value,
49
+ include_schema=request.include_schema,
50
+ include_coercions=request.include_coercions,
51
+ include_validations=request.include_validations,
52
+ include_metadata=request.include_metadata,
53
+ )
54
+
55
+ # Extract schema name and version
56
+ schema_name = contract.schema.get("title") or contract.metadata.get("title")
57
+ version = contract.versions.get("schema") or contract.schema.get("version")
58
+
59
+ return DocsResponse(
60
+ documentation=docs,
61
+ format=request.format,
62
+ schema_name=schema_name,
63
+ version=version,
64
+ )
65
+
66
+ except Exception as e:
67
+ raise HTTPException(status_code=400, detail=f"Failed to generate documentation: {e}")
68
+
69
+
70
+ @router.post(
71
+ "/section",
72
+ response_model=DocsSectionResponse,
73
+ summary="Generate a specific documentation section",
74
+ description="Generate a specific section of documentation from contract data",
75
+ )
76
+ async def generate_section(request: DocsSectionRequest) -> DocsSectionResponse:
77
+ """
78
+ Generate a specific section of documentation.
79
+
80
+ Sections: 'schema', 'coercions', 'validations', 'metadata'
81
+ """
82
+ try:
83
+ # Parse the contract
84
+ contract = parse_contract(request.contract, validate=False)
85
+
86
+ # Select renderer
87
+ if request.format == DocsFormat.HTML:
88
+ renderer = HTMLRenderer()
89
+ else:
90
+ renderer = MarkdownRenderer()
91
+
92
+ generator = DocsGenerator(renderer=renderer)
93
+
94
+ # Generate the requested section
95
+ section = request.section.lower()
96
+ if section == "schema":
97
+ content = generator.generate_schema_section(contract.schema)
98
+ elif section in ("coercions", "coercion_rules"):
99
+ content = generator.generate_coercion_section(contract.coercion_rules)
100
+ elif section in ("validations", "validation_rules"):
101
+ content = generator.generate_validation_section(contract.validation_rules)
102
+ elif section == "metadata":
103
+ content = generator.generate_metadata_section(contract)
104
+ else:
105
+ raise HTTPException(
106
+ status_code=400,
107
+ detail=f"Unknown section: {section}. Valid sections: schema, coercions, validations, metadata",
108
+ )
109
+
110
+ return DocsSectionResponse(
111
+ section=section,
112
+ content=content,
113
+ format=request.format,
114
+ )
115
+
116
+ except HTTPException:
117
+ raise
118
+ except Exception as e:
119
+ raise HTTPException(status_code=400, detail=f"Failed to generate section: {e}")
120
+
121
+
122
+ @router.get(
123
+ "/{schema_name}",
124
+ response_model=DocsResponse,
125
+ summary="Generate documentation for a stored schema",
126
+ description="Generate documentation for a schema stored in the metadata store",
127
+ )
128
+ async def get_schema_docs(
129
+ schema_name: str,
130
+ version: Optional[str] = Query(default=None, description="Schema version (latest if not specified)"),
131
+ format: DocsFormat = Query(default=DocsFormat.MARKDOWN, description="Output format"),
132
+ store: MetadataStoreClient = Depends(get_metadata_store),
133
+ ) -> DocsResponse:
134
+ """
135
+ Generate documentation for a schema from the metadata store.
136
+
137
+ Retrieves the schema from the store and generates documentation.
138
+ """
139
+ try:
140
+ # Get the complete schema from store
141
+ schema_data = store.get_complete_schema(schema_name, version=version)
142
+
143
+ if not schema_data:
144
+ raise HTTPException(
145
+ status_code=404, detail=f"Schema not found: {schema_name}"
146
+ )
147
+
148
+ # Build contract data from stored components
149
+ contract_data = {"schema": schema_data}
150
+
151
+ # Try to get additional components
152
+ try:
153
+ coercion_rules = store.get_coercion_rules(schema_name, version=version)
154
+ if coercion_rules:
155
+ contract_data["coercion_rules"] = coercion_rules
156
+ except Exception:
157
+ pass
158
+
159
+ try:
160
+ validation_rules = store.get_validation_rules(schema_name, version=version)
161
+ if validation_rules:
162
+ contract_data["validation_rules"] = validation_rules
163
+ except Exception:
164
+ pass
165
+
166
+ try:
167
+ metadata = store.get_metadata(schema_name, version=version)
168
+ if metadata:
169
+ contract_data["metadata"] = metadata
170
+ except Exception:
171
+ pass
172
+
173
+ # Parse and generate docs
174
+ contract = parse_contract(contract_data, validate=False)
175
+ docs = generate_docs(contract, format=format.value)
176
+
177
+ return DocsResponse(
178
+ documentation=docs,
179
+ format=format,
180
+ schema_name=schema_name,
181
+ version=version or contract.versions.get("schema"),
182
+ )
183
+
184
+ except HTTPException:
185
+ raise
186
+ except Exception as e:
187
+ raise HTTPException(status_code=500, detail=f"Failed to generate documentation: {e}")