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,461 @@
1
+ """Full-text + filter search over the resolver's materialized schema search index.
2
+
3
+ Sibling to ``LayeredSchemaResolver``: not a ``SchemaSource`` Protocol
4
+ method. Find/filter is a different access pattern from drill-down.
5
+
6
+ Substring + regex match across text-bearing fields, layered on top of
7
+ structured predicate filters. Stable iteration order — no ranking, no
8
+ scoring, no top-N. The schema search index is materialized fresh each
9
+ call through the resolver; the resolver's own per-source caching keeps
10
+ it cheap.
11
+
12
+ Provenance flows through unchanged from the resolver: a hit on a name
13
+ that came from the adapter carries ``source='dbt_adapter'``; a hit on a
14
+ description that came from the cache carries ``source='super_schema'``;
15
+ a hit on a tag/test/relationships from the manifest carries
16
+ ``source='dbt_manifest'``.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import fnmatch
22
+ import re
23
+ from dataclasses import dataclass
24
+ from typing import TYPE_CHECKING, Any
25
+
26
+ from pydantic import BaseModel, Field
27
+
28
+ if TYPE_CHECKING:
29
+ from dataface.core.inspect.resolver import LayeredSchemaResolver
30
+
31
+
32
+ _SNIPPET_INLINE_LIMIT: int = 200
33
+ _SNIPPET_WINDOW: int = 80
34
+
35
+
36
+ class InvalidRegex(ValueError):
37
+ """Raised when ``regex=True`` is paired with a non-compilable pattern."""
38
+
39
+
40
+ class SchemaSearchHit(BaseModel):
41
+ """One match emitted by ``SchemaSearch.search``.
42
+
43
+ ``location`` is the dot-joined path: ``"<source>.<schema>.<table>"`` for a
44
+ table hit, ``"<source>.<schema>.<table>.<column>"`` for a column hit.
45
+ ``matched_field`` says which text-bearing field matched ("name",
46
+ "description", "tag", "tests.<name>", "meta.<key>", "owner", or
47
+ "_missing:<field>" for synthetic missing-field hits). ``source`` is
48
+ provenance — which layer contributed the field ("super_schema",
49
+ "dbt_manifest", "dbt_adapter").
50
+ """
51
+
52
+ location: str
53
+ matched_field: str
54
+ snippet: str
55
+ source: str
56
+ value: str
57
+ attrs: dict[str, Any] = Field(
58
+ default_factory=dict,
59
+ description="Structured metadata attributes (role, tag, fk_to, etc.).",
60
+ )
61
+
62
+
63
+ @dataclass
64
+ class _SchemaSearchEntry:
65
+ """One table or column row, with its searchable fields and structured attrs."""
66
+
67
+ location: str
68
+ is_column: bool
69
+ consulted: tuple[str, ...]
70
+ text_fields: list[tuple[str, str]] # (matched_field, value) — order-stable
71
+ attrs: dict[str, Any] # structured filter inputs (role, tags, tests, ...)
72
+
73
+
74
+ class SchemaSearch:
75
+ """Full-text + filter search over ``LayeredSchemaResolver``."""
76
+
77
+ def __init__(self, resolver: LayeredSchemaResolver) -> None:
78
+ self.resolver = resolver
79
+
80
+ def search(
81
+ self,
82
+ keyword: str,
83
+ *,
84
+ scope: list[str] | None = None,
85
+ regex: bool = False,
86
+ role: str | None = None,
87
+ tag: str | None = None,
88
+ has_test: str | None = None,
89
+ missing: str | None = None,
90
+ meta: dict[str, str] | None = None,
91
+ column_name: str | None = None,
92
+ table_name: str | None = None,
93
+ fk_to: str | None = None,
94
+ ) -> tuple[list[SchemaSearchHit], list[str]]:
95
+ pattern = self._compile_keyword(keyword, regex=regex)
96
+ hits: list[SchemaSearchHit] = []
97
+ entries, warnings = self._materialize_index()
98
+ for entry in entries:
99
+ if not self._matches_filters(
100
+ entry,
101
+ role=role,
102
+ tag=tag,
103
+ has_test=has_test,
104
+ meta=meta,
105
+ column_name=column_name,
106
+ table_name=table_name,
107
+ fk_to=fk_to,
108
+ ):
109
+ continue
110
+ if missing is not None:
111
+ hits.extend(self._missing_hits(entry, missing))
112
+ continue
113
+ if not keyword and any(
114
+ f is not None
115
+ for f in (role, tag, has_test, meta, column_name, table_name, fk_to)
116
+ ):
117
+ # Empty keyword + structured filter = enumerate qualifying entries
118
+ # without text matching. One hit per scope-eligible field.
119
+ hits.extend(self._unfiltered_hits(entry, scope))
120
+ continue
121
+ hits.extend(self._keyword_hits(entry, pattern, scope))
122
+ return hits, warnings
123
+
124
+ # ---- Schema index walk --------------------------------------------------
125
+
126
+ def _materialize_index(self) -> tuple[list[_SchemaSearchEntry], list[str]]:
127
+ """Walk source → schema → table → column. Stable order throughout.
128
+
129
+ Per-source / per-schema failures (unreachable warehouse, missing dbt
130
+ adapter package, etc.) are turned into ``warnings`` strings so the
131
+ verb wrapper can surface them — silent skips would let the user mistake
132
+ an incomplete schema walk for "no matches".
133
+
134
+ Calls ``profile_table`` per-table so each entry carries its own
135
+ ``sources_consulted`` — required for accurate per-field provenance
136
+ when a schema has a mix of cached and uncached tables.
137
+ """
138
+ entries: list[_SchemaSearchEntry] = []
139
+ warnings: list[str] = []
140
+ for source_entry in self.resolver.adapter_registry.list_sql_sources():
141
+ source_name = source_entry["name"]
142
+ try:
143
+ schemas_resp = self.resolver.list_schemas(source_name)
144
+ except Exception as exc: # noqa: BLE001 — surface as warning
145
+ warnings.append(f"source '{source_name}': {exc}")
146
+ continue
147
+ schemas = (
148
+ schemas_resp.get("sources", {}).get(source_name, {}).get("schemas", {})
149
+ )
150
+ for schema_name in schemas:
151
+ try:
152
+ tables_resp = self.resolver.list_tables(source_name, schema_name)
153
+ except Exception as exc: # noqa: BLE001 — surface as warning
154
+ warnings.append(
155
+ f"source '{source_name}' schema '{schema_name}': {exc}"
156
+ )
157
+ continue
158
+ table_names = (
159
+ tables_resp.get("sources", {})
160
+ .get(source_name, {})
161
+ .get("schemas", {})
162
+ .get(schema_name, {})
163
+ .get("tables", {})
164
+ )
165
+ for table_name in table_names:
166
+ try:
167
+ profile_resp = self.resolver.profile_table(
168
+ source=source_name, schema=schema_name, table=table_name
169
+ )
170
+ except Exception as exc: # noqa: BLE001 — surface as warning
171
+ warnings.append(
172
+ f"source '{source_name}' schema '{schema_name}' "
173
+ f"table '{table_name}': {exc}"
174
+ )
175
+ continue
176
+ consulted = tuple(
177
+ profile_resp.get("_meta", {}).get("sources_consulted", [])
178
+ )
179
+ table_profile = (
180
+ profile_resp.get("sources", {})
181
+ .get(source_name, {})
182
+ .get("schemas", {})
183
+ .get(schema_name, {})
184
+ .get("tables", {})
185
+ .get(table_name, {})
186
+ )
187
+ entries.append(
188
+ self._table_entry(
189
+ source_name,
190
+ schema_name,
191
+ table_name,
192
+ table_profile,
193
+ consulted,
194
+ )
195
+ )
196
+ for col_name, col_profile in (
197
+ table_profile.get("columns") or {}
198
+ ).items():
199
+ entries.append(
200
+ self._column_entry(
201
+ source_name,
202
+ schema_name,
203
+ table_name,
204
+ col_name,
205
+ col_profile,
206
+ consulted,
207
+ )
208
+ )
209
+ return entries, warnings
210
+
211
+ @staticmethod
212
+ def _table_entry(
213
+ source: str,
214
+ schema: str,
215
+ table: str,
216
+ profile: dict[str, Any],
217
+ consulted: tuple[str, ...],
218
+ ) -> _SchemaSearchEntry:
219
+ loc = f"{source}.{schema}.{table}"
220
+ text_fields: list[tuple[str, str]] = [("name", table)]
221
+ desc = profile.get("description")
222
+ if isinstance(desc, str) and desc:
223
+ text_fields.append(("description", desc))
224
+ owner = profile.get("owner")
225
+ if isinstance(owner, str) and owner:
226
+ text_fields.append(("owner", owner))
227
+ for t in profile.get("tags") or []:
228
+ if isinstance(t, str) and t:
229
+ text_fields.append(("tag", t))
230
+ attrs: dict[str, Any] = {}
231
+ for k in ("tags", "owner", "row_count", "kind", "grain"):
232
+ if k in profile:
233
+ attrs[k] = profile[k]
234
+ return _SchemaSearchEntry(
235
+ location=loc,
236
+ is_column=False,
237
+ consulted=consulted,
238
+ text_fields=text_fields,
239
+ attrs=attrs,
240
+ )
241
+
242
+ @staticmethod
243
+ def _column_entry(
244
+ source: str,
245
+ schema: str,
246
+ table: str,
247
+ column: str,
248
+ profile: dict[str, Any],
249
+ consulted: tuple[str, ...],
250
+ ) -> _SchemaSearchEntry:
251
+ loc = f"{source}.{schema}.{table}.{column}"
252
+ text_fields: list[tuple[str, str]] = [("name", column)]
253
+ desc = profile.get("description")
254
+ if isinstance(desc, str) and desc:
255
+ text_fields.append(("description", desc))
256
+ for t in profile.get("tags") or []:
257
+ if isinstance(t, str) and t:
258
+ text_fields.append(("tag", t))
259
+ for test in profile.get("tests") or []:
260
+ tname = test.get("name") if isinstance(test, dict) else None
261
+ if isinstance(tname, str) and tname:
262
+ text_fields.append((f"tests.{tname}", tname))
263
+ meta = profile.get("meta")
264
+ if isinstance(meta, dict):
265
+ for k, v in meta.items():
266
+ if isinstance(v, str) and v:
267
+ text_fields.append((f"meta.{k}", v))
268
+ attrs: dict[str, Any] = {}
269
+ for k in (
270
+ "role",
271
+ "key_role",
272
+ "semantic_type",
273
+ "tags",
274
+ "tests",
275
+ "relationships",
276
+ "meta",
277
+ "type",
278
+ "actual_type",
279
+ "declared_type",
280
+ ):
281
+ if k in profile:
282
+ attrs[k] = profile[k]
283
+ return _SchemaSearchEntry(
284
+ location=loc,
285
+ is_column=True,
286
+ consulted=consulted,
287
+ text_fields=text_fields,
288
+ attrs=attrs,
289
+ )
290
+
291
+ # ---- Match helpers ------------------------------------------------------
292
+
293
+ @staticmethod
294
+ def _compile_keyword(keyword: str, regex: bool) -> re.Pattern[str] | None:
295
+ if not keyword:
296
+ return None
297
+ if regex:
298
+ try:
299
+ return re.compile(keyword, re.IGNORECASE)
300
+ except re.error as exc:
301
+ raise InvalidRegex(f"invalid regex: {exc}") from exc
302
+ return re.compile(re.escape(keyword), re.IGNORECASE)
303
+
304
+ def _matches_filters(
305
+ self,
306
+ entry: _SchemaSearchEntry,
307
+ role: str | None,
308
+ tag: str | None,
309
+ has_test: str | None,
310
+ meta: dict[str, str] | None,
311
+ column_name: str | None,
312
+ table_name: str | None,
313
+ fk_to: str | None,
314
+ ) -> bool:
315
+ if role is not None:
316
+ if not entry.is_column or entry.attrs.get("role") != role:
317
+ return False
318
+ if tag is not None:
319
+ tags = entry.attrs.get("tags") or []
320
+ if tag not in tags:
321
+ return False
322
+ if has_test is not None:
323
+ if has_test == "relationships":
324
+ if not entry.attrs.get("relationships"):
325
+ return False
326
+ else:
327
+ tests = entry.attrs.get("tests") or []
328
+ if not any(
329
+ isinstance(t, dict) and t.get("name") == has_test for t in tests
330
+ ):
331
+ return False
332
+ if fk_to is not None:
333
+ rels = entry.attrs.get("relationships") or []
334
+ if not any(
335
+ isinstance(r, dict) and r.get("to_table") == fk_to for r in rels
336
+ ):
337
+ return False
338
+ if column_name is not None:
339
+ if not entry.is_column:
340
+ return False
341
+ cname = entry.location.rsplit(".", 1)[-1]
342
+ if not fnmatch.fnmatch(cname, column_name):
343
+ return False
344
+ if table_name is not None:
345
+ parts = entry.location.split(".")
346
+ # source.schema.table[.column] — table is index 2.
347
+ if len(parts) < 3 or not fnmatch.fnmatch(parts[2], table_name):
348
+ return False
349
+ if meta is not None:
350
+ entry_meta = entry.attrs.get("meta") or {}
351
+ if not isinstance(entry_meta, dict):
352
+ return False
353
+ for k, v in meta.items():
354
+ if str(entry_meta.get(k)) != v:
355
+ return False
356
+ return True
357
+
358
+ def _missing_hits(
359
+ self, entry: _SchemaSearchEntry, field_name: str
360
+ ) -> list[SchemaSearchHit]:
361
+ present = any(name == field_name for name, _ in entry.text_fields)
362
+ if present:
363
+ return []
364
+ return [
365
+ SchemaSearchHit(
366
+ location=entry.location,
367
+ matched_field=f"_missing:{field_name}",
368
+ snippet="",
369
+ source=self._provenance_for(entry, field_name),
370
+ value="",
371
+ attrs=dict(entry.attrs),
372
+ )
373
+ ]
374
+
375
+ def _unfiltered_hits(
376
+ self, entry: _SchemaSearchEntry, scope: list[str] | None
377
+ ) -> list[SchemaSearchHit]:
378
+ hits: list[SchemaSearchHit] = []
379
+ for matched_field, value in entry.text_fields:
380
+ if scope and not _scope_matches(matched_field, scope):
381
+ continue
382
+ hits.append(
383
+ SchemaSearchHit(
384
+ location=entry.location,
385
+ matched_field=matched_field,
386
+ snippet=_snippet(value, None),
387
+ source=self._provenance_for(entry, matched_field),
388
+ value=value,
389
+ attrs=dict(entry.attrs),
390
+ )
391
+ )
392
+ return hits
393
+
394
+ def _keyword_hits(
395
+ self,
396
+ entry: _SchemaSearchEntry,
397
+ pattern: re.Pattern[str] | None,
398
+ scope: list[str] | None,
399
+ ) -> list[SchemaSearchHit]:
400
+ if pattern is None:
401
+ return []
402
+ hits: list[SchemaSearchHit] = []
403
+ for matched_field, value in entry.text_fields:
404
+ if scope and not _scope_matches(matched_field, scope):
405
+ continue
406
+ m = pattern.search(value)
407
+ if m is None:
408
+ continue
409
+ hits.append(
410
+ SchemaSearchHit(
411
+ location=entry.location,
412
+ matched_field=matched_field,
413
+ snippet=_snippet(value, m),
414
+ source=self._provenance_for(entry, matched_field),
415
+ value=value,
416
+ attrs=dict(entry.attrs),
417
+ )
418
+ )
419
+ return hits
420
+
421
+ @staticmethod
422
+ def _provenance_for(entry: _SchemaSearchEntry, matched_field: str) -> str:
423
+ """Attribute a hit to a single layer using the resolver's consulted set.
424
+
425
+ Cache-only path: everything came from super_schema. Otherwise we
426
+ classify the matched field — manifest-only fields (description,
427
+ tag, tests, owner, meta, relationships, declared_type) attribute
428
+ to ``dbt_manifest`` when the manifest was consulted; everything
429
+ else (names, types) attributes to ``dbt_adapter``.
430
+ """
431
+ if entry.consulted == ("super_schema",):
432
+ return "super_schema"
433
+ manifest_field = matched_field in {"description", "tag", "owner"} or any(
434
+ matched_field.startswith(p) for p in ("tests.", "meta.")
435
+ )
436
+ if manifest_field and "dbt_manifest" in entry.consulted:
437
+ return "dbt_manifest"
438
+ return "dbt_adapter"
439
+
440
+
441
+ # ---- Module helpers --------------------------------------------------------
442
+
443
+
444
+ def _scope_matches(matched_field: str, scope: list[str]) -> bool:
445
+ """Match an emitted matched_field against a list of scope prefixes."""
446
+ head = matched_field.split(".", 1)[0]
447
+ return head in scope or matched_field in scope
448
+
449
+
450
+ def _snippet(value: str, match: re.Match[str] | None) -> str:
451
+ """Whole-field snippet when short; an 80-char window centered on first match."""
452
+ if len(value) < _SNIPPET_INLINE_LIMIT:
453
+ return value
454
+ if match is None:
455
+ return value[:_SNIPPET_WINDOW]
456
+ span = match.start()
457
+ half = _SNIPPET_WINDOW // 2
458
+ start = max(0, span - half)
459
+ end = min(len(value), start + _SNIPPET_WINDOW)
460
+ start = max(0, end - _SNIPPET_WINDOW)
461
+ return value[start:end]
@@ -0,0 +1,32 @@
1
+ """Plugin-boundary schema sources.
2
+
3
+ Schema sources are horizontal contributors of facts about a warehouse
4
+ schema. Implementations are independently testable; the resolver composes
5
+ contributions across sources into the wire response.
6
+
7
+ Two peers ship today:
8
+
9
+ * `SuperSchemaSource` — warm cache backed by ``target/super_schema.json``.
10
+ Surfaces the rich profile (stats, distributions, semantic types, role
11
+ tags, enum lists) that a profiling run computed.
12
+ * `DbtSchemaSource` — cold-start, composes a dbt-core adapter + lazy
13
+ ``target/manifest.json`` reader. Surfaces only what's directly
14
+ available — no inference, no naming heuristics. Honest empty.
15
+
16
+ Each source exposes ``name``, ``generated_at``, ``list_schemas``,
17
+ ``list_tables``, ``profile_table``, and ``describe_query``. Methods return
18
+ bare-scalar ``dict | None`` (no per-field provenance wrapping; provenance
19
+ lives only in the response-level ``_meta`` footer).
20
+ ``None`` means "this source has nothing to contribute" — silent
21
+ no-contribution, not an error. The resolver falls through to the next
22
+ source. Field-name conventions communicate origin: ``actual_type`` came
23
+ from the adapter; ``declared_type`` / ``description`` / ``tests`` /
24
+ ``relationships`` came from the manifest. The resolver stamps a single
25
+ response-level ``_meta`` footer with ``retrieved_at``, ``sources_consulted``,
26
+ and (when super_schema contributed) ``cache_built_at``.
27
+
28
+ Each source is *per-source* by construction: ``SuperSchemaSource`` is one
29
+ instance per project (its cache file is per-project); ``DbtSchemaSource``
30
+ is one instance per warehouse adapter. The resolver routes calls to the
31
+ right instance, so the methods don't take a ``source`` argument.
32
+ """