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,154 @@
1
+ """Markdown report file loader and transformer.
2
+
3
+ Translates .md files with YAML frontmatter and {{ chart <id> }} embeds
4
+ into YAML face definitions that the normal compile pipeline accepts.
5
+
6
+ This is a pre-compile step — the output is a YAML string that feeds
7
+ directly into compile().
8
+ """
9
+
10
+ import re
11
+ from pathlib import Path
12
+
13
+ import yaml
14
+
15
+ _CHART_EMBED_RE = re.compile(r"^\s*\{\{\s*chart\s+(\w+)\s*\}\}\s*$", re.MULTILINE)
16
+
17
+ # Frontmatter keys that indicate a Dataface markdown report
18
+ _DATAFACE_KEYS = {"queries", "charts", "variables", "source", "sources"}
19
+
20
+
21
+ def parse_chart_embeds(body: str) -> list[tuple[str, str]]:
22
+ """Split markdown body into ordered (type, value) blocks.
23
+
24
+ Returns a list of tuples:
25
+ ("text", <markdown text>)
26
+ ("chart", <chart_id>)
27
+
28
+ Empty/whitespace-only blocks are dropped.
29
+ """
30
+ if not body or not body.strip():
31
+ return []
32
+
33
+ blocks: list[tuple[str, str]] = []
34
+ last_end = 0
35
+
36
+ for match in _CHART_EMBED_RE.finditer(body):
37
+ # Text before this embed
38
+ text = body[last_end : match.start()].strip()
39
+ if text:
40
+ blocks.append(("text", text))
41
+ blocks.append(("chart", match.group(1)))
42
+ last_end = match.end()
43
+
44
+ # Trailing text after last embed
45
+ text = body[last_end:].strip()
46
+ if text:
47
+ blocks.append(("text", text))
48
+
49
+ return blocks
50
+
51
+
52
+ def _extract_frontmatter(md_text: str) -> tuple[dict, str]:
53
+ """Extract YAML frontmatter and markdown body from text.
54
+
55
+ Raises ValueError if frontmatter is missing or empty.
56
+ """
57
+ if not md_text.startswith("---\n"):
58
+ raise ValueError(
59
+ "Markdown face file must have YAML frontmatter (missing opening ---)"
60
+ )
61
+
62
+ end = md_text.find("\n---\n", 4)
63
+ if end == -1:
64
+ # Check for --- at very end of file
65
+ if md_text.endswith("\n---"):
66
+ end = len(md_text) - 3
67
+ else:
68
+ raise ValueError(
69
+ "Markdown face file must have YAML frontmatter (missing closing ---)"
70
+ )
71
+
72
+ fm_text = md_text[4:end]
73
+ body = md_text[end + 5 :] if end + 5 <= len(md_text) else ""
74
+
75
+ fm = yaml.safe_load(fm_text)
76
+ if not fm or not isinstance(fm, dict):
77
+ raise ValueError("Markdown face file must have non-empty YAML frontmatter")
78
+
79
+ return fm, body
80
+
81
+
82
+ def markdown_to_yaml(md_text: str) -> str:
83
+ """Transform a markdown face file into a YAML string for the compiler.
84
+
85
+ Parses frontmatter, splits the body into content/chart blocks,
86
+ and emits a face definition with a rows layout.
87
+ """
88
+ fm, body = _extract_frontmatter(md_text)
89
+
90
+ # Build rows from markdown body
91
+ blocks = parse_chart_embeds(body)
92
+ rows: list = []
93
+ for block_type, value in blocks:
94
+ if block_type == "text":
95
+ rows.append({"text": value})
96
+ elif block_type == "chart":
97
+ rows.append(value)
98
+
99
+ # If body had content but no embeds, ensure we have at least one row
100
+ if not rows and body.strip():
101
+ rows.append({"text": body.strip()})
102
+
103
+ # Build the face dict from frontmatter + generated rows
104
+ face_dict = dict(fm)
105
+ if rows:
106
+ face_dict["rows"] = rows
107
+ elif "text" not in face_dict:
108
+ # No body content and no content in frontmatter — add empty content
109
+ face_dict["text"] = ""
110
+
111
+ return yaml.dump(face_dict, default_flow_style=False, allow_unicode=True)
112
+
113
+
114
+ def is_markdown_face(path: Path | str) -> bool:
115
+ """Check if a .md file is a Dataface markdown report.
116
+
117
+ Detection rules:
118
+ 1. Must have .md extension
119
+ 2. Must have YAML frontmatter
120
+ 3. Either in a faces/ directory, OR frontmatter contains Dataface keys
121
+ """
122
+ path = Path(path)
123
+ if path.suffix.lower() != ".md":
124
+ return False
125
+
126
+ if not path.exists():
127
+ return False
128
+
129
+ try:
130
+ text = path.read_text(encoding="utf-8")
131
+ except (OSError, UnicodeDecodeError):
132
+ return False
133
+
134
+ # Must have frontmatter
135
+ if not text.startswith("---\n"):
136
+ return False
137
+ end = text.find("\n---\n", 4)
138
+ if end == -1:
139
+ return False
140
+
141
+ # In faces/ directory — any frontmatter is enough
142
+ if "faces" in path.parts:
143
+ return True
144
+
145
+ # Outside faces/ — require Dataface-specific keys in frontmatter
146
+ try:
147
+ fm = yaml.safe_load(text[4:end])
148
+ except yaml.YAMLError:
149
+ return False
150
+
151
+ if not isinstance(fm, dict):
152
+ return False
153
+
154
+ return bool(fm.keys() & _DATAFACE_KEYS)
@@ -0,0 +1,388 @@
1
+ """Meta.yaml cascading configuration resolution.
2
+
3
+ Stage: COMPILE
4
+ Purpose: Resolve meta.yaml chain from face file to repo root and merge
5
+ configurations according to cascading rules.
6
+
7
+ Entry Points:
8
+ - resolve_meta_chain(face_path: Path) -> MetaConfig
9
+ - merge_meta_with_face(meta: MetaConfig, face_data: Dict) -> Dict
10
+ - load_meta_file(meta_path: Path) -> MetaConfig
11
+
12
+ This module implements directory-level configuration files (meta.yaml) that
13
+ cascade settings to all faces in that directory and subdirectories.
14
+
15
+ Merge Rules:
16
+ - EXTEND (merge lists/dicts): access, queries, charts
17
+ - OVERRIDE (child wins): source, face, style, typography
18
+
19
+ ignore_meta Flag:
20
+ - Root level: ignore_meta: true ignores ALL parent meta
21
+ - Field level: access: { ignore_meta: true, ... } ignores just that field
22
+
23
+ Dependencies:
24
+ - yaml
25
+ - pathlib
26
+
27
+ See also:
28
+ - config.py: MetaConfig type definition
29
+ - compiler.py: Integration point
30
+ """
31
+
32
+ from pathlib import Path
33
+ from typing import Any
34
+
35
+ import yaml
36
+
37
+ from dataface.core.compile.config import AccessConfig, MetaConfig
38
+ from dataface.core.compile.errors import CompilationError
39
+
40
+ # ============================================================================
41
+ # META.YAML FILE LOADING
42
+ # ============================================================================
43
+
44
+
45
+ def load_meta_file(meta_path: Path) -> MetaConfig:
46
+ """Load a meta.yaml file and parse it into MetaConfig.
47
+
48
+ Args:
49
+ meta_path: Path to the meta.yaml file
50
+
51
+ Returns:
52
+ MetaConfig instance
53
+
54
+ Raises:
55
+ CompilationError: If file cannot be parsed
56
+ """
57
+ if not meta_path.exists():
58
+ raise CompilationError(f"Meta file not found: {meta_path}")
59
+
60
+ try:
61
+ with open(meta_path) as f:
62
+ data = yaml.safe_load(f) or {}
63
+ except yaml.YAMLError as e:
64
+ raise CompilationError(f"Failed to parse meta.yaml: {e}") from e
65
+
66
+ # Validate no unsupported fields
67
+ unsupported = {"sources", "variables", "faces"}
68
+ found_unsupported = unsupported.intersection(data.keys())
69
+ if found_unsupported:
70
+ raise CompilationError(
71
+ f"Unsupported fields in meta.yaml: {found_unsupported}. "
72
+ f"'sources' must be in dataface.yml, 'variables' and 'faces' "
73
+ f"must be defined in face files."
74
+ )
75
+
76
+ return MetaConfig.from_dict(data, path=meta_path)
77
+
78
+
79
+ def find_meta_files(face_path: Path, root_path: Path | None = None) -> list[Path]:
80
+ """Find all meta.yaml files from face directory up to root.
81
+
82
+ Walks up the directory tree from the face file's directory to the
83
+ root path, collecting all meta.yaml files found.
84
+
85
+ Args:
86
+ face_path: Path to the face file
87
+ root_path: Root directory to stop searching (default: filesystem root)
88
+
89
+ Returns:
90
+ List of meta.yaml paths, ordered from root to face directory
91
+ (so child configs come last for proper override order)
92
+ """
93
+ meta_files: list[Path] = []
94
+
95
+ # Start from the face's directory
96
+ current_dir = face_path.parent.resolve()
97
+
98
+ # If no root specified, try to find git root or use filesystem root
99
+ if root_path is None:
100
+ root_path = find_project_root(current_dir)
101
+
102
+ root_path = root_path.resolve()
103
+
104
+ # Walk up from face directory to root
105
+ while True:
106
+ # Check for meta.yaml (and meta.yml variant)
107
+ for meta_name in ["meta.yaml", "meta.yml"]:
108
+ meta_path = current_dir / meta_name
109
+ if meta_path.exists():
110
+ meta_files.append(meta_path)
111
+ break # Only use one variant per directory
112
+
113
+ # Stop if we've reached the root
114
+ if current_dir == root_path or current_dir.parent == current_dir:
115
+ break
116
+
117
+ current_dir = current_dir.parent
118
+
119
+ # Reverse so root comes first (proper merge order: parent -> child)
120
+ meta_files.reverse()
121
+
122
+ return meta_files
123
+
124
+
125
+ def find_project_root(start_dir: Path) -> Path:
126
+ """Find the project root directory.
127
+
128
+ Looks for common project markers (.git, dataface.yml, pyproject.toml).
129
+
130
+ Args:
131
+ start_dir: Directory to start searching from
132
+
133
+ Returns:
134
+ Project root path, or filesystem root if not found
135
+ """
136
+ current = start_dir.resolve()
137
+ markers = [".git", "dataface.yml", "dataface.yaml", "pyproject.toml"]
138
+
139
+ while current.parent != current:
140
+ for marker in markers:
141
+ if (current / marker).exists():
142
+ return current
143
+ current = current.parent
144
+
145
+ return start_dir # Fallback to start directory
146
+
147
+
148
+ # ============================================================================
149
+ # META CONFIGURATION MERGING
150
+ # ============================================================================
151
+
152
+
153
+ def resolve_meta_chain(
154
+ face_path: Path,
155
+ root_path: Path | None = None,
156
+ ) -> MetaConfig:
157
+ """Resolve the complete meta.yaml chain for a face file.
158
+
159
+ Walks up directory tree from face file to root, collects all meta.yaml
160
+ files, and merges them according to the cascading rules.
161
+
162
+ Args:
163
+ face_path: Path to the face file
164
+ root_path: Root directory to stop searching
165
+
166
+ Returns:
167
+ Merged MetaConfig combining all parent configurations
168
+ """
169
+ meta_files = find_meta_files(face_path, root_path)
170
+
171
+ if not meta_files:
172
+ # No meta files found, return empty config
173
+ return MetaConfig()
174
+
175
+ # Load all meta configs
176
+ meta_configs: list[MetaConfig] = []
177
+ for meta_path in meta_files:
178
+ try:
179
+ meta_config = load_meta_file(meta_path)
180
+ meta_configs.append(meta_config)
181
+ except CompilationError:
182
+ # Skip invalid meta files with a warning (could log here)
183
+ continue
184
+
185
+ if not meta_configs:
186
+ return MetaConfig()
187
+
188
+ # Merge configs from root to leaf
189
+ result = meta_configs[0]
190
+ for child_config in meta_configs[1:]:
191
+ result = _merge_meta_configs(result, child_config)
192
+
193
+ return result
194
+
195
+
196
+ def _merge_meta_configs(parent: MetaConfig, child: MetaConfig) -> MetaConfig:
197
+ """Merge two MetaConfig objects according to cascading rules.
198
+
199
+ Merge rules:
200
+ - EXTEND (merge lists/dicts): access, queries, charts
201
+ - OVERRIDE (child wins): source, face, style, typography
202
+
203
+ ignore_meta handling:
204
+ - If child.ignore_meta is True, return child (ignore all parent)
205
+ - Field-level ignore_meta (e.g., access.ignore_meta) ignores just that field
206
+
207
+ Args:
208
+ parent: Parent (ancestor) configuration
209
+ child: Child (closer to face) configuration
210
+
211
+ Returns:
212
+ Merged MetaConfig
213
+ """
214
+ # If child ignores all parent meta, return child as-is
215
+ if child.ignore_meta:
216
+ return child
217
+
218
+ # Merge lint config: union of ignore lists, union inner lists per query
219
+ merged_lint_ignore = list(dict.fromkeys(parent.lint_ignore + child.lint_ignore))
220
+ merged_lint_ignore_queries: dict[str, list[str]] = dict(parent.lint_ignore_queries)
221
+ for k, v in child.lint_ignore_queries.items():
222
+ merged_lint_ignore_queries[k] = list(
223
+ dict.fromkeys((merged_lint_ignore_queries.get(k) or []) + v)
224
+ )
225
+
226
+ # Merge each field according to its rule
227
+ return MetaConfig(
228
+ path=child.path,
229
+ ignore_meta=False,
230
+ access=_merge_access(parent.access, child.access),
231
+ source=child.source if child.source else parent.source, # Override
232
+ queries=_merge_dicts(parent.queries, child.queries), # Extend
233
+ charts=_merge_dicts(parent.charts, child.charts), # Extend
234
+ face=_merge_dicts(parent.face, child.face), # Override (child wins)
235
+ style=_merge_dicts(parent.style, child.style), # Override (child wins)
236
+ typography=_merge_dicts(
237
+ parent.typography, child.typography
238
+ ), # Override (child wins)
239
+ lint_ignore=merged_lint_ignore,
240
+ lint_ignore_queries=merged_lint_ignore_queries,
241
+ )
242
+
243
+
244
+ def _merge_access(
245
+ parent: AccessConfig | None,
246
+ child: AccessConfig | None,
247
+ ) -> AccessConfig | None:
248
+ """Merge access configurations.
249
+
250
+ Access rules are EXTENDED (merged), unless child has ignore_meta.
251
+
252
+ Args:
253
+ parent: Parent access config
254
+ child: Child access config
255
+
256
+ Returns:
257
+ Merged access config
258
+ """
259
+ if child is None:
260
+ return parent
261
+ if parent is None:
262
+ return child
263
+
264
+ # If child ignores parent access, use only child
265
+ if child.ignore_meta:
266
+ return AccessConfig(rules=child.rules, ignore_meta=False)
267
+
268
+ # Extend: combine rules from both
269
+ merged_rules = parent.rules + child.rules
270
+ return AccessConfig(rules=merged_rules, ignore_meta=False)
271
+
272
+
273
+ def _merge_dicts(parent: dict[str, Any], child: dict[str, Any]) -> dict[str, Any]:
274
+ """Merge two dictionaries with child overriding parent.
275
+
276
+ For EXTEND fields (queries, charts), items are merged with child taking
277
+ precedence for same keys.
278
+
279
+ For OVERRIDE fields (face, style, typography), child values completely
280
+ replace parent values for the same keys.
281
+
282
+ Args:
283
+ parent: Parent dictionary
284
+ child: Child dictionary
285
+
286
+ Returns:
287
+ Merged dictionary
288
+ """
289
+ if not parent:
290
+ return dict(child)
291
+ if not child:
292
+ return dict(parent)
293
+
294
+ result = dict(parent)
295
+ result.update(child)
296
+ return result
297
+
298
+
299
+ # ============================================================================
300
+ # FACE INTEGRATION
301
+ # ============================================================================
302
+
303
+
304
+ def merge_meta_with_face(
305
+ meta: MetaConfig,
306
+ face_data: dict[str, Any],
307
+ ) -> dict[str, Any]:
308
+ """Merge resolved meta configuration with face's own definitions.
309
+
310
+ The face's own definitions take precedence over meta settings.
311
+ This applies the meta configuration as "defaults" that the face can override.
312
+
313
+ Args:
314
+ meta: Resolved MetaConfig from the directory chain
315
+ face_data: Raw face data from YAML
316
+
317
+ Returns:
318
+ Face data with meta settings applied as defaults
319
+ """
320
+ if meta.is_empty():
321
+ return face_data
322
+
323
+ result = dict(face_data)
324
+
325
+ # Check if face ignores meta entirely
326
+ if result.get("ignore_meta", False):
327
+ # Remove ignore_meta key and return face as-is
328
+ result.pop("ignore_meta", None)
329
+ return result
330
+
331
+ # Apply source as default (face can override)
332
+ if meta.source and "source" not in result:
333
+ result["source"] = meta.source
334
+
335
+ # Merge queries (meta queries are defaults, face can override)
336
+ if meta.queries:
337
+ merged_queries = dict(meta.queries)
338
+ merged_queries.update(result.get("queries", {}))
339
+ result["queries"] = merged_queries
340
+
341
+ # Merge charts (meta charts are defaults, face can override)
342
+ if meta.charts:
343
+ merged_charts = dict(meta.charts)
344
+ merged_charts.update(result.get("charts", {}))
345
+ result["charts"] = merged_charts
346
+
347
+ # Apply style as defaults (face style overrides)
348
+ if meta.style:
349
+ merged_style = dict(meta.style)
350
+ merged_style.update(result.get("style", {}))
351
+ result["style"] = merged_style
352
+
353
+ # Apply face config as defaults
354
+ if meta.face:
355
+ for key, value in meta.face.items():
356
+ if key not in result:
357
+ result[key] = value
358
+
359
+ # Apply typography as defaults
360
+ if meta.typography:
361
+ merged_typography = dict(meta.typography)
362
+ merged_typography.update(result.get("typography", {}))
363
+ result["typography"] = merged_typography
364
+
365
+ # Note: access is typically handled at a different layer (routing/auth)
366
+ # We store it in the result for downstream processing
367
+ if meta.access and "access" not in result:
368
+ result["access"] = {"rules": meta.access.rules}
369
+
370
+ return result
371
+
372
+
373
+ def get_meta_for_face(
374
+ face_path: Path,
375
+ root_path: Path | None = None,
376
+ ) -> MetaConfig:
377
+ """Convenience function to get resolved meta config for a face.
378
+
379
+ This is the main entry point for compiler integration.
380
+
381
+ Args:
382
+ face_path: Path to the face file
383
+ root_path: Optional project root path
384
+
385
+ Returns:
386
+ Resolved MetaConfig for the face
387
+ """
388
+ return resolve_meta_chain(face_path, root_path)
File without changes
File without changes