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,261 @@
|
|
|
1
|
+
"""Chart update utilities for edit-back support.
|
|
2
|
+
|
|
3
|
+
This module provides functions to update chart definitions in dashboard YAML,
|
|
4
|
+
preserving the original formatting as much as possible.
|
|
5
|
+
|
|
6
|
+
When a user edits a chart in the chart editor:
|
|
7
|
+
1. The edit produces new values (SQL, type, x, y, title, etc.)
|
|
8
|
+
2. This module updates the original YAML with those changes
|
|
9
|
+
3. Inline charts/queries are promoted to named entries for clarity
|
|
10
|
+
|
|
11
|
+
Example:
|
|
12
|
+
>>> yaml_content = '''
|
|
13
|
+
... title: Dashboard
|
|
14
|
+
... rows:
|
|
15
|
+
... - title: Revenue
|
|
16
|
+
... query: {sql: SELECT * FROM sales}
|
|
17
|
+
... type: bar
|
|
18
|
+
... '''
|
|
19
|
+
>>> edits = {"sql": "SELECT * FROM orders", "type": "line"}
|
|
20
|
+
>>> updated = update_chart_in_yaml(yaml_content, "revenue", edits)
|
|
21
|
+
>>> # Chart is now promoted to named, query to named
|
|
22
|
+
|
|
23
|
+
Usage:
|
|
24
|
+
from dataface.core.compile.chart_update import update_chart_in_yaml
|
|
25
|
+
|
|
26
|
+
updated_yaml = update_chart_in_yaml(
|
|
27
|
+
yaml_content=original_yaml,
|
|
28
|
+
chart_id="revenue",
|
|
29
|
+
edits={
|
|
30
|
+
"sql": "SELECT * FROM new_query",
|
|
31
|
+
"type": "bar",
|
|
32
|
+
"x": "date",
|
|
33
|
+
"y": "amount",
|
|
34
|
+
"title": "New Title",
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
import logging
|
|
40
|
+
from typing import Any
|
|
41
|
+
|
|
42
|
+
import yaml
|
|
43
|
+
|
|
44
|
+
logger = logging.getLogger(__name__)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def update_chart_in_yaml(
|
|
48
|
+
yaml_content: str,
|
|
49
|
+
chart_id: str,
|
|
50
|
+
edits: dict[str, Any],
|
|
51
|
+
source_path: str | None = None,
|
|
52
|
+
query_is_inline: bool = False,
|
|
53
|
+
) -> str:
|
|
54
|
+
"""Update a chart definition in dashboard YAML.
|
|
55
|
+
|
|
56
|
+
This function:
|
|
57
|
+
1. Parses the YAML
|
|
58
|
+
2. Finds the chart by ID
|
|
59
|
+
3. Updates the chart with new values
|
|
60
|
+
4. Promotes inline charts/queries to named entries
|
|
61
|
+
5. Returns the updated YAML
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
yaml_content: Original dashboard YAML content
|
|
65
|
+
chart_id: ID of the chart to update
|
|
66
|
+
edits: Dict of fields to update (sql, type, x, y, title, etc.)
|
|
67
|
+
source_path: Original source path (e.g., "charts.revenue", "rows[0]")
|
|
68
|
+
query_is_inline: Whether the chart had an inline query
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Updated YAML content as string
|
|
72
|
+
|
|
73
|
+
Example:
|
|
74
|
+
>>> edits = {"sql": "SELECT * FROM orders", "type": "line", "x": "date"}
|
|
75
|
+
>>> updated = update_chart_in_yaml(yaml, "revenue", edits)
|
|
76
|
+
|
|
77
|
+
Raises:
|
|
78
|
+
ValueError: If YAML content is invalid or SQL is provided but empty/whitespace.
|
|
79
|
+
"""
|
|
80
|
+
# Parse the YAML with error handling
|
|
81
|
+
try:
|
|
82
|
+
data = yaml.safe_load(yaml_content) or {}
|
|
83
|
+
except yaml.YAMLError as e:
|
|
84
|
+
raise ValueError(f"Invalid YAML content: {e}") from e
|
|
85
|
+
|
|
86
|
+
# Initialize sections if needed
|
|
87
|
+
if "charts" not in data:
|
|
88
|
+
data["charts"] = {}
|
|
89
|
+
if "queries" not in data:
|
|
90
|
+
data["queries"] = {}
|
|
91
|
+
|
|
92
|
+
# Get the SQL if provided (validate it's a non-empty string)
|
|
93
|
+
new_sql = edits.get("sql")
|
|
94
|
+
if new_sql is not None:
|
|
95
|
+
if not isinstance(new_sql, str):
|
|
96
|
+
raise ValueError("SQL must be a string")
|
|
97
|
+
if not new_sql.strip():
|
|
98
|
+
raise ValueError("SQL cannot be empty or whitespace only")
|
|
99
|
+
new_source = edits.get("source")
|
|
100
|
+
|
|
101
|
+
# Try to find the existing source from the original chart's query
|
|
102
|
+
if not new_source:
|
|
103
|
+
existing_chart = data.get("charts", {}).get(chart_id)
|
|
104
|
+
if existing_chart and isinstance(existing_chart, dict):
|
|
105
|
+
existing_query_ref = existing_chart.get("query")
|
|
106
|
+
if existing_query_ref:
|
|
107
|
+
# It might be a string reference to a named query
|
|
108
|
+
if isinstance(existing_query_ref, str):
|
|
109
|
+
existing_query = data.get("queries", {}).get(existing_query_ref)
|
|
110
|
+
if existing_query and isinstance(existing_query, dict):
|
|
111
|
+
new_source = existing_query.get("source")
|
|
112
|
+
# Or it might be an inline query dict
|
|
113
|
+
elif isinstance(existing_query_ref, dict):
|
|
114
|
+
new_source = existing_query_ref.get("source")
|
|
115
|
+
|
|
116
|
+
# Also check face-level source as fallback
|
|
117
|
+
if not new_source and "source" in data:
|
|
118
|
+
new_source = data["source"]
|
|
119
|
+
|
|
120
|
+
# If SQL is being edited, create/update a named query
|
|
121
|
+
query_name = f"{chart_id}_query"
|
|
122
|
+
if new_sql:
|
|
123
|
+
query_def: dict[str, Any] = {"sql": new_sql}
|
|
124
|
+
if new_source:
|
|
125
|
+
query_def["source"] = new_source
|
|
126
|
+
data["queries"][query_name] = query_def
|
|
127
|
+
|
|
128
|
+
# Build the chart definition
|
|
129
|
+
chart_def: dict[str, Any] = {}
|
|
130
|
+
|
|
131
|
+
# Title
|
|
132
|
+
if "title" in edits and edits["title"]:
|
|
133
|
+
chart_def["title"] = edits["title"]
|
|
134
|
+
|
|
135
|
+
# Query reference (always use named query if SQL was edited)
|
|
136
|
+
if new_sql:
|
|
137
|
+
chart_def["query"] = query_name
|
|
138
|
+
elif "query" in edits:
|
|
139
|
+
chart_def["query"] = edits["query"]
|
|
140
|
+
|
|
141
|
+
# Chart type
|
|
142
|
+
if "type" in edits and edits["type"]:
|
|
143
|
+
chart_def["type"] = edits["type"]
|
|
144
|
+
|
|
145
|
+
# Data mappings
|
|
146
|
+
for field in ["x", "y", "color", "size", "value"]:
|
|
147
|
+
if field in edits and edits[field]:
|
|
148
|
+
chart_def[field] = edits[field]
|
|
149
|
+
|
|
150
|
+
# Update the charts section
|
|
151
|
+
# If chart already exists, merge with existing values
|
|
152
|
+
if chart_id in data["charts"]:
|
|
153
|
+
existing = data["charts"][chart_id]
|
|
154
|
+
if isinstance(existing, dict):
|
|
155
|
+
# Preserve existing fields not being edited
|
|
156
|
+
for key, value in existing.items():
|
|
157
|
+
if key not in chart_def and key not in ["query"]:
|
|
158
|
+
chart_def[key] = value
|
|
159
|
+
data["charts"][chart_id] = chart_def
|
|
160
|
+
|
|
161
|
+
# If chart was inline, we need to update the layout reference
|
|
162
|
+
if source_path and (
|
|
163
|
+
source_path.startswith("rows[") or source_path.startswith("cols[")
|
|
164
|
+
):
|
|
165
|
+
_update_layout_reference(data, source_path, chart_id)
|
|
166
|
+
|
|
167
|
+
# Serialize back to YAML
|
|
168
|
+
return yaml.dump(
|
|
169
|
+
data, default_flow_style=False, sort_keys=False, allow_unicode=True
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _update_layout_reference(
|
|
174
|
+
data: dict[str, Any], source_path: str, chart_id: str
|
|
175
|
+
) -> None:
|
|
176
|
+
"""Update a layout position to reference a named chart instead of inline.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
data: Parsed YAML data (modified in place)
|
|
180
|
+
source_path: Path like "rows[0]" or "cols[1]"
|
|
181
|
+
chart_id: ID of the chart to reference
|
|
182
|
+
"""
|
|
183
|
+
# Parse the source path to get the layout type and index
|
|
184
|
+
if source_path.startswith("rows["):
|
|
185
|
+
layout_key = "rows"
|
|
186
|
+
try:
|
|
187
|
+
index = int(source_path.split("[")[1].rstrip("]"))
|
|
188
|
+
except (ValueError, IndexError):
|
|
189
|
+
logger.warning(f"Failed to parse row index from source_path: {source_path}")
|
|
190
|
+
return
|
|
191
|
+
elif source_path.startswith("cols["):
|
|
192
|
+
layout_key = "cols"
|
|
193
|
+
try:
|
|
194
|
+
index = int(source_path.split("[")[1].rstrip("]"))
|
|
195
|
+
except (ValueError, IndexError):
|
|
196
|
+
logger.warning(
|
|
197
|
+
f"Failed to parse column index from source_path: {source_path}"
|
|
198
|
+
)
|
|
199
|
+
return
|
|
200
|
+
else:
|
|
201
|
+
logger.debug(f"Unrecognized source_path format: {source_path}")
|
|
202
|
+
return
|
|
203
|
+
|
|
204
|
+
# Update the layout
|
|
205
|
+
if (
|
|
206
|
+
layout_key in data
|
|
207
|
+
and isinstance(data[layout_key], list)
|
|
208
|
+
and 0 <= index < len(data[layout_key])
|
|
209
|
+
):
|
|
210
|
+
# Replace inline chart with named reference
|
|
211
|
+
data[layout_key][index] = chart_id
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def get_chart_edits_from_form(
|
|
215
|
+
sql: str | None = None,
|
|
216
|
+
source: str | None = None,
|
|
217
|
+
chart_type: str | None = None,
|
|
218
|
+
x: str | None = None,
|
|
219
|
+
y: str | None = None,
|
|
220
|
+
color: str | None = None,
|
|
221
|
+
title: str | None = None,
|
|
222
|
+
value: str | None = None,
|
|
223
|
+
) -> dict[str, Any]:
|
|
224
|
+
"""Build an edits dict from form parameters.
|
|
225
|
+
|
|
226
|
+
Convenience function for building the edits dict from
|
|
227
|
+
individual form fields.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
sql: Query SQL
|
|
231
|
+
source: Query source name
|
|
232
|
+
chart_type: Chart type (bar, line, etc.)
|
|
233
|
+
x: X-axis field
|
|
234
|
+
y: Y-axis field
|
|
235
|
+
color: Color field
|
|
236
|
+
title: Chart title
|
|
237
|
+
value: Value field (for KPI charts: column reference — string column name)
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
Dict of non-None edits
|
|
241
|
+
"""
|
|
242
|
+
edits: dict[str, Any] = {}
|
|
243
|
+
|
|
244
|
+
if sql is not None:
|
|
245
|
+
edits["sql"] = sql
|
|
246
|
+
if source is not None:
|
|
247
|
+
edits["source"] = source
|
|
248
|
+
if chart_type is not None:
|
|
249
|
+
edits["type"] = chart_type
|
|
250
|
+
if x is not None:
|
|
251
|
+
edits["x"] = x
|
|
252
|
+
if y is not None:
|
|
253
|
+
edits["y"] = y
|
|
254
|
+
if color is not None:
|
|
255
|
+
edits["color"] = color
|
|
256
|
+
if title is not None:
|
|
257
|
+
edits["title"] = title
|
|
258
|
+
if value is not None:
|
|
259
|
+
edits["value"] = value
|
|
260
|
+
|
|
261
|
+
return edits
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Color utility functions for Dataface renderers.
|
|
2
|
+
|
|
3
|
+
Provides is_dark_color and sanitize_color for use across compile and render layers.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
from typing import overload
|
|
10
|
+
|
|
11
|
+
_CSS_HEX_COLOR_PATTERN = re.compile(r"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def is_dark_color(color: str) -> bool:
|
|
15
|
+
"""Check if a color is dark based on luminance.
|
|
16
|
+
|
|
17
|
+
Uses the relative luminance formula to determine if a color is
|
|
18
|
+
dark (luminance < 0.5) or light (luminance >= 0.5).
|
|
19
|
+
"""
|
|
20
|
+
if not color or not color.startswith("#"):
|
|
21
|
+
return False
|
|
22
|
+
|
|
23
|
+
hex_color = color.lstrip("#")
|
|
24
|
+
if len(hex_color) == 3:
|
|
25
|
+
hex_color = "".join(c * 2 for c in hex_color)
|
|
26
|
+
|
|
27
|
+
if len(hex_color) != 6:
|
|
28
|
+
return False
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
r = int(hex_color[0:2], 16) / 255.0
|
|
32
|
+
g = int(hex_color[2:4], 16) / 255.0
|
|
33
|
+
b = int(hex_color[4:6], 16) / 255.0
|
|
34
|
+
except ValueError:
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
luminance = 0.299 * r + 0.587 * g + 0.114 * b
|
|
38
|
+
return luminance < 0.5
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@overload
|
|
42
|
+
def sanitize_color(color: str | None, fallback: str) -> str: ...
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@overload
|
|
46
|
+
def sanitize_color(color: str | None, fallback: None) -> str | None: ...
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def sanitize_color(color: str | None, fallback: str | None = "#000000") -> str | None:
|
|
50
|
+
"""Validate and sanitize a color value for safe use in SVG/HTML.
|
|
51
|
+
|
|
52
|
+
Accepts hex colors (``#fff``, ``#FFFFFF``) and the CSS keywords
|
|
53
|
+
``transparent`` and ``none``. Presets use ``transparent`` to
|
|
54
|
+
explicitly clear an inherited theme color (YAML ``null`` means
|
|
55
|
+
"no override" in the merge system, so it doesn't disable an
|
|
56
|
+
inherited fill).
|
|
57
|
+
"""
|
|
58
|
+
if not color or not isinstance(color, str):
|
|
59
|
+
return fallback
|
|
60
|
+
if _CSS_HEX_COLOR_PATTERN.match(color):
|
|
61
|
+
return color
|
|
62
|
+
if color.lower() in {"transparent", "none"}:
|
|
63
|
+
return "transparent"
|
|
64
|
+
return fallback
|