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,66 @@
|
|
|
1
|
+
"""DuckDB-specific helpers for schema inspection.
|
|
2
|
+
|
|
3
|
+
When a DuckDB adapter connects to a file (e.g. ``dundersign.duckdb``),
|
|
4
|
+
the database is attached under a non-system name (e.g. ``dundersign``).
|
|
5
|
+
Passing ``None`` to ``adapter.list_relations(None, schema)`` returns an
|
|
6
|
+
empty list for file-backed databases — the database argument must be the
|
|
7
|
+
actual attach name.
|
|
8
|
+
|
|
9
|
+
``PRAGMA database_list`` is the authoritative source: each row is
|
|
10
|
+
``(seq, name, file)``. We skip the two system databases (``system`` and
|
|
11
|
+
``temp``); the remaining row is the user's attached database. For
|
|
12
|
+
``path=None`` or ``path=':memory:'`` the attach name is always ``memory``.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def duckdb_resolve_database(adapter: Any, db_path: str | Path | None) -> str:
|
|
22
|
+
"""Return the DuckDB attach name that ``list_relations`` must receive.
|
|
23
|
+
|
|
24
|
+
Reads ``PRAGMA database_list`` from the already-open adapter connection.
|
|
25
|
+
The caller must be inside a ``connection_named`` context.
|
|
26
|
+
|
|
27
|
+
``db_path`` is the configured file path (or ``None`` / ``':memory:'`` for
|
|
28
|
+
in-memory). When the path is in-memory we short-circuit and return
|
|
29
|
+
``'memory'`` without a PRAGMA round-trip.
|
|
30
|
+
|
|
31
|
+
For file-backed databases we prefer the row whose ``file`` column matches
|
|
32
|
+
the absolute ``db_path``; if no row matches (e.g. path is relative or
|
|
33
|
+
symlinked) we fall back to the first non-system entry.
|
|
34
|
+
|
|
35
|
+
Raises ``ValueError`` if no non-system database is found in the pragma
|
|
36
|
+
output — this would indicate a DuckDB connection without an attached
|
|
37
|
+
database, which should be impossible for a live adapter.
|
|
38
|
+
"""
|
|
39
|
+
path_str = str(db_path) if db_path is not None else None
|
|
40
|
+
if path_str is None or path_str == ":memory:":
|
|
41
|
+
return "memory"
|
|
42
|
+
|
|
43
|
+
_, result = adapter.execute("PRAGMA database_list", fetch=True)
|
|
44
|
+
# Rows are (seq, name, file). Column order is stable in DuckDB.
|
|
45
|
+
# System databases to skip: 'system' and 'temp'.
|
|
46
|
+
_SKIP = frozenset({"system", "temp"})
|
|
47
|
+
|
|
48
|
+
abs_path = str(Path(path_str).resolve())
|
|
49
|
+
candidates: list[str] = []
|
|
50
|
+
for row in result.rows:
|
|
51
|
+
name = str(row[1])
|
|
52
|
+
file_col = str(row[2])
|
|
53
|
+
if name in _SKIP:
|
|
54
|
+
continue
|
|
55
|
+
# Prefer exact match on resolved absolute path.
|
|
56
|
+
if file_col == abs_path:
|
|
57
|
+
return name
|
|
58
|
+
candidates.append(name)
|
|
59
|
+
|
|
60
|
+
if candidates:
|
|
61
|
+
return candidates[0]
|
|
62
|
+
|
|
63
|
+
raise ValueError(
|
|
64
|
+
f"PRAGMA database_list returned no non-system database for path {path_str!r}. "
|
|
65
|
+
"This is unexpected for a live DuckDB file-backed adapter."
|
|
66
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Profile dashboard templates
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# Categorical Column Profile Template
|
|
2
|
+
#
|
|
3
|
+
# Analysis of a categorical column with low cardinality. Schema panel
|
|
4
|
+
# pulls from the LayeredSchemaResolver; live aggregations stay on
|
|
5
|
+
# warehouse SQL.
|
|
6
|
+
#
|
|
7
|
+
# URL: /inspect/categorical_column/?model=<table>&column=<col>
|
|
8
|
+
#
|
|
9
|
+
# Required variables:
|
|
10
|
+
# - model: Table/model name
|
|
11
|
+
# - column: Column name to analyze
|
|
12
|
+
#
|
|
13
|
+
# Optional variables:
|
|
14
|
+
# - connection: DuckDB file path or :memory: (default: :memory:)
|
|
15
|
+
#
|
|
16
|
+
# Theme is set via DFT_DEFAULT_THEME env var at serve startup.
|
|
17
|
+
|
|
18
|
+
title: "Categorical: {{ model }}.{{ column }}"
|
|
19
|
+
|
|
20
|
+
text: |
|
|
21
|
+
[Back to {{ model }}](/inspect/model/?model={{ model }}&connection={{ connection }})
|
|
22
|
+
|
|
23
|
+
variables:
|
|
24
|
+
model:
|
|
25
|
+
input: text
|
|
26
|
+
visible: false
|
|
27
|
+
default: ""
|
|
28
|
+
column:
|
|
29
|
+
input: text
|
|
30
|
+
visible: false
|
|
31
|
+
default: ""
|
|
32
|
+
connection:
|
|
33
|
+
input: text
|
|
34
|
+
visible: false
|
|
35
|
+
default: ":memory:"
|
|
36
|
+
source_name:
|
|
37
|
+
input: text
|
|
38
|
+
visible: false
|
|
39
|
+
default: ""
|
|
40
|
+
schema_name:
|
|
41
|
+
input: text
|
|
42
|
+
visible: false
|
|
43
|
+
default: ""
|
|
44
|
+
|
|
45
|
+
queries:
|
|
46
|
+
column_schema:
|
|
47
|
+
type: schema_resolver
|
|
48
|
+
source: "{{ source_name }}"
|
|
49
|
+
schema: "{{ schema_name }}"
|
|
50
|
+
table: "{{ model }}"
|
|
51
|
+
column: "{{ column }}"
|
|
52
|
+
|
|
53
|
+
# Basic category stats
|
|
54
|
+
stats:
|
|
55
|
+
source:
|
|
56
|
+
type: duckdb
|
|
57
|
+
path: "{{ connection }}"
|
|
58
|
+
sql: |
|
|
59
|
+
SELECT
|
|
60
|
+
COUNT(*) as total_count,
|
|
61
|
+
COUNT({{ column }}) as non_null_count,
|
|
62
|
+
COUNT(*) - COUNT({{ column }}) as null_count,
|
|
63
|
+
ROUND(100.0 * (COUNT(*) - COUNT({{ column }})) / COUNT(*), 2) as null_pct,
|
|
64
|
+
COUNT(DISTINCT {{ column }}) as category_count
|
|
65
|
+
FROM {{ model }}
|
|
66
|
+
|
|
67
|
+
# Category distribution
|
|
68
|
+
categories:
|
|
69
|
+
source:
|
|
70
|
+
type: duckdb
|
|
71
|
+
path: "{{ connection }}"
|
|
72
|
+
sql: |
|
|
73
|
+
SELECT
|
|
74
|
+
COALESCE(CAST({{ column }} AS VARCHAR), '(null)') as category,
|
|
75
|
+
COUNT(*) as count,
|
|
76
|
+
ROUND(100.0 * COUNT(*) / (SELECT COUNT(*) FROM {{ model }}), 2) as pct
|
|
77
|
+
FROM {{ model }}
|
|
78
|
+
GROUP BY {{ column }}
|
|
79
|
+
ORDER BY COUNT(*) DESC
|
|
80
|
+
|
|
81
|
+
# Category distribution for pie chart (top 10 + other)
|
|
82
|
+
categories_pie:
|
|
83
|
+
source:
|
|
84
|
+
type: duckdb
|
|
85
|
+
path: "{{ connection }}"
|
|
86
|
+
sql: |
|
|
87
|
+
WITH ranked AS (
|
|
88
|
+
SELECT
|
|
89
|
+
COALESCE(CAST({{ column }} AS VARCHAR), '(null)') as category,
|
|
90
|
+
COUNT(*) as count,
|
|
91
|
+
ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC) as rn
|
|
92
|
+
FROM {{ model }}
|
|
93
|
+
GROUP BY {{ column }}
|
|
94
|
+
)
|
|
95
|
+
SELECT
|
|
96
|
+
CASE WHEN rn <= 10 THEN category ELSE 'Other' END as category,
|
|
97
|
+
SUM(count) as count
|
|
98
|
+
FROM ranked
|
|
99
|
+
GROUP BY CASE WHEN rn <= 10 THEN category ELSE 'Other' END
|
|
100
|
+
ORDER BY SUM(count) DESC
|
|
101
|
+
|
|
102
|
+
charts:
|
|
103
|
+
schema_panel:
|
|
104
|
+
title: Schema
|
|
105
|
+
type: table
|
|
106
|
+
query: column_schema
|
|
107
|
+
style:
|
|
108
|
+
header_overflow: wrap-two
|
|
109
|
+
columns:
|
|
110
|
+
name:
|
|
111
|
+
label: Column
|
|
112
|
+
type:
|
|
113
|
+
label: Type
|
|
114
|
+
role:
|
|
115
|
+
label: Role
|
|
116
|
+
semantic_type:
|
|
117
|
+
label: Semantic
|
|
118
|
+
description:
|
|
119
|
+
label: Description
|
|
120
|
+
|
|
121
|
+
# Stats KPIs
|
|
122
|
+
total:
|
|
123
|
+
label: Total Records
|
|
124
|
+
type: kpi
|
|
125
|
+
query: stats
|
|
126
|
+
value: total_count
|
|
127
|
+
format: ",.0f"
|
|
128
|
+
|
|
129
|
+
categories:
|
|
130
|
+
label: Categories
|
|
131
|
+
type: kpi
|
|
132
|
+
query: stats
|
|
133
|
+
value: category_count
|
|
134
|
+
format: ",.0f"
|
|
135
|
+
|
|
136
|
+
null_pct:
|
|
137
|
+
label: "Null %"
|
|
138
|
+
type: kpi
|
|
139
|
+
query: stats
|
|
140
|
+
value: null_pct
|
|
141
|
+
format: ".1f"
|
|
142
|
+
|
|
143
|
+
# Pie chart
|
|
144
|
+
pie_chart:
|
|
145
|
+
title: Category Distribution
|
|
146
|
+
type: pie
|
|
147
|
+
query: categories_pie
|
|
148
|
+
theta: count
|
|
149
|
+
color: category
|
|
150
|
+
|
|
151
|
+
# Spark bar (compact distribution for profiler cards)
|
|
152
|
+
spark_bar_chart:
|
|
153
|
+
title: Distribution
|
|
154
|
+
type: spark_bar
|
|
155
|
+
query: categories
|
|
156
|
+
x: count
|
|
157
|
+
y: category
|
|
158
|
+
|
|
159
|
+
# Bar chart
|
|
160
|
+
bar_chart:
|
|
161
|
+
title: Category Counts
|
|
162
|
+
type: bar
|
|
163
|
+
query: categories
|
|
164
|
+
x: category
|
|
165
|
+
y: count
|
|
166
|
+
style:
|
|
167
|
+
orientation: horizontal
|
|
168
|
+
|
|
169
|
+
# Category table
|
|
170
|
+
category_table:
|
|
171
|
+
title: All Categories
|
|
172
|
+
type: table
|
|
173
|
+
query: categories
|
|
174
|
+
style:
|
|
175
|
+
header_overflow: wrap-two
|
|
176
|
+
columns:
|
|
177
|
+
category:
|
|
178
|
+
label: Category
|
|
179
|
+
count:
|
|
180
|
+
label: Count
|
|
181
|
+
format: ",.0f"
|
|
182
|
+
pct:
|
|
183
|
+
label: "%"
|
|
184
|
+
format: ".1f"
|
|
185
|
+
|
|
186
|
+
rows:
|
|
187
|
+
- schema_panel
|
|
188
|
+
- cols:
|
|
189
|
+
- total
|
|
190
|
+
- categories
|
|
191
|
+
- null_pct
|
|
192
|
+
- cols:
|
|
193
|
+
- pie_chart
|
|
194
|
+
- spark_bar_chart
|
|
195
|
+
- bar_chart
|
|
196
|
+
- category_table
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Auto-Generated Charts Template
|
|
2
|
+
#
|
|
3
|
+
# Dynamically generates appropriate visualizations for each column type using
|
|
4
|
+
# the foreach construct and board file imports.
|
|
5
|
+
#
|
|
6
|
+
# URL: /inspect/charts/?model=<table>
|
|
7
|
+
#
|
|
8
|
+
# Required variables:
|
|
9
|
+
# - model: Table/model name
|
|
10
|
+
#
|
|
11
|
+
# Optional variables:
|
|
12
|
+
# - connection: DuckDB file path or :memory: (default: :memory:)
|
|
13
|
+
#
|
|
14
|
+
# This template demonstrates the foreach + board imports pattern:
|
|
15
|
+
# 1. Queries fetch column metadata grouped by type
|
|
16
|
+
# 2. foreach iterates over each column
|
|
17
|
+
# 3. Board imports load type-specific partials (numeric.yml, date.yml, etc.)
|
|
18
|
+
# 4. Partials inherit parent variables (model, column, connection)
|
|
19
|
+
#
|
|
20
|
+
# Theme is set via DFT_DEFAULT_THEME env var at serve startup.
|
|
21
|
+
|
|
22
|
+
title: "Charts: {{ model }}"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
variables:
|
|
26
|
+
model:
|
|
27
|
+
input: text
|
|
28
|
+
visible: false
|
|
29
|
+
default: ""
|
|
30
|
+
connection:
|
|
31
|
+
input: text
|
|
32
|
+
visible: false
|
|
33
|
+
default: ":memory:"
|
|
34
|
+
|
|
35
|
+
queries:
|
|
36
|
+
# Get numeric columns for histograms
|
|
37
|
+
numeric_columns:
|
|
38
|
+
source:
|
|
39
|
+
type: duckdb
|
|
40
|
+
path: "{{ connection }}"
|
|
41
|
+
sql: |
|
|
42
|
+
SELECT column_name as column, 'numeric' as type
|
|
43
|
+
FROM information_schema.columns
|
|
44
|
+
WHERE table_name = '{{ model }}'
|
|
45
|
+
AND data_type IN ('INTEGER', 'BIGINT', 'SMALLINT', 'TINYINT',
|
|
46
|
+
'DOUBLE', 'FLOAT', 'REAL', 'DECIMAL', 'NUMERIC')
|
|
47
|
+
ORDER BY ordinal_position
|
|
48
|
+
# Static data for compile-time iteration (used by foreach)
|
|
49
|
+
static_data: []
|
|
50
|
+
|
|
51
|
+
# Get date columns for time series
|
|
52
|
+
date_columns:
|
|
53
|
+
source:
|
|
54
|
+
type: duckdb
|
|
55
|
+
path: "{{ connection }}"
|
|
56
|
+
sql: |
|
|
57
|
+
SELECT column_name as column, 'date' as type
|
|
58
|
+
FROM information_schema.columns
|
|
59
|
+
WHERE table_name = '{{ model }}'
|
|
60
|
+
AND data_type IN ('DATE', 'TIMESTAMP', 'DATETIME', 'TIMESTAMPTZ')
|
|
61
|
+
ORDER BY ordinal_position
|
|
62
|
+
static_data: []
|
|
63
|
+
|
|
64
|
+
# Get categorical columns for bar charts
|
|
65
|
+
categorical_columns:
|
|
66
|
+
source:
|
|
67
|
+
type: duckdb
|
|
68
|
+
path: "{{ connection }}"
|
|
69
|
+
sql: |
|
|
70
|
+
SELECT column_name as column, 'categorical' as type
|
|
71
|
+
FROM information_schema.columns
|
|
72
|
+
WHERE table_name = '{{ model }}'
|
|
73
|
+
AND data_type IN ('VARCHAR', 'CHAR', 'TEXT', 'STRING', 'BOOLEAN')
|
|
74
|
+
ORDER BY ordinal_position
|
|
75
|
+
static_data: []
|
|
76
|
+
|
|
77
|
+
rows:
|
|
78
|
+
# Numeric column histograms
|
|
79
|
+
- title: "Numeric Columns"
|
|
80
|
+
text: |
|
|
81
|
+
Distribution histograms for numeric columns.
|
|
82
|
+
rows:
|
|
83
|
+
- foreach:
|
|
84
|
+
query: numeric_columns
|
|
85
|
+
as: col
|
|
86
|
+
items:
|
|
87
|
+
- ../partials/numeric.yml
|
|
88
|
+
|
|
89
|
+
# Date column time series
|
|
90
|
+
- title: "Date Columns"
|
|
91
|
+
text: |
|
|
92
|
+
Time series for date/timestamp columns.
|
|
93
|
+
rows:
|
|
94
|
+
- foreach:
|
|
95
|
+
query: date_columns
|
|
96
|
+
as: col
|
|
97
|
+
items:
|
|
98
|
+
- ../partials/date.yml
|
|
99
|
+
|
|
100
|
+
# Categorical column bar charts
|
|
101
|
+
- title: "Categorical Columns"
|
|
102
|
+
text: |
|
|
103
|
+
Distribution for categorical columns.
|
|
104
|
+
rows:
|
|
105
|
+
- foreach:
|
|
106
|
+
query: categorical_columns
|
|
107
|
+
as: col
|
|
108
|
+
items:
|
|
109
|
+
- ../partials/categorical.yml
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# Date Column Profile Template
|
|
2
|
+
#
|
|
3
|
+
# Analysis of a date/timestamp column. Schema panel pulls from the
|
|
4
|
+
# LayeredSchemaResolver; live time-series, gap, and distribution panels
|
|
5
|
+
# stay on warehouse SQL.
|
|
6
|
+
#
|
|
7
|
+
# URL: /inspect/date_column/?model=<table>&column=<col>
|
|
8
|
+
#
|
|
9
|
+
# Required variables:
|
|
10
|
+
# - model: Table/model name
|
|
11
|
+
# - column: Column name to analyze
|
|
12
|
+
#
|
|
13
|
+
# Optional variables:
|
|
14
|
+
# - connection: DuckDB file path or :memory: (default: :memory:)
|
|
15
|
+
#
|
|
16
|
+
# Theme is set via DFT_DEFAULT_THEME env var at serve startup.
|
|
17
|
+
|
|
18
|
+
title: "Date Column: {{ model }}.{{ column }}"
|
|
19
|
+
|
|
20
|
+
text: |
|
|
21
|
+
[Back to {{ model }}](/inspect/model/?model={{ model }}&connection={{ connection }})
|
|
22
|
+
|
|
23
|
+
variables:
|
|
24
|
+
model:
|
|
25
|
+
input: text
|
|
26
|
+
visible: false
|
|
27
|
+
default: ""
|
|
28
|
+
column:
|
|
29
|
+
input: text
|
|
30
|
+
visible: false
|
|
31
|
+
default: ""
|
|
32
|
+
connection:
|
|
33
|
+
input: text
|
|
34
|
+
visible: false
|
|
35
|
+
default: ":memory:"
|
|
36
|
+
source_name:
|
|
37
|
+
input: text
|
|
38
|
+
visible: false
|
|
39
|
+
default: ""
|
|
40
|
+
schema_name:
|
|
41
|
+
input: text
|
|
42
|
+
visible: false
|
|
43
|
+
default: ""
|
|
44
|
+
|
|
45
|
+
queries:
|
|
46
|
+
column_schema:
|
|
47
|
+
type: schema_resolver
|
|
48
|
+
source: "{{ source_name }}"
|
|
49
|
+
schema: "{{ schema_name }}"
|
|
50
|
+
table: "{{ model }}"
|
|
51
|
+
column: "{{ column }}"
|
|
52
|
+
|
|
53
|
+
# Basic date stats
|
|
54
|
+
stats:
|
|
55
|
+
source:
|
|
56
|
+
type: duckdb
|
|
57
|
+
path: "{{ connection }}"
|
|
58
|
+
sql: |
|
|
59
|
+
SELECT
|
|
60
|
+
COUNT(*) as total_count,
|
|
61
|
+
COUNT({{ column }}) as non_null_count,
|
|
62
|
+
COUNT(*) - COUNT({{ column }}) as null_count,
|
|
63
|
+
ROUND(100.0 * (COUNT(*) - COUNT({{ column }})) / COUNT(*), 2) as null_pct,
|
|
64
|
+
MIN({{ column }})::DATE as min_date,
|
|
65
|
+
MAX({{ column }})::DATE as max_date,
|
|
66
|
+
COUNT(DISTINCT {{ column }}::DATE) as distinct_dates,
|
|
67
|
+
MAX({{ column }})::DATE - MIN({{ column }})::DATE as date_span_days
|
|
68
|
+
FROM {{ model }}
|
|
69
|
+
|
|
70
|
+
# Records per day
|
|
71
|
+
daily_counts:
|
|
72
|
+
source:
|
|
73
|
+
type: duckdb
|
|
74
|
+
path: "{{ connection }}"
|
|
75
|
+
sql: |
|
|
76
|
+
SELECT
|
|
77
|
+
{{ column }}::DATE as date,
|
|
78
|
+
COUNT(*) as count
|
|
79
|
+
FROM {{ model }}
|
|
80
|
+
WHERE {{ column }} IS NOT NULL
|
|
81
|
+
GROUP BY {{ column }}::DATE
|
|
82
|
+
ORDER BY date
|
|
83
|
+
|
|
84
|
+
# Day of week distribution
|
|
85
|
+
dow_distribution:
|
|
86
|
+
source:
|
|
87
|
+
type: duckdb
|
|
88
|
+
path: "{{ connection }}"
|
|
89
|
+
sql: |
|
|
90
|
+
SELECT
|
|
91
|
+
CASE DAYOFWEEK({{ column }})
|
|
92
|
+
WHEN 0 THEN 'Sunday'
|
|
93
|
+
WHEN 1 THEN 'Monday'
|
|
94
|
+
WHEN 2 THEN 'Tuesday'
|
|
95
|
+
WHEN 3 THEN 'Wednesday'
|
|
96
|
+
WHEN 4 THEN 'Thursday'
|
|
97
|
+
WHEN 5 THEN 'Friday'
|
|
98
|
+
WHEN 6 THEN 'Saturday'
|
|
99
|
+
END as day_of_week,
|
|
100
|
+
DAYOFWEEK({{ column }}) as dow_num,
|
|
101
|
+
COUNT(*) as count
|
|
102
|
+
FROM {{ model }}
|
|
103
|
+
WHERE {{ column }} IS NOT NULL
|
|
104
|
+
GROUP BY DAYOFWEEK({{ column }})
|
|
105
|
+
ORDER BY DAYOFWEEK({{ column }})
|
|
106
|
+
|
|
107
|
+
# Monthly distribution
|
|
108
|
+
monthly_counts:
|
|
109
|
+
source:
|
|
110
|
+
type: duckdb
|
|
111
|
+
path: "{{ connection }}"
|
|
112
|
+
sql: |
|
|
113
|
+
SELECT
|
|
114
|
+
DATE_TRUNC('month', {{ column }})::DATE as month,
|
|
115
|
+
COUNT(*) as count
|
|
116
|
+
FROM {{ model }}
|
|
117
|
+
WHERE {{ column }} IS NOT NULL
|
|
118
|
+
GROUP BY DATE_TRUNC('month', {{ column }})
|
|
119
|
+
ORDER BY month
|
|
120
|
+
|
|
121
|
+
# Date gaps (missing dates)
|
|
122
|
+
date_gaps:
|
|
123
|
+
source:
|
|
124
|
+
type: duckdb
|
|
125
|
+
path: "{{ connection }}"
|
|
126
|
+
sql: |
|
|
127
|
+
WITH date_range AS (
|
|
128
|
+
SELECT GENERATE_SERIES(
|
|
129
|
+
MIN({{ column }})::DATE,
|
|
130
|
+
MAX({{ column }})::DATE,
|
|
131
|
+
INTERVAL '1 day'
|
|
132
|
+
)::DATE as expected_date
|
|
133
|
+
FROM {{ model }}
|
|
134
|
+
WHERE {{ column }} IS NOT NULL
|
|
135
|
+
),
|
|
136
|
+
actual_dates AS (
|
|
137
|
+
SELECT DISTINCT {{ column }}::DATE as actual_date
|
|
138
|
+
FROM {{ model }}
|
|
139
|
+
WHERE {{ column }} IS NOT NULL
|
|
140
|
+
)
|
|
141
|
+
SELECT
|
|
142
|
+
expected_date as missing_date
|
|
143
|
+
FROM date_range
|
|
144
|
+
WHERE expected_date NOT IN (SELECT actual_date FROM actual_dates)
|
|
145
|
+
ORDER BY expected_date
|
|
146
|
+
LIMIT 50
|
|
147
|
+
|
|
148
|
+
charts:
|
|
149
|
+
schema_panel:
|
|
150
|
+
title: Schema
|
|
151
|
+
type: table
|
|
152
|
+
query: column_schema
|
|
153
|
+
style:
|
|
154
|
+
header_overflow: wrap-two
|
|
155
|
+
columns:
|
|
156
|
+
name:
|
|
157
|
+
label: Column
|
|
158
|
+
type:
|
|
159
|
+
label: Type
|
|
160
|
+
role:
|
|
161
|
+
label: Role
|
|
162
|
+
semantic_type:
|
|
163
|
+
label: Semantic
|
|
164
|
+
description:
|
|
165
|
+
label: Description
|
|
166
|
+
|
|
167
|
+
# Stats KPIs
|
|
168
|
+
min_date:
|
|
169
|
+
label: Earliest Date
|
|
170
|
+
type: kpi
|
|
171
|
+
query: stats
|
|
172
|
+
value: min_date
|
|
173
|
+
|
|
174
|
+
max_date:
|
|
175
|
+
label: Latest Date
|
|
176
|
+
type: kpi
|
|
177
|
+
query: stats
|
|
178
|
+
value: max_date
|
|
179
|
+
|
|
180
|
+
distinct_dates:
|
|
181
|
+
label: Distinct Dates
|
|
182
|
+
type: kpi
|
|
183
|
+
query: stats
|
|
184
|
+
value: distinct_dates
|
|
185
|
+
format: ",.0f"
|
|
186
|
+
|
|
187
|
+
date_span:
|
|
188
|
+
label: Date Span (Days)
|
|
189
|
+
type: kpi
|
|
190
|
+
query: stats
|
|
191
|
+
value: date_span_days
|
|
192
|
+
format: ",.0f"
|
|
193
|
+
|
|
194
|
+
null_pct:
|
|
195
|
+
label: "Null %"
|
|
196
|
+
type: kpi
|
|
197
|
+
query: stats
|
|
198
|
+
value: null_pct
|
|
199
|
+
format: ".1f"
|
|
200
|
+
|
|
201
|
+
# Time series
|
|
202
|
+
daily_chart:
|
|
203
|
+
title: Records per Day
|
|
204
|
+
type: line
|
|
205
|
+
query: daily_counts
|
|
206
|
+
x: date
|
|
207
|
+
y: count
|
|
208
|
+
|
|
209
|
+
# Monthly trend
|
|
210
|
+
monthly_chart:
|
|
211
|
+
title: Records per Month
|
|
212
|
+
type: bar
|
|
213
|
+
query: monthly_counts
|
|
214
|
+
x: month
|
|
215
|
+
y: count
|
|
216
|
+
|
|
217
|
+
# Day of week
|
|
218
|
+
dow_chart:
|
|
219
|
+
title: Day of Week Distribution
|
|
220
|
+
type: bar
|
|
221
|
+
query: dow_distribution
|
|
222
|
+
x: day_of_week
|
|
223
|
+
y: count
|
|
224
|
+
|
|
225
|
+
# Gaps table
|
|
226
|
+
gaps_table:
|
|
227
|
+
title: Missing Dates (first 50)
|
|
228
|
+
type: table
|
|
229
|
+
query: date_gaps
|
|
230
|
+
style:
|
|
231
|
+
header_overflow: wrap-two
|
|
232
|
+
columns:
|
|
233
|
+
missing_date:
|
|
234
|
+
label: Missing Date
|
|
235
|
+
|
|
236
|
+
rows:
|
|
237
|
+
- schema_panel
|
|
238
|
+
- cols:
|
|
239
|
+
- min_date
|
|
240
|
+
- max_date
|
|
241
|
+
- distinct_dates
|
|
242
|
+
- date_span
|
|
243
|
+
- null_pct
|
|
244
|
+
- daily_chart
|
|
245
|
+
- cols:
|
|
246
|
+
- monthly_chart
|
|
247
|
+
- dow_chart
|
|
248
|
+
- gaps_table
|