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,358 @@
1
+ """Chart decisions pipeline stage.
2
+
3
+ Stage: between EXECUTE and RENDER (layout/sizing)
4
+ Purpose: Enrich charts with data-aware defaults before rendering.
5
+
6
+ This module analyzes query result data and enriches Chart objects
7
+ with smarter defaults for axis domains and field assignment.
8
+ It runs for ALL charts (not just type: auto).
9
+
10
+ Pipeline position:
11
+ compile → execute → CHART DECISIONS → layout/sizing → render
12
+
13
+ Key principle: every auto-decision is overridable by explicit YAML.
14
+ If the user sets scale/fields, those win over any auto-detection.
15
+
16
+ Format defaults come from the theme cascade (axis_quantitative.format in stark.yaml),
17
+ not from column-name inference. Authors override per-chart via chart.format or
18
+ style.axis_y.format.
19
+
20
+ Dependencies:
21
+ - chart_type_detection (for column classification, reused here)
22
+
23
+ Note on database driver compatibility:
24
+ The type_code mapping in db_type_to_dtype works for DuckDB (which uses
25
+ string type names like "INTEGER", "TIMESTAMP"). Other DB-API drivers use
26
+ opaque type codes (psycopg2 uses integer OIDs, sqlite3 returns None).
27
+ When type_code can't be mapped, the stage falls back to data-value heuristics.
28
+ """
29
+
30
+ import math
31
+ from dataclasses import dataclass
32
+ from decimal import Decimal
33
+ from typing import Any
34
+
35
+ from dataface.core.compile.models.chart.compiled import Chart
36
+ from dataface.core.compile.models.style.authored import ChartStylePatch
37
+
38
+ # Chart types that use x/y axes (not KPI, table, pie)
39
+ _AXIS_CHART_TYPES = frozenset({"bar", "line", "area", "scatter", "heatmap"})
40
+
41
+ # Chart types where zero anchoring is optional; the smart-auto heuristic applies.
42
+ # Bar and area/heatmap fall through to the legacy ratio > 0.8 rule below.
43
+ _OPTIONAL_ZERO_CHART_TYPES = frozenset({"line", "scatter"})
44
+
45
+ # Threshold for smart-auto zero extension. When data_min/data_max <= this value,
46
+ # data starts in the bottom 25% of [0, max] and extending to zero adds honest
47
+ # context without crushing the data. Above the threshold, keep the data-fitted
48
+ # domain.
49
+ _ZERO_EXTEND_THRESHOLD = 0.25
50
+
51
+ # PEP 249 cursor.description index constants
52
+ _DESC_TYPE_CODE = 1
53
+
54
+ # Database type strings → our dtype categories
55
+ _NUMERIC_DB_TYPES = frozenset(
56
+ {
57
+ "BIGINT",
58
+ "INTEGER",
59
+ "INT",
60
+ "SMALLINT",
61
+ "TINYINT",
62
+ "DOUBLE",
63
+ "FLOAT",
64
+ "REAL",
65
+ "DECIMAL",
66
+ "NUMERIC",
67
+ "HUGEINT",
68
+ "UBIGINT",
69
+ "UINTEGER",
70
+ "USMALLINT",
71
+ "UTINYINT",
72
+ "NUMBER",
73
+ "MONEY",
74
+ }
75
+ )
76
+ _TEMPORAL_DB_TYPES = frozenset(
77
+ {
78
+ "DATE",
79
+ "TIME",
80
+ "DATETIME",
81
+ "TIMESTAMP",
82
+ "TIMESTAMPTZ",
83
+ "TIMESTAMP WITH TIME ZONE",
84
+ "TIMESTAMP WITHOUT TIME ZONE",
85
+ "INTERVAL",
86
+ }
87
+ )
88
+ _BOOLEAN_DB_TYPES = frozenset({"BOOLEAN", "BOOL"})
89
+
90
+
91
+ @dataclass
92
+ class ColumnProfile:
93
+ """Lightweight profile of a query result column."""
94
+
95
+ name: str
96
+ dtype: str # "numeric", "temporal", "categorical"
97
+ min_val: float | None = None
98
+ max_val: float | None = None
99
+
100
+
101
+ def db_type_to_dtype(db_type: str) -> str | None:
102
+ """Map a database type string to our dtype classification."""
103
+ upper = db_type.upper().split("(")[0].strip()
104
+ if upper in _NUMERIC_DB_TYPES:
105
+ return "numeric"
106
+ if upper in _TEMPORAL_DB_TYPES:
107
+ return "temporal"
108
+ if upper in _BOOLEAN_DB_TYPES:
109
+ return "categorical"
110
+ if upper.startswith("VARCHAR") or upper in ("TEXT", "CHAR", "STRING"):
111
+ return "categorical"
112
+ return None
113
+
114
+
115
+ def enrich_chart(
116
+ chart: Chart,
117
+ data: list[dict[str, Any]],
118
+ column_descriptions: dict[str, tuple] | None = None,
119
+ ) -> Chart:
120
+ """Enrich chart with data-aware defaults.
121
+
122
+ Runs for ALL chart types. Layers applied in order (later override earlier):
123
+ 1. Data value analysis (always available)
124
+ 2. Database column metadata from cursor.description (when available; type_code
125
+ mapping works reliably for DuckDB which uses string type names; other drivers
126
+ fall back to value-based heuristics)
127
+ 3. User YAML annotations (always win — never overridden)
128
+
129
+ Format defaults come from the theme cascade (axis_quantitative.format) —
130
+ not from inference here. Authors override via chart.format or style.axis_y.format.
131
+ """
132
+ if not data:
133
+ return chart
134
+
135
+ col_profiles = _profile_columns(data, column_descriptions)
136
+ updates: dict[str, Any] = {}
137
+
138
+ effective_type = chart.type
139
+
140
+ # Field assignment: only for chart types that use x/y axes
141
+ if effective_type in _AXIS_CHART_TYPES:
142
+ field_updates = _pick_fields(chart, col_profiles)
143
+ updates.update(field_updates)
144
+
145
+ effective_y = updates.get("y", chart.y)
146
+
147
+ # Only enrich axis-based charts with format/scale decisions
148
+ if effective_type in _AXIS_CHART_TYPES and effective_y:
149
+ y_field = effective_y[0] if isinstance(effective_y, list) else effective_y
150
+ y_profile = col_profiles.get(y_field)
151
+
152
+ if y_profile and y_profile.dtype == "numeric":
153
+ style_updates = _pick_axis_style(chart, y_profile)
154
+ if style_updates:
155
+ from dataface.core.compile.normalize_charts import (
156
+ _AUTHORED_FAMILY_TO_MONOLITHIC_KEY,
157
+ )
158
+
159
+ family_key = _AUTHORED_FAMILY_TO_MONOLITHIC_KEY.get(effective_type)
160
+ existing = (
161
+ chart.style.model_dump(exclude_none=True) if chart.style else {}
162
+ )
163
+ if family_key is not None:
164
+ existing_fam = existing.get(family_key, {})
165
+ existing_fam.update(style_updates)
166
+ existing[family_key] = existing_fam
167
+ updates["style"] = ChartStylePatch.model_validate(existing)
168
+
169
+ if updates:
170
+ return chart.model_copy(update=updates)
171
+ return chart
172
+
173
+
174
+ def _profile_columns(
175
+ data: list[dict[str, Any]],
176
+ column_descriptions: dict[str, tuple] | None = None,
177
+ ) -> dict[str, ColumnProfile]:
178
+ """Profile each column from query result data.
179
+
180
+ Uses two layers:
181
+ - Layer 1: Data value analysis (always available)
182
+ - Layer 2: Database column descriptions from cursor.description (when available)
183
+ Provides type for more confident classification.
184
+ """
185
+ if not data:
186
+ return {}
187
+
188
+ from dataface.core.compile.chart_type_detection import classify_column_type
189
+
190
+ columns = list(data[0].keys())
191
+ profiles: dict[str, ColumnProfile] = {}
192
+
193
+ for col in columns:
194
+ sample_values = [row.get(col) for row in data[:100] if row.get(col) is not None]
195
+ if not sample_values:
196
+ profiles[col] = ColumnProfile(name=col, dtype="categorical")
197
+ continue
198
+
199
+ # Layer 2: Use database description if available
200
+ desc = column_descriptions.get(col) if column_descriptions else None
201
+ db_type_str = (
202
+ str(desc[_DESC_TYPE_CODE])
203
+ if desc and desc[_DESC_TYPE_CODE] is not None
204
+ else None
205
+ )
206
+ db_dtype = db_type_to_dtype(db_type_str) if db_type_str else None
207
+
208
+ # Layer 1: Fall back to value-based classification
209
+ dtype = db_dtype or classify_column_type(col, sample_values, db_type_str)
210
+
211
+ profile = ColumnProfile(name=col, dtype=dtype)
212
+
213
+ if dtype == "numeric":
214
+ numeric_vals = [
215
+ float(v)
216
+ for v in sample_values
217
+ if isinstance(v, (int, float, Decimal))
218
+ and not isinstance(v, bool)
219
+ and math.isfinite(float(v))
220
+ ]
221
+ if numeric_vals:
222
+ profile.min_val = min(numeric_vals)
223
+ profile.max_val = max(numeric_vals)
224
+
225
+ profiles[col] = profile
226
+
227
+ return profiles
228
+
229
+
230
+ def _pick_fields(
231
+ chart: Chart,
232
+ col_profiles: dict[str, ColumnProfile],
233
+ ) -> dict[str, Any]:
234
+ """Pick best x/y fields when not user-specified."""
235
+ updates: dict[str, Any] = {}
236
+
237
+ if chart.x and chart.y:
238
+ return updates
239
+
240
+ numeric_cols = [p for p in col_profiles.values() if p.dtype == "numeric"]
241
+ temporal_cols = [p for p in col_profiles.values() if p.dtype == "temporal"]
242
+ categorical_cols = [p for p in col_profiles.values() if p.dtype == "categorical"]
243
+
244
+ if not chart.x and not chart.y:
245
+ if temporal_cols:
246
+ updates["x"] = temporal_cols[0].name
247
+ elif categorical_cols:
248
+ updates["x"] = categorical_cols[0].name
249
+
250
+ if numeric_cols:
251
+ updates["y"] = numeric_cols[0].name
252
+
253
+ elif chart.x and not chart.y:
254
+ if numeric_cols:
255
+ updates["y"] = numeric_cols[0].name
256
+
257
+ elif chart.y and not chart.x:
258
+ if temporal_cols:
259
+ updates["x"] = temporal_cols[0].name
260
+ elif categorical_cols:
261
+ updates["x"] = categorical_cols[0].name
262
+
263
+ return updates
264
+
265
+
266
+ def _pick_axis_style(
267
+ chart: Chart,
268
+ y_profile: ColumnProfile,
269
+ ) -> dict[str, Any]:
270
+ """Pick y-axis scale style updates.
271
+
272
+ Format defaults come from the theme cascade (axis_quantitative.format in stark.yaml).
273
+ This function only handles the zero-baseline heuristic.
274
+
275
+ Returns flat dict with a ``scale`` key when a scale override is needed — the caller
276
+ wraps it into the family sub-patch key before building the updated ChartStylePatch.
277
+ """
278
+ updates: dict[str, Any] = {}
279
+
280
+ from dataface.core.compile.normalize_charts import (
281
+ _AUTHORED_FAMILY_TO_MONOLITHIC_KEY,
282
+ )
283
+
284
+ family_key = _AUTHORED_FAMILY_TO_MONOLITHIC_KEY.get(chart.type)
285
+ existing_patch = (
286
+ getattr(chart.style, family_key, None) if chart.style and family_key else None
287
+ )
288
+ existing_scale = getattr(existing_patch, "scale", None) if existing_patch else None
289
+ existing_axis_y = (
290
+ getattr(existing_patch, "axis_y", None) if existing_patch else None
291
+ )
292
+ existing_axis_y_scale = (
293
+ getattr(existing_axis_y, "scale", None) if existing_axis_y else None
294
+ )
295
+
296
+ has_zero = (existing_scale is not None and existing_scale.zero is not None) or (
297
+ existing_axis_y_scale is not None and existing_axis_y_scale.zero is not None
298
+ )
299
+ if not has_zero:
300
+ scale = _pick_scale(y_profile, chart_type=chart.type)
301
+ if scale:
302
+ scale_data = (
303
+ existing_scale.model_dump(exclude_none=True) if existing_scale else {}
304
+ )
305
+ scale_data.update(scale)
306
+ updates["scale"] = scale_data
307
+
308
+ return updates
309
+
310
+
311
+ def _pick_scale(
312
+ profile: ColumnProfile, chart_type: str | None = None
313
+ ) -> dict[str, Any] | None:
314
+ """Decide the explicit zero setting for the y-axis scale.
315
+
316
+ Returns ``{"zero": False}`` to keep the domain data-fitted, ``{"zero": True}``
317
+ to extend to zero, or ``None`` when zero is not relevant (data spans or
318
+ touches zero, or no data profiled).
319
+
320
+ Optional-zero chart types (line, scatter): smart-auto heuristic.
321
+ Extend to zero when data_min/data_max <= 0.25 (data starts in the bottom
322
+ quarter of [0, max]). Return zero:False when data lives far from zero.
323
+
324
+ Non-optional-zero types (bar, area, …): always extend to zero for
325
+ all-positive data — bar marks encode absolute magnitude and suppressing
326
+ zero produces truncated bars (a known misleading chart pattern).
327
+
328
+ Returning an explicit True/False here means resolved_chart.zero carries
329
+ the full intent; callers (profile.py) do not need to know chart-type
330
+ defaults in VL.
331
+
332
+ All-negative data (max < 0): out of scope — skip the heuristic.
333
+ """
334
+ if profile.min_val is None or profile.max_val is None:
335
+ return None
336
+ if profile.max_val == 0:
337
+ return None
338
+ # Negative-spanning or all-negative: zero already in domain, or out of scope.
339
+ if profile.min_val < 0:
340
+ return None
341
+
342
+ if chart_type in _OPTIONAL_ZERO_CHART_TYPES:
343
+ # Smart-auto: keep data-fitted when data lives too far from zero.
344
+ ratio = profile.min_val / profile.max_val
345
+ if ratio > _ZERO_EXTEND_THRESHOLD:
346
+ return {"zero": False}
347
+ # Data is close to zero — let VL's zero=true default extend naturally.
348
+ # No explicit True needed: blank space is negligible and we preserve
349
+ # the tick range from the pre-existing behaviour.
350
+ return None
351
+
352
+ # Non-optional-zero types (bar, area, …) with all-positive data: return
353
+ # explicit True so resolved_chart.zero carries the intent and callers
354
+ # (profile.py) need no chart-type knowledge to decide domain pinning.
355
+ if profile.min_val > 0:
356
+ return {"zero": True}
357
+
358
+ return None