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,496 @@
1
+ """Spark chart (sparkline) rendering functions.
2
+
3
+ Stage: RENDER
4
+ Purpose: Render inline spark charts as SVG for table cells.
5
+
6
+ Variants:
7
+ - line: mini line chart from an array of values
8
+ - area: filled line chart from an array of values
9
+ - bar: single horizontal bar, absolute magnitude, no track
10
+ - bar-normalize: single horizontal bar, scaled to explicit max, with track
11
+ - columns: mini vertical bar chart from an array of values
12
+
13
+ All functions return SVG strings that can be embedded directly in table cells.
14
+ """
15
+
16
+ import html
17
+ from dataclasses import dataclass
18
+ from typing import Any
19
+
20
+ from dataface.core.compile.models.primitives import FontStyle, SpacingValues
21
+ from dataface.core.compile.models.style.compiled import SparkStyle
22
+ from dataface.core.compile.models.style.merged import MergedChartsStyle
23
+
24
+ _SPARK_WIDTH = 80.0
25
+ _SPARK_HEIGHT = 24.0
26
+ _SPARK_BAR_WIDTH = 100.0
27
+ _SPARK_BAR_HEIGHT = 16.0
28
+
29
+
30
+ def _resolve_spark_color(call_site_color: str | None, spark_cfg: SparkStyle) -> str:
31
+ """Return the spark line/fill color, preferring the call-site override.
32
+
33
+ Raises if neither the call site nor the theme resolves a color — spark
34
+ cells without an accent set are a theme configuration bug, not something
35
+ to silently paper over with a default.
36
+ """
37
+ resolved = call_site_color if call_site_color is not None else spark_cfg.color
38
+ if resolved is None:
39
+ raise ValueError(
40
+ "spark color is unset: theme must set charts.spark.color or the "
41
+ "caller must pass color=... explicitly"
42
+ )
43
+ return resolved
44
+
45
+
46
+ @dataclass
47
+ class NormalizedPoints:
48
+ """Pre-computed normalized points for spark chart rendering."""
49
+
50
+ points: list[str]
51
+ min_val: float
52
+ max_val: float
53
+ min_idx: int
54
+ max_idx: int
55
+ plot_width: float
56
+ plot_height: float
57
+ padding: SpacingValues
58
+
59
+
60
+ def _normalize_points(
61
+ values: list[int | float],
62
+ width: float | int,
63
+ height: float | int,
64
+ spark_style: SparkStyle,
65
+ padding: SpacingValues | None = None,
66
+ ) -> NormalizedPoints:
67
+ padding = spark_style.padding if padding is None else padding
68
+ min_val = min(values)
69
+ max_val = max(values)
70
+ value_range = max_val - min_val
71
+
72
+ plot_width = width - padding.horizontal
73
+ plot_height = height - padding.vertical
74
+
75
+ points: list[str] = []
76
+ num_values = len(values)
77
+
78
+ for i, val in enumerate(values):
79
+ x = padding.left + (i / (num_values - 1)) * plot_width
80
+ if value_range > 0:
81
+ y = padding.top + (1 - (val - min_val) / value_range) * plot_height
82
+ else:
83
+ y = padding.top + plot_height / 2
84
+ points.append(f"{x:.1f},{y:.1f}")
85
+
86
+ return NormalizedPoints(
87
+ points=points,
88
+ min_val=min_val,
89
+ max_val=max_val,
90
+ min_idx=values.index(min_val),
91
+ max_idx=values.index(max_val),
92
+ plot_width=plot_width,
93
+ plot_height=plot_height,
94
+ padding=padding,
95
+ )
96
+
97
+
98
+ def _svg_wrapper(content: str, width: float | int, height: float | int) -> str:
99
+ return f'<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}" viewBox="0 0 {width} {height}">{content}</svg>'
100
+
101
+
102
+ def _render_empty_spark(
103
+ width: float | int,
104
+ height: float | int,
105
+ spark_style: SparkStyle,
106
+ ) -> str:
107
+ empty_config = spark_style.empty
108
+ y = height / 2
109
+ content = (
110
+ f'<line x1="{empty_config.inset_x}" y1="{y}" '
111
+ f'x2="{width - empty_config.inset_x}" y2="{y}" '
112
+ f'stroke="{empty_config.stroke.color}" stroke-width="{empty_config.stroke.width}" '
113
+ f'stroke-dasharray="{empty_config.stroke.dasharray}"/>'
114
+ )
115
+ return _svg_wrapper(content, width, height)
116
+
117
+
118
+ def _render_single_value_line(
119
+ width: float | int,
120
+ height: float | int,
121
+ color: str | None,
122
+ stroke_width: float,
123
+ spark_style: SparkStyle,
124
+ ) -> str:
125
+ single_value = spark_style.single_value
126
+ y = height / 2
127
+ escaped_color = html.escape(color or "")
128
+ content = (
129
+ f'<line x1="{single_value.inset_x}" y1="{y}" x2="{width - single_value.inset_x}" y2="{y}" '
130
+ f'stroke="{escaped_color}" stroke-width="{stroke_width}"/>'
131
+ f'<circle cx="{width/2}" cy="{y}" r="{single_value.marker_radius}" fill="{escaped_color}"/>'
132
+ )
133
+ return _svg_wrapper(content, width, height)
134
+
135
+
136
+ def render_spark_line(
137
+ values: list[int | float],
138
+ width: float | None = None,
139
+ height: float | None = None,
140
+ color: str | None = None,
141
+ last_visible: bool = False,
142
+ min_max_visible: bool = False,
143
+ stroke_width: float = 1.5,
144
+ *,
145
+ resolved_style: MergedChartsStyle,
146
+ ) -> str:
147
+ """Render a line sparkline as SVG."""
148
+ spark_cfg = resolved_style.spark
149
+ width = width if width is not None else _SPARK_WIDTH
150
+ height = height if height is not None else _SPARK_HEIGHT
151
+ color = _resolve_spark_color(color, spark_cfg)
152
+
153
+ if not values:
154
+ return _render_empty_spark(width, height, spark_cfg)
155
+
156
+ if len(values) == 1:
157
+ return _render_single_value_line(width, height, color, stroke_width, spark_cfg)
158
+
159
+ norm = _normalize_points(values, width, height, spark_cfg)
160
+ escaped_color = html.escape(color or "")
161
+
162
+ polyline = (
163
+ f'<polyline points="{" ".join(norm.points)}" fill="none" '
164
+ f'stroke="{escaped_color}" stroke-width="{stroke_width}" '
165
+ f'stroke-linecap="round" stroke-linejoin="round"/>'
166
+ )
167
+
168
+ markers = ""
169
+ if last_visible:
170
+ last_point = norm.points[-1].split(",")
171
+ markers += f'<circle cx="{last_point[0]}" cy="{last_point[1]}" r="2.5" fill="{escaped_color}"/>'
172
+
173
+ if min_max_visible:
174
+ for idx in [norm.min_idx, norm.max_idx]:
175
+ pt = norm.points[idx].split(",")
176
+ markers += (
177
+ f'<circle cx="{pt[0]}" cy="{pt[1]}" r="2" fill="{escaped_color}"/>'
178
+ )
179
+
180
+ return _svg_wrapper(polyline + markers, width, height)
181
+
182
+
183
+ def render_spark_area(
184
+ values: list[int | float],
185
+ width: float | None = None,
186
+ height: float | None = None,
187
+ color: str | None = None,
188
+ fill_opacity: float | None = None,
189
+ last_visible: bool = False,
190
+ stroke_width: float = 1.5,
191
+ *,
192
+ resolved_style: MergedChartsStyle,
193
+ ) -> str:
194
+ """Render a filled area sparkline as SVG."""
195
+ spark_cfg = resolved_style.spark
196
+ width = width if width is not None else _SPARK_WIDTH
197
+ height = height if height is not None else _SPARK_HEIGHT
198
+ color = _resolve_spark_color(color, spark_cfg)
199
+ fill_opacity = spark_cfg.area.fill_opacity if fill_opacity is None else fill_opacity
200
+
201
+ if not values:
202
+ return _render_empty_spark(width, height, spark_cfg)
203
+
204
+ if len(values) == 1:
205
+ return _render_single_value_line(width, height, color, stroke_width, spark_cfg)
206
+
207
+ norm = _normalize_points(values, width, height, spark_cfg)
208
+ escaped_color = html.escape(color or "")
209
+
210
+ bottom_y = height - norm.padding.bottom
211
+ first_x = norm.padding.left
212
+ last_x = norm.padding.left + norm.plot_width
213
+ polygon_points = (
214
+ f"{first_x},{bottom_y} " + " ".join(norm.points) + f" {last_x},{bottom_y}"
215
+ )
216
+
217
+ polygon = f'<polygon points="{polygon_points}" fill="{escaped_color}" fill-opacity="{fill_opacity}"/>'
218
+ polyline = (
219
+ f'<polyline points="{" ".join(norm.points)}" fill="none" '
220
+ f'stroke="{escaped_color}" stroke-width="{stroke_width}" '
221
+ f'stroke-linecap="round" stroke-linejoin="round"/>'
222
+ )
223
+
224
+ markers = ""
225
+ if last_visible:
226
+ last_point = norm.points[-1].split(",")
227
+ markers = f'<circle cx="{last_point[0]}" cy="{last_point[1]}" r="2.5" fill="{escaped_color}"/>'
228
+
229
+ return _svg_wrapper(polygon + polyline + markers, width, height)
230
+
231
+
232
+ def render_spark_columns(
233
+ values: list[int | float],
234
+ width: float | None = None,
235
+ height: float | None = None,
236
+ color: str | None = None,
237
+ gap: float | None = None,
238
+ *,
239
+ resolved_style: MergedChartsStyle,
240
+ ) -> str:
241
+ """Render a multi-value vertical bar sparkline (`spark.type: columns`) as SVG."""
242
+ spark_cfg = resolved_style.spark
243
+ width = width if width is not None else _SPARK_WIDTH
244
+ height = height if height is not None else _SPARK_HEIGHT
245
+ color = _resolve_spark_color(color, spark_cfg)
246
+ gap = spark_cfg.columns.gap if gap is None else gap
247
+
248
+ if not values:
249
+ return _render_empty_spark(width, height, spark_cfg)
250
+
251
+ min_val = min(values)
252
+ max_val = max(values)
253
+ value_range = max_val - min_val
254
+
255
+ padding = spark_cfg.columns.padding
256
+ plot_height = height - (2 * padding)
257
+
258
+ num_bars = len(values)
259
+ total_gap = gap * (num_bars - 1)
260
+ bar_width = (width - total_gap) / num_bars
261
+
262
+ escaped_color = html.escape(color or "")
263
+ bars: list[str] = []
264
+
265
+ for i, val in enumerate(values):
266
+ x = i * (bar_width + gap)
267
+ if value_range > 0:
268
+ bar_height = max(
269
+ spark_cfg.columns.min_bar_height,
270
+ ((val - min_val) / value_range) * plot_height,
271
+ )
272
+ else:
273
+ bar_height = plot_height / 2
274
+
275
+ y = height - padding - bar_height
276
+ bars.append(
277
+ f'<rect x="{x:.1f}" y="{y:.1f}" width="{bar_width:.1f}" '
278
+ f'height="{bar_height:.1f}" fill="{escaped_color}" rx="{spark_cfg.columns.border.radius}"/>'
279
+ )
280
+
281
+ return _svg_wrapper("".join(bars), width, height)
282
+
283
+
284
+ def _get_threshold_color(
285
+ value: int | float,
286
+ thresholds: dict[int | float, str],
287
+ spark_style: SparkStyle,
288
+ ) -> str:
289
+ assert spark_style.bar.color is not None
290
+ color = spark_style.bar.color
291
+ for threshold in sorted(thresholds.keys()):
292
+ if value >= threshold:
293
+ color = thresholds[threshold]
294
+ return color
295
+
296
+
297
+ def render_spark_bar(
298
+ value: int | float,
299
+ max_value: float | None = None,
300
+ width: float | None = None,
301
+ height: float | None = None,
302
+ color: str | None = None,
303
+ background: str | None = None,
304
+ thresholds: dict[int | float, str] | None = None,
305
+ border_radius: float | None = None,
306
+ value_visible: bool = False,
307
+ value_suffix: str | None = None,
308
+ font: FontStyle | None = None,
309
+ *,
310
+ normalize: bool = False,
311
+ resolved_style: MergedChartsStyle,
312
+ ) -> str:
313
+ """Render a single horizontal bar as SVG.
314
+
315
+ Two variants share this renderer:
316
+ - `bar` (normalize=False): no background track. The caller is expected to
317
+ pass a column-scoped `max_value` so widths read as magnitude-proportional.
318
+ - `bar-normalize` (normalize=True): background track always drawn — the
319
+ width reads as "% of max".
320
+
321
+ Args:
322
+ value: Single numeric value.
323
+ max_value: Upper bound for width scaling. Falls back to the theme
324
+ `default_max` when None.
325
+ normalize: True → `bar-normalize` (with background track);
326
+ False → `bar` (no track, magnitude only).
327
+ Other args: see field docs.
328
+ """
329
+ spark_cfg = resolved_style.spark
330
+ assert spark_cfg.bar.color is not None
331
+ assert spark_cfg.bar.background is not None
332
+ bar_config = spark_cfg.bar
333
+ max_value = bar_config.default_max if max_value is None else max_value
334
+ width = _SPARK_BAR_WIDTH if width is None else width
335
+ height = _SPARK_BAR_HEIGHT if height is None else height
336
+ background = spark_cfg.bar.background if background is None else background
337
+ border_radius = bar_config.border.radius if border_radius is None else border_radius
338
+
339
+ clamped_value = max(0, min(value, max_value))
340
+ fill_pct = (clamped_value / max_value) if max_value > 0 else 0
341
+ fill_width = fill_pct * width
342
+
343
+ if color:
344
+ fill_color = color
345
+ elif thresholds:
346
+ fill_color = _get_threshold_color(clamped_value, thresholds, spark_cfg)
347
+ else:
348
+ fill_color = spark_cfg.bar.color
349
+
350
+ escaped_fill = html.escape(fill_color)
351
+
352
+ bg_rect = ""
353
+ if normalize:
354
+ escaped_bg = html.escape(background)
355
+ bg_rect = (
356
+ f'<rect x="0" y="0" width="{width}" height="{height}" '
357
+ f'fill="{escaped_bg}" rx="{border_radius}"/>'
358
+ )
359
+ fill_rect = (
360
+ f'<rect x="0" y="0" width="{fill_width:.1f}" height="{height}" '
361
+ f'fill="{escaped_fill}" rx="{border_radius}"/>'
362
+ if fill_width > 0
363
+ else ""
364
+ )
365
+
366
+ value_label = ""
367
+ if value_visible:
368
+ display = (
369
+ f"{value:g}"
370
+ if isinstance(value, int) or value.is_integer()
371
+ else f"{value:.1f}"
372
+ )
373
+ if value_suffix:
374
+ display += value_suffix
375
+ text_x = width - bar_config.label.inset_x
376
+ text_y = height / 2
377
+ _font_size = font.size if font is not None else None
378
+ fs = (
379
+ _font_size
380
+ if _font_size is not None
381
+ else max(
382
+ bar_config.label.min_size,
383
+ height - bar_config.label.height_offset,
384
+ )
385
+ )
386
+ # cascade guarantees table.font.family is non-None after _apply_cascade
387
+ _label_family = (
388
+ font.family if font is not None else None
389
+ ) or resolved_style.table.font.family
390
+ assert _label_family is not None
391
+ value_label = (
392
+ f'<text x="{text_x}" y="{text_y}" '
393
+ f'font-size="{fs}" fill="{bar_config.label.fill}" fill-opacity="{bar_config.label.fill_opacity}" '
394
+ f'text-anchor="end" dominant-baseline="central" '
395
+ f'font-family="{html.escape(str(_label_family))}" '
396
+ f'style="font-variant-numeric: tabular-nums lining-nums;">'
397
+ f"{html.escape(display)}</text>"
398
+ )
399
+
400
+ return _svg_wrapper(bg_rect + fill_rect + value_label, width, height)
401
+
402
+
403
+ def render_spark(
404
+ data: Any,
405
+ spark_type: str,
406
+ width: float | None = None,
407
+ height: float | None = None,
408
+ color: str | None = None,
409
+ *,
410
+ font: FontStyle | None = None,
411
+ resolved_style: MergedChartsStyle,
412
+ **options: Any,
413
+ ) -> str:
414
+ """Render a spark chart based on type.
415
+
416
+ Args:
417
+ spark_type: One of "line", "area", "bar", "bar-normalize", "columns".
418
+ """
419
+ spark_cfg = resolved_style.spark
420
+ if spark_type in ("bar", "bar-normalize"):
421
+ w = width or _SPARK_BAR_WIDTH
422
+ h = height or None
423
+ c = color or spark_cfg.bar.color
424
+ else:
425
+ w = width or _SPARK_WIDTH
426
+ h = height or _SPARK_HEIGHT
427
+ c = color or spark_cfg.color
428
+
429
+ if spark_type in ("line", "area", "columns"):
430
+ if data is None:
431
+ values: list[int | float] = []
432
+ elif isinstance(data, (list, tuple)):
433
+ values = list(data)
434
+ else:
435
+ values = [data]
436
+
437
+ if spark_type == "line":
438
+ return render_spark_line(
439
+ values,
440
+ width=w,
441
+ height=h,
442
+ color=c,
443
+ last_visible=options.get("last_visible", False),
444
+ min_max_visible=options.get("min_max_visible", False),
445
+ resolved_style=resolved_style,
446
+ )
447
+ elif spark_type == "area":
448
+ return render_spark_area(
449
+ values,
450
+ width=w,
451
+ height=h,
452
+ color=c,
453
+ fill_opacity=options.get("fill_opacity"),
454
+ last_visible=options.get("last_visible", False),
455
+ resolved_style=resolved_style,
456
+ )
457
+ else: # spark_type == "columns"
458
+ return render_spark_columns(
459
+ values, width=w, height=h, color=c, resolved_style=resolved_style
460
+ )
461
+
462
+ if spark_type in ("bar", "bar-normalize"):
463
+ if isinstance(data, (int, float)):
464
+ val = float(data)
465
+ elif isinstance(data, (list, tuple)) and len(data) > 0:
466
+ first = data[0]
467
+ if isinstance(first, (int, float)):
468
+ val = float(first)
469
+ else:
470
+ try:
471
+ val = float(str(first))
472
+ except (TypeError, ValueError):
473
+ val = 0.0
474
+ else:
475
+ try:
476
+ val = float(str(data))
477
+ except (TypeError, ValueError):
478
+ val = 0.0
479
+
480
+ return render_spark_bar(
481
+ val,
482
+ max_value=options.get("max"),
483
+ width=w,
484
+ height=h,
485
+ color=c if color else None,
486
+ background=options.get("background"),
487
+ thresholds=options.get("thresholds"),
488
+ border_radius=options.get("border_radius"),
489
+ value_visible=options.get("value_visible", False),
490
+ value_suffix=options.get("value_suffix"),
491
+ font=font,
492
+ normalize=(spark_type == "bar-normalize"),
493
+ resolved_style=resolved_style,
494
+ )
495
+
496
+ raise ValueError(f"Unknown spark type: {spark_type}")