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,221 @@
|
|
|
1
|
+
"""Chat session API for the Dataface agent."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Generator
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from datetime import datetime, timezone
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import TYPE_CHECKING, Any
|
|
10
|
+
|
|
11
|
+
from dataface.agent_api._session_store import (
|
|
12
|
+
SessionIndex,
|
|
13
|
+
SessionWriter,
|
|
14
|
+
load_session_for_resume,
|
|
15
|
+
new_session,
|
|
16
|
+
)
|
|
17
|
+
from dataface.ai.agent import run_agent
|
|
18
|
+
from dataface.ai.context import DatafaceAIContext
|
|
19
|
+
from dataface.ai.events import AgentEvent
|
|
20
|
+
from dataface.ai.llm import create_client
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from dataface.ai.external_mcp import ExternalMCPManager
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _now_iso() -> str:
|
|
27
|
+
return datetime.now(tz=timezone.utc).astimezone().isoformat()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class ChatSession:
|
|
32
|
+
"""Runtime handle for an active chat session."""
|
|
33
|
+
|
|
34
|
+
session_id: str
|
|
35
|
+
messages: list[dict[str, Any]]
|
|
36
|
+
context: DatafaceAIContext
|
|
37
|
+
model: str
|
|
38
|
+
created_at: str
|
|
39
|
+
last_used_at: str
|
|
40
|
+
client: Any = field(repr=False)
|
|
41
|
+
writer: SessionWriter = field(repr=False)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class ChatSessionSummary:
|
|
46
|
+
"""Persisted metadata for a saved session."""
|
|
47
|
+
|
|
48
|
+
session_id: str
|
|
49
|
+
cwd: Path
|
|
50
|
+
started_at: str
|
|
51
|
+
model: str = ""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def start_session(
|
|
55
|
+
*,
|
|
56
|
+
model: str | None = None,
|
|
57
|
+
project_dir: Path | None = None,
|
|
58
|
+
server_port: int | None = None,
|
|
59
|
+
) -> ChatSession:
|
|
60
|
+
"""Create a new chat session.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
model: LLM model name, optionally provider-prefixed.
|
|
64
|
+
project_dir: Working directory for the session. Defaults to cwd.
|
|
65
|
+
server_port: Port of the embedded HTTP preview server, if running.
|
|
66
|
+
"""
|
|
67
|
+
from dataface.core.execute.adapters import (
|
|
68
|
+
LOCAL_AUTHORING_REGISTRY_KWARGS,
|
|
69
|
+
build_adapter_registry,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
cwd = (project_dir or Path.cwd()).resolve()
|
|
73
|
+
client = create_client(model=model)
|
|
74
|
+
context = DatafaceAIContext(
|
|
75
|
+
adapter_registry=build_adapter_registry(
|
|
76
|
+
cwd,
|
|
77
|
+
**LOCAL_AUTHORING_REGISTRY_KWARGS,
|
|
78
|
+
),
|
|
79
|
+
default_project_dir=cwd,
|
|
80
|
+
server_port=server_port,
|
|
81
|
+
)
|
|
82
|
+
writer, _ = new_session(cwd, provider=client.provider, model=client.model)
|
|
83
|
+
return ChatSession(
|
|
84
|
+
session_id=writer.session_id,
|
|
85
|
+
messages=[],
|
|
86
|
+
context=context,
|
|
87
|
+
model=client.model,
|
|
88
|
+
created_at=writer.started_at,
|
|
89
|
+
last_used_at=writer.started_at,
|
|
90
|
+
client=client,
|
|
91
|
+
writer=writer,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def resume_session(
|
|
96
|
+
session_id: str,
|
|
97
|
+
*,
|
|
98
|
+
project_dir: Path | None = None,
|
|
99
|
+
server_port: int | None = None,
|
|
100
|
+
model: str | None = None,
|
|
101
|
+
max_tokens: int = 32_000,
|
|
102
|
+
) -> ChatSession:
|
|
103
|
+
"""Load and resume a previously saved session.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
session_id: The session id to resume.
|
|
107
|
+
project_dir: Working directory for the resumed session. Defaults to cwd.
|
|
108
|
+
server_port: Port of the embedded HTTP preview server, if running.
|
|
109
|
+
model: LLM model to use. Defaults to the same provider as the session.
|
|
110
|
+
max_tokens: Token budget for trimming old messages. 0 disables trimming.
|
|
111
|
+
|
|
112
|
+
Raises:
|
|
113
|
+
ValueError: If session not found, provider mismatch, or context window exceeded.
|
|
114
|
+
"""
|
|
115
|
+
from dataface.core.execute.adapters import (
|
|
116
|
+
LOCAL_AUTHORING_REGISTRY_KWARGS,
|
|
117
|
+
build_adapter_registry,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
cwd = (project_dir or Path.cwd()).resolve()
|
|
121
|
+
client = create_client(model=model)
|
|
122
|
+
|
|
123
|
+
messages, meta, _ = load_session_for_resume(
|
|
124
|
+
session_id,
|
|
125
|
+
expected_provider=client.provider,
|
|
126
|
+
max_tokens=max_tokens or None,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
if client.provider == "openai" and _has_tool_history(messages):
|
|
130
|
+
raise ValueError(
|
|
131
|
+
f"Cannot resume session {session_id[:8]}: "
|
|
132
|
+
"OpenAI sessions with tool-call history cannot be resumed "
|
|
133
|
+
"(no previous_response_id chain). Start a new session."
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
context = DatafaceAIContext(
|
|
137
|
+
adapter_registry=build_adapter_registry(
|
|
138
|
+
cwd,
|
|
139
|
+
**LOCAL_AUTHORING_REGISTRY_KWARGS,
|
|
140
|
+
),
|
|
141
|
+
default_project_dir=cwd,
|
|
142
|
+
server_port=server_port,
|
|
143
|
+
)
|
|
144
|
+
writer, _ = new_session(cwd, provider=client.provider, model=client.model)
|
|
145
|
+
return ChatSession(
|
|
146
|
+
session_id=session_id,
|
|
147
|
+
messages=messages,
|
|
148
|
+
context=context,
|
|
149
|
+
model=client.model,
|
|
150
|
+
created_at=meta.get("started_at", _now_iso()),
|
|
151
|
+
last_used_at=_now_iso(),
|
|
152
|
+
client=client,
|
|
153
|
+
writer=writer,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def send_message(
|
|
158
|
+
session: ChatSession,
|
|
159
|
+
prompt: str,
|
|
160
|
+
*,
|
|
161
|
+
tools: list[dict[str, Any]] | None = None,
|
|
162
|
+
external_manager: ExternalMCPManager | None = None,
|
|
163
|
+
) -> Generator[AgentEvent, None, None]:
|
|
164
|
+
"""Send a prompt and stream typed agent events.
|
|
165
|
+
|
|
166
|
+
Mutates ``session.messages`` in place (run_agent appends as it goes).
|
|
167
|
+
Persists the new messages to disk after the turn completes.
|
|
168
|
+
|
|
169
|
+
Yields:
|
|
170
|
+
AgentEvent instances (ContentDelta, ToolCallEvent, ToolResultEvent,
|
|
171
|
+
ThinkingStatus, AgentDone, AgentError).
|
|
172
|
+
"""
|
|
173
|
+
msg_count_before = len(session.messages)
|
|
174
|
+
try:
|
|
175
|
+
yield from run_agent(
|
|
176
|
+
prompt,
|
|
177
|
+
client=session.client,
|
|
178
|
+
context=session.context,
|
|
179
|
+
messages=session.messages,
|
|
180
|
+
tools=tools,
|
|
181
|
+
external_manager=external_manager,
|
|
182
|
+
)
|
|
183
|
+
finally:
|
|
184
|
+
session.last_used_at = _now_iso()
|
|
185
|
+
new_msgs = session.messages[msg_count_before:]
|
|
186
|
+
if new_msgs:
|
|
187
|
+
session.writer.write_turn(new_msgs)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def list_sessions(*, owner: Path | None = None) -> list[ChatSessionSummary]:
|
|
191
|
+
"""List all saved sessions.
|
|
192
|
+
|
|
193
|
+
A session-index entry missing or empty cwd indicates a corrupt index;
|
|
194
|
+
raise rather than coerce to Path(".") and mask the corruption.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
owner: When set, filter to sessions whose cwd equals this path.
|
|
198
|
+
"""
|
|
199
|
+
idx = SessionIndex()
|
|
200
|
+
entries = idx.list_all() if owner is None else idx.list_for_cwd(owner)
|
|
201
|
+
summaries: list[ChatSessionSummary] = []
|
|
202
|
+
for e in entries:
|
|
203
|
+
cwd = e.get("cwd", "")
|
|
204
|
+
if not cwd:
|
|
205
|
+
raise ValueError(
|
|
206
|
+
f"Session index entry {e.get('session_id', '?')} has no cwd; "
|
|
207
|
+
"the index is corrupt. Delete ~/.dft/sessions/index.json to rebuild."
|
|
208
|
+
)
|
|
209
|
+
summaries.append(
|
|
210
|
+
ChatSessionSummary(
|
|
211
|
+
session_id=e["session_id"],
|
|
212
|
+
cwd=Path(cwd),
|
|
213
|
+
started_at=e.get("started_at", ""),
|
|
214
|
+
model=e.get("model", ""),
|
|
215
|
+
)
|
|
216
|
+
)
|
|
217
|
+
return summaries
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _has_tool_history(messages: list[dict[str, Any]]) -> bool:
|
|
221
|
+
return any(m.get("role") == "tool" or bool(m.get("tool_calls")) for m in messages)
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"""Typed dashboard verbs for the Dataface agent API.
|
|
2
|
+
|
|
3
|
+
All public functions have concrete typed arguments and typed Pydantic return
|
|
4
|
+
values. No bare dict[str, Any] in any return position. MCP/CLI callers use
|
|
5
|
+
.model_dump() at the wire boundary.
|
|
6
|
+
|
|
7
|
+
`RenderedDashboard` and `render_dashboard` live in `dataface.core.dashboard`
|
|
8
|
+
so the embedded HTTP server can call them without inverting the layer stack; they
|
|
9
|
+
are re-exported here so the agent_api thin-wrapper rule still holds.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any, Literal
|
|
16
|
+
|
|
17
|
+
import yaml
|
|
18
|
+
from pydantic import BaseModel, Field
|
|
19
|
+
|
|
20
|
+
from dataface.agent_api._paths import resolve_scoped_path
|
|
21
|
+
from dataface.core.compile import Face, compile_file
|
|
22
|
+
from dataface.core.compile.errors import DatafaceError
|
|
23
|
+
from dataface.core.dashboard import (
|
|
24
|
+
RenderedDashboard as RenderedDashboard,
|
|
25
|
+
render_dashboard as render_dashboard,
|
|
26
|
+
)
|
|
27
|
+
from dataface.core.errors import DF_UNKNOWN_INTERNAL, StructuredError
|
|
28
|
+
from dataface.core.render.warnings.base import RenderWarning
|
|
29
|
+
|
|
30
|
+
# ---------------------------------------------------------------------------
|
|
31
|
+
# Return-type models
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class DashboardSummary(BaseModel):
|
|
36
|
+
path: Path
|
|
37
|
+
absolute_path: Path
|
|
38
|
+
title: str
|
|
39
|
+
description: str = ""
|
|
40
|
+
queries: list[str] = []
|
|
41
|
+
charts: list[str] = []
|
|
42
|
+
variables: list[str] = []
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class SkippedFile(BaseModel):
|
|
46
|
+
path: Path
|
|
47
|
+
reason: str
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class ListDashboardsResult(BaseModel):
|
|
51
|
+
success: bool
|
|
52
|
+
errors: list[StructuredError] = []
|
|
53
|
+
dashboards: list[DashboardSummary] = []
|
|
54
|
+
count: int = 0
|
|
55
|
+
directory: Path
|
|
56
|
+
skipped_files: list[SkippedFile] = []
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class CompiledDashboard(BaseModel):
|
|
60
|
+
success: bool
|
|
61
|
+
dashboard: Face | None = None
|
|
62
|
+
errors: list[StructuredError] = []
|
|
63
|
+
warnings: list[RenderWarning] = []
|
|
64
|
+
raw_yaml: str | None = None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class RenderDashboardArgs(BaseModel):
|
|
68
|
+
"""Validate, compile, and render a Dataface dashboard, returning resolved chart semantics + executed data for reasoning. Use this to verify YAML is valid, inspect query results, and iterate on a dashboard. When 'path' is provided, the response also includes a localhost URL the user can open in a browser to view the live dashboard (the embedded HTTP server runs alongside `dft mcp serve`). Pass `as_link=true` with a path to skip execution and return only the preview URL."""
|
|
69
|
+
|
|
70
|
+
path: Path | None = Field(
|
|
71
|
+
None, description="Path to dashboard YAML file (use this OR yaml_content)"
|
|
72
|
+
)
|
|
73
|
+
yaml_content: str | None = Field(
|
|
74
|
+
None, description="YAML content to render directly (use this OR path)"
|
|
75
|
+
)
|
|
76
|
+
project_dir: Path | None = Field(
|
|
77
|
+
None, description="Project root for resolving relative paths"
|
|
78
|
+
)
|
|
79
|
+
variables: dict[str, Any] | None = Field(
|
|
80
|
+
None, description="Variable values to apply to the dashboard"
|
|
81
|
+
)
|
|
82
|
+
format: Literal["json", "text", "yaml", "svg"] | None = Field(
|
|
83
|
+
None,
|
|
84
|
+
description=(
|
|
85
|
+
"Output format. 'json' (default) returns resolved chart "
|
|
86
|
+
"semantics and executed data as structured JSON. 'text' "
|
|
87
|
+
"returns a compact markdown summary of charts and data "
|
|
88
|
+
"(most token-efficient). 'yaml' returns resolved Dataface "
|
|
89
|
+
"YAML with inline data — valid input for re-compilation, "
|
|
90
|
+
"ideal for round-trip editing. 'svg' returns the rendered "
|
|
91
|
+
"dashboard as inline SVG (under result['data']) for hosts "
|
|
92
|
+
"that embed the rendered output directly."
|
|
93
|
+
),
|
|
94
|
+
)
|
|
95
|
+
as_link: bool = Field(
|
|
96
|
+
False,
|
|
97
|
+
description=(
|
|
98
|
+
"When true and 'path' is provided, skip query execution and return only "
|
|
99
|
+
"the preview URL. Use this to surface an existing dashboard in the browser "
|
|
100
|
+
"without paying the render cost — replaces the old view_dashboard tool."
|
|
101
|
+
),
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# ---------------------------------------------------------------------------
|
|
106
|
+
# Public verbs
|
|
107
|
+
# ---------------------------------------------------------------------------
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def list_dashboards(
|
|
111
|
+
directory: Path = Path("."),
|
|
112
|
+
recursive: bool = True,
|
|
113
|
+
) -> ListDashboardsResult:
|
|
114
|
+
"""List all available dashboards in a directory."""
|
|
115
|
+
dir_path = directory.resolve()
|
|
116
|
+
|
|
117
|
+
if not dir_path.exists():
|
|
118
|
+
return ListDashboardsResult(
|
|
119
|
+
success=False,
|
|
120
|
+
errors=[
|
|
121
|
+
DatafaceError.from_code(
|
|
122
|
+
DF_UNKNOWN_INTERNAL,
|
|
123
|
+
message=f"Directory not found: {directory}",
|
|
124
|
+
).to_structured()
|
|
125
|
+
],
|
|
126
|
+
directory=dir_path,
|
|
127
|
+
)
|
|
128
|
+
if not dir_path.is_dir():
|
|
129
|
+
return ListDashboardsResult(
|
|
130
|
+
success=False,
|
|
131
|
+
errors=[
|
|
132
|
+
DatafaceError.from_code(
|
|
133
|
+
DF_UNKNOWN_INTERNAL,
|
|
134
|
+
message=f"Not a directory: {directory}",
|
|
135
|
+
).to_structured()
|
|
136
|
+
],
|
|
137
|
+
directory=dir_path,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
pattern = "**/*.yml" if recursive else "*.yml"
|
|
141
|
+
yaml_files = list(dir_path.glob(pattern))
|
|
142
|
+
yaml_files.extend(dir_path.glob(pattern.replace(".yml", ".yaml")))
|
|
143
|
+
|
|
144
|
+
dashboards: list[DashboardSummary] = []
|
|
145
|
+
skipped: list[SkippedFile] = []
|
|
146
|
+
|
|
147
|
+
for yaml_file in sorted(yaml_files):
|
|
148
|
+
if yaml_file.name.startswith("_"):
|
|
149
|
+
continue
|
|
150
|
+
try:
|
|
151
|
+
content = yaml.safe_load(yaml_file.read_text())
|
|
152
|
+
if not isinstance(content, dict):
|
|
153
|
+
skipped.append(
|
|
154
|
+
SkippedFile(
|
|
155
|
+
path=yaml_file.relative_to(dir_path),
|
|
156
|
+
reason="Not a YAML mapping",
|
|
157
|
+
)
|
|
158
|
+
)
|
|
159
|
+
continue
|
|
160
|
+
dashboard_keys = {"queries", "charts", "rows", "cols", "grid", "tabs"}
|
|
161
|
+
if not any(key in content for key in dashboard_keys):
|
|
162
|
+
continue
|
|
163
|
+
dashboards.append(
|
|
164
|
+
DashboardSummary(
|
|
165
|
+
path=yaml_file.relative_to(dir_path),
|
|
166
|
+
absolute_path=yaml_file,
|
|
167
|
+
title=content.get("title", yaml_file.stem),
|
|
168
|
+
description=content.get("description", ""),
|
|
169
|
+
queries=list(content.get("queries", {}).keys()),
|
|
170
|
+
charts=list(content.get("charts", {}).keys()),
|
|
171
|
+
variables=list(content.get("variables", {}).keys()),
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
except yaml.YAMLError as e:
|
|
175
|
+
skipped.append(
|
|
176
|
+
SkippedFile(
|
|
177
|
+
path=yaml_file.relative_to(dir_path),
|
|
178
|
+
reason=f"YAML parse error: {e}",
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
except OSError as e:
|
|
182
|
+
skipped.append(
|
|
183
|
+
SkippedFile(
|
|
184
|
+
path=yaml_file.relative_to(dir_path),
|
|
185
|
+
reason=f"Read error: {e}",
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
return ListDashboardsResult(
|
|
190
|
+
success=True,
|
|
191
|
+
dashboards=dashboards,
|
|
192
|
+
count=len(dashboards),
|
|
193
|
+
directory=dir_path,
|
|
194
|
+
skipped_files=skipped,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def get_dashboard(
|
|
199
|
+
path: Path,
|
|
200
|
+
include_raw: bool = False,
|
|
201
|
+
project_dir: Path | None = None,
|
|
202
|
+
) -> CompiledDashboard:
|
|
203
|
+
"""Get the compiled structure of a dashboard."""
|
|
204
|
+
try:
|
|
205
|
+
file_path = resolve_scoped_path(path, project_dir)
|
|
206
|
+
except ValueError as exc:
|
|
207
|
+
return CompiledDashboard(
|
|
208
|
+
success=False,
|
|
209
|
+
errors=[
|
|
210
|
+
DatafaceError.from_code(
|
|
211
|
+
DF_UNKNOWN_INTERNAL, message=str(exc)
|
|
212
|
+
).to_structured()
|
|
213
|
+
],
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
if not file_path.exists():
|
|
217
|
+
return CompiledDashboard(
|
|
218
|
+
success=False,
|
|
219
|
+
errors=[
|
|
220
|
+
DatafaceError.from_code(
|
|
221
|
+
DF_UNKNOWN_INTERNAL,
|
|
222
|
+
message=f"File not found: {path}",
|
|
223
|
+
).to_structured(file=str(file_path))
|
|
224
|
+
],
|
|
225
|
+
)
|
|
226
|
+
if not file_path.is_file():
|
|
227
|
+
return CompiledDashboard(
|
|
228
|
+
success=False,
|
|
229
|
+
errors=[
|
|
230
|
+
DatafaceError.from_code(
|
|
231
|
+
DF_UNKNOWN_INTERNAL,
|
|
232
|
+
message=f"Not a file: {path}",
|
|
233
|
+
).to_structured(file=str(file_path))
|
|
234
|
+
],
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
try:
|
|
238
|
+
raw_yaml = file_path.read_text()
|
|
239
|
+
except OSError as e:
|
|
240
|
+
return CompiledDashboard(
|
|
241
|
+
success=False,
|
|
242
|
+
errors=[
|
|
243
|
+
DatafaceError.from_code(
|
|
244
|
+
DF_UNKNOWN_INTERNAL,
|
|
245
|
+
message=f"Failed to read file: {e}",
|
|
246
|
+
).to_structured(file=str(file_path))
|
|
247
|
+
],
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
result = compile_file(file_path)
|
|
251
|
+
return CompiledDashboard(
|
|
252
|
+
success=result.success,
|
|
253
|
+
dashboard=result.face if result.success else None,
|
|
254
|
+
errors=list(result.errors),
|
|
255
|
+
warnings=result.warnings,
|
|
256
|
+
raw_yaml=raw_yaml if include_raw else None,
|
|
257
|
+
)
|