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,319 @@
|
|
|
1
|
+
"""Declarative SQL filter injection via sqlglot AST manipulation.
|
|
2
|
+
|
|
3
|
+
Stage: COMPILE / EXECUTE bridge
|
|
4
|
+
Purpose: Parse SQL with sqlglot, inject WHERE clauses from a declarative
|
|
5
|
+
filters spec, and return parameterized SQL.
|
|
6
|
+
|
|
7
|
+
Users write pure SQL and declare variable-to-column bindings in YAML:
|
|
8
|
+
|
|
9
|
+
queries:
|
|
10
|
+
sales:
|
|
11
|
+
sql: SELECT region, SUM(revenue) FROM orders GROUP BY region
|
|
12
|
+
filters:
|
|
13
|
+
region: region # equality (auto-IN for arrays)
|
|
14
|
+
revenue: { gte: min_revenue } # comparison
|
|
15
|
+
order_date: { between: date_range } # date range
|
|
16
|
+
|
|
17
|
+
The engine parses the SQL with sqlglot, builds predicate AST nodes,
|
|
18
|
+
and injects them into the WHERE clause. Null variables produce no
|
|
19
|
+
predicate — no 1=1 hacks.
|
|
20
|
+
|
|
21
|
+
Entry Point:
|
|
22
|
+
inject_filters(sql, filters, variables, dialect, param_offset) → (sql, params)
|
|
23
|
+
|
|
24
|
+
Dependencies:
|
|
25
|
+
- sqlglot
|
|
26
|
+
- dataface.core.execute.dialects (SQLDialect for placeholder format)
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
import sqlglot
|
|
32
|
+
from sqlglot import exp
|
|
33
|
+
|
|
34
|
+
from dataface.core.execute.dialects.base import SQLDialect
|
|
35
|
+
|
|
36
|
+
# Map YAML operator keys to sqlglot binary expression types.
|
|
37
|
+
_OP_MAP: dict[str, type[exp.Binary]] = {
|
|
38
|
+
"eq": exp.EQ,
|
|
39
|
+
"neq": exp.NEQ,
|
|
40
|
+
"gt": exp.GT,
|
|
41
|
+
"gte": exp.GTE,
|
|
42
|
+
"lt": exp.LT,
|
|
43
|
+
"lte": exp.LTE,
|
|
44
|
+
"like": exp.Like,
|
|
45
|
+
"ilike": exp.ILike,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Map our dialect names to sqlglot dialect names.
|
|
49
|
+
_SQLGLOT_DIALECT: dict[str, str] = {
|
|
50
|
+
"postgres": "postgres",
|
|
51
|
+
"postgresql": "postgres",
|
|
52
|
+
"duckdb": "duckdb",
|
|
53
|
+
"mysql": "mysql",
|
|
54
|
+
"mariadb": "mysql",
|
|
55
|
+
"snowflake": "snowflake",
|
|
56
|
+
"bigquery": "bigquery",
|
|
57
|
+
"redshift": "redshift",
|
|
58
|
+
"sqlserver": "tsql",
|
|
59
|
+
"mssql": "tsql",
|
|
60
|
+
"databricks": "databricks",
|
|
61
|
+
"spark": "spark",
|
|
62
|
+
"athena": "presto",
|
|
63
|
+
"presto": "presto",
|
|
64
|
+
"trino": "trino",
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def inject_filters(
|
|
69
|
+
sql: str,
|
|
70
|
+
filters: dict[str, Any] | None,
|
|
71
|
+
variables: dict[str, Any],
|
|
72
|
+
dialect: SQLDialect,
|
|
73
|
+
param_offset: int = 0,
|
|
74
|
+
) -> tuple[str, list[Any]]:
|
|
75
|
+
"""Inject WHERE clauses into SQL from a declarative filters spec.
|
|
76
|
+
|
|
77
|
+
Parses the SQL with sqlglot, builds predicate nodes for each filter
|
|
78
|
+
where the bound variable has a non-null value, and injects them into
|
|
79
|
+
the appropriate WHERE clause (outermost SELECT or targeted CTE).
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
sql: SQL string (may already contain parameterized placeholders
|
|
83
|
+
from Jinja rendering).
|
|
84
|
+
filters: Declarative filter spec from YAML. Keys are column names
|
|
85
|
+
(or CTE names for CTE targeting). Values are variable names
|
|
86
|
+
(str) or operator dicts ({op: var_name}).
|
|
87
|
+
variables: Current variable values.
|
|
88
|
+
dialect: SQLDialect instance for placeholder format.
|
|
89
|
+
param_offset: Number of existing params (from prior Jinja rendering).
|
|
90
|
+
New placeholders start numbering after this offset.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Tuple of (modified_sql, new_params). The new_params list contains
|
|
94
|
+
only the params added by filter injection — caller should append
|
|
95
|
+
these to any existing Jinja params.
|
|
96
|
+
"""
|
|
97
|
+
if not filters:
|
|
98
|
+
return sql, []
|
|
99
|
+
|
|
100
|
+
sg_dialect = _SQLGLOT_DIALECT.get(dialect.name, dialect.name)
|
|
101
|
+
tree = sqlglot.parse_one(sql, read=sg_dialect)
|
|
102
|
+
|
|
103
|
+
# Identify CTE names so we can distinguish CTE targeting from column filters.
|
|
104
|
+
cte_names = {cte.alias: cte for cte in tree.find_all(exp.CTE) if cte.alias}
|
|
105
|
+
|
|
106
|
+
all_params: list[Any] = []
|
|
107
|
+
idx = param_offset
|
|
108
|
+
|
|
109
|
+
# Separate CTE-targeted filters from column-level filters.
|
|
110
|
+
# A filter key is CTE-targeted when: (a) its name matches a CTE in the SQL,
|
|
111
|
+
# (b) its value is a dict, and (c) that dict's keys are NOT recognized
|
|
112
|
+
# operators (i.e., it contains column→binding pairs, not an operator→var pair).
|
|
113
|
+
column_filters: dict[str, str | dict[str, str]] = {}
|
|
114
|
+
cte_filters: dict[str, dict[str, str | dict[str, str]]] = {}
|
|
115
|
+
|
|
116
|
+
for key, binding in filters.items():
|
|
117
|
+
if (
|
|
118
|
+
key in cte_names
|
|
119
|
+
and isinstance(binding, dict)
|
|
120
|
+
and not _is_operator_dict(binding)
|
|
121
|
+
):
|
|
122
|
+
# Validate each value in the CTE binding is a valid filter binding
|
|
123
|
+
for col, col_binding in binding.items():
|
|
124
|
+
if not isinstance(col_binding, (str, dict)):
|
|
125
|
+
raise ValueError(
|
|
126
|
+
f"Invalid filter binding for CTE '{key}', column '{col}': "
|
|
127
|
+
f"expected string or operator dict, got {type(col_binding).__name__}"
|
|
128
|
+
)
|
|
129
|
+
cte_filters[key] = binding
|
|
130
|
+
else:
|
|
131
|
+
column_filters[key] = binding
|
|
132
|
+
|
|
133
|
+
# Inject column-level filters against the query result shape rather than the
|
|
134
|
+
# innermost SELECT body. This preserves support for filters that target
|
|
135
|
+
# projected aliases (common in generated SQL) because aliases are not visible
|
|
136
|
+
# inside the same SELECT's WHERE clause in BigQuery and other dialects.
|
|
137
|
+
column_conditions, params, idx = _build_conditions(
|
|
138
|
+
column_filters, variables, dialect, idx
|
|
139
|
+
)
|
|
140
|
+
all_params.extend(params)
|
|
141
|
+
|
|
142
|
+
# Inject CTE-targeted filters.
|
|
143
|
+
for cte_name, cte_bindings in cte_filters.items():
|
|
144
|
+
cte_node = cte_names[cte_name]
|
|
145
|
+
cte_select = cte_node.find(exp.Select)
|
|
146
|
+
cte_conditions, params, idx = _build_conditions(
|
|
147
|
+
cte_bindings, variables, dialect, idx
|
|
148
|
+
)
|
|
149
|
+
all_params.extend(params)
|
|
150
|
+
if cte_conditions:
|
|
151
|
+
if cte_select is None:
|
|
152
|
+
raise ValueError(f"Could not find SELECT node for CTE '{cte_name}'")
|
|
153
|
+
_inject_where(cte_select, cte_conditions)
|
|
154
|
+
|
|
155
|
+
if column_conditions:
|
|
156
|
+
alias = exp.TableAlias(this=exp.to_identifier("_df_filter_base"))
|
|
157
|
+
wrapped = exp.select("*").from_(exp.Subquery(this=tree, alias=alias))
|
|
158
|
+
_inject_where(wrapped, column_conditions)
|
|
159
|
+
return wrapped.sql(dialect=sg_dialect), all_params
|
|
160
|
+
|
|
161
|
+
return tree.sql(dialect=sg_dialect), all_params
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _is_operator_dict(d: dict) -> bool:
|
|
165
|
+
"""Check if a dict's keys are all recognized filter operators."""
|
|
166
|
+
operators = set(_OP_MAP) | {"in", "not_in", "between"}
|
|
167
|
+
return all(k in operators for k in d)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _build_conditions(
|
|
171
|
+
filters: dict[str, Any],
|
|
172
|
+
variables: dict[str, Any],
|
|
173
|
+
dialect: SQLDialect,
|
|
174
|
+
start_idx: int,
|
|
175
|
+
) -> tuple[list[exp.Expression], list[Any], int]:
|
|
176
|
+
"""Build sqlglot condition expressions from filter bindings.
|
|
177
|
+
|
|
178
|
+
Returns (conditions, params, next_idx).
|
|
179
|
+
"""
|
|
180
|
+
conditions: list[exp.Expression] = []
|
|
181
|
+
params: list[Any] = []
|
|
182
|
+
idx = start_idx
|
|
183
|
+
|
|
184
|
+
for col_name, binding in filters.items():
|
|
185
|
+
op_key, var_name = _parse_binding(binding)
|
|
186
|
+
value = variables.get(var_name)
|
|
187
|
+
|
|
188
|
+
# Skip null / missing / empty string — the whole point of declarative filters.
|
|
189
|
+
if value is None or value == "":
|
|
190
|
+
continue
|
|
191
|
+
|
|
192
|
+
col = exp.column(col_name)
|
|
193
|
+
cond, cond_params, idx = _build_condition(col, op_key, value, dialect, idx)
|
|
194
|
+
if cond is not None:
|
|
195
|
+
conditions.append(cond)
|
|
196
|
+
params.extend(cond_params)
|
|
197
|
+
|
|
198
|
+
return conditions, params, idx
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _parse_binding(binding: Any) -> tuple[str, str]:
|
|
202
|
+
"""Parse a filter binding into (operator_key, variable_name).
|
|
203
|
+
|
|
204
|
+
Simple binding: "region" → ("eq", "region")
|
|
205
|
+
Operator binding: {"gte": "min"} → ("gte", "min")
|
|
206
|
+
"""
|
|
207
|
+
if isinstance(binding, str):
|
|
208
|
+
return "eq", binding
|
|
209
|
+
if isinstance(binding, dict):
|
|
210
|
+
op_key, var_name = next(iter(binding.items()))
|
|
211
|
+
return op_key, var_name
|
|
212
|
+
raise ValueError(f"Invalid filter binding: {binding!r}")
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _build_condition(
|
|
216
|
+
col: exp.Column,
|
|
217
|
+
op_key: str,
|
|
218
|
+
value: Any,
|
|
219
|
+
dialect: SQLDialect,
|
|
220
|
+
start_idx: int,
|
|
221
|
+
) -> tuple[exp.Expression | None, list[Any], int]:
|
|
222
|
+
"""Build a single sqlglot predicate + params.
|
|
223
|
+
|
|
224
|
+
Returns (condition, params, next_param_idx).
|
|
225
|
+
"""
|
|
226
|
+
# BETWEEN
|
|
227
|
+
if op_key == "between":
|
|
228
|
+
if not isinstance(value, (list, tuple)) or len(value) != 2:
|
|
229
|
+
return None, [], start_idx
|
|
230
|
+
start_val, end_val = value
|
|
231
|
+
if start_val is None or end_val is None:
|
|
232
|
+
return None, [], start_idx
|
|
233
|
+
p1 = _param_node(dialect, start_idx + 1)
|
|
234
|
+
p2 = _param_node(dialect, start_idx + 2)
|
|
235
|
+
return (
|
|
236
|
+
exp.Between(this=col, low=p1, high=p2),
|
|
237
|
+
[start_val, end_val],
|
|
238
|
+
start_idx + 2,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
# IN / NOT IN (explicit operator or implicit from list value)
|
|
242
|
+
if op_key in ("in", "not_in") or (op_key == "eq" and isinstance(value, list)):
|
|
243
|
+
if not value:
|
|
244
|
+
return None, [], start_idx
|
|
245
|
+
placeholders = []
|
|
246
|
+
for _v in value:
|
|
247
|
+
start_idx += 1
|
|
248
|
+
placeholders.append(_param_node(dialect, start_idx))
|
|
249
|
+
in_node = exp.In(this=col, expressions=placeholders)
|
|
250
|
+
if op_key == "not_in":
|
|
251
|
+
return exp.Not(this=in_node), list(value), start_idx
|
|
252
|
+
return in_node, list(value), start_idx
|
|
253
|
+
|
|
254
|
+
# Standard comparison (eq, neq, gt, gte, lt, lte, like, ilike)
|
|
255
|
+
cls = _OP_MAP.get(op_key, exp.EQ)
|
|
256
|
+
start_idx += 1
|
|
257
|
+
return cls(this=col, expression=_param_node(dialect, start_idx)), [value], start_idx
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _param_node(dialect: SQLDialect, idx: int) -> exp.Expression:
|
|
261
|
+
"""Create a sqlglot node that renders as the dialect's placeholder.
|
|
262
|
+
|
|
263
|
+
Uses exp.Literal(is_string=False) so the placeholder text is emitted
|
|
264
|
+
verbatim without quotes.
|
|
265
|
+
"""
|
|
266
|
+
return exp.Literal(this=dialect.param(idx), is_string=False)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _inject_where(
|
|
270
|
+
select: exp.Select,
|
|
271
|
+
conditions: list[exp.Expression],
|
|
272
|
+
) -> None:
|
|
273
|
+
"""Inject conditions into a SELECT's WHERE clause.
|
|
274
|
+
|
|
275
|
+
If the SELECT already has a WHERE, the new conditions are ANDed.
|
|
276
|
+
If not, a new WHERE is created.
|
|
277
|
+
"""
|
|
278
|
+
combined = conditions[0]
|
|
279
|
+
for c in conditions[1:]:
|
|
280
|
+
combined = exp.And(this=combined, expression=c)
|
|
281
|
+
|
|
282
|
+
existing_where = select.args.get("where")
|
|
283
|
+
if existing_where:
|
|
284
|
+
select.set(
|
|
285
|
+
"where",
|
|
286
|
+
exp.Where(this=exp.And(this=existing_where.this, expression=combined)),
|
|
287
|
+
)
|
|
288
|
+
else:
|
|
289
|
+
select.set("where", exp.Where(this=combined))
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def extract_filter_variable_deps(filters: dict[str, Any]) -> set[str]:
|
|
293
|
+
"""Extract variable names referenced in a declarative filters spec.
|
|
294
|
+
|
|
295
|
+
Used during normalization to compute variable_dependencies for a query.
|
|
296
|
+
Handles both flat column filters and CTE-targeted nested filters.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
filters: Declarative filter spec (same format as inject_filters).
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
Set of variable names referenced.
|
|
303
|
+
"""
|
|
304
|
+
if not filters:
|
|
305
|
+
return set()
|
|
306
|
+
|
|
307
|
+
deps: set[str] = set()
|
|
308
|
+
for binding in filters.values():
|
|
309
|
+
_collect_binding_vars(binding, deps)
|
|
310
|
+
return deps
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _collect_binding_vars(binding: Any, deps: set[str]) -> None:
|
|
314
|
+
"""Recursively collect variable names from a filter binding."""
|
|
315
|
+
if isinstance(binding, str):
|
|
316
|
+
deps.add(binding)
|
|
317
|
+
elif isinstance(binding, dict):
|
|
318
|
+
for v in binding.values():
|
|
319
|
+
_collect_binding_vars(v, deps)
|