dataface 0.1.2__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 (455) hide show
  1. d3_format/__init__.py +14 -0
  2. d3_format/errors.py +19 -0
  3. d3_format/format.py +551 -0
  4. d3_format/spec.py +159 -0
  5. dataface/DATAFACE_SYNTAX.md +1135 -0
  6. dataface/__init__.py +93 -0
  7. dataface/_docs_site.py +20 -0
  8. dataface/_install_hint.py +26 -0
  9. dataface/agent_api/__init__.py +79 -0
  10. dataface/agent_api/_init_templates/__init__.py +0 -0
  11. dataface/agent_api/_init_templates/agents_dft_snippet.md +26 -0
  12. dataface/agent_api/_init_templates/dataface.yml +15 -0
  13. dataface/agent_api/_init_templates/faces-dataface.yml +144 -0
  14. dataface/agent_api/_init_templates/index.md +24 -0
  15. dataface/agent_api/_paths.py +118 -0
  16. dataface/agent_api/_project_agents_md.py +43 -0
  17. dataface/agent_api/_session_store.py +486 -0
  18. dataface/agent_api/_state.py +28 -0
  19. dataface/agent_api/chat.py +221 -0
  20. dataface/agent_api/dashboards.py +257 -0
  21. dataface/agent_api/describe.py +366 -0
  22. dataface/agent_api/describe_query.py +120 -0
  23. dataface/agent_api/docs/__init__.py +25 -0
  24. dataface/agent_api/docs/_loader.py +292 -0
  25. dataface/agent_api/docs/yaml-reference.md +2757 -0
  26. dataface/agent_api/file_refs.py +118 -0
  27. dataface/agent_api/init.py +126 -0
  28. dataface/agent_api/inspect.py +128 -0
  29. dataface/agent_api/mcp_install.py +170 -0
  30. dataface/agent_api/query.py +274 -0
  31. dataface/agent_api/schema.py +658 -0
  32. dataface/agent_api/schema_search.py +284 -0
  33. dataface/agent_api/search.py +270 -0
  34. dataface/agent_api/skill_install.py +141 -0
  35. dataface/agent_api/skill_render.py +90 -0
  36. dataface/agent_api/skills.py +293 -0
  37. dataface/agent_api/surface_aliases.yaml +128 -0
  38. dataface/agent_api/validate.py +175 -0
  39. dataface/agent_api/validate_query.py +84 -0
  40. dataface/ai/__init__.py +39 -0
  41. dataface/ai/agent.py +139 -0
  42. dataface/ai/context.py +45 -0
  43. dataface/ai/events.py +62 -0
  44. dataface/ai/external_mcp.py +610 -0
  45. dataface/ai/generate_sql.py +96 -0
  46. dataface/ai/llm.py +403 -0
  47. dataface/ai/mcp/__init__.py +51 -0
  48. dataface/ai/mcp/server.py +289 -0
  49. dataface/ai/memories.py +85 -0
  50. dataface/ai/prompts.py +177 -0
  51. dataface/ai/schema_context.py +138 -0
  52. dataface/ai/skills/before-after-comparison/SKILL.md +102 -0
  53. dataface/ai/skills/before-after-comparison/examples/before-after-comparison.yml +24 -0
  54. dataface/ai/skills/dashboard-build/SKILL.md +212 -0
  55. dataface/ai/skills/dashboard-build/examples/_smoke.yml +15 -0
  56. dataface/ai/skills/dashboard-design/SKILL.md +182 -0
  57. dataface/ai/skills/dashboard-review/SKILL.md +113 -0
  58. dataface/ai/skills/dashboard-structural-review/SKILL.md +173 -0
  59. dataface/ai/skills/dashboard-visual-review/SKILL.md +139 -0
  60. dataface/ai/skills/dataface-mcp-setup/SKILL.md +177 -0
  61. dataface/ai/skills/dataface-troubleshooting/SKILL.md +225 -0
  62. dataface/ai/skills/drill-down-link/SKILL.md +112 -0
  63. dataface/ai/skills/drill-down-link/examples/drill-down-link.yml +27 -0
  64. dataface/ai/skills/faceted-small-multiples/SKILL.md +116 -0
  65. dataface/ai/skills/faceted-small-multiples/examples/faceted-small-multiples.yml +33 -0
  66. dataface/ai/skills/filter-bar-with-variables/SKILL.md +105 -0
  67. dataface/ai/skills/filter-bar-with-variables/examples/filter-bar-with-variables.yml +49 -0
  68. dataface/ai/skills/kpi-row/SKILL.md +101 -0
  69. dataface/ai/skills/kpi-row/examples/kpi-row.yml +55 -0
  70. dataface/ai/skills/report-design/SKILL.md +184 -0
  71. dataface/ai/skills/single-metric-bignum/SKILL.md +90 -0
  72. dataface/ai/skills/single-metric-bignum/examples/single-metric-bignum.yml +27 -0
  73. dataface/ai/skills/table-heavy-ops-dashboard/SKILL.md +114 -0
  74. dataface/ai/skills/table-heavy-ops-dashboard/examples/table-heavy-ops-dashboard.yml +48 -0
  75. dataface/ai/skills/time-series-trend/SKILL.md +93 -0
  76. dataface/ai/skills/time-series-trend/examples/time-series-trend.yml +26 -0
  77. dataface/ai/skills/top-n-with-detail/SKILL.md +98 -0
  78. dataface/ai/skills/top-n-with-detail/examples/top-n-with-detail.yml +45 -0
  79. dataface/ai/skills/two-by-two-grid-overview/SKILL.md +78 -0
  80. dataface/ai/skills/two-by-two-grid-overview/examples/two-by-two-grid-overview.yml +64 -0
  81. dataface/ai/tool_schemas.py +132 -0
  82. dataface/ai/tools/__init__.py +312 -0
  83. dataface/ai/yaml_utils.py +57 -0
  84. dataface/cli/__init__.py +3 -0
  85. dataface/cli/_console.py +48 -0
  86. dataface/cli/_error_format.py +83 -0
  87. dataface/cli/_extras.py +190 -0
  88. dataface/cli/_json_output.py +8 -0
  89. dataface/cli/_parsing.py +17 -0
  90. dataface/cli/_version_info.py +56 -0
  91. dataface/cli/commands/__init__.py +3 -0
  92. dataface/cli/commands/_agent_input.py +205 -0
  93. dataface/cli/commands/_agent_server.py +115 -0
  94. dataface/cli/commands/chat.py +645 -0
  95. dataface/cli/commands/describe.py +107 -0
  96. dataface/cli/commands/docs.py +131 -0
  97. dataface/cli/commands/extension.py +179 -0
  98. dataface/cli/commands/init.py +240 -0
  99. dataface/cli/commands/inspect.py +94 -0
  100. dataface/cli/commands/mcp_init.py +167 -0
  101. dataface/cli/commands/query.py +386 -0
  102. dataface/cli/commands/render.py +291 -0
  103. dataface/cli/commands/schema.py +411 -0
  104. dataface/cli/commands/search.py +49 -0
  105. dataface/cli/commands/serve.py +114 -0
  106. dataface/cli/commands/skills.py +133 -0
  107. dataface/cli/commands/skills_init.py +161 -0
  108. dataface/cli/commands/validate.py +63 -0
  109. dataface/cli/main.py +1501 -0
  110. dataface/core/__init__.py +75 -0
  111. dataface/core/compile/__init__.py +244 -0
  112. dataface/core/compile/_jinja_helpers.py +78 -0
  113. dataface/core/compile/channel.py +222 -0
  114. dataface/core/compile/chart_focus.py +101 -0
  115. dataface/core/compile/chart_resolved.py +169 -0
  116. dataface/core/compile/chart_type_detection.py +489 -0
  117. dataface/core/compile/chart_update.py +261 -0
  118. dataface/core/compile/colors.py +64 -0
  119. dataface/core/compile/compiler.py +904 -0
  120. dataface/core/compile/config.py +823 -0
  121. dataface/core/compile/custom_chart_types.py +208 -0
  122. dataface/core/compile/data_table_attachment.py +1287 -0
  123. dataface/core/compile/detect.py +110 -0
  124. dataface/core/compile/errors.py +302 -0
  125. dataface/core/compile/filter_injection.py +319 -0
  126. dataface/core/compile/introspection.py +527 -0
  127. dataface/core/compile/jinja.py +511 -0
  128. dataface/core/compile/labels_env.py +52 -0
  129. dataface/core/compile/markdown.py +154 -0
  130. dataface/core/compile/meta.py +388 -0
  131. dataface/core/compile/models/__init__.py +0 -0
  132. dataface/core/compile/models/chart/__init__.py +0 -0
  133. dataface/core/compile/models/chart/authored.py +2137 -0
  134. dataface/core/compile/models/chart/compiled.py +398 -0
  135. dataface/core/compile/models/config.py +347 -0
  136. dataface/core/compile/models/face/__init__.py +0 -0
  137. dataface/core/compile/models/face/authored.py +659 -0
  138. dataface/core/compile/models/face/compiled.py +522 -0
  139. dataface/core/compile/models/factories.py +201 -0
  140. dataface/core/compile/models/markers.py +40 -0
  141. dataface/core/compile/models/palette.py +36 -0
  142. dataface/core/compile/models/primitives.py +415 -0
  143. dataface/core/compile/models/query/__init__.py +0 -0
  144. dataface/core/compile/models/query/authored.py +246 -0
  145. dataface/core/compile/models/query/compiled.py +710 -0
  146. dataface/core/compile/models/refs.py +137 -0
  147. dataface/core/compile/models/source.py +611 -0
  148. dataface/core/compile/models/style/__init__.py +0 -0
  149. dataface/core/compile/models/style/authored.py +481 -0
  150. dataface/core/compile/models/style/compiled.py +3399 -0
  151. dataface/core/compile/models/style/merged.py +1682 -0
  152. dataface/core/compile/models/theme.py +362 -0
  153. dataface/core/compile/models/variable/__init__.py +0 -0
  154. dataface/core/compile/models/variable/authored.py +254 -0
  155. dataface/core/compile/models/vega_lite/__init__.py +0 -0
  156. dataface/core/compile/models/vega_lite/config.py +510 -0
  157. dataface/core/compile/models/vega_lite/contracts.py +171 -0
  158. dataface/core/compile/normalize_charts.py +494 -0
  159. dataface/core/compile/normalize_layout.py +1000 -0
  160. dataface/core/compile/normalize_queries.py +297 -0
  161. dataface/core/compile/normalize_variables.py +489 -0
  162. dataface/core/compile/normalizer.py +543 -0
  163. dataface/core/compile/palette.py +1100 -0
  164. dataface/core/compile/parameterized.py +658 -0
  165. dataface/core/compile/parser.py +228 -0
  166. dataface/core/compile/schema.py +20 -0
  167. dataface/core/compile/schema_renderers/__init__.py +0 -0
  168. dataface/core/compile/schema_renderers/json_schema.py +163 -0
  169. dataface/core/compile/schema_renderers/prompt.py +152 -0
  170. dataface/core/compile/schema_renderers/vscode_schema.py +301 -0
  171. dataface/core/compile/sizing.py +2126 -0
  172. dataface/core/compile/sources.py +518 -0
  173. dataface/core/compile/sql_authoring_lint.py +56 -0
  174. dataface/core/compile/style_cascade.py +471 -0
  175. dataface/core/compile/typography.py +299 -0
  176. dataface/core/compile/validator.py +301 -0
  177. dataface/core/compile/variables.py +53 -0
  178. dataface/core/compile/vega_config.py +98 -0
  179. dataface/core/compile/vega_lite/__init__.py +6 -0
  180. dataface/core/compile/vega_lite/validation.py +95 -0
  181. dataface/core/compile/yaml_error_formatter.py +838 -0
  182. dataface/core/connections.py +38 -0
  183. dataface/core/dashboard.py +358 -0
  184. dataface/core/defaults/default_config.yml +101 -0
  185. dataface/core/defaults/palettes/categorical/category-10-dark.yml +32 -0
  186. dataface/core/defaults/palettes/categorical/category-10-light.yml +43 -0
  187. dataface/core/defaults/palettes/categorical/category-10.yml +31 -0
  188. dataface/core/defaults/palettes/categorical/category-6-tonal-blue.yml +22 -0
  189. dataface/core/defaults/palettes/categorical/category-6-tonal-brown.yml +29 -0
  190. dataface/core/defaults/palettes/categorical/category-6-tonal-green.yml +20 -0
  191. dataface/core/defaults/palettes/categorical/category-6-tonal-orange.yml +21 -0
  192. dataface/core/defaults/palettes/categorical/category-6-tonal-purple.yml +20 -0
  193. dataface/core/defaults/palettes/categorical/editorial-10-dark.yml +32 -0
  194. dataface/core/defaults/palettes/categorical/editorial-10.yml +40 -0
  195. dataface/core/defaults/palettes/categorical/hero-6.yml +17 -0
  196. dataface/core/defaults/palettes/categorical/single-blue.yml +11 -0
  197. dataface/core/defaults/palettes/categorical/tableau.yml +20 -0
  198. dataface/core/defaults/palettes/data/xkcd_colors.json +3803 -0
  199. dataface/core/defaults/palettes/diverging/blue-red.yml +25 -0
  200. dataface/core/defaults/palettes/diverging/coolwarm.yml +24 -0
  201. dataface/core/defaults/palettes/diverging/crimson-green.yml +23 -0
  202. dataface/core/defaults/palettes/diverging/orange-teal.yml +23 -0
  203. dataface/core/defaults/palettes/diverging/sunset.yml +24 -0
  204. dataface/core/defaults/palettes/scaffold/dft-creams.yml +38 -0
  205. dataface/core/defaults/palettes/scaffold/dft-grays.yml +53 -0
  206. dataface/core/defaults/palettes/sequential/amber.yml +22 -0
  207. dataface/core/defaults/palettes/sequential/blue.yml +22 -0
  208. dataface/core/defaults/palettes/sequential/brown.yml +22 -0
  209. dataface/core/defaults/palettes/sequential/gray.yml +22 -0
  210. dataface/core/defaults/palettes/sequential/green.yml +22 -0
  211. dataface/core/defaults/palettes/sequential/purple.yml +22 -0
  212. dataface/core/defaults/palettes/sequential/rust.yml +22 -0
  213. dataface/core/defaults/palettes/sequential/teal.yml +22 -0
  214. dataface/core/defaults/palettes/tone/negative.yml +32 -0
  215. dataface/core/defaults/palettes/tone/positive.yml +22 -0
  216. dataface/core/defaults/palettes/tone/warning.yml +22 -0
  217. dataface/core/defaults/themes/_base.yaml +786 -0
  218. dataface/core/defaults/themes/bi.yaml +16 -0
  219. dataface/core/defaults/themes/carbong100.yaml +41 -0
  220. dataface/core/defaults/themes/cream.yaml +122 -0
  221. dataface/core/defaults/themes/dark.yaml +40 -0
  222. dataface/core/defaults/themes/diagnostics-title-angle-extreme.yaml +9 -0
  223. dataface/core/defaults/themes/diagnostics-title-baseline-extreme.yaml +9 -0
  224. dataface/core/defaults/themes/diagnostics-title-baseline.yaml +24 -0
  225. dataface/core/defaults/themes/diagnostics-title-center.yaml +8 -0
  226. dataface/core/defaults/themes/diagnostics-title-color-extreme.yaml +24 -0
  227. dataface/core/defaults/themes/diagnostics-title-font-extreme.yaml +25 -0
  228. dataface/core/defaults/themes/diagnostics-title-left.yaml +8 -0
  229. dataface/core/defaults/themes/diagnostics-title-offset-extreme.yaml +9 -0
  230. dataface/core/defaults/themes/diagnostics-title-size-extreme.yaml +24 -0
  231. dataface/core/defaults/themes/diagnostics-title-weight-extreme.yaml +24 -0
  232. dataface/core/defaults/themes/editorial.yaml +147 -0
  233. dataface/core/defaults/themes/light.yaml +30 -0
  234. dataface/core/defaults/themes/looker.yaml +17 -0
  235. dataface/core/defaults/themes/stark.yaml +134 -0
  236. dataface/core/errors/__init__.py +67 -0
  237. dataface/core/errors/codes_compile.py +56 -0
  238. dataface/core/errors/codes_execute.py +177 -0
  239. dataface/core/errors/codes_render.py +106 -0
  240. dataface/core/errors/codes_unknown.py +15 -0
  241. dataface/core/errors/hints.py +74 -0
  242. dataface/core/errors/registry.py +42 -0
  243. dataface/core/errors/structured.py +92 -0
  244. dataface/core/execute/__init__.py +91 -0
  245. dataface/core/execute/adapters/__init__.py +49 -0
  246. dataface/core/execute/adapters/adapter_registry.py +400 -0
  247. dataface/core/execute/adapters/base.py +245 -0
  248. dataface/core/execute/adapters/csv_adapter.py +239 -0
  249. dataface/core/execute/adapters/dbt_adapter.py +283 -0
  250. dataface/core/execute/adapters/dbt_adapter_factory.py +212 -0
  251. dataface/core/execute/adapters/dbt_macro_loader.py +95 -0
  252. dataface/core/execute/adapters/dbt_utils.py +150 -0
  253. dataface/core/execute/adapters/http_adapter.py +224 -0
  254. dataface/core/execute/adapters/metricflow_adapter.py +94 -0
  255. dataface/core/execute/adapters/schema_resolver_adapter.py +144 -0
  256. dataface/core/execute/adapters/sql_adapter.py +710 -0
  257. dataface/core/execute/adapters/values_adapter.py +58 -0
  258. dataface/core/execute/batch.py +744 -0
  259. dataface/core/execute/cache_backend.py +135 -0
  260. dataface/core/execute/cache_keys.py +66 -0
  261. dataface/core/execute/dbt_jinja.py +21 -0
  262. dataface/core/execute/dialects/__init__.py +121 -0
  263. dataface/core/execute/dialects/athena.py +75 -0
  264. dataface/core/execute/dialects/base.py +302 -0
  265. dataface/core/execute/dialects/bigquery.py +38 -0
  266. dataface/core/execute/dialects/databricks.py +68 -0
  267. dataface/core/execute/dialects/duckdb.py +35 -0
  268. dataface/core/execute/dialects/mysql.py +68 -0
  269. dataface/core/execute/dialects/postgres.py +39 -0
  270. dataface/core/execute/dialects/redshift.py +12 -0
  271. dataface/core/execute/dialects/snowflake.py +51 -0
  272. dataface/core/execute/dialects/sqlserver.py +92 -0
  273. dataface/core/execute/duckdb_cache.py +712 -0
  274. dataface/core/execute/duckdb_config.py +26 -0
  275. dataface/core/execute/errors.py +213 -0
  276. dataface/core/execute/executor.py +1249 -0
  277. dataface/core/execute/parallel.py +162 -0
  278. dataface/core/execute/setup_sql.py +58 -0
  279. dataface/core/execute/source_registry.py +72 -0
  280. dataface/core/execute/source_resolver.py +255 -0
  281. dataface/core/execute/sql_guard.py +387 -0
  282. dataface/core/execute/sql_literals.py +199 -0
  283. dataface/core/fonts.py +52 -0
  284. dataface/core/inspect/__init__.py +32 -0
  285. dataface/core/inspect/cache_factory.py +98 -0
  286. dataface/core/inspect/db_types.py +162 -0
  287. dataface/core/inspect/dbt_schema.py +96 -0
  288. dataface/core/inspect/defaults.yml +37 -0
  289. dataface/core/inspect/fanout_risk.py +109 -0
  290. dataface/core/inspect/manifest_utils.py +77 -0
  291. dataface/core/inspect/partials/categorical.yml +40 -0
  292. dataface/core/inspect/partials/date.yml +40 -0
  293. dataface/core/inspect/partials/numeric.yml +55 -0
  294. dataface/core/inspect/partition_types.py +38 -0
  295. dataface/core/inspect/query_validator.py +975 -0
  296. dataface/core/inspect/renderer.py +354 -0
  297. dataface/core/inspect/resolver.py +808 -0
  298. dataface/core/inspect/search.py +461 -0
  299. dataface/core/inspect/sources/__init__.py +32 -0
  300. dataface/core/inspect/sources/dbt.py +738 -0
  301. dataface/core/inspect/sources/duckdb_utils.py +66 -0
  302. dataface/core/inspect/templates/__init__.py +1 -0
  303. dataface/core/inspect/templates/categorical_column.yml +196 -0
  304. dataface/core/inspect/templates/charts.yml +109 -0
  305. dataface/core/inspect/templates/date_column.yml +248 -0
  306. dataface/core/inspect/templates/model.yml +138 -0
  307. dataface/core/inspect/templates/numeric_column.yml +261 -0
  308. dataface/core/inspect/templates/quality.yml +80 -0
  309. dataface/core/inspect/templates/string_column.yml +263 -0
  310. dataface/core/project_roots.py +165 -0
  311. dataface/core/render/__init__.py +87 -0
  312. dataface/core/render/board_links.py +176 -0
  313. dataface/core/render/chart/__init__.py +27 -0
  314. dataface/core/render/chart/arc_attached_table.py +251 -0
  315. dataface/core/render/chart/artifacts.py +16 -0
  316. dataface/core/render/chart/callout.py +225 -0
  317. dataface/core/render/chart/decisions.py +358 -0
  318. dataface/core/render/chart/geo.py +700 -0
  319. dataface/core/render/chart/kpi.py +916 -0
  320. dataface/core/render/chart/labels.py +76 -0
  321. dataface/core/render/chart/pipeline.py +818 -0
  322. dataface/core/render/chart/presentation.py +36 -0
  323. dataface/core/render/chart/profile.py +3438 -0
  324. dataface/core/render/chart/render_single.py +347 -0
  325. dataface/core/render/chart/renderers.py +193 -0
  326. dataface/core/render/chart/rendering.py +565 -0
  327. dataface/core/render/chart/serialization.py +90 -0
  328. dataface/core/render/chart/spark.py +496 -0
  329. dataface/core/render/chart/spark_bar.py +370 -0
  330. dataface/core/render/chart/spec_builders.py +154 -0
  331. dataface/core/render/chart/standard_renderer.py +2645 -0
  332. dataface/core/render/chart/table.py +2957 -0
  333. dataface/core/render/chart/table_support.py +1452 -0
  334. dataface/core/render/chart/tick_values.py +66 -0
  335. dataface/core/render/chart/time_unit_detect.py +809 -0
  336. dataface/core/render/chart/title_overflow.py +157 -0
  337. dataface/core/render/chart/type_inference.py +122 -0
  338. dataface/core/render/chart/validation.py +99 -0
  339. dataface/core/render/chart/vega_lite.py +125 -0
  340. dataface/core/render/chart/vega_lite_types.py +268 -0
  341. dataface/core/render/chart/vl_field_maps.py +346 -0
  342. dataface/core/render/chart_interactivity.py +24 -0
  343. dataface/core/render/control_registry.py +287 -0
  344. dataface/core/render/converters/__init__.py +24 -0
  345. dataface/core/render/converters/chart.py +276 -0
  346. dataface/core/render/converters/html.py +98 -0
  347. dataface/core/render/converters/pdf.py +40 -0
  348. dataface/core/render/converters/png.py +41 -0
  349. dataface/core/render/errors.py +144 -0
  350. dataface/core/render/face_api.py +160 -0
  351. dataface/core/render/faces.py +1194 -0
  352. dataface/core/render/font_measurement.py +48 -0
  353. dataface/core/render/font_support.py +197 -0
  354. dataface/core/render/fonts/DFTSansTabular-Regular.ttf +0 -0
  355. dataface/core/render/fonts/DFTSansTabular-Regular.woff2 +0 -0
  356. dataface/core/render/fonts/DFTSerifOldstyleProportional-Regular.ttf +0 -0
  357. dataface/core/render/fonts/DFTSerifOldstyleTabular-Regular.ttf +0 -0
  358. dataface/core/render/fonts/InterVariable.ttf +0 -0
  359. dataface/core/render/fonts/InterVariable.woff2 +0 -0
  360. dataface/core/render/fonts/NOTO_COLOR_EMOJI_LICENSE.txt +93 -0
  361. dataface/core/render/fonts/NOTO_EMOJI_LICENSE.txt +93 -0
  362. dataface/core/render/fonts/NotoColorEmoji-Regular.ttf +0 -0
  363. dataface/core/render/fonts/NotoColorEmoji-Regular.woff2 +0 -0
  364. dataface/core/render/fonts/NotoEmoji-Regular.ttf +0 -0
  365. dataface/core/render/fonts/NotoEmoji-Regular.woff2 +0 -0
  366. dataface/core/render/fonts/SOURCE_CODE_PRO_LICENSE.txt +93 -0
  367. dataface/core/render/fonts/SOURCE_SERIF_4_LICENSE.txt +98 -0
  368. dataface/core/render/fonts/SourceCodePro-Regular.ttf +0 -0
  369. dataface/core/render/fonts/SourceSerif4-Regular.ttf +0 -0
  370. dataface/core/render/fonts/_emoji_font_face.css +43 -0
  371. dataface/core/render/fonts/source-serif-4-variable-latin.woff2 +0 -0
  372. dataface/core/render/format_utils.py +329 -0
  373. dataface/core/render/geo_defaults.yml +28 -0
  374. dataface/core/render/json_format.py +146 -0
  375. dataface/core/render/layout_sizing.py +865 -0
  376. dataface/core/render/layouts.py +541 -0
  377. dataface/core/render/markdown_defaults.yml +16 -0
  378. dataface/core/render/missing_vars_prompt.py +79 -0
  379. dataface/core/render/placeholder.py +389 -0
  380. dataface/core/render/render_result.py +14 -0
  381. dataface/core/render/renderer.py +467 -0
  382. dataface/core/render/script_embedding.py +16 -0
  383. dataface/core/render/svg_utils.py +212 -0
  384. dataface/core/render/template_loader.py +69 -0
  385. dataface/core/render/templates/controls/_styles.css +606 -0
  386. dataface/core/render/templates/controls/checkbox.html +16 -0
  387. dataface/core/render/templates/controls/date.html +16 -0
  388. dataface/core/render/templates/controls/number.html +19 -0
  389. dataface/core/render/templates/controls/readonly.html +9 -0
  390. dataface/core/render/templates/controls/select.html +21 -0
  391. dataface/core/render/templates/controls/slider.html +22 -0
  392. dataface/core/render/templates/controls/text.html +16 -0
  393. dataface/core/render/templates/scripts/chart_interactivity.js +191 -0
  394. dataface/core/render/templates/scripts/variables.js +976 -0
  395. dataface/core/render/templates/svg/grid_pattern.svg +3 -0
  396. dataface/core/render/templates/svg/styles.css +51 -0
  397. dataface/core/render/terminal.py +311 -0
  398. dataface/core/render/terminal_charts.py +563 -0
  399. dataface/core/render/terminal_defaults.yml +2 -0
  400. dataface/core/render/terminal_layouts.py +299 -0
  401. dataface/core/render/terminal_text.py +31 -0
  402. dataface/core/render/text/__init__.py +1 -0
  403. dataface/core/render/text/case.py +113 -0
  404. dataface/core/render/text_format.py +129 -0
  405. dataface/core/render/utils.py +106 -0
  406. dataface/core/render/variable_controls.py +946 -0
  407. dataface/core/render/variable_input_refinement.py +140 -0
  408. dataface/core/render/warnings/__init__.py +15 -0
  409. dataface/core/render/warnings/bar_color_1_to_1_with_x.py +80 -0
  410. dataface/core/render/warnings/base.py +44 -0
  411. dataface/core/render/warnings/fanout_risk.py +15 -0
  412. dataface/core/render/warnings/from_query_diagnostic.py +56 -0
  413. dataface/core/render/warnings/missing_join_predicate.py +13 -0
  414. dataface/core/render/warnings/query_parse_error.py +14 -0
  415. dataface/core/render/warnings/query_returned_zero_rows.py +42 -0
  416. dataface/core/render/warnings/reaggregation.py +14 -0
  417. dataface/core/render/warnings/registry.py +45 -0
  418. dataface/core/render/warnings/suppression.py +46 -0
  419. dataface/core/render/warnings/temporal_single_point.py +63 -0
  420. dataface/core/render/warnings/unreferenced_chart.py +15 -0
  421. dataface/core/render/warnings/y_encoding_mostly_null.py +76 -0
  422. dataface/core/render/yaml_format.py +167 -0
  423. dataface/core/resolve_face.py +195 -0
  424. dataface/core/schema/__init__.py +0 -0
  425. dataface/core/schema/guidance.py +151 -0
  426. dataface/core/scoped_paths.py +59 -0
  427. dataface/core/serve/__init__.py +14 -0
  428. dataface/core/serve/bootstrap.py +39 -0
  429. dataface/core/serve/embedded.py +57 -0
  430. dataface/core/serve/port.py +129 -0
  431. dataface/core/serve/server.py +938 -0
  432. dataface/core/serve/templates/__init__.py +0 -0
  433. dataface/core/serve/templates/directory.yml +6 -0
  434. dataface/core/serve/templates/error.html.j2 +217 -0
  435. dataface/core/utils.py +121 -0
  436. dataface/core/validate.py +64 -0
  437. dataface/integrations/__init__.py +0 -0
  438. dataface/integrations/highlighting.py +351 -0
  439. dataface/integrations/markdown.py +537 -0
  440. dataface/py.typed +0 -0
  441. dataface-0.1.2.dist-info/METADATA +375 -0
  442. dataface-0.1.2.dist-info/RECORD +455 -0
  443. dataface-0.1.2.dist-info/WHEEL +4 -0
  444. dataface-0.1.2.dist-info/entry_points.txt +2 -0
  445. dataface-0.1.2.dist-info/licenses/LICENSE +202 -0
  446. mdsvg/__init__.py +168 -0
  447. mdsvg/fonts.py +656 -0
  448. mdsvg/images.py +299 -0
  449. mdsvg/parser.py +629 -0
  450. mdsvg/playground.py +284 -0
  451. mdsvg/py.typed +2 -0
  452. mdsvg/renderer.py +1623 -0
  453. mdsvg/style.py +355 -0
  454. mdsvg/types.py +200 -0
  455. mdsvg/utils.py +86 -0
@@ -0,0 +1,66 @@
1
+ """DuckDB-specific helpers for schema inspection.
2
+
3
+ When a DuckDB adapter connects to a file (e.g. ``dundersign.duckdb``),
4
+ the database is attached under a non-system name (e.g. ``dundersign``).
5
+ Passing ``None`` to ``adapter.list_relations(None, schema)`` returns an
6
+ empty list for file-backed databases — the database argument must be the
7
+ actual attach name.
8
+
9
+ ``PRAGMA database_list`` is the authoritative source: each row is
10
+ ``(seq, name, file)``. We skip the two system databases (``system`` and
11
+ ``temp``); the remaining row is the user's attached database. For
12
+ ``path=None`` or ``path=':memory:'`` the attach name is always ``memory``.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from pathlib import Path
18
+ from typing import Any
19
+
20
+
21
+ def duckdb_resolve_database(adapter: Any, db_path: str | Path | None) -> str:
22
+ """Return the DuckDB attach name that ``list_relations`` must receive.
23
+
24
+ Reads ``PRAGMA database_list`` from the already-open adapter connection.
25
+ The caller must be inside a ``connection_named`` context.
26
+
27
+ ``db_path`` is the configured file path (or ``None`` / ``':memory:'`` for
28
+ in-memory). When the path is in-memory we short-circuit and return
29
+ ``'memory'`` without a PRAGMA round-trip.
30
+
31
+ For file-backed databases we prefer the row whose ``file`` column matches
32
+ the absolute ``db_path``; if no row matches (e.g. path is relative or
33
+ symlinked) we fall back to the first non-system entry.
34
+
35
+ Raises ``ValueError`` if no non-system database is found in the pragma
36
+ output — this would indicate a DuckDB connection without an attached
37
+ database, which should be impossible for a live adapter.
38
+ """
39
+ path_str = str(db_path) if db_path is not None else None
40
+ if path_str is None or path_str == ":memory:":
41
+ return "memory"
42
+
43
+ _, result = adapter.execute("PRAGMA database_list", fetch=True)
44
+ # Rows are (seq, name, file). Column order is stable in DuckDB.
45
+ # System databases to skip: 'system' and 'temp'.
46
+ _SKIP = frozenset({"system", "temp"})
47
+
48
+ abs_path = str(Path(path_str).resolve())
49
+ candidates: list[str] = []
50
+ for row in result.rows:
51
+ name = str(row[1])
52
+ file_col = str(row[2])
53
+ if name in _SKIP:
54
+ continue
55
+ # Prefer exact match on resolved absolute path.
56
+ if file_col == abs_path:
57
+ return name
58
+ candidates.append(name)
59
+
60
+ if candidates:
61
+ return candidates[0]
62
+
63
+ raise ValueError(
64
+ f"PRAGMA database_list returned no non-system database for path {path_str!r}. "
65
+ "This is unexpected for a live DuckDB file-backed adapter."
66
+ )
@@ -0,0 +1 @@
1
+ # Profile dashboard templates
@@ -0,0 +1,196 @@
1
+ # Categorical Column Profile Template
2
+ #
3
+ # Analysis of a categorical column with low cardinality. Schema panel
4
+ # pulls from the LayeredSchemaResolver; live aggregations stay on
5
+ # warehouse SQL.
6
+ #
7
+ # URL: /inspect/categorical_column/?model=<table>&column=<col>
8
+ #
9
+ # Required variables:
10
+ # - model: Table/model name
11
+ # - column: Column name to analyze
12
+ #
13
+ # Optional variables:
14
+ # - connection: DuckDB file path or :memory: (default: :memory:)
15
+ #
16
+ # Theme is set via DFT_DEFAULT_THEME env var at serve startup.
17
+
18
+ title: "Categorical: {{ model }}.{{ column }}"
19
+
20
+ text: |
21
+ [Back to {{ model }}](/inspect/model/?model={{ model }}&connection={{ connection }})
22
+
23
+ variables:
24
+ model:
25
+ input: text
26
+ visible: false
27
+ default: ""
28
+ column:
29
+ input: text
30
+ visible: false
31
+ default: ""
32
+ connection:
33
+ input: text
34
+ visible: false
35
+ default: ":memory:"
36
+ source_name:
37
+ input: text
38
+ visible: false
39
+ default: ""
40
+ schema_name:
41
+ input: text
42
+ visible: false
43
+ default: ""
44
+
45
+ queries:
46
+ column_schema:
47
+ type: schema_resolver
48
+ source: "{{ source_name }}"
49
+ schema: "{{ schema_name }}"
50
+ table: "{{ model }}"
51
+ column: "{{ column }}"
52
+
53
+ # Basic category stats
54
+ stats:
55
+ source:
56
+ type: duckdb
57
+ path: "{{ connection }}"
58
+ sql: |
59
+ SELECT
60
+ COUNT(*) as total_count,
61
+ COUNT({{ column }}) as non_null_count,
62
+ COUNT(*) - COUNT({{ column }}) as null_count,
63
+ ROUND(100.0 * (COUNT(*) - COUNT({{ column }})) / COUNT(*), 2) as null_pct,
64
+ COUNT(DISTINCT {{ column }}) as category_count
65
+ FROM {{ model }}
66
+
67
+ # Category distribution
68
+ categories:
69
+ source:
70
+ type: duckdb
71
+ path: "{{ connection }}"
72
+ sql: |
73
+ SELECT
74
+ COALESCE(CAST({{ column }} AS VARCHAR), '(null)') as category,
75
+ COUNT(*) as count,
76
+ ROUND(100.0 * COUNT(*) / (SELECT COUNT(*) FROM {{ model }}), 2) as pct
77
+ FROM {{ model }}
78
+ GROUP BY {{ column }}
79
+ ORDER BY COUNT(*) DESC
80
+
81
+ # Category distribution for pie chart (top 10 + other)
82
+ categories_pie:
83
+ source:
84
+ type: duckdb
85
+ path: "{{ connection }}"
86
+ sql: |
87
+ WITH ranked AS (
88
+ SELECT
89
+ COALESCE(CAST({{ column }} AS VARCHAR), '(null)') as category,
90
+ COUNT(*) as count,
91
+ ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC) as rn
92
+ FROM {{ model }}
93
+ GROUP BY {{ column }}
94
+ )
95
+ SELECT
96
+ CASE WHEN rn <= 10 THEN category ELSE 'Other' END as category,
97
+ SUM(count) as count
98
+ FROM ranked
99
+ GROUP BY CASE WHEN rn <= 10 THEN category ELSE 'Other' END
100
+ ORDER BY SUM(count) DESC
101
+
102
+ charts:
103
+ schema_panel:
104
+ title: Schema
105
+ type: table
106
+ query: column_schema
107
+ style:
108
+ header_overflow: wrap-two
109
+ columns:
110
+ name:
111
+ label: Column
112
+ type:
113
+ label: Type
114
+ role:
115
+ label: Role
116
+ semantic_type:
117
+ label: Semantic
118
+ description:
119
+ label: Description
120
+
121
+ # Stats KPIs
122
+ total:
123
+ label: Total Records
124
+ type: kpi
125
+ query: stats
126
+ value: total_count
127
+ format: ",.0f"
128
+
129
+ categories:
130
+ label: Categories
131
+ type: kpi
132
+ query: stats
133
+ value: category_count
134
+ format: ",.0f"
135
+
136
+ null_pct:
137
+ label: "Null %"
138
+ type: kpi
139
+ query: stats
140
+ value: null_pct
141
+ format: ".1f"
142
+
143
+ # Pie chart
144
+ pie_chart:
145
+ title: Category Distribution
146
+ type: pie
147
+ query: categories_pie
148
+ theta: count
149
+ color: category
150
+
151
+ # Spark bar (compact distribution for profiler cards)
152
+ spark_bar_chart:
153
+ title: Distribution
154
+ type: spark_bar
155
+ query: categories
156
+ x: count
157
+ y: category
158
+
159
+ # Bar chart
160
+ bar_chart:
161
+ title: Category Counts
162
+ type: bar
163
+ query: categories
164
+ x: category
165
+ y: count
166
+ style:
167
+ orientation: horizontal
168
+
169
+ # Category table
170
+ category_table:
171
+ title: All Categories
172
+ type: table
173
+ query: categories
174
+ style:
175
+ header_overflow: wrap-two
176
+ columns:
177
+ category:
178
+ label: Category
179
+ count:
180
+ label: Count
181
+ format: ",.0f"
182
+ pct:
183
+ label: "%"
184
+ format: ".1f"
185
+
186
+ rows:
187
+ - schema_panel
188
+ - cols:
189
+ - total
190
+ - categories
191
+ - null_pct
192
+ - cols:
193
+ - pie_chart
194
+ - spark_bar_chart
195
+ - bar_chart
196
+ - category_table
@@ -0,0 +1,109 @@
1
+ # Auto-Generated Charts Template
2
+ #
3
+ # Dynamically generates appropriate visualizations for each column type using
4
+ # the foreach construct and board file imports.
5
+ #
6
+ # URL: /inspect/charts/?model=<table>
7
+ #
8
+ # Required variables:
9
+ # - model: Table/model name
10
+ #
11
+ # Optional variables:
12
+ # - connection: DuckDB file path or :memory: (default: :memory:)
13
+ #
14
+ # This template demonstrates the foreach + board imports pattern:
15
+ # 1. Queries fetch column metadata grouped by type
16
+ # 2. foreach iterates over each column
17
+ # 3. Board imports load type-specific partials (numeric.yml, date.yml, etc.)
18
+ # 4. Partials inherit parent variables (model, column, connection)
19
+ #
20
+ # Theme is set via DFT_DEFAULT_THEME env var at serve startup.
21
+
22
+ title: "Charts: {{ model }}"
23
+
24
+
25
+ variables:
26
+ model:
27
+ input: text
28
+ visible: false
29
+ default: ""
30
+ connection:
31
+ input: text
32
+ visible: false
33
+ default: ":memory:"
34
+
35
+ queries:
36
+ # Get numeric columns for histograms
37
+ numeric_columns:
38
+ source:
39
+ type: duckdb
40
+ path: "{{ connection }}"
41
+ sql: |
42
+ SELECT column_name as column, 'numeric' as type
43
+ FROM information_schema.columns
44
+ WHERE table_name = '{{ model }}'
45
+ AND data_type IN ('INTEGER', 'BIGINT', 'SMALLINT', 'TINYINT',
46
+ 'DOUBLE', 'FLOAT', 'REAL', 'DECIMAL', 'NUMERIC')
47
+ ORDER BY ordinal_position
48
+ # Static data for compile-time iteration (used by foreach)
49
+ static_data: []
50
+
51
+ # Get date columns for time series
52
+ date_columns:
53
+ source:
54
+ type: duckdb
55
+ path: "{{ connection }}"
56
+ sql: |
57
+ SELECT column_name as column, 'date' as type
58
+ FROM information_schema.columns
59
+ WHERE table_name = '{{ model }}'
60
+ AND data_type IN ('DATE', 'TIMESTAMP', 'DATETIME', 'TIMESTAMPTZ')
61
+ ORDER BY ordinal_position
62
+ static_data: []
63
+
64
+ # Get categorical columns for bar charts
65
+ categorical_columns:
66
+ source:
67
+ type: duckdb
68
+ path: "{{ connection }}"
69
+ sql: |
70
+ SELECT column_name as column, 'categorical' as type
71
+ FROM information_schema.columns
72
+ WHERE table_name = '{{ model }}'
73
+ AND data_type IN ('VARCHAR', 'CHAR', 'TEXT', 'STRING', 'BOOLEAN')
74
+ ORDER BY ordinal_position
75
+ static_data: []
76
+
77
+ rows:
78
+ # Numeric column histograms
79
+ - title: "Numeric Columns"
80
+ text: |
81
+ Distribution histograms for numeric columns.
82
+ rows:
83
+ - foreach:
84
+ query: numeric_columns
85
+ as: col
86
+ items:
87
+ - ../partials/numeric.yml
88
+
89
+ # Date column time series
90
+ - title: "Date Columns"
91
+ text: |
92
+ Time series for date/timestamp columns.
93
+ rows:
94
+ - foreach:
95
+ query: date_columns
96
+ as: col
97
+ items:
98
+ - ../partials/date.yml
99
+
100
+ # Categorical column bar charts
101
+ - title: "Categorical Columns"
102
+ text: |
103
+ Distribution for categorical columns.
104
+ rows:
105
+ - foreach:
106
+ query: categorical_columns
107
+ as: col
108
+ items:
109
+ - ../partials/categorical.yml
@@ -0,0 +1,248 @@
1
+ # Date Column Profile Template
2
+ #
3
+ # Analysis of a date/timestamp column. Schema panel pulls from the
4
+ # LayeredSchemaResolver; live time-series, gap, and distribution panels
5
+ # stay on warehouse SQL.
6
+ #
7
+ # URL: /inspect/date_column/?model=<table>&column=<col>
8
+ #
9
+ # Required variables:
10
+ # - model: Table/model name
11
+ # - column: Column name to analyze
12
+ #
13
+ # Optional variables:
14
+ # - connection: DuckDB file path or :memory: (default: :memory:)
15
+ #
16
+ # Theme is set via DFT_DEFAULT_THEME env var at serve startup.
17
+
18
+ title: "Date Column: {{ model }}.{{ column }}"
19
+
20
+ text: |
21
+ [Back to {{ model }}](/inspect/model/?model={{ model }}&connection={{ connection }})
22
+
23
+ variables:
24
+ model:
25
+ input: text
26
+ visible: false
27
+ default: ""
28
+ column:
29
+ input: text
30
+ visible: false
31
+ default: ""
32
+ connection:
33
+ input: text
34
+ visible: false
35
+ default: ":memory:"
36
+ source_name:
37
+ input: text
38
+ visible: false
39
+ default: ""
40
+ schema_name:
41
+ input: text
42
+ visible: false
43
+ default: ""
44
+
45
+ queries:
46
+ column_schema:
47
+ type: schema_resolver
48
+ source: "{{ source_name }}"
49
+ schema: "{{ schema_name }}"
50
+ table: "{{ model }}"
51
+ column: "{{ column }}"
52
+
53
+ # Basic date stats
54
+ stats:
55
+ source:
56
+ type: duckdb
57
+ path: "{{ connection }}"
58
+ sql: |
59
+ SELECT
60
+ COUNT(*) as total_count,
61
+ COUNT({{ column }}) as non_null_count,
62
+ COUNT(*) - COUNT({{ column }}) as null_count,
63
+ ROUND(100.0 * (COUNT(*) - COUNT({{ column }})) / COUNT(*), 2) as null_pct,
64
+ MIN({{ column }})::DATE as min_date,
65
+ MAX({{ column }})::DATE as max_date,
66
+ COUNT(DISTINCT {{ column }}::DATE) as distinct_dates,
67
+ MAX({{ column }})::DATE - MIN({{ column }})::DATE as date_span_days
68
+ FROM {{ model }}
69
+
70
+ # Records per day
71
+ daily_counts:
72
+ source:
73
+ type: duckdb
74
+ path: "{{ connection }}"
75
+ sql: |
76
+ SELECT
77
+ {{ column }}::DATE as date,
78
+ COUNT(*) as count
79
+ FROM {{ model }}
80
+ WHERE {{ column }} IS NOT NULL
81
+ GROUP BY {{ column }}::DATE
82
+ ORDER BY date
83
+
84
+ # Day of week distribution
85
+ dow_distribution:
86
+ source:
87
+ type: duckdb
88
+ path: "{{ connection }}"
89
+ sql: |
90
+ SELECT
91
+ CASE DAYOFWEEK({{ column }})
92
+ WHEN 0 THEN 'Sunday'
93
+ WHEN 1 THEN 'Monday'
94
+ WHEN 2 THEN 'Tuesday'
95
+ WHEN 3 THEN 'Wednesday'
96
+ WHEN 4 THEN 'Thursday'
97
+ WHEN 5 THEN 'Friday'
98
+ WHEN 6 THEN 'Saturday'
99
+ END as day_of_week,
100
+ DAYOFWEEK({{ column }}) as dow_num,
101
+ COUNT(*) as count
102
+ FROM {{ model }}
103
+ WHERE {{ column }} IS NOT NULL
104
+ GROUP BY DAYOFWEEK({{ column }})
105
+ ORDER BY DAYOFWEEK({{ column }})
106
+
107
+ # Monthly distribution
108
+ monthly_counts:
109
+ source:
110
+ type: duckdb
111
+ path: "{{ connection }}"
112
+ sql: |
113
+ SELECT
114
+ DATE_TRUNC('month', {{ column }})::DATE as month,
115
+ COUNT(*) as count
116
+ FROM {{ model }}
117
+ WHERE {{ column }} IS NOT NULL
118
+ GROUP BY DATE_TRUNC('month', {{ column }})
119
+ ORDER BY month
120
+
121
+ # Date gaps (missing dates)
122
+ date_gaps:
123
+ source:
124
+ type: duckdb
125
+ path: "{{ connection }}"
126
+ sql: |
127
+ WITH date_range AS (
128
+ SELECT GENERATE_SERIES(
129
+ MIN({{ column }})::DATE,
130
+ MAX({{ column }})::DATE,
131
+ INTERVAL '1 day'
132
+ )::DATE as expected_date
133
+ FROM {{ model }}
134
+ WHERE {{ column }} IS NOT NULL
135
+ ),
136
+ actual_dates AS (
137
+ SELECT DISTINCT {{ column }}::DATE as actual_date
138
+ FROM {{ model }}
139
+ WHERE {{ column }} IS NOT NULL
140
+ )
141
+ SELECT
142
+ expected_date as missing_date
143
+ FROM date_range
144
+ WHERE expected_date NOT IN (SELECT actual_date FROM actual_dates)
145
+ ORDER BY expected_date
146
+ LIMIT 50
147
+
148
+ charts:
149
+ schema_panel:
150
+ title: Schema
151
+ type: table
152
+ query: column_schema
153
+ style:
154
+ header_overflow: wrap-two
155
+ columns:
156
+ name:
157
+ label: Column
158
+ type:
159
+ label: Type
160
+ role:
161
+ label: Role
162
+ semantic_type:
163
+ label: Semantic
164
+ description:
165
+ label: Description
166
+
167
+ # Stats KPIs
168
+ min_date:
169
+ label: Earliest Date
170
+ type: kpi
171
+ query: stats
172
+ value: min_date
173
+
174
+ max_date:
175
+ label: Latest Date
176
+ type: kpi
177
+ query: stats
178
+ value: max_date
179
+
180
+ distinct_dates:
181
+ label: Distinct Dates
182
+ type: kpi
183
+ query: stats
184
+ value: distinct_dates
185
+ format: ",.0f"
186
+
187
+ date_span:
188
+ label: Date Span (Days)
189
+ type: kpi
190
+ query: stats
191
+ value: date_span_days
192
+ format: ",.0f"
193
+
194
+ null_pct:
195
+ label: "Null %"
196
+ type: kpi
197
+ query: stats
198
+ value: null_pct
199
+ format: ".1f"
200
+
201
+ # Time series
202
+ daily_chart:
203
+ title: Records per Day
204
+ type: line
205
+ query: daily_counts
206
+ x: date
207
+ y: count
208
+
209
+ # Monthly trend
210
+ monthly_chart:
211
+ title: Records per Month
212
+ type: bar
213
+ query: monthly_counts
214
+ x: month
215
+ y: count
216
+
217
+ # Day of week
218
+ dow_chart:
219
+ title: Day of Week Distribution
220
+ type: bar
221
+ query: dow_distribution
222
+ x: day_of_week
223
+ y: count
224
+
225
+ # Gaps table
226
+ gaps_table:
227
+ title: Missing Dates (first 50)
228
+ type: table
229
+ query: date_gaps
230
+ style:
231
+ header_overflow: wrap-two
232
+ columns:
233
+ missing_date:
234
+ label: Missing Date
235
+
236
+ rows:
237
+ - schema_panel
238
+ - cols:
239
+ - min_date
240
+ - max_date
241
+ - distinct_dates
242
+ - date_span
243
+ - null_pct
244
+ - daily_chart
245
+ - cols:
246
+ - monthly_chart
247
+ - dow_chart
248
+ - gaps_table