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,489 @@
1
+ """Variable normalization, validation, and dependency analysis."""
2
+
3
+ import re
4
+ from typing import Any
5
+
6
+ from dataface.core.compile.errors import CompilationError
7
+ from dataface.core.compile.jinja import extract_variable_dependencies
8
+ from dataface.core.compile.models.chart.compiled import (
9
+ Chart,
10
+ )
11
+ from dataface.core.compile.models.face.compiled import Face, Layout
12
+ from dataface.core.compile.models.query.compiled import AnyQuery, SqlQuery
13
+ from dataface.core.compile.models.variable.authored import (
14
+ Variable,
15
+ VariableInputType,
16
+ )
17
+
18
+ _QUERY_NAME_RE = re.compile(r"^(?:queries\.)?[a-zA-Z_][a-zA-Z0-9_]*$")
19
+
20
+
21
+ def _detect_variable_input_type(var: Variable) -> VariableInputType:
22
+ """Infer the input type for a variable from its authored fields.
23
+
24
+ When input is "auto" (the default), examines options, default, min/max/step,
25
+ hidden, and column fields to determine the appropriate input type.
26
+ If input is explicitly set to something other than "auto", returns it unchanged.
27
+
28
+ Returns:
29
+ The resolved VariableInputType string.
30
+ """
31
+ if var.input != "auto":
32
+ return var.input
33
+
34
+ has_options = var.options and (var.options.static or var.options.query)
35
+ has_slider_fields = (
36
+ var.min is not None or var.max is not None or var.step is not None
37
+ )
38
+
39
+ # List default (with or without options) → multiselect
40
+ if isinstance(var.default, list):
41
+ return "multiselect"
42
+
43
+ # Slider fields → slider
44
+ if has_slider_fields:
45
+ return "slider"
46
+
47
+ # Bool default → checkbox
48
+ if isinstance(var.default, bool):
49
+ return "checkbox"
50
+
51
+ # Options present → select
52
+ if has_options:
53
+ return "select"
54
+
55
+ # Column binding → select
56
+ if var.column:
57
+ return "select"
58
+
59
+ # Not visible (hidden) with no other signals → text
60
+ if not var.visible:
61
+ return "text"
62
+
63
+ # No signals → text (generic fallback)
64
+ return "text"
65
+
66
+
67
+ def _promote_inline_option_queries(
68
+ variables: dict[str, Variable],
69
+ query_registry: dict[str, AnyQuery],
70
+ default_source: str | None = None,
71
+ ) -> None:
72
+ """Convert inline SQL in variable options.query to named queries.
73
+
74
+ When a variable defines options.query with raw SQL instead of a query
75
+ name reference, this creates a synthetic SqlQuery in the registry and
76
+ replaces options.query with the synthetic name.
77
+
78
+ Modifies both variables and query_registry in place.
79
+
80
+ Args:
81
+ variables: Dict of variable definitions to process
82
+ query_registry: Query registry to add synthetic queries to
83
+ default_source: Default source for queries without explicit source
84
+ """
85
+ for var_name, var in variables.items():
86
+ if not var.options or not var.options.query:
87
+ continue
88
+
89
+ query_ref = var.options.query.strip()
90
+ if not query_ref or _QUERY_NAME_RE.match(query_ref):
91
+ continue
92
+
93
+ synthetic_name = f"_var_{var_name}_options"
94
+ query_registry[synthetic_name] = SqlQuery(
95
+ sql=query_ref,
96
+ source=default_source,
97
+ )
98
+ var.options.query = synthetic_name
99
+
100
+
101
+ def _build_variable_registry(face: Face) -> dict[str, Variable]:
102
+ """Build global variable registry by traversing compiled face tree.
103
+
104
+ Collects all variables from the entire face tree (root and nested faces).
105
+ Validates that variable names are unique across the entire tree.
106
+
107
+ Args:
108
+ face: Root Face to traverse
109
+
110
+ Returns:
111
+ Dict mapping variable name to Variable object (all variables from entire tree)
112
+
113
+ Raises:
114
+ CompilationError: If duplicate variable names found
115
+ """
116
+ registry: dict[str, Variable] = {}
117
+ _collect_variables_recursive(face, registry)
118
+ return registry
119
+
120
+
121
+ def _collect_variables_recursive(face: Face, registry: dict[str, Variable]) -> None:
122
+ """Recursively collect variables from face and nested faces.
123
+
124
+ Args:
125
+ face: Face to collect variables from
126
+ registry: Registry dict to add variables to
127
+
128
+ Raises:
129
+ CompilationError: If duplicate variable names found
130
+ """
131
+ # Add variables from this face
132
+ if face.variables:
133
+ for var_name, var in face.variables.items():
134
+ if var_name in registry:
135
+ raise CompilationError(
136
+ f"Duplicate variable name '{var_name}'. Variable names must be unique "
137
+ "within a file (and those that are imported)."
138
+ )
139
+ registry[var_name] = var
140
+
141
+ # Recursively collect from nested faces in layout
142
+ if face.layout.items:
143
+ for item in face.layout.items:
144
+ if item.type == "face" and item.face:
145
+ _collect_variables_recursive(item.face, registry)
146
+
147
+
148
+ def _validate_variable_references(face: Face) -> list[str]:
149
+ """Validate that all referenced variables are defined.
150
+
151
+ Checks that all variables referenced in queries and charts exist in
152
+ the variable registry. Returns warnings for any undefined references.
153
+
154
+ Args:
155
+ face: Root Face with variable_registry set
156
+
157
+ Returns:
158
+ List of warning messages for undefined variable references
159
+ """
160
+ warnings: list[str] = []
161
+ defined_vars = (
162
+ set(face.variable_registry.keys()) if face.variable_registry else set()
163
+ )
164
+
165
+ # Collect all referenced variables from charts
166
+ _collect_referenced_vars_recursive(face, defined_vars, warnings)
167
+
168
+ return warnings
169
+
170
+
171
+ def _collect_referenced_vars_recursive(
172
+ face: Face,
173
+ defined_vars: set,
174
+ warnings: list[str],
175
+ ) -> None:
176
+ """Recursively check variable references in face and nested faces.
177
+
178
+ Args:
179
+ face: Face to check
180
+ defined_vars: Set of defined variable names
181
+ warnings: List to append warnings to
182
+ """
183
+ # Check charts in this face
184
+ for chart_name, chart in face.charts.items():
185
+ for var_name in chart.variable_dependencies:
186
+ if var_name not in defined_vars:
187
+ warnings.append(
188
+ f"Chart '{chart_name}' references undefined variable '{var_name}'"
189
+ )
190
+
191
+ # Check queries in this face
192
+ for query_name, query in face.queries.items():
193
+ for var_name in query.variable_dependencies:
194
+ if var_name not in defined_vars:
195
+ warnings.append(
196
+ f"Query '{query_name}' references undefined variable '{var_name}'"
197
+ )
198
+
199
+ # Recursively check nested faces
200
+ if face.layout.items:
201
+ for item in face.layout.items:
202
+ if item.type == "face" and item.face:
203
+ _collect_referenced_vars_recursive(item.face, defined_vars, warnings)
204
+
205
+
206
+ def _generate_layout_variables(layout: Layout) -> dict[str, Variable]:
207
+ """Generate hidden variables for tabs and details in this layout only.
208
+
209
+ Tabs get a hidden select variable (slug values).
210
+ Details get a hidden checkbox variable (boolean).
211
+
212
+ Only generates variables for the current layout level — nested faces
213
+ handle their own variables during their own normalize_face() call.
214
+
215
+ Returns:
216
+ Dict of variable_name → Variable for all auto-generated variables.
217
+ """
218
+ from dataface.core.compile.models.variable.authored import VariableOptions
219
+
220
+ variables: dict[str, Variable] = {}
221
+
222
+ # Tabs: generate a select variable
223
+ if layout.type == "tabs" and layout.tab_variable and layout.tab_slugs:
224
+ variables[layout.tab_variable] = Variable(
225
+ input="select",
226
+ visible=False,
227
+ default=layout.tab_slugs[layout.default_tab or 0],
228
+ options=VariableOptions(
229
+ static=[*layout.tab_slugs],
230
+ ),
231
+ )
232
+
233
+ # Walk direct child items for details variables only.
234
+ # Don't recurse into nested faces — they already ran normalize_face()
235
+ # which called _generate_layout_variables() for their own layouts.
236
+ for item in layout.items:
237
+ if item.details_variable:
238
+ default = False
239
+ if item.face and item.face.meta:
240
+ default = item.face.meta.get("details_expanded_default", False)
241
+ variables[item.details_variable] = Variable(
242
+ input="checkbox",
243
+ visible=False,
244
+ default=default,
245
+ )
246
+
247
+ return variables
248
+
249
+
250
+ # Type validators: input_type -> (type_check_fn, expected_type_desc)
251
+ # text/select/input/textarea/radio accept any scalar type (no validation needed)
252
+ _TYPE_VALIDATORS: dict[str, tuple[type | tuple[type, ...], str]] = {
253
+ "number": ((int, float), "a number"),
254
+ "slider": ((int, float), "a number"),
255
+ "range": ((int, float), "a number"),
256
+ "checkbox": (bool, "a boolean"),
257
+ "multiselect": (list, "a list"),
258
+ "date": (str, "a string"),
259
+ "datepicker": (str, "a string"),
260
+ }
261
+
262
+
263
+ def _validate_variable_value(var_name: str, var: Variable, value: Any) -> None:
264
+ """Validate a variable value against its input type.
265
+
266
+ Called at compile time to validate default values. Uses data-driven
267
+ validation via _TYPE_VALIDATORS dict.
268
+
269
+ Args:
270
+ var_name: Variable name (for error messages)
271
+ var: Variable definition with input type
272
+ value: Value to validate
273
+
274
+ Raises:
275
+ CompilationError: If value doesn't match expected type/format
276
+ """
277
+ if value is None:
278
+ return
279
+
280
+ input_type = var.input
281
+
282
+ # Special case: daterange needs structural validation (2-element list)
283
+ if input_type == "daterange":
284
+ if not isinstance(value, (list, tuple)) or len(value) != 2:
285
+ raise CompilationError(
286
+ f"Variable '{var_name}': daterange default must be [start, end], "
287
+ f"got {type(value).__name__}"
288
+ )
289
+ for i, v in enumerate(value):
290
+ if v is not None and not isinstance(v, str):
291
+ raise CompilationError(
292
+ f"Variable '{var_name}': daterange[{i}] must be string, "
293
+ f"got {type(v).__name__}"
294
+ )
295
+ return
296
+
297
+ # Standard type validation from dict
298
+ if input_type in _TYPE_VALIDATORS:
299
+ expected_type, desc = _TYPE_VALIDATORS[input_type]
300
+ if not isinstance(value, expected_type):
301
+ raise CompilationError(
302
+ f"Variable '{var_name}': {input_type} default must be {desc}, "
303
+ f"got {type(value).__name__}"
304
+ )
305
+
306
+ # Slider/range: additional min/max bounds check
307
+ if input_type in ("slider", "range"):
308
+ if var.min is not None and value < var.min:
309
+ raise CompilationError(
310
+ f"Variable '{var_name}': {input_type} default {value} < min {var.min}"
311
+ )
312
+ if var.max is not None and value > var.max:
313
+ raise CompilationError(
314
+ f"Variable '{var_name}': {input_type} default {value} > max {var.max}"
315
+ )
316
+
317
+
318
+ def _compute_variable_dependencies(
319
+ variables: dict[str, Variable],
320
+ query_registry: dict[str, AnyQuery],
321
+ ) -> None:
322
+ """Compute variable dependencies for cascading dropdowns.
323
+
324
+ Analyzes each variable's options query SQL to find Jinja references
325
+ to other variables. Updates the variable_dependencies field in-place.
326
+
327
+ Also detects circular dependencies and raises an error if found.
328
+
329
+ Args:
330
+ variables: Dict of variable definitions to analyze
331
+ query_registry: Query registry to look up options queries
332
+
333
+ Raises:
334
+ CompilationError: If circular variable dependencies detected
335
+
336
+ Example:
337
+ If variable 'state' has options.query = 'state_options' and the
338
+ state_options query SQL contains {{ filter('country', country) }},
339
+ then state.variable_dependencies will be {'country'}.
340
+ """
341
+ # Build dependency graph
342
+ dep_graph: dict[str, set] = {}
343
+
344
+ for var_name, var in variables.items():
345
+ deps: set = set()
346
+
347
+ # Get the options query name
348
+ options_query_name = var.get_option_query()
349
+ if options_query_name and options_query_name in query_registry:
350
+ query = query_registry[options_query_name]
351
+
352
+ # Extract variable dependencies from query SQL and setup_sql
353
+ if hasattr(query, "sql") and query.sql:
354
+ sql_deps = extract_variable_dependencies(query.sql)
355
+ # Filter to only include variables that actually exist
356
+ deps = sql_deps & set(variables.keys())
357
+ if hasattr(query, "setup_sql") and query.setup_sql:
358
+ setup_deps = extract_variable_dependencies(query.setup_sql)
359
+ deps |= setup_deps & set(variables.keys())
360
+
361
+ # Also check query filters
362
+ if hasattr(query, "filters") and query.filters:
363
+ for filter_value in query.filters.values():
364
+ if isinstance(filter_value, str):
365
+ filter_deps = extract_variable_dependencies(filter_value)
366
+ deps |= filter_deps & set(variables.keys())
367
+
368
+ # Also extract deps from disabled Jinja expressions so the UI
369
+ # re-evaluates disabled state when any referenced variable changes.
370
+ if isinstance(var.disabled, str):
371
+ disabled_deps = extract_variable_dependencies(var.disabled)
372
+ deps |= disabled_deps & set(variables.keys())
373
+
374
+ # Remove self-reference (a variable can't depend on itself)
375
+ deps.discard(var_name)
376
+
377
+ # Update the variable's dependencies
378
+ var.variable_dependencies = deps
379
+ dep_graph[var_name] = deps
380
+
381
+ # Check for circular dependencies
382
+ _detect_circular_variable_deps(dep_graph)
383
+
384
+
385
+ def _detect_circular_variable_deps(dep_graph: dict[str, set]) -> None:
386
+ """Detect circular dependencies in variable dependency graph.
387
+
388
+ Uses depth-first search to find cycles.
389
+
390
+ Args:
391
+ dep_graph: Dict mapping variable name to set of dependencies
392
+
393
+ Raises:
394
+ CompilationError: If circular dependency found
395
+ """
396
+ visited: set = set()
397
+ rec_stack: set = set()
398
+
399
+ def dfs(node: str, path: list[str]) -> None:
400
+ if node in rec_stack:
401
+ # Found a cycle - build the cycle path for error message
402
+ cycle_start = path.index(node)
403
+ cycle = path[cycle_start:] + [node]
404
+ raise CompilationError(
405
+ f"Circular variable dependency detected: {' → '.join(cycle)}. "
406
+ "Variables cannot depend on each other in a cycle. "
407
+ "Check the options queries for these variables."
408
+ )
409
+
410
+ if node in visited:
411
+ return
412
+
413
+ visited.add(node)
414
+ rec_stack.add(node)
415
+
416
+ for dep in dep_graph.get(node, set()):
417
+ if dep in dep_graph: # Only follow known variables
418
+ dfs(dep, path + [node])
419
+
420
+ rec_stack.remove(node)
421
+
422
+ for var_name in dep_graph:
423
+ dfs(var_name, [])
424
+
425
+
426
+ def get_transitive_variable_dependencies(
427
+ variable_name: str,
428
+ variables: dict[str, Variable],
429
+ ) -> set:
430
+ """Get all transitive dependencies for a variable.
431
+
432
+ If variable A depends on B, and B depends on C, then A's transitive
433
+ dependencies are {B, C}.
434
+
435
+ Args:
436
+ variable_name: Variable to get dependencies for
437
+ variables: Dict of all variables
438
+
439
+ Returns:
440
+ Set of all variable names that this variable depends on (directly or transitively)
441
+ """
442
+ result: set = set()
443
+ visited: set = set()
444
+
445
+ def collect_deps(var_name: str) -> None:
446
+ if var_name in visited:
447
+ return
448
+ visited.add(var_name)
449
+
450
+ var = variables.get(var_name)
451
+ if not var:
452
+ return
453
+
454
+ for dep in var.variable_dependencies:
455
+ result.add(dep)
456
+ collect_deps(dep)
457
+
458
+ collect_deps(variable_name)
459
+ return result
460
+
461
+
462
+ def _expand_chart_variable_dependencies(
463
+ charts: dict[str, "Chart"],
464
+ variables: dict[str, Variable],
465
+ ) -> None:
466
+ """Expand chart variable dependencies to include transitive dependencies.
467
+
468
+ If a chart depends on variable A, and A depends on B (cascading dropdown),
469
+ then the chart should also track dependency on B so the UI knows to
470
+ re-render when B changes.
471
+
472
+ Modifies charts in place.
473
+
474
+ Args:
475
+ charts: Dict of compiled charts
476
+ variables: Dict of variables with computed dependencies
477
+ """
478
+ for chart in charts.values():
479
+ expanded_deps: set = set()
480
+
481
+ # For each variable the chart directly depends on
482
+ for var_name in chart.variable_dependencies:
483
+ expanded_deps.add(var_name)
484
+ # Add all transitive dependencies of that variable
485
+ transitive = get_transitive_variable_dependencies(var_name, variables)
486
+ expanded_deps |= transitive
487
+
488
+ # Update chart's variable dependencies
489
+ chart.variable_dependencies = expanded_deps