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.
- d3_format/__init__.py +14 -0
- d3_format/errors.py +19 -0
- d3_format/format.py +551 -0
- d3_format/spec.py +159 -0
- dataface/DATAFACE_SYNTAX.md +1135 -0
- dataface/__init__.py +93 -0
- dataface/_docs_site.py +20 -0
- dataface/_install_hint.py +26 -0
- dataface/agent_api/__init__.py +79 -0
- dataface/agent_api/_init_templates/__init__.py +0 -0
- dataface/agent_api/_init_templates/agents_dft_snippet.md +26 -0
- dataface/agent_api/_init_templates/dataface.yml +15 -0
- dataface/agent_api/_init_templates/faces-dataface.yml +144 -0
- dataface/agent_api/_init_templates/index.md +24 -0
- dataface/agent_api/_paths.py +118 -0
- dataface/agent_api/_project_agents_md.py +43 -0
- dataface/agent_api/_session_store.py +486 -0
- dataface/agent_api/_state.py +28 -0
- dataface/agent_api/chat.py +221 -0
- dataface/agent_api/dashboards.py +257 -0
- dataface/agent_api/describe.py +366 -0
- dataface/agent_api/describe_query.py +120 -0
- dataface/agent_api/docs/__init__.py +25 -0
- dataface/agent_api/docs/_loader.py +292 -0
- dataface/agent_api/docs/yaml-reference.md +2757 -0
- dataface/agent_api/file_refs.py +118 -0
- dataface/agent_api/init.py +126 -0
- dataface/agent_api/inspect.py +128 -0
- dataface/agent_api/mcp_install.py +170 -0
- dataface/agent_api/query.py +274 -0
- dataface/agent_api/schema.py +658 -0
- dataface/agent_api/schema_search.py +284 -0
- dataface/agent_api/search.py +270 -0
- dataface/agent_api/skill_install.py +141 -0
- dataface/agent_api/skill_render.py +90 -0
- dataface/agent_api/skills.py +293 -0
- dataface/agent_api/surface_aliases.yaml +128 -0
- dataface/agent_api/validate.py +175 -0
- dataface/agent_api/validate_query.py +84 -0
- dataface/ai/__init__.py +39 -0
- dataface/ai/agent.py +139 -0
- dataface/ai/context.py +45 -0
- dataface/ai/events.py +62 -0
- dataface/ai/external_mcp.py +610 -0
- dataface/ai/generate_sql.py +96 -0
- dataface/ai/llm.py +403 -0
- dataface/ai/mcp/__init__.py +51 -0
- dataface/ai/mcp/server.py +289 -0
- dataface/ai/memories.py +85 -0
- dataface/ai/prompts.py +177 -0
- dataface/ai/schema_context.py +138 -0
- dataface/ai/skills/before-after-comparison/SKILL.md +102 -0
- dataface/ai/skills/before-after-comparison/examples/before-after-comparison.yml +24 -0
- dataface/ai/skills/dashboard-build/SKILL.md +212 -0
- dataface/ai/skills/dashboard-build/examples/_smoke.yml +15 -0
- dataface/ai/skills/dashboard-design/SKILL.md +182 -0
- dataface/ai/skills/dashboard-review/SKILL.md +113 -0
- dataface/ai/skills/dashboard-structural-review/SKILL.md +173 -0
- dataface/ai/skills/dashboard-visual-review/SKILL.md +139 -0
- dataface/ai/skills/dataface-mcp-setup/SKILL.md +177 -0
- dataface/ai/skills/dataface-troubleshooting/SKILL.md +225 -0
- dataface/ai/skills/drill-down-link/SKILL.md +112 -0
- dataface/ai/skills/drill-down-link/examples/drill-down-link.yml +27 -0
- dataface/ai/skills/faceted-small-multiples/SKILL.md +116 -0
- dataface/ai/skills/faceted-small-multiples/examples/faceted-small-multiples.yml +33 -0
- dataface/ai/skills/filter-bar-with-variables/SKILL.md +105 -0
- dataface/ai/skills/filter-bar-with-variables/examples/filter-bar-with-variables.yml +49 -0
- dataface/ai/skills/kpi-row/SKILL.md +101 -0
- dataface/ai/skills/kpi-row/examples/kpi-row.yml +55 -0
- dataface/ai/skills/report-design/SKILL.md +184 -0
- dataface/ai/skills/single-metric-bignum/SKILL.md +90 -0
- dataface/ai/skills/single-metric-bignum/examples/single-metric-bignum.yml +27 -0
- dataface/ai/skills/table-heavy-ops-dashboard/SKILL.md +114 -0
- dataface/ai/skills/table-heavy-ops-dashboard/examples/table-heavy-ops-dashboard.yml +48 -0
- dataface/ai/skills/time-series-trend/SKILL.md +93 -0
- dataface/ai/skills/time-series-trend/examples/time-series-trend.yml +26 -0
- dataface/ai/skills/top-n-with-detail/SKILL.md +98 -0
- dataface/ai/skills/top-n-with-detail/examples/top-n-with-detail.yml +45 -0
- dataface/ai/skills/two-by-two-grid-overview/SKILL.md +78 -0
- dataface/ai/skills/two-by-two-grid-overview/examples/two-by-two-grid-overview.yml +64 -0
- dataface/ai/tool_schemas.py +132 -0
- dataface/ai/tools/__init__.py +312 -0
- dataface/ai/yaml_utils.py +57 -0
- dataface/cli/__init__.py +3 -0
- dataface/cli/_console.py +48 -0
- dataface/cli/_error_format.py +83 -0
- dataface/cli/_extras.py +190 -0
- dataface/cli/_json_output.py +8 -0
- dataface/cli/_parsing.py +17 -0
- dataface/cli/_version_info.py +56 -0
- dataface/cli/commands/__init__.py +3 -0
- dataface/cli/commands/_agent_input.py +205 -0
- dataface/cli/commands/_agent_server.py +115 -0
- dataface/cli/commands/chat.py +645 -0
- dataface/cli/commands/describe.py +107 -0
- dataface/cli/commands/docs.py +131 -0
- dataface/cli/commands/extension.py +179 -0
- dataface/cli/commands/init.py +240 -0
- dataface/cli/commands/inspect.py +94 -0
- dataface/cli/commands/mcp_init.py +167 -0
- dataface/cli/commands/query.py +386 -0
- dataface/cli/commands/render.py +291 -0
- dataface/cli/commands/schema.py +411 -0
- dataface/cli/commands/search.py +49 -0
- dataface/cli/commands/serve.py +114 -0
- dataface/cli/commands/skills.py +133 -0
- dataface/cli/commands/skills_init.py +161 -0
- dataface/cli/commands/validate.py +63 -0
- dataface/cli/main.py +1501 -0
- dataface/core/__init__.py +75 -0
- dataface/core/compile/__init__.py +244 -0
- dataface/core/compile/_jinja_helpers.py +78 -0
- dataface/core/compile/channel.py +222 -0
- dataface/core/compile/chart_focus.py +101 -0
- dataface/core/compile/chart_resolved.py +169 -0
- dataface/core/compile/chart_type_detection.py +489 -0
- dataface/core/compile/chart_update.py +261 -0
- dataface/core/compile/colors.py +64 -0
- dataface/core/compile/compiler.py +904 -0
- dataface/core/compile/config.py +823 -0
- dataface/core/compile/custom_chart_types.py +208 -0
- dataface/core/compile/data_table_attachment.py +1287 -0
- dataface/core/compile/detect.py +110 -0
- dataface/core/compile/errors.py +302 -0
- dataface/core/compile/filter_injection.py +319 -0
- dataface/core/compile/introspection.py +527 -0
- dataface/core/compile/jinja.py +511 -0
- dataface/core/compile/labels_env.py +52 -0
- dataface/core/compile/markdown.py +154 -0
- dataface/core/compile/meta.py +388 -0
- dataface/core/compile/models/__init__.py +0 -0
- dataface/core/compile/models/chart/__init__.py +0 -0
- dataface/core/compile/models/chart/authored.py +2137 -0
- dataface/core/compile/models/chart/compiled.py +398 -0
- dataface/core/compile/models/config.py +347 -0
- dataface/core/compile/models/face/__init__.py +0 -0
- dataface/core/compile/models/face/authored.py +659 -0
- dataface/core/compile/models/face/compiled.py +522 -0
- dataface/core/compile/models/factories.py +201 -0
- dataface/core/compile/models/markers.py +40 -0
- dataface/core/compile/models/palette.py +36 -0
- dataface/core/compile/models/primitives.py +415 -0
- dataface/core/compile/models/query/__init__.py +0 -0
- dataface/core/compile/models/query/authored.py +246 -0
- dataface/core/compile/models/query/compiled.py +710 -0
- dataface/core/compile/models/refs.py +137 -0
- dataface/core/compile/models/source.py +611 -0
- dataface/core/compile/models/style/__init__.py +0 -0
- dataface/core/compile/models/style/authored.py +481 -0
- dataface/core/compile/models/style/compiled.py +3399 -0
- dataface/core/compile/models/style/merged.py +1682 -0
- dataface/core/compile/models/theme.py +362 -0
- dataface/core/compile/models/variable/__init__.py +0 -0
- dataface/core/compile/models/variable/authored.py +254 -0
- dataface/core/compile/models/vega_lite/__init__.py +0 -0
- dataface/core/compile/models/vega_lite/config.py +510 -0
- dataface/core/compile/models/vega_lite/contracts.py +171 -0
- dataface/core/compile/normalize_charts.py +494 -0
- dataface/core/compile/normalize_layout.py +1000 -0
- dataface/core/compile/normalize_queries.py +297 -0
- dataface/core/compile/normalize_variables.py +489 -0
- dataface/core/compile/normalizer.py +543 -0
- dataface/core/compile/palette.py +1100 -0
- dataface/core/compile/parameterized.py +658 -0
- dataface/core/compile/parser.py +228 -0
- dataface/core/compile/schema.py +20 -0
- dataface/core/compile/schema_renderers/__init__.py +0 -0
- dataface/core/compile/schema_renderers/json_schema.py +163 -0
- dataface/core/compile/schema_renderers/prompt.py +152 -0
- dataface/core/compile/schema_renderers/vscode_schema.py +301 -0
- dataface/core/compile/sizing.py +2126 -0
- dataface/core/compile/sources.py +518 -0
- dataface/core/compile/sql_authoring_lint.py +56 -0
- dataface/core/compile/style_cascade.py +471 -0
- dataface/core/compile/typography.py +299 -0
- dataface/core/compile/validator.py +301 -0
- dataface/core/compile/variables.py +53 -0
- dataface/core/compile/vega_config.py +98 -0
- dataface/core/compile/vega_lite/__init__.py +6 -0
- dataface/core/compile/vega_lite/validation.py +95 -0
- dataface/core/compile/yaml_error_formatter.py +838 -0
- dataface/core/connections.py +38 -0
- dataface/core/dashboard.py +358 -0
- dataface/core/defaults/default_config.yml +101 -0
- dataface/core/defaults/palettes/categorical/category-10-dark.yml +32 -0
- dataface/core/defaults/palettes/categorical/category-10-light.yml +43 -0
- dataface/core/defaults/palettes/categorical/category-10.yml +31 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-blue.yml +22 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-brown.yml +29 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-green.yml +20 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-orange.yml +21 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-purple.yml +20 -0
- dataface/core/defaults/palettes/categorical/editorial-10-dark.yml +32 -0
- dataface/core/defaults/palettes/categorical/editorial-10.yml +40 -0
- dataface/core/defaults/palettes/categorical/hero-6.yml +17 -0
- dataface/core/defaults/palettes/categorical/single-blue.yml +11 -0
- dataface/core/defaults/palettes/categorical/tableau.yml +20 -0
- dataface/core/defaults/palettes/data/xkcd_colors.json +3803 -0
- dataface/core/defaults/palettes/diverging/blue-red.yml +25 -0
- dataface/core/defaults/palettes/diverging/coolwarm.yml +24 -0
- dataface/core/defaults/palettes/diverging/crimson-green.yml +23 -0
- dataface/core/defaults/palettes/diverging/orange-teal.yml +23 -0
- dataface/core/defaults/palettes/diverging/sunset.yml +24 -0
- dataface/core/defaults/palettes/scaffold/dft-creams.yml +38 -0
- dataface/core/defaults/palettes/scaffold/dft-grays.yml +53 -0
- dataface/core/defaults/palettes/sequential/amber.yml +22 -0
- dataface/core/defaults/palettes/sequential/blue.yml +22 -0
- dataface/core/defaults/palettes/sequential/brown.yml +22 -0
- dataface/core/defaults/palettes/sequential/gray.yml +22 -0
- dataface/core/defaults/palettes/sequential/green.yml +22 -0
- dataface/core/defaults/palettes/sequential/purple.yml +22 -0
- dataface/core/defaults/palettes/sequential/rust.yml +22 -0
- dataface/core/defaults/palettes/sequential/teal.yml +22 -0
- dataface/core/defaults/palettes/tone/negative.yml +32 -0
- dataface/core/defaults/palettes/tone/positive.yml +22 -0
- dataface/core/defaults/palettes/tone/warning.yml +22 -0
- dataface/core/defaults/themes/_base.yaml +786 -0
- dataface/core/defaults/themes/bi.yaml +16 -0
- dataface/core/defaults/themes/carbong100.yaml +41 -0
- dataface/core/defaults/themes/cream.yaml +122 -0
- dataface/core/defaults/themes/dark.yaml +40 -0
- dataface/core/defaults/themes/diagnostics-title-angle-extreme.yaml +9 -0
- dataface/core/defaults/themes/diagnostics-title-baseline-extreme.yaml +9 -0
- dataface/core/defaults/themes/diagnostics-title-baseline.yaml +24 -0
- dataface/core/defaults/themes/diagnostics-title-center.yaml +8 -0
- dataface/core/defaults/themes/diagnostics-title-color-extreme.yaml +24 -0
- dataface/core/defaults/themes/diagnostics-title-font-extreme.yaml +25 -0
- dataface/core/defaults/themes/diagnostics-title-left.yaml +8 -0
- dataface/core/defaults/themes/diagnostics-title-offset-extreme.yaml +9 -0
- dataface/core/defaults/themes/diagnostics-title-size-extreme.yaml +24 -0
- dataface/core/defaults/themes/diagnostics-title-weight-extreme.yaml +24 -0
- dataface/core/defaults/themes/editorial.yaml +147 -0
- dataface/core/defaults/themes/light.yaml +30 -0
- dataface/core/defaults/themes/looker.yaml +17 -0
- dataface/core/defaults/themes/stark.yaml +134 -0
- dataface/core/errors/__init__.py +67 -0
- dataface/core/errors/codes_compile.py +56 -0
- dataface/core/errors/codes_execute.py +177 -0
- dataface/core/errors/codes_render.py +106 -0
- dataface/core/errors/codes_unknown.py +15 -0
- dataface/core/errors/hints.py +74 -0
- dataface/core/errors/registry.py +42 -0
- dataface/core/errors/structured.py +92 -0
- dataface/core/execute/__init__.py +91 -0
- dataface/core/execute/adapters/__init__.py +49 -0
- dataface/core/execute/adapters/adapter_registry.py +400 -0
- dataface/core/execute/adapters/base.py +245 -0
- dataface/core/execute/adapters/csv_adapter.py +239 -0
- dataface/core/execute/adapters/dbt_adapter.py +283 -0
- dataface/core/execute/adapters/dbt_adapter_factory.py +212 -0
- dataface/core/execute/adapters/dbt_macro_loader.py +95 -0
- dataface/core/execute/adapters/dbt_utils.py +150 -0
- dataface/core/execute/adapters/http_adapter.py +224 -0
- dataface/core/execute/adapters/metricflow_adapter.py +94 -0
- dataface/core/execute/adapters/schema_resolver_adapter.py +144 -0
- dataface/core/execute/adapters/sql_adapter.py +710 -0
- dataface/core/execute/adapters/values_adapter.py +58 -0
- dataface/core/execute/batch.py +744 -0
- dataface/core/execute/cache_backend.py +135 -0
- dataface/core/execute/cache_keys.py +66 -0
- dataface/core/execute/dbt_jinja.py +21 -0
- dataface/core/execute/dialects/__init__.py +121 -0
- dataface/core/execute/dialects/athena.py +75 -0
- dataface/core/execute/dialects/base.py +302 -0
- dataface/core/execute/dialects/bigquery.py +38 -0
- dataface/core/execute/dialects/databricks.py +68 -0
- dataface/core/execute/dialects/duckdb.py +35 -0
- dataface/core/execute/dialects/mysql.py +68 -0
- dataface/core/execute/dialects/postgres.py +39 -0
- dataface/core/execute/dialects/redshift.py +12 -0
- dataface/core/execute/dialects/snowflake.py +51 -0
- dataface/core/execute/dialects/sqlserver.py +92 -0
- dataface/core/execute/duckdb_cache.py +712 -0
- dataface/core/execute/duckdb_config.py +26 -0
- dataface/core/execute/errors.py +213 -0
- dataface/core/execute/executor.py +1249 -0
- dataface/core/execute/parallel.py +162 -0
- dataface/core/execute/setup_sql.py +58 -0
- dataface/core/execute/source_registry.py +72 -0
- dataface/core/execute/source_resolver.py +255 -0
- dataface/core/execute/sql_guard.py +387 -0
- dataface/core/execute/sql_literals.py +199 -0
- dataface/core/fonts.py +52 -0
- dataface/core/inspect/__init__.py +32 -0
- dataface/core/inspect/cache_factory.py +98 -0
- dataface/core/inspect/db_types.py +162 -0
- dataface/core/inspect/dbt_schema.py +96 -0
- dataface/core/inspect/defaults.yml +37 -0
- dataface/core/inspect/fanout_risk.py +109 -0
- dataface/core/inspect/manifest_utils.py +77 -0
- dataface/core/inspect/partials/categorical.yml +40 -0
- dataface/core/inspect/partials/date.yml +40 -0
- dataface/core/inspect/partials/numeric.yml +55 -0
- dataface/core/inspect/partition_types.py +38 -0
- dataface/core/inspect/query_validator.py +975 -0
- dataface/core/inspect/renderer.py +354 -0
- dataface/core/inspect/resolver.py +808 -0
- dataface/core/inspect/search.py +461 -0
- dataface/core/inspect/sources/__init__.py +32 -0
- dataface/core/inspect/sources/dbt.py +738 -0
- dataface/core/inspect/sources/duckdb_utils.py +66 -0
- dataface/core/inspect/templates/__init__.py +1 -0
- dataface/core/inspect/templates/categorical_column.yml +196 -0
- dataface/core/inspect/templates/charts.yml +109 -0
- dataface/core/inspect/templates/date_column.yml +248 -0
- dataface/core/inspect/templates/model.yml +138 -0
- dataface/core/inspect/templates/numeric_column.yml +261 -0
- dataface/core/inspect/templates/quality.yml +80 -0
- dataface/core/inspect/templates/string_column.yml +263 -0
- dataface/core/project_roots.py +165 -0
- dataface/core/render/__init__.py +87 -0
- dataface/core/render/board_links.py +176 -0
- dataface/core/render/chart/__init__.py +27 -0
- dataface/core/render/chart/arc_attached_table.py +251 -0
- dataface/core/render/chart/artifacts.py +16 -0
- dataface/core/render/chart/callout.py +225 -0
- dataface/core/render/chart/decisions.py +358 -0
- dataface/core/render/chart/geo.py +700 -0
- dataface/core/render/chart/kpi.py +916 -0
- dataface/core/render/chart/labels.py +76 -0
- dataface/core/render/chart/pipeline.py +818 -0
- dataface/core/render/chart/presentation.py +36 -0
- dataface/core/render/chart/profile.py +3438 -0
- dataface/core/render/chart/render_single.py +347 -0
- dataface/core/render/chart/renderers.py +193 -0
- dataface/core/render/chart/rendering.py +565 -0
- dataface/core/render/chart/serialization.py +90 -0
- dataface/core/render/chart/spark.py +496 -0
- dataface/core/render/chart/spark_bar.py +370 -0
- dataface/core/render/chart/spec_builders.py +154 -0
- dataface/core/render/chart/standard_renderer.py +2645 -0
- dataface/core/render/chart/table.py +2957 -0
- dataface/core/render/chart/table_support.py +1452 -0
- dataface/core/render/chart/tick_values.py +66 -0
- dataface/core/render/chart/time_unit_detect.py +809 -0
- dataface/core/render/chart/title_overflow.py +157 -0
- dataface/core/render/chart/type_inference.py +122 -0
- dataface/core/render/chart/validation.py +99 -0
- dataface/core/render/chart/vega_lite.py +125 -0
- dataface/core/render/chart/vega_lite_types.py +268 -0
- dataface/core/render/chart/vl_field_maps.py +346 -0
- dataface/core/render/chart_interactivity.py +24 -0
- dataface/core/render/control_registry.py +287 -0
- dataface/core/render/converters/__init__.py +24 -0
- dataface/core/render/converters/chart.py +276 -0
- dataface/core/render/converters/html.py +98 -0
- dataface/core/render/converters/pdf.py +40 -0
- dataface/core/render/converters/png.py +41 -0
- dataface/core/render/errors.py +144 -0
- dataface/core/render/face_api.py +160 -0
- dataface/core/render/faces.py +1194 -0
- dataface/core/render/font_measurement.py +48 -0
- dataface/core/render/font_support.py +197 -0
- dataface/core/render/fonts/DFTSansTabular-Regular.ttf +0 -0
- dataface/core/render/fonts/DFTSansTabular-Regular.woff2 +0 -0
- dataface/core/render/fonts/DFTSerifOldstyleProportional-Regular.ttf +0 -0
- dataface/core/render/fonts/DFTSerifOldstyleTabular-Regular.ttf +0 -0
- dataface/core/render/fonts/InterVariable.ttf +0 -0
- dataface/core/render/fonts/InterVariable.woff2 +0 -0
- dataface/core/render/fonts/NOTO_COLOR_EMOJI_LICENSE.txt +93 -0
- dataface/core/render/fonts/NOTO_EMOJI_LICENSE.txt +93 -0
- dataface/core/render/fonts/NotoColorEmoji-Regular.ttf +0 -0
- dataface/core/render/fonts/NotoColorEmoji-Regular.woff2 +0 -0
- dataface/core/render/fonts/NotoEmoji-Regular.ttf +0 -0
- dataface/core/render/fonts/NotoEmoji-Regular.woff2 +0 -0
- dataface/core/render/fonts/SOURCE_CODE_PRO_LICENSE.txt +93 -0
- dataface/core/render/fonts/SOURCE_SERIF_4_LICENSE.txt +98 -0
- dataface/core/render/fonts/SourceCodePro-Regular.ttf +0 -0
- dataface/core/render/fonts/SourceSerif4-Regular.ttf +0 -0
- dataface/core/render/fonts/_emoji_font_face.css +43 -0
- dataface/core/render/fonts/source-serif-4-variable-latin.woff2 +0 -0
- dataface/core/render/format_utils.py +329 -0
- dataface/core/render/geo_defaults.yml +28 -0
- dataface/core/render/json_format.py +146 -0
- dataface/core/render/layout_sizing.py +865 -0
- dataface/core/render/layouts.py +541 -0
- dataface/core/render/markdown_defaults.yml +16 -0
- dataface/core/render/missing_vars_prompt.py +79 -0
- dataface/core/render/placeholder.py +389 -0
- dataface/core/render/render_result.py +14 -0
- dataface/core/render/renderer.py +467 -0
- dataface/core/render/script_embedding.py +16 -0
- dataface/core/render/svg_utils.py +212 -0
- dataface/core/render/template_loader.py +69 -0
- dataface/core/render/templates/controls/_styles.css +606 -0
- dataface/core/render/templates/controls/checkbox.html +16 -0
- dataface/core/render/templates/controls/date.html +16 -0
- dataface/core/render/templates/controls/number.html +19 -0
- dataface/core/render/templates/controls/readonly.html +9 -0
- dataface/core/render/templates/controls/select.html +21 -0
- dataface/core/render/templates/controls/slider.html +22 -0
- dataface/core/render/templates/controls/text.html +16 -0
- dataface/core/render/templates/scripts/chart_interactivity.js +191 -0
- dataface/core/render/templates/scripts/variables.js +976 -0
- dataface/core/render/templates/svg/grid_pattern.svg +3 -0
- dataface/core/render/templates/svg/styles.css +51 -0
- dataface/core/render/terminal.py +311 -0
- dataface/core/render/terminal_charts.py +563 -0
- dataface/core/render/terminal_defaults.yml +2 -0
- dataface/core/render/terminal_layouts.py +299 -0
- dataface/core/render/terminal_text.py +31 -0
- dataface/core/render/text/__init__.py +1 -0
- dataface/core/render/text/case.py +113 -0
- dataface/core/render/text_format.py +129 -0
- dataface/core/render/utils.py +106 -0
- dataface/core/render/variable_controls.py +946 -0
- dataface/core/render/variable_input_refinement.py +140 -0
- dataface/core/render/warnings/__init__.py +15 -0
- dataface/core/render/warnings/bar_color_1_to_1_with_x.py +80 -0
- dataface/core/render/warnings/base.py +44 -0
- dataface/core/render/warnings/fanout_risk.py +15 -0
- dataface/core/render/warnings/from_query_diagnostic.py +56 -0
- dataface/core/render/warnings/missing_join_predicate.py +13 -0
- dataface/core/render/warnings/query_parse_error.py +14 -0
- dataface/core/render/warnings/query_returned_zero_rows.py +42 -0
- dataface/core/render/warnings/reaggregation.py +14 -0
- dataface/core/render/warnings/registry.py +45 -0
- dataface/core/render/warnings/suppression.py +46 -0
- dataface/core/render/warnings/temporal_single_point.py +63 -0
- dataface/core/render/warnings/unreferenced_chart.py +15 -0
- dataface/core/render/warnings/y_encoding_mostly_null.py +76 -0
- dataface/core/render/yaml_format.py +167 -0
- dataface/core/resolve_face.py +195 -0
- dataface/core/schema/__init__.py +0 -0
- dataface/core/schema/guidance.py +151 -0
- dataface/core/scoped_paths.py +59 -0
- dataface/core/serve/__init__.py +14 -0
- dataface/core/serve/bootstrap.py +39 -0
- dataface/core/serve/embedded.py +57 -0
- dataface/core/serve/port.py +129 -0
- dataface/core/serve/server.py +938 -0
- dataface/core/serve/templates/__init__.py +0 -0
- dataface/core/serve/templates/directory.yml +6 -0
- dataface/core/serve/templates/error.html.j2 +217 -0
- dataface/core/utils.py +121 -0
- dataface/core/validate.py +64 -0
- dataface/integrations/__init__.py +0 -0
- dataface/integrations/highlighting.py +351 -0
- dataface/integrations/markdown.py +537 -0
- dataface/py.typed +0 -0
- dataface-0.1.2.dist-info/METADATA +375 -0
- dataface-0.1.2.dist-info/RECORD +455 -0
- dataface-0.1.2.dist-info/WHEEL +4 -0
- dataface-0.1.2.dist-info/entry_points.txt +2 -0
- dataface-0.1.2.dist-info/licenses/LICENSE +202 -0
- mdsvg/__init__.py +168 -0
- mdsvg/fonts.py +656 -0
- mdsvg/images.py +299 -0
- mdsvg/parser.py +629 -0
- mdsvg/playground.py +284 -0
- mdsvg/py.typed +2 -0
- mdsvg/renderer.py +1623 -0
- mdsvg/style.py +355 -0
- mdsvg/types.py +200 -0
- mdsvg/utils.py +86 -0
dataface/__init__.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""Dataface - dbt-native dataface and visualization layer.
|
|
2
|
+
|
|
3
|
+
This package provides a declarative YAML-based approach to building
|
|
4
|
+
datafaces (analytics dashboards) that integrate with dbt projects.
|
|
5
|
+
|
|
6
|
+
Architecture:
|
|
7
|
+
The codebase is organized into:
|
|
8
|
+
|
|
9
|
+
- core/: The engine (compile, execute, render, serve, inspect)
|
|
10
|
+
- cli/: Command-line interface
|
|
11
|
+
- ai/: AI interfaces (MCP server, tool schemas, prompts)
|
|
12
|
+
- apps/: Web applications (playground, cloud)
|
|
13
|
+
|
|
14
|
+
Quick Start:
|
|
15
|
+
>>> from pathlib import Path
|
|
16
|
+
>>> from dataface import compile, Executor, render
|
|
17
|
+
>>> from dataface.core.execute.adapters import build_adapter_registry
|
|
18
|
+
>>>
|
|
19
|
+
>>> # Compile YAML to Face
|
|
20
|
+
>>> result = compile(yaml_content)
|
|
21
|
+
>>> if result.success:
|
|
22
|
+
... face = result.face
|
|
23
|
+
...
|
|
24
|
+
... # Create executor and render
|
|
25
|
+
... registry = build_adapter_registry(Path.cwd())
|
|
26
|
+
... executor = Executor(face, registry, query_registry=result.query_registry)
|
|
27
|
+
... svg = render(face, executor, format="svg")
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
import logging
|
|
31
|
+
from importlib.metadata import version as _v
|
|
32
|
+
|
|
33
|
+
__version__ = _v("dataface")
|
|
34
|
+
|
|
35
|
+
# Set up package-level logger
|
|
36
|
+
# This logger is the parent for all dataface submodule loggers
|
|
37
|
+
logger = logging.getLogger("dataface")
|
|
38
|
+
|
|
39
|
+
# Configure a NullHandler to avoid "No handler found" warnings
|
|
40
|
+
# Actual handlers should be configured by the application entry points (CLI, serve)
|
|
41
|
+
logger.addHandler(logging.NullHandler())
|
|
42
|
+
|
|
43
|
+
# Re-export from core for convenience
|
|
44
|
+
from dataface.core import validate
|
|
45
|
+
from dataface.core.compile import (
|
|
46
|
+
AnyQuery,
|
|
47
|
+
AuthoredFace,
|
|
48
|
+
Chart,
|
|
49
|
+
CompilationError,
|
|
50
|
+
CompileResult,
|
|
51
|
+
Face,
|
|
52
|
+
Layout,
|
|
53
|
+
LayoutItem,
|
|
54
|
+
Variable,
|
|
55
|
+
compile,
|
|
56
|
+
compile_file,
|
|
57
|
+
)
|
|
58
|
+
from dataface.core.execute import (
|
|
59
|
+
ExecutionError,
|
|
60
|
+
Executor,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Import from render submodule
|
|
64
|
+
# Note: We import `render` function but this shadows `dataface.render` module access
|
|
65
|
+
# Tests that need to patch `dataface.core.render.renderer` should import the module directly
|
|
66
|
+
from dataface.core.render import RenderError, render
|
|
67
|
+
|
|
68
|
+
__all__ = [
|
|
69
|
+
# Version
|
|
70
|
+
"__version__",
|
|
71
|
+
# Logging
|
|
72
|
+
"logger",
|
|
73
|
+
# Compile
|
|
74
|
+
"compile",
|
|
75
|
+
"compile_file",
|
|
76
|
+
"CompileResult",
|
|
77
|
+
"Face",
|
|
78
|
+
"Chart",
|
|
79
|
+
"AnyQuery",
|
|
80
|
+
"Layout",
|
|
81
|
+
"LayoutItem",
|
|
82
|
+
"AuthoredFace",
|
|
83
|
+
"Variable",
|
|
84
|
+
"CompilationError",
|
|
85
|
+
# Execute
|
|
86
|
+
"Executor",
|
|
87
|
+
"ExecutionError",
|
|
88
|
+
# Render
|
|
89
|
+
"render",
|
|
90
|
+
"RenderError",
|
|
91
|
+
# Validate
|
|
92
|
+
"validate",
|
|
93
|
+
]
|
dataface/_docs_site.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Public docs site base URL for CLI hints and cross-links."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
DEFAULT_DOCS_SITE_URL = "https://docs.dataface.com"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def docs_site_url() -> str:
|
|
11
|
+
"""Return the docs site base URL without a trailing slash.
|
|
12
|
+
|
|
13
|
+
``DFT_DOCS_URL`` overrides the shipped default when set to a non-empty
|
|
14
|
+
value. Used by ``dft docs`` (web link on the topic index), the playground
|
|
15
|
+
gallery, and other surfaces that link out to the MkDocs site.
|
|
16
|
+
"""
|
|
17
|
+
raw = os.environ.get("DFT_DOCS_URL", DEFAULT_DOCS_SITE_URL).strip()
|
|
18
|
+
if not raw:
|
|
19
|
+
return DEFAULT_DOCS_SITE_URL
|
|
20
|
+
return raw.rstrip("/")
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Canonical install-command hints surfaced from runtime error messages.
|
|
2
|
+
|
|
3
|
+
Centralized here so every CLI/runtime hint stays in lock-step with
|
|
4
|
+
``dataface/README.md`` and the wheel-shipped MCP-setup skill. The bare
|
|
5
|
+
``pip install`` form is used (not ``uv pip install``) — customers may
|
|
6
|
+
not have ``uv`` on PATH, and bare ``pip`` is universally available with
|
|
7
|
+
any Python install. ``cli/_extras.py`` separately emits ``uv pip install
|
|
8
|
+
--python <path>`` when uv is the active installer (the smoke-install CI
|
|
9
|
+
test pins both legs); this helper is only the canonical fallback string.
|
|
10
|
+
|
|
11
|
+
If you change the canonical form, also update the TypeScript mirror at
|
|
12
|
+
``apps/ide/vscode-extension/src/utils/install-hint.ts``.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def install_hint(extra: str | None = None) -> str:
|
|
19
|
+
"""Return the canonical ``pip install`` one-liner for Dataface.
|
|
20
|
+
|
|
21
|
+
``extra`` is an optional optional-dependency group (``"mcp"``,
|
|
22
|
+
``"chat"``, ``"playground"``, ``"bigquery"``); when set, the
|
|
23
|
+
returned command installs Dataface with that extras bracket.
|
|
24
|
+
"""
|
|
25
|
+
spec = f"dataface[{extra}]" if extra else "dataface"
|
|
26
|
+
return f'pip install "{spec}"'
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""dataface.agent_api — typed Python API for Dataface agent surfaces.
|
|
2
|
+
|
|
3
|
+
This package is the canonical home for every function exposed to AI agents
|
|
4
|
+
via CLI commands (``dataface/cli/commands/``) or MCP server modules
|
|
5
|
+
(``dataface/ai/mcp/``).
|
|
6
|
+
|
|
7
|
+
Contract
|
|
8
|
+
--------
|
|
9
|
+
Every function in this package must satisfy all three rules:
|
|
10
|
+
|
|
11
|
+
1. **Typed args and typed returns.** Function signatures use concrete Python
|
|
12
|
+
types — ``Path``, ``str``, ``int``, Pydantic models, or ``TypedDict``
|
|
13
|
+
subclasses. Return types are never ``dict[str, Any]`` or JSON strings.
|
|
14
|
+
Callers should not need to parse, coerce, or guess the shape of results.
|
|
15
|
+
|
|
16
|
+
2. **No Cloud/Django assumptions.** Do not import ``django``, Django ORM
|
|
17
|
+
models, ``HttpRequest``, ``settings``, or any ``apps.cloud`` module.
|
|
18
|
+
Functions here must work in a standalone Python environment — a CLI
|
|
19
|
+
session, a test, or an MCP server — without a running Django process.
|
|
20
|
+
|
|
21
|
+
3. **I/O scoped to the verb.** A ``render`` function does not write to disk.
|
|
22
|
+
A ``save`` function writes exactly one file. An ``execute`` function runs
|
|
23
|
+
SQL and nothing else. Side effects must match what the verb name implies
|
|
24
|
+
and nothing more.
|
|
25
|
+
|
|
26
|
+
Thin-wrapper rule
|
|
27
|
+
-----------------
|
|
28
|
+
``dataface/cli/commands/`` and ``dataface/ai/mcp/`` are *thin wrappers* over
|
|
29
|
+
this package. They are permitted to contain only:
|
|
30
|
+
|
|
31
|
+
- Argument parsing and output formatting
|
|
32
|
+
- A single call into ``dataface.agent_api``
|
|
33
|
+
|
|
34
|
+
Any business logic (validation, path resolution, compilation, execution,
|
|
35
|
+
rendering) belongs here, not in the CLI or MCP layer. Code review blocks
|
|
36
|
+
CLI/MCP files that contain logic beyond parse-and-dispatch.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from dataface.agent_api import skills as skills
|
|
40
|
+
from dataface.agent_api._paths import (
|
|
41
|
+
RenderSetup as RenderSetup,
|
|
42
|
+
setup_render_for_face as setup_render_for_face,
|
|
43
|
+
)
|
|
44
|
+
from dataface.agent_api.dashboards import (
|
|
45
|
+
RenderedDashboard as RenderedDashboard,
|
|
46
|
+
render_dashboard as render_dashboard,
|
|
47
|
+
)
|
|
48
|
+
from dataface.agent_api.describe import (
|
|
49
|
+
DescribeFaceArgs,
|
|
50
|
+
DescribeFaceResult,
|
|
51
|
+
describe_face,
|
|
52
|
+
)
|
|
53
|
+
from dataface.agent_api.docs import DocsArgs, DocsResult, DocsSearchHit, Topic
|
|
54
|
+
from dataface.agent_api.init import (
|
|
55
|
+
InitResult as InitResult,
|
|
56
|
+
init_project as init_project,
|
|
57
|
+
)
|
|
58
|
+
from dataface.agent_api.query import QueryFaceResult, query_face
|
|
59
|
+
from dataface.agent_api.validate import ValidateDashboardArgs, ValidateResult
|
|
60
|
+
|
|
61
|
+
__all__ = [
|
|
62
|
+
"DescribeFaceArgs",
|
|
63
|
+
"DescribeFaceResult",
|
|
64
|
+
"DocsArgs",
|
|
65
|
+
"DocsResult",
|
|
66
|
+
"DocsSearchHit",
|
|
67
|
+
"InitResult",
|
|
68
|
+
"QueryFaceResult",
|
|
69
|
+
"RenderSetup",
|
|
70
|
+
"RenderedDashboard",
|
|
71
|
+
"Topic",
|
|
72
|
+
"ValidateDashboardArgs",
|
|
73
|
+
"ValidateResult",
|
|
74
|
+
"describe_face",
|
|
75
|
+
"init_project",
|
|
76
|
+
"query_face",
|
|
77
|
+
"render_dashboard",
|
|
78
|
+
"setup_render_for_face",
|
|
79
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
|
|
2
|
+
<!-- dft-dataface:start -->
|
|
3
|
+
## Dataface
|
|
4
|
+
|
|
5
|
+
This project uses [Dataface](https://github.com/fivetran/dataface) (`dft`) for visualizations, dashboards, and reports (YAML in `faces/`).
|
|
6
|
+
|
|
7
|
+
Learn how to work here — run:
|
|
8
|
+
|
|
9
|
+
- `dft skills dashboard-build`
|
|
10
|
+
- `dft skills dashboard-design`
|
|
11
|
+
- `dft skills report-design`
|
|
12
|
+
- `dft skills dashboard-review`
|
|
13
|
+
- `dft skills dataface-troubleshooting`
|
|
14
|
+
|
|
15
|
+
| Command | Use for |
|
|
16
|
+
|---------|---------|
|
|
17
|
+
| `dft schema` → `dft schema SRC` → `dft schema SRC SCH` → `dft schema SRC SCH TBL` → `dft schema SRC SCH TBL COL` | Drill data hierarchy: sources → schemas → tables → columns (each positional arg narrows one level) |
|
|
18
|
+
| `dft query` | Run queries |
|
|
19
|
+
| `dft serve` | Preview dashboards |
|
|
20
|
+
| `dft validate` | Check face YAML |
|
|
21
|
+
| `dft render` | Export |
|
|
22
|
+
|
|
23
|
+
Use `dft schema` for real names — do not invent columns.
|
|
24
|
+
|
|
25
|
+
More: `dft -h`, `dft docs`, `dft skills`. `dft init mcp` installs skills + wires MCP.
|
|
26
|
+
<!-- dft-dataface:end -->
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Dataface project configuration
|
|
2
|
+
#
|
|
3
|
+
# This file anchors your project root. Running `dft serve` from any
|
|
4
|
+
# subdirectory will walk up to find this file and use its directory
|
|
5
|
+
# as the serve root.
|
|
6
|
+
#
|
|
7
|
+
# Credentials and database identity stay in dbt profiles.yml / env vars.
|
|
8
|
+
# Only Dataface runtime and serving preferences belong here.
|
|
9
|
+
#
|
|
10
|
+
# serve:
|
|
11
|
+
# # Override the SQL dialect (auto-detected from dbt profile when possible)
|
|
12
|
+
# # dialect: bigquery
|
|
13
|
+
#
|
|
14
|
+
# # dbt target override (defaults to DBT_TARGET env var, then profile default)
|
|
15
|
+
# # target: dev
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Dataface Guide — your first dashboard
|
|
2
|
+
#
|
|
3
|
+
# This file is a self-contained tour of Dataface features.
|
|
4
|
+
# Every section works without a database: data lives inline.
|
|
5
|
+
# When you're ready, swap inline data for real SQL queries.
|
|
6
|
+
#
|
|
7
|
+
# Key concepts:
|
|
8
|
+
# title: dashboard title shown at the top
|
|
9
|
+
# text: markdown prose block — supports **bold**, *italic*, lists, code
|
|
10
|
+
# queries: data sources — inline values, CSV files, or SQL against a warehouse
|
|
11
|
+
# charts: visualizations wired to a query
|
|
12
|
+
# rows/cols: layout — rows stack vertically, cols split horizontally
|
|
13
|
+
# variables: interactive dropdowns and filters
|
|
14
|
+
|
|
15
|
+
title: "Dataface Guide"
|
|
16
|
+
description: "A tour of Dataface: queries, charts, layout, KPIs, and variables."
|
|
17
|
+
|
|
18
|
+
# ── Text blocks ───────────────────────────────────────────────────────────────
|
|
19
|
+
# Any `text:` field accepts markdown.
|
|
20
|
+
text: |
|
|
21
|
+
Welcome to **Dataface** — dashboards as YAML files.
|
|
22
|
+
|
|
23
|
+
This file is your starter guide. Edit it, add new `.yml` files under
|
|
24
|
+
`faces/`, and run `dft serve` to preview everything in your browser.
|
|
25
|
+
|
|
26
|
+
# ── Queries ───────────────────────────────────────────────────────────────────
|
|
27
|
+
# `type: values` embeds data directly — no database needed.
|
|
28
|
+
# Swap for `type: csv` (pointing at a file) or a SQL query once you're ready.
|
|
29
|
+
queries:
|
|
30
|
+
monthly_revenue:
|
|
31
|
+
type: values
|
|
32
|
+
columns: [month, revenue, target]
|
|
33
|
+
values:
|
|
34
|
+
- [Jan, 82000, 80000]
|
|
35
|
+
- [Feb, 91000, 85000]
|
|
36
|
+
- [Mar, 78000, 90000]
|
|
37
|
+
- [Apr, 105000, 95000]
|
|
38
|
+
- [May, 112000, 100000]
|
|
39
|
+
- [Jun, 99000, 105000]
|
|
40
|
+
|
|
41
|
+
category_mix:
|
|
42
|
+
type: values
|
|
43
|
+
columns: [category, amount]
|
|
44
|
+
values:
|
|
45
|
+
- [Software, 48000]
|
|
46
|
+
- [Services, 31000]
|
|
47
|
+
- [Hardware, 20000]
|
|
48
|
+
|
|
49
|
+
kpi_summary:
|
|
50
|
+
type: values
|
|
51
|
+
columns: [total_revenue, revenue_delta, avg_deal]
|
|
52
|
+
values:
|
|
53
|
+
- [567000, 0.14, 18900]
|
|
54
|
+
|
|
55
|
+
# ── Charts ────────────────────────────────────────────────────────────────────
|
|
56
|
+
# `type:` can be bar, line, area, pie, kpi, table, or scatter.
|
|
57
|
+
# `x:` and `y:` name columns from the query.
|
|
58
|
+
charts:
|
|
59
|
+
revenue_trend:
|
|
60
|
+
type: line
|
|
61
|
+
query: monthly_revenue
|
|
62
|
+
title: Monthly Revenue vs Target
|
|
63
|
+
x: month
|
|
64
|
+
y: revenue
|
|
65
|
+
|
|
66
|
+
revenue_bars:
|
|
67
|
+
type: bar
|
|
68
|
+
query: monthly_revenue
|
|
69
|
+
title: Revenue by Month
|
|
70
|
+
x: month
|
|
71
|
+
y: revenue
|
|
72
|
+
|
|
73
|
+
category_pie:
|
|
74
|
+
type: pie
|
|
75
|
+
query: category_mix
|
|
76
|
+
title: Revenue Mix
|
|
77
|
+
theta: amount
|
|
78
|
+
color: category
|
|
79
|
+
|
|
80
|
+
kpi_total:
|
|
81
|
+
type: kpi
|
|
82
|
+
query: kpi_summary
|
|
83
|
+
label: Total Revenue (H1)
|
|
84
|
+
value: total_revenue
|
|
85
|
+
format: currency_compact
|
|
86
|
+
support:
|
|
87
|
+
value: revenue_delta
|
|
88
|
+
label: vs H1 last year
|
|
89
|
+
format: percent_delta
|
|
90
|
+
glyph: "▲"
|
|
91
|
+
tone: positive
|
|
92
|
+
|
|
93
|
+
kpi_avg_deal:
|
|
94
|
+
type: kpi
|
|
95
|
+
query: kpi_summary
|
|
96
|
+
label: Avg Deal Size
|
|
97
|
+
value: avg_deal
|
|
98
|
+
format: currency_compact
|
|
99
|
+
|
|
100
|
+
data_table:
|
|
101
|
+
type: table
|
|
102
|
+
query: monthly_revenue
|
|
103
|
+
title: Monthly Detail
|
|
104
|
+
|
|
105
|
+
# ── Variables ─────────────────────────────────────────────────────────────────
|
|
106
|
+
# Variables add interactive dropdowns. Wire them to SQL queries with {{ varname }}.
|
|
107
|
+
# With `type: values` data above they show the UI but don't filter — swap to SQL
|
|
108
|
+
# queries and use `WHERE category = '{{ segment }}'` to make them live.
|
|
109
|
+
variables:
|
|
110
|
+
segment:
|
|
111
|
+
label: Segment
|
|
112
|
+
input: select
|
|
113
|
+
default: All
|
|
114
|
+
options:
|
|
115
|
+
static: [All, Software, Services, Hardware]
|
|
116
|
+
|
|
117
|
+
# ── Layout ────────────────────────────────────────────────────────────────────
|
|
118
|
+
# `rows:` stacks content vertically.
|
|
119
|
+
# `cols:` inside a row splits horizontally.
|
|
120
|
+
# Reference a chart by its key, or use a dict for text/title blocks.
|
|
121
|
+
rows:
|
|
122
|
+
# KPI row — four equal-width columns
|
|
123
|
+
- cols:
|
|
124
|
+
- kpi_total
|
|
125
|
+
- kpi_avg_deal
|
|
126
|
+
|
|
127
|
+
# Charts side by side
|
|
128
|
+
- cols:
|
|
129
|
+
- revenue_trend
|
|
130
|
+
- category_pie
|
|
131
|
+
|
|
132
|
+
# Full-width bar chart
|
|
133
|
+
- revenue_bars
|
|
134
|
+
|
|
135
|
+
# Annotated table with intro text
|
|
136
|
+
- cols:
|
|
137
|
+
- title: "Monthly Detail"
|
|
138
|
+
text: |
|
|
139
|
+
Scroll the table to see all rows.
|
|
140
|
+
Add `style.pagination.enabled: true` to paginate large datasets.
|
|
141
|
+
style:
|
|
142
|
+
padding: "16px"
|
|
143
|
+
|
|
144
|
+
- data_table
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Dataface
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Welcome to Dataface
|
|
6
|
+
|
|
7
|
+
This is your project's landing page. Everything under `faces/` becomes a
|
|
8
|
+
served page — edit the files here to build dashboards, reports, and docs
|
|
9
|
+
for your data project.
|
|
10
|
+
|
|
11
|
+
## Authoring modes
|
|
12
|
+
|
|
13
|
+
**YAML (`.yml`)** — structured dashboards with queries, charts, and layout.
|
|
14
|
+
See the [dataface guide](dataface) for a working example covering queries, charts, and variables.
|
|
15
|
+
|
|
16
|
+
**Markdown (`.md`)** — prose pages like this one. Add YAML frontmatter for
|
|
17
|
+
queries and charts, then embed them inline with `{% raw %}{{ chart my_chart }}{% endraw %}`.
|
|
18
|
+
|
|
19
|
+
## Next steps
|
|
20
|
+
|
|
21
|
+
1. Open `faces/dataface.yml` and tweak the content.
|
|
22
|
+
2. Run `dft serve` and open the URL it prints.
|
|
23
|
+
3. Add new `.yml` or `.md` files under `faces/` — they appear automatically.
|
|
24
|
+
4. Run `dft validate` to validate your face YAML for errors.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Shared path helpers for MCP tools and CLI render setup."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from dataface.core.execute.adapters import AdapterRegistry, build_adapter_registry
|
|
9
|
+
from dataface.core.project_roots import (
|
|
10
|
+
discover_render_context,
|
|
11
|
+
discovery_boundary_for_face,
|
|
12
|
+
find_dataface_project,
|
|
13
|
+
)
|
|
14
|
+
from dataface.core.scoped_paths import (
|
|
15
|
+
_resolve_against_project,
|
|
16
|
+
project_root_for as project_root_for,
|
|
17
|
+
resolve_scoped_path as resolve_scoped_path,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def no_project_hint(project_dir: Path | None) -> str:
|
|
22
|
+
"""Return a hint string when no project marker is found, else empty string."""
|
|
23
|
+
check = project_dir if project_dir is not None else Path.cwd()
|
|
24
|
+
if find_dataface_project(check) is not None:
|
|
25
|
+
return ""
|
|
26
|
+
return (
|
|
27
|
+
f" No Dataface project marker found at or above {check}."
|
|
28
|
+
f" Run from inside a Dataface project, or set project_dir."
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class RenderSetup:
|
|
34
|
+
"""Resolved arguments for an `agent_api.render_dashboard` call.
|
|
35
|
+
|
|
36
|
+
Bundles the scoped path, scoped base, and adapter registry produced by
|
|
37
|
+
discovery. CLI/serve callers building this from a face on disk avoid
|
|
38
|
+
duplicating discovery boilerplate; tests can construct one directly.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
adapter_registry: AdapterRegistry
|
|
42
|
+
scoped_path: Path | None
|
|
43
|
+
scoped_base: Path | None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def setup_render_for_face(
|
|
47
|
+
face_path: Path,
|
|
48
|
+
project_dir: Path | None = None,
|
|
49
|
+
*,
|
|
50
|
+
read_only: bool = True,
|
|
51
|
+
) -> tuple[RenderSetup, Path]:
|
|
52
|
+
"""Build a RenderSetup + resolved face_file for a face on disk.
|
|
53
|
+
|
|
54
|
+
Mirrors `core.render.face_api.render_face`'s discovery: walk upward from
|
|
55
|
+
the face's parent to find dbt_project.yml and any sibling _sources.yaml.
|
|
56
|
+
`project_dir` overrides project_root for project-level data paths but
|
|
57
|
+
never the discovered dbt path.
|
|
58
|
+
"""
|
|
59
|
+
face_file = face_path
|
|
60
|
+
if not face_file.is_absolute():
|
|
61
|
+
face_file = _resolve_against_project(face_file, project_dir)
|
|
62
|
+
else:
|
|
63
|
+
face_file = face_file.resolve()
|
|
64
|
+
|
|
65
|
+
project_root, dbt_project_path = discover_render_context(
|
|
66
|
+
face_file.parent,
|
|
67
|
+
discovery_boundary_for_face(face_file.parent, project_dir),
|
|
68
|
+
)
|
|
69
|
+
if project_dir:
|
|
70
|
+
project_root = project_dir.resolve()
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
scoped_path: Path | None = face_file.relative_to(project_root)
|
|
74
|
+
scoped_base: Path | None = project_root
|
|
75
|
+
except ValueError:
|
|
76
|
+
scoped_path = face_file
|
|
77
|
+
scoped_base = None
|
|
78
|
+
|
|
79
|
+
adapter_registry = build_adapter_registry(
|
|
80
|
+
project_root,
|
|
81
|
+
read_only=read_only,
|
|
82
|
+
dbt_project_path=dbt_project_path,
|
|
83
|
+
)
|
|
84
|
+
return (
|
|
85
|
+
RenderSetup(
|
|
86
|
+
adapter_registry=adapter_registry,
|
|
87
|
+
scoped_path=scoped_path,
|
|
88
|
+
scoped_base=scoped_base,
|
|
89
|
+
),
|
|
90
|
+
face_file,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def setup_render_for_yaml(
|
|
95
|
+
project_dir: Path | None = None,
|
|
96
|
+
*,
|
|
97
|
+
read_only: bool = True,
|
|
98
|
+
) -> RenderSetup:
|
|
99
|
+
"""Build a RenderSetup for a stdin/yaml_content render (no face path).
|
|
100
|
+
|
|
101
|
+
Walks upward from `project_dir` (or cwd) to discover the dbt project so
|
|
102
|
+
yaml fed via stdin still resolves dbt-managed sources correctly.
|
|
103
|
+
"""
|
|
104
|
+
output_dir = project_root_for(project_dir)
|
|
105
|
+
project_root, dbt_project_path = discover_render_context(output_dir, None)
|
|
106
|
+
if project_dir:
|
|
107
|
+
project_root = output_dir
|
|
108
|
+
|
|
109
|
+
adapter_registry = build_adapter_registry(
|
|
110
|
+
project_root,
|
|
111
|
+
read_only=read_only,
|
|
112
|
+
dbt_project_path=dbt_project_path,
|
|
113
|
+
)
|
|
114
|
+
return RenderSetup(
|
|
115
|
+
adapter_registry=adapter_registry,
|
|
116
|
+
scoped_path=None,
|
|
117
|
+
scoped_base=project_root,
|
|
118
|
+
)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Append or refresh the Dataface blurb in a project's AGENTS.md."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib.resources
|
|
6
|
+
from typing import Literal
|
|
7
|
+
|
|
8
|
+
FileAction = Literal["created", "refreshed", "appended"]
|
|
9
|
+
|
|
10
|
+
_START = "<!-- dft-dataface:start -->"
|
|
11
|
+
_END = "<!-- dft-dataface:end -->"
|
|
12
|
+
|
|
13
|
+
_TEMPLATES = importlib.resources.files("dataface.agent_api._init_templates")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def load_agents_snippet() -> str:
|
|
17
|
+
"""Return the packaged markdown blurb (includes marker comments)."""
|
|
18
|
+
return _TEMPLATES.joinpath("agents_dft_snippet.md").read_text()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def has_dataface_markers(text: str) -> bool:
|
|
22
|
+
if _START not in text or _END not in text:
|
|
23
|
+
return False
|
|
24
|
+
return text.index(_START) < text.index(_END)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def merge_agents_snippet(
|
|
28
|
+
existing_text: str | None,
|
|
29
|
+
snippet: str,
|
|
30
|
+
) -> tuple[str, FileAction]:
|
|
31
|
+
"""Create, append, or refresh only the marked Dataface section."""
|
|
32
|
+
body = snippet if snippet.endswith("\n") else f"{snippet}\n"
|
|
33
|
+
|
|
34
|
+
if existing_text is None:
|
|
35
|
+
return body.lstrip("\n"), "created"
|
|
36
|
+
|
|
37
|
+
if has_dataface_markers(existing_text):
|
|
38
|
+
before = existing_text[: existing_text.index(_START)].rstrip()
|
|
39
|
+
after = existing_text[existing_text.index(_END) + len(_END) :].lstrip()
|
|
40
|
+
parts = [p for p in (before, body.strip(), after) if p]
|
|
41
|
+
return "\n\n".join(parts) + "\n", "refreshed"
|
|
42
|
+
|
|
43
|
+
return f"{existing_text.rstrip()}\n\n{body.lstrip()}", "appended"
|