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
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
"""Typed describe verb — face structure inspection."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
from dataface.core.compile.models.chart.compiled import (
|
|
11
|
+
Chart,
|
|
12
|
+
)
|
|
13
|
+
from dataface.core.compile.models.face.compiled import Layout
|
|
14
|
+
from dataface.core.compile.models.query.compiled import (
|
|
15
|
+
CsvQuery,
|
|
16
|
+
DbtModelQuery,
|
|
17
|
+
HttpQuery,
|
|
18
|
+
MetricFlowQuery,
|
|
19
|
+
Query,
|
|
20
|
+
SqlQuery,
|
|
21
|
+
ValuesQuery,
|
|
22
|
+
)
|
|
23
|
+
from dataface.core.errors import DF_UNKNOWN_INTERNAL, StructuredError
|
|
24
|
+
|
|
25
|
+
# Chart-encoding fields surfaced to agents. Covers the stable channel fields
|
|
26
|
+
# across chart families. `label` is excluded — it's a KPI-specific render hint,
|
|
27
|
+
# not an encoding channel that binds a column.
|
|
28
|
+
_ENCODING_FIELDS: tuple[str, ...] = (
|
|
29
|
+
"x",
|
|
30
|
+
"y",
|
|
31
|
+
"color",
|
|
32
|
+
"size",
|
|
33
|
+
"shape",
|
|
34
|
+
"theta",
|
|
35
|
+
"value",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
# Sub-models
|
|
41
|
+
# ---------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class QueryDescription(BaseModel):
|
|
45
|
+
name: str
|
|
46
|
+
type: str
|
|
47
|
+
summary: str
|
|
48
|
+
source: str | None = None
|
|
49
|
+
sql: str | None = None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class ChartDescription(BaseModel):
|
|
53
|
+
name: str
|
|
54
|
+
type: str
|
|
55
|
+
query: str
|
|
56
|
+
title: str | None = None
|
|
57
|
+
encoding: dict[str, Any] = Field(
|
|
58
|
+
default_factory=dict, description="Vega-Lite encoding channels keyed by role."
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class VariableDescription(BaseModel):
|
|
63
|
+
name: str
|
|
64
|
+
type: str
|
|
65
|
+
default: Any | None = None
|
|
66
|
+
options: list[str] | None = None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class LayoutDescription(BaseModel):
|
|
70
|
+
primitive: str
|
|
71
|
+
items: list[str] = Field(
|
|
72
|
+
default_factory=list,
|
|
73
|
+
description="Layout item identifiers (chart IDs, text keys).",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# ---------------------------------------------------------------------------
|
|
78
|
+
# Result models
|
|
79
|
+
# ---------------------------------------------------------------------------
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class DescribeFaceArgs(BaseModel):
|
|
83
|
+
"""Describe the structure of a face file: queries (with type and source), charts (with query bindings and encoding), variables, and layout. Use this for orientation when picking up an unfamiliar dashboard — it reads the YAML and returns a curated narrative summary without executing any queries."""
|
|
84
|
+
|
|
85
|
+
path: Path = Field(description="Path to the dashboard YAML file to describe")
|
|
86
|
+
project_dir: Path | None = Field(
|
|
87
|
+
None, description="Project root for resolving relative paths"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class DescribeFaceResult(BaseModel):
|
|
92
|
+
success: bool
|
|
93
|
+
path: Path
|
|
94
|
+
title: str | None = None
|
|
95
|
+
description: str | None = None
|
|
96
|
+
queries: list[QueryDescription] = Field(
|
|
97
|
+
default_factory=list, description="Queries defined in the face."
|
|
98
|
+
)
|
|
99
|
+
charts: list[ChartDescription] = Field(
|
|
100
|
+
default_factory=list, description="Charts defined in the face."
|
|
101
|
+
)
|
|
102
|
+
variables: list[VariableDescription] = Field(
|
|
103
|
+
default_factory=list, description="Variables defined in the face."
|
|
104
|
+
)
|
|
105
|
+
layout: LayoutDescription | None = None
|
|
106
|
+
errors: list[StructuredError] = Field(default_factory=list)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# ---------------------------------------------------------------------------
|
|
110
|
+
# Helpers
|
|
111
|
+
# ---------------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _build_query_description(name: str, query: Query) -> QueryDescription:
|
|
115
|
+
"""Map an AnyQuery union variant onto a typed description."""
|
|
116
|
+
match query:
|
|
117
|
+
case SqlQuery():
|
|
118
|
+
# SqlQuery.source can be a dict for inline configs; only surface the
|
|
119
|
+
# name when it's a plain string reference.
|
|
120
|
+
source = query.source if isinstance(query.source, str) else None
|
|
121
|
+
sql = query.sql or None
|
|
122
|
+
summary = f"SQL query{f' against `{source}`' if source else ''}"
|
|
123
|
+
return QueryDescription(
|
|
124
|
+
name=name, type="sql", summary=summary, source=source, sql=sql
|
|
125
|
+
)
|
|
126
|
+
case CsvQuery():
|
|
127
|
+
return QueryDescription(
|
|
128
|
+
name=name, type="csv", summary=f"CSV file: {query.file}"
|
|
129
|
+
)
|
|
130
|
+
case DbtModelQuery():
|
|
131
|
+
return QueryDescription(
|
|
132
|
+
name=name, type="dbt_model", summary=f"dbt model: {query.model}"
|
|
133
|
+
)
|
|
134
|
+
case HttpQuery():
|
|
135
|
+
return QueryDescription(
|
|
136
|
+
name=name, type="http", summary=f"HTTP request: {query.url}"
|
|
137
|
+
)
|
|
138
|
+
case MetricFlowQuery():
|
|
139
|
+
return QueryDescription(
|
|
140
|
+
name=name, type="metricflow", summary="MetricFlow query"
|
|
141
|
+
)
|
|
142
|
+
case ValuesQuery():
|
|
143
|
+
return QueryDescription(name=name, type="values", summary="Inline values")
|
|
144
|
+
case _:
|
|
145
|
+
return QueryDescription(
|
|
146
|
+
name=name, type=query.query_type, summary=f"{query.query_type} query"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _collect_layout_items(layout: Layout, prefix: str = "") -> list[str]:
|
|
151
|
+
"""Walk a Layout, returning chart names in display order.
|
|
152
|
+
|
|
153
|
+
Nested faces recurse with a prefix so the caller can tell layers apart.
|
|
154
|
+
"""
|
|
155
|
+
out: list[str] = []
|
|
156
|
+
for item in layout.items:
|
|
157
|
+
if item.type == "chart" and item.chart is not None:
|
|
158
|
+
out.append(f"{prefix}{item.chart.id}" if prefix else item.chart.id)
|
|
159
|
+
elif item.type == "face" and item.face is not None:
|
|
160
|
+
sub_prefix = f"{item.face.id or '<nested>'}."
|
|
161
|
+
out.extend(_collect_layout_items(item.face.layout, sub_prefix))
|
|
162
|
+
return out
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _encoding_for_chart(chart: Chart) -> dict[str, Any]:
|
|
166
|
+
"""Surface chart encoding bindings (column names) as JSON-friendly values.
|
|
167
|
+
|
|
168
|
+
`y` is `str | list[str]` (multi-series), and `color` accepts
|
|
169
|
+
dicts. Pass scalars and lists through unchanged so multi-series faces
|
|
170
|
+
don't get their `y: [revenue, profit]` flattened into Python repr. The
|
|
171
|
+
`value` is always a column reference string; pass through unchanged.
|
|
172
|
+
"""
|
|
173
|
+
enc: dict[str, Any] = {}
|
|
174
|
+
for field in _ENCODING_FIELDS:
|
|
175
|
+
# No default on getattr — a typo in _ENCODING_FIELDS should raise
|
|
176
|
+
# loudly, not be silently skipped.
|
|
177
|
+
v = getattr(chart, field)
|
|
178
|
+
if v is None or v == "":
|
|
179
|
+
continue
|
|
180
|
+
enc[field] = v
|
|
181
|
+
return enc
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# ---------------------------------------------------------------------------
|
|
185
|
+
# Public API
|
|
186
|
+
# ---------------------------------------------------------------------------
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def describe_face(path: Path, project_dir: Path | None = None) -> DescribeFaceResult:
|
|
190
|
+
"""Describe the structure of a face: queries, charts, variables, layout."""
|
|
191
|
+
from dataface.agent_api._paths import no_project_hint, resolve_scoped_path
|
|
192
|
+
from dataface.core.compile.compiler import compile_file
|
|
193
|
+
from dataface.core.compile.errors import DatafaceError
|
|
194
|
+
|
|
195
|
+
try:
|
|
196
|
+
resolved = resolve_scoped_path(path, project_dir)
|
|
197
|
+
except ValueError as exc:
|
|
198
|
+
return DescribeFaceResult(
|
|
199
|
+
success=False,
|
|
200
|
+
path=path,
|
|
201
|
+
errors=[
|
|
202
|
+
DatafaceError.from_code(
|
|
203
|
+
DF_UNKNOWN_INTERNAL, message=str(exc)
|
|
204
|
+
).to_structured()
|
|
205
|
+
],
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
if not resolved.exists():
|
|
209
|
+
hint = no_project_hint(project_dir)
|
|
210
|
+
return DescribeFaceResult(
|
|
211
|
+
success=False,
|
|
212
|
+
path=path,
|
|
213
|
+
errors=[
|
|
214
|
+
DatafaceError.from_code(
|
|
215
|
+
DF_UNKNOWN_INTERNAL,
|
|
216
|
+
message=f"File not found: {resolved}{hint}",
|
|
217
|
+
).to_structured()
|
|
218
|
+
],
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
try:
|
|
222
|
+
result = compile_file(resolved)
|
|
223
|
+
except OSError as exc:
|
|
224
|
+
return DescribeFaceResult(
|
|
225
|
+
success=False,
|
|
226
|
+
path=resolved,
|
|
227
|
+
errors=[
|
|
228
|
+
DatafaceError.from_code(
|
|
229
|
+
DF_UNKNOWN_INTERNAL, message=str(exc)
|
|
230
|
+
).to_structured(file=str(resolved))
|
|
231
|
+
],
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
if result.errors:
|
|
235
|
+
return DescribeFaceResult(
|
|
236
|
+
success=False,
|
|
237
|
+
path=resolved,
|
|
238
|
+
errors=list(result.errors),
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
face = result.face
|
|
242
|
+
if face is None:
|
|
243
|
+
return DescribeFaceResult(
|
|
244
|
+
success=False,
|
|
245
|
+
path=resolved,
|
|
246
|
+
errors=[
|
|
247
|
+
DatafaceError.from_code(
|
|
248
|
+
DF_UNKNOWN_INTERNAL, message="Compilation produced no face"
|
|
249
|
+
).to_structured(file=str(resolved))
|
|
250
|
+
],
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
query_descs = [
|
|
254
|
+
_build_query_description(name, query) for name, query in face.queries.items()
|
|
255
|
+
]
|
|
256
|
+
|
|
257
|
+
chart_descs = [
|
|
258
|
+
ChartDescription(
|
|
259
|
+
name=name,
|
|
260
|
+
type=chart.type,
|
|
261
|
+
query=chart.query_name or name,
|
|
262
|
+
title=chart.title or None,
|
|
263
|
+
encoding=_encoding_for_chart(chart),
|
|
264
|
+
)
|
|
265
|
+
for name, chart in face.charts.items()
|
|
266
|
+
]
|
|
267
|
+
|
|
268
|
+
var_descs: list[VariableDescription] = []
|
|
269
|
+
for name, var in face.variables.items():
|
|
270
|
+
static = var.options.static if var.options is not None else None
|
|
271
|
+
var_descs.append(
|
|
272
|
+
VariableDescription(
|
|
273
|
+
name=name,
|
|
274
|
+
type=str(var.input),
|
|
275
|
+
default=var.default,
|
|
276
|
+
options=[str(o) for o in static] if static else None,
|
|
277
|
+
)
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
layout_type = face.layout.type
|
|
281
|
+
layout_primitive = (
|
|
282
|
+
layout_type.value if hasattr(layout_type, "value") else str(layout_type)
|
|
283
|
+
)
|
|
284
|
+
layout_desc = LayoutDescription(
|
|
285
|
+
primitive=layout_primitive,
|
|
286
|
+
items=_collect_layout_items(face.layout),
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
return DescribeFaceResult(
|
|
290
|
+
success=True,
|
|
291
|
+
path=resolved,
|
|
292
|
+
title=face.title or None,
|
|
293
|
+
description=face.description or None,
|
|
294
|
+
queries=query_descs,
|
|
295
|
+
charts=chart_descs,
|
|
296
|
+
variables=var_descs,
|
|
297
|
+
layout=layout_desc,
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def describe_paths(
|
|
302
|
+
paths: list[Path],
|
|
303
|
+
project_dir: Path | None = None,
|
|
304
|
+
) -> list[DescribeFaceResult]:
|
|
305
|
+
"""Describe N face files / directories.
|
|
306
|
+
|
|
307
|
+
Each argv path expands like the single-path verb: a file describes directly,
|
|
308
|
+
a directory walks recursively (skipping ``_*.yml`` partials and ejected
|
|
309
|
+
inspect-template directories). Results are concatenated in argv order.
|
|
310
|
+
"""
|
|
311
|
+
out: list[DescribeFaceResult] = []
|
|
312
|
+
for p in paths:
|
|
313
|
+
out.extend(_describe_one_path(p, project_dir=project_dir))
|
|
314
|
+
return out
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _describe_one_path(
|
|
318
|
+
path: Path,
|
|
319
|
+
project_dir: Path | None = None,
|
|
320
|
+
) -> list[DescribeFaceResult]:
|
|
321
|
+
"""Per-argv expansion: file → [one], dir → walk."""
|
|
322
|
+
# WHY: dataface.core.inspect.manifest_utils triggers the inspect package
|
|
323
|
+
# __init__, which eagerly imports TableInspector + grain/quality/semantic
|
|
324
|
+
# detectors. Keep this lazy so `dft --help` doesn't pay that startup cost.
|
|
325
|
+
from dataface.agent_api._paths import resolve_scoped_path
|
|
326
|
+
from dataface.core.compile.errors import DatafaceError
|
|
327
|
+
from dataface.core.inspect.manifest_utils import INSPECT_TEMPLATE_MANIFEST
|
|
328
|
+
|
|
329
|
+
try:
|
|
330
|
+
resolved = resolve_scoped_path(path, project_dir)
|
|
331
|
+
except ValueError as exc:
|
|
332
|
+
return [
|
|
333
|
+
DescribeFaceResult(
|
|
334
|
+
success=False,
|
|
335
|
+
path=path,
|
|
336
|
+
errors=[
|
|
337
|
+
DatafaceError.from_code(
|
|
338
|
+
DF_UNKNOWN_INTERNAL, message=str(exc)
|
|
339
|
+
).to_structured()
|
|
340
|
+
],
|
|
341
|
+
)
|
|
342
|
+
]
|
|
343
|
+
|
|
344
|
+
if not resolved.is_dir():
|
|
345
|
+
return [describe_face(resolved, project_dir=project_dir)]
|
|
346
|
+
|
|
347
|
+
template_dirs = {m.parent for m in resolved.glob(f"**/{INSPECT_TEMPLATE_MANIFEST}")}
|
|
348
|
+
yaml_files = sorted(
|
|
349
|
+
f
|
|
350
|
+
for f in (list(resolved.glob("**/*.yml")) + list(resolved.glob("**/*.yaml")))
|
|
351
|
+
if not f.name.startswith("_") and f.parent not in template_dirs
|
|
352
|
+
)
|
|
353
|
+
if not yaml_files:
|
|
354
|
+
return [
|
|
355
|
+
DescribeFaceResult(
|
|
356
|
+
success=False,
|
|
357
|
+
path=resolved,
|
|
358
|
+
errors=[
|
|
359
|
+
DatafaceError.from_code(
|
|
360
|
+
DF_UNKNOWN_INTERNAL,
|
|
361
|
+
message=f"No face files found in {resolved}",
|
|
362
|
+
).to_structured()
|
|
363
|
+
],
|
|
364
|
+
)
|
|
365
|
+
]
|
|
366
|
+
return [describe_face(f, project_dir=project_dir) for f in yaml_files]
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""describe_query verb — return column schema for a SQL string without fetching rows."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
from dataface.core.execute.adapters import AdapterRegistry
|
|
8
|
+
from dataface.core.execute.adapters.dbt_adapter_factory import build_adapter
|
|
9
|
+
from dataface.core.inspect.query_validator import validate_query
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DescribeQueryArgs(BaseModel):
|
|
13
|
+
"""Return the column schema (names + types) for a SQL string without executing it for data.
|
|
14
|
+
|
|
15
|
+
Gates on validate_query first — short-circuits on parse errors and missing join
|
|
16
|
+
predicates with actionable diagnostics; otherwise returns columns alongside any
|
|
17
|
+
non-error diagnostics (e.g. fanout_risk warnings) so the agent can read them
|
|
18
|
+
without being blocked from the column shape.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
sql: str = Field(..., description="SQL query to describe.")
|
|
22
|
+
source: str | None = Field(
|
|
23
|
+
None, description="Data source name to describe against."
|
|
24
|
+
)
|
|
25
|
+
dialect: str | None = Field(
|
|
26
|
+
None, description="SQL dialect hint for the validator (duckdb, bigquery, etc.)."
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class DescribeQueryColumn(BaseModel):
|
|
31
|
+
name: str
|
|
32
|
+
type: str
|
|
33
|
+
char_size: int | None = None
|
|
34
|
+
numeric_precision: int | None = None
|
|
35
|
+
numeric_scale: int | None = None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class DescribeQueryResult(BaseModel):
|
|
39
|
+
success: bool
|
|
40
|
+
columns: list[DescribeQueryColumn] | None = None
|
|
41
|
+
diagnostics: list[dict] = Field(
|
|
42
|
+
default_factory=list,
|
|
43
|
+
description="Dialect-specific diagnostic messages from the query runner.",
|
|
44
|
+
)
|
|
45
|
+
error: str | None = None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def describe_query(
|
|
49
|
+
sql: str,
|
|
50
|
+
*,
|
|
51
|
+
source: str | None = None,
|
|
52
|
+
dialect: str | None = None,
|
|
53
|
+
adapter_registry: AdapterRegistry,
|
|
54
|
+
) -> DescribeQueryResult:
|
|
55
|
+
"""Return the column schema for a SQL string using the dbt adapter.
|
|
56
|
+
|
|
57
|
+
Runs validate_query first; short-circuits on error-severity diagnostics
|
|
58
|
+
(parse_error, missing_join_predicate) without calling the warehouse.
|
|
59
|
+
Non-error diagnostics are surfaced on the result alongside the column schema.
|
|
60
|
+
"""
|
|
61
|
+
try:
|
|
62
|
+
diags = validate_query(sql, dialect=dialect)
|
|
63
|
+
|
|
64
|
+
error_diags = [d for d in diags if d.severity == "error"]
|
|
65
|
+
if error_diags:
|
|
66
|
+
return DescribeQueryResult(
|
|
67
|
+
success=False,
|
|
68
|
+
columns=None,
|
|
69
|
+
diagnostics=[d.to_dict() for d in diags],
|
|
70
|
+
error=error_diags[0].message,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
cfg = adapter_registry.resolve_source_config(source)
|
|
74
|
+
|
|
75
|
+
if cfg.get("type") == "duckdb":
|
|
76
|
+
# Run DESCRIBE via the registry's existing read-only SqlAdapter so
|
|
77
|
+
# we don't open a writable dbt-duckdb connection that would conflict
|
|
78
|
+
# with dft serve.
|
|
79
|
+
cols = _duckdb_describe(sql, source, adapter_registry)
|
|
80
|
+
else:
|
|
81
|
+
adapter = build_adapter(cfg, read_only=True)
|
|
82
|
+
with adapter.connection_named("dataface_describe"):
|
|
83
|
+
raw = adapter.get_column_schema_from_query(sql)
|
|
84
|
+
cols = [
|
|
85
|
+
DescribeQueryColumn(
|
|
86
|
+
name=c.name,
|
|
87
|
+
type=c.dtype,
|
|
88
|
+
char_size=c.char_size,
|
|
89
|
+
numeric_precision=c.numeric_precision,
|
|
90
|
+
numeric_scale=c.numeric_scale,
|
|
91
|
+
)
|
|
92
|
+
for c in raw
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
return DescribeQueryResult(
|
|
96
|
+
success=True,
|
|
97
|
+
columns=cols,
|
|
98
|
+
diagnostics=[d.to_dict() for d in diags],
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
except Exception as e: # noqa: BLE001 — adapter boundary, mirrors execute_query
|
|
102
|
+
return DescribeQueryResult(
|
|
103
|
+
success=False,
|
|
104
|
+
error=str(e),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _duckdb_describe(
|
|
109
|
+
sql: str, source: str | None, adapter_registry: AdapterRegistry
|
|
110
|
+
) -> list[DescribeQueryColumn]:
|
|
111
|
+
"""Run DESCRIBE ({sql}) via the registry's read-only SqlAdapter."""
|
|
112
|
+
from dataface.core.compile.models.query.compiled import SqlQuery
|
|
113
|
+
|
|
114
|
+
result = adapter_registry.execute(SqlQuery(sql=f"DESCRIBE ({sql})", source=source))
|
|
115
|
+
if result.error:
|
|
116
|
+
raise RuntimeError(result.error)
|
|
117
|
+
return [
|
|
118
|
+
DescribeQueryColumn(name=row["column_name"], type=row["column_type"])
|
|
119
|
+
for row in result.data
|
|
120
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""dataface.agent_api.docs — single-file syntax reference, sliced by H2."""
|
|
2
|
+
|
|
3
|
+
from dataface.agent_api.docs._loader import (
|
|
4
|
+
DocsArgs,
|
|
5
|
+
DocsCorpusMissingError,
|
|
6
|
+
DocsResult,
|
|
7
|
+
DocsSearchHit,
|
|
8
|
+
Topic,
|
|
9
|
+
TopicEntry,
|
|
10
|
+
docs,
|
|
11
|
+
read_full_text,
|
|
12
|
+
slugify,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"docs",
|
|
17
|
+
"read_full_text",
|
|
18
|
+
"slugify",
|
|
19
|
+
"TopicEntry",
|
|
20
|
+
"DocsArgs",
|
|
21
|
+
"DocsCorpusMissingError",
|
|
22
|
+
"DocsResult",
|
|
23
|
+
"DocsSearchHit",
|
|
24
|
+
"Topic",
|
|
25
|
+
]
|