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,75 @@
|
|
|
1
|
+
"""Dataface Core Engine.
|
|
2
|
+
|
|
3
|
+
This module provides the core compilation, execution, and rendering
|
|
4
|
+
functionality for Dataface dashboards.
|
|
5
|
+
|
|
6
|
+
Submodules:
|
|
7
|
+
- compile/: Transform YAML → Face
|
|
8
|
+
- execute/: Execute queries and fetch data
|
|
9
|
+
- render/: Transform Face + data → output
|
|
10
|
+
- serve/: HTTP server for dashboard serving
|
|
11
|
+
- inspect/: Database inspection and profiling
|
|
12
|
+
- validate: Validation utilities
|
|
13
|
+
|
|
14
|
+
High-Level API:
|
|
15
|
+
>>> from pathlib import Path
|
|
16
|
+
>>> from dataface.core import compile, Executor, render
|
|
17
|
+
>>> from dataface.core.execute.adapters import build_adapter_registry
|
|
18
|
+
>>> result = compile(yaml_content)
|
|
19
|
+
>>> registry = build_adapter_registry(Path.cwd())
|
|
20
|
+
>>> executor = Executor(result.face, registry, query_registry=result.query_registry)
|
|
21
|
+
>>> html = render(result.face, executor, format="html")
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
# Re-export main compile functions and types
|
|
25
|
+
# Re-export validate module
|
|
26
|
+
from dataface.core import validate
|
|
27
|
+
from dataface.core.compile import (
|
|
28
|
+
AnyQuery,
|
|
29
|
+
AuthoredFace,
|
|
30
|
+
Chart,
|
|
31
|
+
CompilationError,
|
|
32
|
+
CompileResult,
|
|
33
|
+
Face,
|
|
34
|
+
Layout,
|
|
35
|
+
LayoutItem,
|
|
36
|
+
Variable,
|
|
37
|
+
compile,
|
|
38
|
+
compile_file,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# Re-export execute functions and types
|
|
42
|
+
from dataface.core.execute import (
|
|
43
|
+
ExecutionError,
|
|
44
|
+
Executor,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Re-export render functions and types
|
|
48
|
+
from dataface.core.render import (
|
|
49
|
+
RenderError,
|
|
50
|
+
render,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
# Compile
|
|
55
|
+
"compile",
|
|
56
|
+
"compile_file",
|
|
57
|
+
"CompileResult",
|
|
58
|
+
"Face",
|
|
59
|
+
"Chart",
|
|
60
|
+
"AnyQuery",
|
|
61
|
+
"Layout",
|
|
62
|
+
"LayoutItem",
|
|
63
|
+
"AuthoredFace",
|
|
64
|
+
"Chart",
|
|
65
|
+
"Variable",
|
|
66
|
+
"CompilationError",
|
|
67
|
+
# Execute
|
|
68
|
+
"Executor",
|
|
69
|
+
"ExecutionError",
|
|
70
|
+
# Render
|
|
71
|
+
"render",
|
|
72
|
+
"RenderError",
|
|
73
|
+
# Validate
|
|
74
|
+
"validate",
|
|
75
|
+
]
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"""AuthoredFace compilation module.
|
|
2
|
+
|
|
3
|
+
Stage: COMPILE
|
|
4
|
+
Purpose: Transform YAML face definitions into Face objects
|
|
5
|
+
ready for execution and rendering.
|
|
6
|
+
|
|
7
|
+
Entry Points:
|
|
8
|
+
- compile(yaml_content: str) -> CompileResult
|
|
9
|
+
- compile_file(file_path: Path) -> CompileResult
|
|
10
|
+
|
|
11
|
+
Outputs:
|
|
12
|
+
- Face: Fully normalized face with all references resolved
|
|
13
|
+
- CompileResult: Result container with face, errors, and warnings
|
|
14
|
+
|
|
15
|
+
Dependencies:
|
|
16
|
+
- yaml (PyYAML)
|
|
17
|
+
- pydantic
|
|
18
|
+
|
|
19
|
+
This module is pure - it does NOT import from execute/ or render/.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from dataface.core.compile.chart_focus import focus_on_chart
|
|
23
|
+
from dataface.core.compile.compiler import CompileResult, compile, compile_file
|
|
24
|
+
from dataface.core.compile.config import (
|
|
25
|
+
AccessConfig,
|
|
26
|
+
MetaConfig,
|
|
27
|
+
ProjectSourcesConfig,
|
|
28
|
+
get_config,
|
|
29
|
+
get_project_sources,
|
|
30
|
+
get_project_warnings_ignore,
|
|
31
|
+
load_config,
|
|
32
|
+
)
|
|
33
|
+
from dataface.core.compile.errors import (
|
|
34
|
+
CompilationError,
|
|
35
|
+
JinjaError,
|
|
36
|
+
ParseError,
|
|
37
|
+
ReferenceError,
|
|
38
|
+
ValidationError,
|
|
39
|
+
)
|
|
40
|
+
from dataface.core.compile.meta import (
|
|
41
|
+
get_meta_for_face,
|
|
42
|
+
load_meta_file,
|
|
43
|
+
merge_meta_with_face,
|
|
44
|
+
resolve_meta_chain,
|
|
45
|
+
)
|
|
46
|
+
from dataface.core.compile.models.chart.authored import (
|
|
47
|
+
AreaChart,
|
|
48
|
+
AuthoredChart,
|
|
49
|
+
BarChart,
|
|
50
|
+
CalloutChart,
|
|
51
|
+
ChartType,
|
|
52
|
+
GeoshapeChart,
|
|
53
|
+
HeatmapChart,
|
|
54
|
+
KpiChart,
|
|
55
|
+
LayeredChart,
|
|
56
|
+
LineChart,
|
|
57
|
+
PieChart,
|
|
58
|
+
PointMapChart,
|
|
59
|
+
ScatterChart,
|
|
60
|
+
SparkBarChart,
|
|
61
|
+
SparkConfig,
|
|
62
|
+
SparkTypeLiteral,
|
|
63
|
+
TableChart,
|
|
64
|
+
TableColumnConfig,
|
|
65
|
+
)
|
|
66
|
+
from dataface.core.compile.models.chart.compiled import (
|
|
67
|
+
Chart,
|
|
68
|
+
ChartDependencies,
|
|
69
|
+
is_chart,
|
|
70
|
+
)
|
|
71
|
+
from dataface.core.compile.models.face.authored import (
|
|
72
|
+
AuthoredFace,
|
|
73
|
+
LayoutType,
|
|
74
|
+
SourcesSection,
|
|
75
|
+
)
|
|
76
|
+
from dataface.core.compile.models.face.compiled import (
|
|
77
|
+
Face,
|
|
78
|
+
Layout,
|
|
79
|
+
LayoutItem,
|
|
80
|
+
VariableValues,
|
|
81
|
+
is_face,
|
|
82
|
+
)
|
|
83
|
+
from dataface.core.compile.models.query.authored import AuthoredQuery
|
|
84
|
+
from dataface.core.compile.models.query.compiled import (
|
|
85
|
+
VALID_QUERY_TYPES,
|
|
86
|
+
AnyQuery,
|
|
87
|
+
CsvQuery,
|
|
88
|
+
DbtModelQuery,
|
|
89
|
+
HttpQuery,
|
|
90
|
+
MetricFlowQuery,
|
|
91
|
+
SqlQuery,
|
|
92
|
+
is_csv_query,
|
|
93
|
+
is_dbt_model_query,
|
|
94
|
+
is_http_query,
|
|
95
|
+
is_metricflow_query,
|
|
96
|
+
is_sql_query,
|
|
97
|
+
)
|
|
98
|
+
from dataface.core.compile.models.source import (
|
|
99
|
+
VALID_SOURCE_TYPES,
|
|
100
|
+
BigQuerySourceConfig,
|
|
101
|
+
CsvSourceConfig,
|
|
102
|
+
DbtProfileSourceConfig,
|
|
103
|
+
DuckDBSourceConfig,
|
|
104
|
+
HttpSourceConfig,
|
|
105
|
+
JsonSourceConfig,
|
|
106
|
+
MySQLSourceConfig,
|
|
107
|
+
ParquetSourceConfig,
|
|
108
|
+
PostgresSourceConfig,
|
|
109
|
+
RedshiftSourceConfig,
|
|
110
|
+
SnowflakeSourceConfig,
|
|
111
|
+
SourceConfig,
|
|
112
|
+
is_api_source,
|
|
113
|
+
is_database_source,
|
|
114
|
+
is_file_source,
|
|
115
|
+
parse_source_config,
|
|
116
|
+
resolve_env_var,
|
|
117
|
+
)
|
|
118
|
+
from dataface.core.compile.models.variable.authored import (
|
|
119
|
+
Variable,
|
|
120
|
+
VariableInputType,
|
|
121
|
+
)
|
|
122
|
+
from dataface.core.compile.parameterized import (
|
|
123
|
+
ParameterizedQuery,
|
|
124
|
+
render_parameterized,
|
|
125
|
+
render_parameterized_with_queries,
|
|
126
|
+
)
|
|
127
|
+
from dataface.core.compile.sources import (
|
|
128
|
+
DB_INFO_MAP,
|
|
129
|
+
detect_database_type_from_registry,
|
|
130
|
+
detect_dbt_connection_string,
|
|
131
|
+
detect_dbt_connections,
|
|
132
|
+
detect_dbt_database_type,
|
|
133
|
+
get_database_info,
|
|
134
|
+
)
|
|
135
|
+
from dataface.core.compile.variables import parse_variable_json_strings
|
|
136
|
+
|
|
137
|
+
__all__ = [
|
|
138
|
+
# Main entry points
|
|
139
|
+
"compile",
|
|
140
|
+
"compile_file",
|
|
141
|
+
"CompileResult",
|
|
142
|
+
"focus_on_chart",
|
|
143
|
+
# Input types
|
|
144
|
+
"AuthoredFace",
|
|
145
|
+
"AuthoredChart",
|
|
146
|
+
"BarChart",
|
|
147
|
+
"LineChart",
|
|
148
|
+
"AreaChart",
|
|
149
|
+
"ScatterChart",
|
|
150
|
+
"HeatmapChart",
|
|
151
|
+
"PieChart",
|
|
152
|
+
"KpiChart",
|
|
153
|
+
"TableChart",
|
|
154
|
+
"PointMapChart",
|
|
155
|
+
"GeoshapeChart",
|
|
156
|
+
"LayeredChart",
|
|
157
|
+
"CalloutChart",
|
|
158
|
+
"SparkBarChart",
|
|
159
|
+
"AuthoredQuery",
|
|
160
|
+
"Variable",
|
|
161
|
+
"SourcesSection",
|
|
162
|
+
# Compiled types
|
|
163
|
+
"Face",
|
|
164
|
+
"Chart",
|
|
165
|
+
"AnyQuery",
|
|
166
|
+
"Layout",
|
|
167
|
+
"LayoutItem",
|
|
168
|
+
"VariableValues",
|
|
169
|
+
"ChartDependencies",
|
|
170
|
+
# Unified query types
|
|
171
|
+
"SqlQuery",
|
|
172
|
+
"CsvQuery",
|
|
173
|
+
"MetricFlowQuery",
|
|
174
|
+
"HttpQuery",
|
|
175
|
+
"DbtModelQuery",
|
|
176
|
+
"VALID_QUERY_TYPES",
|
|
177
|
+
# Source types
|
|
178
|
+
"SourceConfig",
|
|
179
|
+
"PostgresSourceConfig",
|
|
180
|
+
"SnowflakeSourceConfig",
|
|
181
|
+
"BigQuerySourceConfig",
|
|
182
|
+
"RedshiftSourceConfig",
|
|
183
|
+
"MySQLSourceConfig",
|
|
184
|
+
"DuckDBSourceConfig",
|
|
185
|
+
"CsvSourceConfig",
|
|
186
|
+
"ParquetSourceConfig",
|
|
187
|
+
"JsonSourceConfig",
|
|
188
|
+
"HttpSourceConfig",
|
|
189
|
+
"DbtProfileSourceConfig",
|
|
190
|
+
"VALID_SOURCE_TYPES",
|
|
191
|
+
"parse_source_config",
|
|
192
|
+
"is_database_source",
|
|
193
|
+
"is_file_source",
|
|
194
|
+
"is_api_source",
|
|
195
|
+
"resolve_env_var",
|
|
196
|
+
# Config
|
|
197
|
+
"ProjectSourcesConfig",
|
|
198
|
+
"get_config",
|
|
199
|
+
"load_config",
|
|
200
|
+
"get_project_sources",
|
|
201
|
+
"get_project_warnings_ignore",
|
|
202
|
+
# Meta configuration
|
|
203
|
+
"MetaConfig",
|
|
204
|
+
"AccessConfig",
|
|
205
|
+
"get_meta_for_face",
|
|
206
|
+
"load_meta_file",
|
|
207
|
+
"merge_meta_with_face",
|
|
208
|
+
"resolve_meta_chain",
|
|
209
|
+
# Type guards
|
|
210
|
+
"is_face",
|
|
211
|
+
"is_chart",
|
|
212
|
+
"is_sql_query",
|
|
213
|
+
"is_csv_query",
|
|
214
|
+
"is_metricflow_query",
|
|
215
|
+
"is_http_query",
|
|
216
|
+
"is_dbt_model_query",
|
|
217
|
+
# Enums
|
|
218
|
+
"ChartType",
|
|
219
|
+
"LayoutType",
|
|
220
|
+
"VariableInputType",
|
|
221
|
+
"SparkTypeLiteral",
|
|
222
|
+
# Spark charts
|
|
223
|
+
"SparkConfig",
|
|
224
|
+
"TableColumnConfig",
|
|
225
|
+
# Errors
|
|
226
|
+
"CompilationError",
|
|
227
|
+
"ParseError",
|
|
228
|
+
"ValidationError",
|
|
229
|
+
"ReferenceError",
|
|
230
|
+
"JinjaError",
|
|
231
|
+
# Parameterized queries
|
|
232
|
+
"ParameterizedQuery",
|
|
233
|
+
"render_parameterized",
|
|
234
|
+
"render_parameterized_with_queries",
|
|
235
|
+
# Variable utilities
|
|
236
|
+
"parse_variable_json_strings",
|
|
237
|
+
# Source detection utilities
|
|
238
|
+
"DB_INFO_MAP",
|
|
239
|
+
"detect_database_type_from_registry",
|
|
240
|
+
"detect_dbt_connection_string",
|
|
241
|
+
"detect_dbt_connections",
|
|
242
|
+
"detect_dbt_database_type",
|
|
243
|
+
"get_database_info",
|
|
244
|
+
]
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Shared Jinja helpers used by compile/parameterized.py and compile/jinja.py.
|
|
2
|
+
|
|
3
|
+
Both modules need identical lenient-undefined and query-namespace behaviour;
|
|
4
|
+
this module is the single canonical home. Consumers import from here.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from jinja2 import Undefined
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class _LenientUndefined(Undefined):
|
|
15
|
+
"""Lenient undefined for non-strict Jinja rendering.
|
|
16
|
+
|
|
17
|
+
Undefined variables become empty strings rather than raising errors.
|
|
18
|
+
Used in interactive editing contexts where variables may not yet be defined.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __str__(self) -> str:
|
|
22
|
+
return ""
|
|
23
|
+
|
|
24
|
+
def __repr__(self) -> str:
|
|
25
|
+
return ""
|
|
26
|
+
|
|
27
|
+
def __bool__(self) -> bool:
|
|
28
|
+
return False
|
|
29
|
+
|
|
30
|
+
def __iter__(self) -> Any:
|
|
31
|
+
return iter([])
|
|
32
|
+
|
|
33
|
+
def __len__(self) -> int:
|
|
34
|
+
return 0
|
|
35
|
+
|
|
36
|
+
def __eq__(self, other: Any) -> bool:
|
|
37
|
+
return isinstance(other, Undefined) or other is None or other == ""
|
|
38
|
+
|
|
39
|
+
def __ne__(self, other: Any) -> bool:
|
|
40
|
+
return not self.__eq__(other)
|
|
41
|
+
|
|
42
|
+
def __getitem__(self, key: Any) -> _LenientUndefined: # type: ignore[override]
|
|
43
|
+
return self
|
|
44
|
+
|
|
45
|
+
def __getattr__(self, name: str) -> _LenientUndefined:
|
|
46
|
+
# Return self for chaining like {{ foo.bar.baz }}; guard dunder lookups.
|
|
47
|
+
if name.startswith("_"):
|
|
48
|
+
raise AttributeError(name)
|
|
49
|
+
return self
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class _QueryNamespace:
|
|
53
|
+
"""Proxy for {{ queries.query_name }} template resolution.
|
|
54
|
+
|
|
55
|
+
Returns the SQL body of the named query. Raises AttributeError (the
|
|
56
|
+
correct Python __getattr__ protocol) when the query is not found.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(self, queries: dict[str, Any]):
|
|
60
|
+
self._queries = queries
|
|
61
|
+
|
|
62
|
+
def __getattr__(self, name: str) -> str:
|
|
63
|
+
if name.startswith("_"):
|
|
64
|
+
raise AttributeError(name)
|
|
65
|
+
|
|
66
|
+
if name not in self._queries:
|
|
67
|
+
raise AttributeError(f"Query '{name}' not found")
|
|
68
|
+
|
|
69
|
+
query = self._queries[name]
|
|
70
|
+
|
|
71
|
+
if hasattr(query, "sql"):
|
|
72
|
+
sql = query.sql
|
|
73
|
+
return sql if isinstance(sql, str) else ""
|
|
74
|
+
elif isinstance(query, dict):
|
|
75
|
+
sql = query.get("sql", "")
|
|
76
|
+
return sql if isinstance(sql, str) else ""
|
|
77
|
+
|
|
78
|
+
return str(query)
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"""Chart style channel parsing and normalization.
|
|
2
|
+
|
|
3
|
+
Handles the {column, value, scale} grammar for chart-level color
|
|
4
|
+
channels (color, background, opacity, stroke.color, stroke.width).
|
|
5
|
+
|
|
6
|
+
Rule-driven styling lives in the chart-level ``conditional_formatting:``
|
|
7
|
+
block (indexed by column name), not per-channel. See ``types.py`` for
|
|
8
|
+
``FieldConditionalFormatting``.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from typing import Any, Literal
|
|
15
|
+
|
|
16
|
+
from dataface.core.compile.models.chart.authored import (
|
|
17
|
+
GEO_CHART_TYPES,
|
|
18
|
+
ConditionalRule,
|
|
19
|
+
)
|
|
20
|
+
from dataface.core.compile.models.primitives import ScaleTargetConfig, StyleColorConfig
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class ResolvedStyleChannel:
|
|
25
|
+
channel: str
|
|
26
|
+
mode: Literal["series", "literal", "gradient", "conditional"]
|
|
27
|
+
data_field: str = ""
|
|
28
|
+
literal_value: Any = None
|
|
29
|
+
scale: ScaleTargetConfig | None = None
|
|
30
|
+
rules: tuple[ConditionalRule, ...] = ()
|
|
31
|
+
# When mode == "conditional", optional gradient fallback used when no
|
|
32
|
+
# threshold rule matches. Enables Looker-style "scale with rule override"
|
|
33
|
+
# where threshold rules take priority and scale shows through otherwise.
|
|
34
|
+
fallback_scale: ScaleTargetConfig | None = None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def parse_style_channel(
|
|
38
|
+
raw: str | dict[str, Any], channel_name: str
|
|
39
|
+
) -> ResolvedStyleChannel:
|
|
40
|
+
"""Parse a raw authored channel value into a typed ResolvedStyleChannel.
|
|
41
|
+
|
|
42
|
+
Handles shorthand (string) and four expanded forms.
|
|
43
|
+
Validates mutual exclusion. Does NOT validate field references (needs data).
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
raw: The authored value — string shorthand or {column,value,scale,when} dict
|
|
47
|
+
channel_name: The channel name for error messages
|
|
48
|
+
|
|
49
|
+
Raises:
|
|
50
|
+
ValueError: On mutual exclusion violations or unknown form
|
|
51
|
+
"""
|
|
52
|
+
if isinstance(raw, str):
|
|
53
|
+
return ResolvedStyleChannel(channel=channel_name, mode="series", data_field=raw)
|
|
54
|
+
|
|
55
|
+
if not isinstance(raw, dict):
|
|
56
|
+
raise ValueError(
|
|
57
|
+
f"Channel '{channel_name}' must be a string or dict, got {type(raw).__name__}"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
has_column = "column" in raw
|
|
61
|
+
has_value = "value" in raw
|
|
62
|
+
has_scale = "scale" in raw
|
|
63
|
+
|
|
64
|
+
extra = set(raw.keys()) - {"column", "value", "scale"}
|
|
65
|
+
if extra:
|
|
66
|
+
raise ValueError(
|
|
67
|
+
f"Channel '{channel_name}': unknown keys {sorted(extra)} (supported: column, value, scale)."
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if has_value and (has_column or has_scale):
|
|
71
|
+
raise ValueError(
|
|
72
|
+
f"Channel '{channel_name}': 'value' cannot be combined with 'column' or 'scale'."
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if has_value:
|
|
76
|
+
if raw["value"] is None:
|
|
77
|
+
raise ValueError(
|
|
78
|
+
f"Channel '{channel_name}': 'value' cannot be None — use a non-null style value."
|
|
79
|
+
)
|
|
80
|
+
return ResolvedStyleChannel(
|
|
81
|
+
channel=channel_name, mode="literal", literal_value=raw["value"]
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
if not has_column:
|
|
85
|
+
raise ValueError(
|
|
86
|
+
f"Channel '{channel_name}': must specify 'column' or 'value' (got keys: {list(raw.keys())})."
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
data_field = raw["column"]
|
|
90
|
+
|
|
91
|
+
if has_scale:
|
|
92
|
+
if not isinstance(raw["scale"], dict):
|
|
93
|
+
raise ValueError(
|
|
94
|
+
f"Channel '{channel_name}': 'scale' must be a mapping, "
|
|
95
|
+
f"got {type(raw['scale']).__name__}."
|
|
96
|
+
)
|
|
97
|
+
scale_dict = dict(raw["scale"])
|
|
98
|
+
if channel_name in ("opacity", "stroke_width") and "palette" not in scale_dict:
|
|
99
|
+
scale_dict["palette"] = [0.2, 1.0]
|
|
100
|
+
scale_cfg = ScaleTargetConfig.model_validate(scale_dict)
|
|
101
|
+
return ResolvedStyleChannel(
|
|
102
|
+
channel=channel_name,
|
|
103
|
+
mode="gradient",
|
|
104
|
+
data_field=data_field,
|
|
105
|
+
scale=scale_cfg,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
return ResolvedStyleChannel(
|
|
109
|
+
channel=channel_name, mode="series", data_field=data_field
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def validate_channel_fields(
|
|
114
|
+
channels: dict[str, ResolvedStyleChannel],
|
|
115
|
+
available_columns: set[str],
|
|
116
|
+
) -> None:
|
|
117
|
+
"""Raise ValueError if any channel references a field not in available_columns.
|
|
118
|
+
|
|
119
|
+
Only validates data-bound channels (mode in series/gradient/conditional).
|
|
120
|
+
"""
|
|
121
|
+
for channel_name, ch in channels.items():
|
|
122
|
+
if ch.mode == "literal":
|
|
123
|
+
continue
|
|
124
|
+
if ch.data_field and ch.data_field not in available_columns:
|
|
125
|
+
# Common mistake: author writes `color: "#fff"` intending a literal.
|
|
126
|
+
# The string shorthand parses as series mode with data_field="#fff".
|
|
127
|
+
if ch.data_field.startswith("#"):
|
|
128
|
+
raise ValueError(
|
|
129
|
+
f"Channel '{channel_name}': '{ch.data_field}' looks like a literal color — "
|
|
130
|
+
f'use {{{channel_name}: {{value: "{ch.data_field}"}}}}'
|
|
131
|
+
)
|
|
132
|
+
raise ValueError(
|
|
133
|
+
f"Channel '{channel_name}' column '{ch.data_field}' not found. "
|
|
134
|
+
f"Available: {sorted(available_columns)}"
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
_COLOR_CHANNELS = frozenset({"color"})
|
|
139
|
+
_NUMERIC_CHANNELS: frozenset[str] = frozenset()
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _validate_palette_types(channels: dict[str, ResolvedStyleChannel]) -> None:
|
|
143
|
+
"""Raise ValueError if palette type doesn't match channel semantics."""
|
|
144
|
+
for ch_name, ch in channels.items():
|
|
145
|
+
if ch.mode != "gradient" or ch.scale is None:
|
|
146
|
+
continue
|
|
147
|
+
palette = ch.scale.palette
|
|
148
|
+
if not palette:
|
|
149
|
+
continue
|
|
150
|
+
if ch_name in _COLOR_CHANNELS:
|
|
151
|
+
if not all(isinstance(c, str) for c in palette):
|
|
152
|
+
raise ValueError(
|
|
153
|
+
f"Channel '{ch_name}' expects a color palette (list of strings), "
|
|
154
|
+
f"got numeric values. Use strings like '#ffffff'."
|
|
155
|
+
)
|
|
156
|
+
elif ch_name in _NUMERIC_CHANNELS:
|
|
157
|
+
if not all(
|
|
158
|
+
isinstance(c, (int, float)) and not isinstance(c, bool) for c in palette
|
|
159
|
+
):
|
|
160
|
+
raise ValueError(
|
|
161
|
+
f"Channel '{ch_name}' expects a numeric palette (list of floats), "
|
|
162
|
+
f"got non-numeric values."
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def normalize_chart_channels(
|
|
167
|
+
chart: Any,
|
|
168
|
+
available_columns: set[str],
|
|
169
|
+
style_color: str | StyleColorConfig | None = None,
|
|
170
|
+
) -> dict[str, ResolvedStyleChannel]:
|
|
171
|
+
"""Parse and validate chart-level style channels.
|
|
172
|
+
|
|
173
|
+
Only ``color`` is a data channel at chart root. background/opacity/stroke
|
|
174
|
+
were removed as data channels (they are paint fields, not data bindings).
|
|
175
|
+
|
|
176
|
+
``style_color``: when a ``StyleColorConfig`` with a scale is provided, the
|
|
177
|
+
series color channel is upgraded to gradient mode. A plain string value
|
|
178
|
+
means static paint — no data channel is created.
|
|
179
|
+
|
|
180
|
+
Raises ValueError on unresolvable field references or invalid syntax.
|
|
181
|
+
"""
|
|
182
|
+
channels: dict[str, ResolvedStyleChannel] = {}
|
|
183
|
+
|
|
184
|
+
chart_type = getattr(chart, "type", None)
|
|
185
|
+
|
|
186
|
+
color_raw = getattr(chart, "color", None)
|
|
187
|
+
if color_raw is not None:
|
|
188
|
+
channels["color"] = parse_style_channel(color_raw, "color")
|
|
189
|
+
|
|
190
|
+
# Upgrade series channel to gradient when style.color carries a scale.
|
|
191
|
+
if isinstance(style_color, StyleColorConfig) and style_color.scale is not None:
|
|
192
|
+
if "color" not in channels:
|
|
193
|
+
raise ValueError(
|
|
194
|
+
"style.color: {scale: ...} requires chart.color to name a data field.\n"
|
|
195
|
+
"Add a data column binding, e.g.:\n"
|
|
196
|
+
" color: revenue\n"
|
|
197
|
+
" style:\n color: {scale: {palette: ['#fff', '#00f']}}"
|
|
198
|
+
)
|
|
199
|
+
if channels["color"].mode == "series":
|
|
200
|
+
channels["color"] = ResolvedStyleChannel(
|
|
201
|
+
channel="color",
|
|
202
|
+
mode="gradient",
|
|
203
|
+
data_field=channels["color"].data_field,
|
|
204
|
+
scale=style_color.scale,
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# Geo renderers only consume the color field name — gradient/conditional modes
|
|
208
|
+
# silently drop palette or rules. Raise early so the author gets a clear error.
|
|
209
|
+
if chart_type in GEO_CHART_TYPES and "color" in channels:
|
|
210
|
+
color_mode = channels["color"].mode
|
|
211
|
+
if color_mode not in ("series", "literal"):
|
|
212
|
+
raise ValueError(
|
|
213
|
+
f"Geo charts ('{chart_type}') only support series or literal color channels — "
|
|
214
|
+
f"got mode '{color_mode}'. Use chart.color: <field> or chart.style.color: <hex>."
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
if available_columns:
|
|
218
|
+
validate_channel_fields(channels, available_columns)
|
|
219
|
+
|
|
220
|
+
_validate_palette_types(channels)
|
|
221
|
+
|
|
222
|
+
return channels
|