dagster-evidence 0.1.7__py3-none-any.whl → 0.2.0__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.
@@ -0,0 +1,168 @@
1
+ """Translator for Evidence project assets.
2
+
3
+ This module provides the translator class for converting Evidence project data
4
+ into Dagster AssetSpecs. Subclass DagsterEvidenceTranslator to customize
5
+ how Evidence sources and projects are represented in Dagster.
6
+ """
7
+
8
+ from typing import Union
9
+
10
+ import dagster as dg
11
+ from dagster._annotations import beta, public
12
+
13
+ from .sources import (
14
+ BaseEvidenceProjectSource,
15
+ BigQueryEvidenceProjectSource,
16
+ DuckdbEvidenceProjectSource,
17
+ EvidenceProjectTranslatorData,
18
+ EvidenceSourceTranslatorData,
19
+ GSheetsEvidenceProjectSource,
20
+ MotherDuckEvidenceProjectSource,
21
+ )
22
+
23
+
24
+ @beta
25
+ @public
26
+ class DagsterEvidenceTranslator:
27
+ """Translator class which converts Evidence project data into AssetSpecs.
28
+
29
+ Subclass this class to provide custom translation logic.
30
+
31
+ Examples:
32
+ Extend the source type registry via class attribute:
33
+
34
+ class MyTranslator(DagsterEvidenceTranslator):
35
+ SOURCE_TYPE_REGISTRY = {
36
+ **DagsterEvidenceTranslator.SOURCE_TYPE_REGISTRY,
37
+ "postgres": PostgresEvidenceProjectSource,
38
+ }
39
+
40
+ Or override get_source_type_registry() for dynamic configuration:
41
+
42
+ class MyTranslator(DagsterEvidenceTranslator):
43
+ def get_source_type_registry(self):
44
+ return {
45
+ **super().get_source_type_registry(),
46
+ "postgres": PostgresEvidenceProjectSource,
47
+ }
48
+
49
+ Customize asset spec generation:
50
+
51
+ class MyTranslator(DagsterEvidenceTranslator):
52
+ def get_asset_spec(self, data):
53
+ spec = super().get_asset_spec(data)
54
+ if isinstance(data, EvidenceSourceTranslatorData):
55
+ return spec.replace_attributes(
56
+ key=spec.key.with_prefix("my_prefix"),
57
+ )
58
+ return spec
59
+ """
60
+
61
+ # Source type registry - maps source type string to source class
62
+ # Subclasses can extend via class attribute or override get_source_type_registry()
63
+ SOURCE_TYPE_REGISTRY: dict[str, type[BaseEvidenceProjectSource]] = {
64
+ "duckdb": DuckdbEvidenceProjectSource,
65
+ "motherduck": MotherDuckEvidenceProjectSource,
66
+ "bigquery": BigQueryEvidenceProjectSource,
67
+ "gsheets": GSheetsEvidenceProjectSource,
68
+ }
69
+
70
+ @public
71
+ def get_source_type_registry(
72
+ self,
73
+ ) -> dict[str, type[BaseEvidenceProjectSource]]:
74
+ """Get the source type registry mapping source types to source classes.
75
+
76
+ Override this method to dynamically configure the source type registry.
77
+ By default, returns the SOURCE_TYPE_REGISTRY class attribute.
78
+
79
+ Returns:
80
+ Dictionary mapping source type strings to source class types.
81
+
82
+ Example:
83
+
84
+ .. code-block:: python
85
+
86
+ class CustomTranslator(DagsterEvidenceTranslator):
87
+ def get_source_type_registry(self):
88
+ return {
89
+ **super().get_source_type_registry(),
90
+ "postgres": PostgresEvidenceProjectSource,
91
+ "mysql": MySQLEvidenceProjectSource,
92
+ }
93
+ """
94
+ return self.SOURCE_TYPE_REGISTRY
95
+
96
+ @public
97
+ def get_source_class(self, source_type: str) -> type[BaseEvidenceProjectSource]:
98
+ """Get the source class for a given source type.
99
+
100
+ Args:
101
+ source_type: The source type identifier (e.g., "duckdb", "bigquery").
102
+
103
+ Returns:
104
+ The source class for the given type.
105
+
106
+ Raises:
107
+ NotImplementedError: If the source type is not in the registry.
108
+ """
109
+ registry = self.get_source_type_registry()
110
+ if source_type not in registry:
111
+ raise NotImplementedError(f"Unknown source type: {source_type}")
112
+ return registry[source_type]
113
+
114
+ @public
115
+ def get_asset_spec(
116
+ self, data: Union[EvidenceSourceTranslatorData, EvidenceProjectTranslatorData]
117
+ ) -> Union[dg.AssetSpec, dg.AssetsDefinition]:
118
+ """Get the asset for an Evidence object (source query or project).
119
+
120
+ Override this method to customize asset generation.
121
+
122
+ Args:
123
+ data: Either EvidenceSourceTranslatorData for source queries
124
+ or EvidenceProjectTranslatorData for the main project asset.
125
+
126
+ Returns:
127
+ For source queries: AssetsDefinition with automation condition.
128
+ For project: AssetSpec for the Evidence project.
129
+
130
+ Example:
131
+
132
+ .. code-block:: python
133
+
134
+ class CustomTranslator(DagsterEvidenceTranslator):
135
+ def get_asset_spec(self, data):
136
+ asset = super().get_asset_spec(data)
137
+ # Customize as needed
138
+ return asset
139
+ """
140
+ if isinstance(data, EvidenceSourceTranslatorData):
141
+ return self._get_source_asset(data)
142
+ elif isinstance(data, EvidenceProjectTranslatorData):
143
+ return self._get_project_asset_spec(data)
144
+ else:
145
+ raise TypeError(f"Unknown data type: {type(data)}")
146
+
147
+ def _get_source_asset(
148
+ self, data: EvidenceSourceTranslatorData
149
+ ) -> dg.AssetsDefinition:
150
+ """Default translation for source query assets.
151
+
152
+ Delegates to the source class's get_source_asset method, allowing
153
+ each source type to customize its asset generation.
154
+ """
155
+ source_type = data.source_content.connection.type
156
+ source_class = self.get_source_class(source_type)
157
+ return source_class.get_source_asset(data)
158
+
159
+ def _get_project_asset_spec(
160
+ self, data: EvidenceProjectTranslatorData
161
+ ) -> dg.AssetSpec:
162
+ """Default translation for main project asset."""
163
+ return dg.AssetSpec(
164
+ key=dg.AssetKey([data.project_name]),
165
+ kinds={"evidence"},
166
+ deps=data.source_deps,
167
+ group_name=data.effective_group_name,
168
+ )
@@ -41,7 +41,7 @@ class EvidenceProject(Component, Resolvable):
41
41
  def build_defs(self, context: ComponentLoadContext) -> Definitions:
42
42
  project_path = os.path.abspath(context.path / self.project_path)
43
43
 
44
- def _run_cmd(cmd: Sequence[str]):
44
+ def _run_cmd(cmd: Sequence[str]) -> None:
45
45
  print(
46
46
  f"{project_path}$ {' '.join(cmd)}",
47
47
  file=sys.stderr,
@@ -51,7 +51,7 @@ class EvidenceProject(Component, Resolvable):
51
51
  cwd=project_path,
52
52
  check=True,
53
53
  capture_output=False,
54
- env=os.environ,
54
+ env=dict(os.environ),
55
55
  )
56
56
 
57
57
  @multi_asset(specs=[self.asset])
@@ -41,7 +41,7 @@ class EvidenceResource(dg.ConfigurableResource):
41
41
  description="The executable to use for commands (npm, yarn, etc.)",
42
42
  )
43
43
 
44
- def _run_cmd(self, cmd: Sequence[str]):
44
+ def _run_cmd(self, cmd: Sequence[str]) -> None:
45
45
  """Run a command in the project directory.
46
46
 
47
47
  Args:
@@ -56,7 +56,7 @@ class EvidenceResource(dg.ConfigurableResource):
56
56
  cwd=self.project_path,
57
57
  check=True,
58
58
  capture_output=False,
59
- env=os.environ,
59
+ env=dict(os.environ),
60
60
  )
61
61
 
62
62
  def build(self) -> None:
@@ -0,0 +1,5 @@
1
+ """Utility functions for dagster-evidence."""
2
+
3
+ from .sql_parser import extract_table_references
4
+
5
+ __all__ = ["extract_table_references"]
@@ -0,0 +1,87 @@
1
+ """SQL parsing utilities for extracting table references.
2
+
3
+ This module provides utilities for parsing SQL queries and extracting
4
+ table references that can be used to determine asset dependencies.
5
+ """
6
+
7
+ import logging
8
+
9
+ from dagster._annotations import beta, public
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ @beta
15
+ @public
16
+ def extract_table_references(
17
+ sql_query: str,
18
+ default_database: str | None = None,
19
+ default_schema: str | None = None,
20
+ ) -> list[dict[str, str | None]]:
21
+ """Parse SQL and extract table references.
22
+
23
+ Uses SQLGlot to parse SQL queries and extract all table references,
24
+ including tables in JOINs, subqueries, and CTEs.
25
+
26
+ Args:
27
+ sql_query: The SQL query to parse.
28
+ default_database: Default database name to use if not specified in query.
29
+ default_schema: Default schema name to use if not specified in query.
30
+
31
+ Returns:
32
+ List of dictionaries, each containing:
33
+ - database: The database/catalog name (or default_database if not specified)
34
+ - schema: The schema name (or default_schema if not specified)
35
+ - table: The table name
36
+
37
+ Example:
38
+
39
+ .. code-block:: python
40
+
41
+ from dagster_evidence.utils import extract_table_references
42
+
43
+ # Simple query
44
+ refs = extract_table_references("SELECT * FROM orders")
45
+ # Returns: [{"database": None, "schema": None, "table": "orders"}]
46
+
47
+ # With defaults
48
+ refs = extract_table_references(
49
+ "SELECT * FROM orders",
50
+ default_database="my_db",
51
+ default_schema="main"
52
+ )
53
+ # Returns: [{"database": "my_db", "schema": "main", "table": "orders"}]
54
+
55
+ # Fully qualified table
56
+ refs = extract_table_references("SELECT * FROM prod.sales.orders")
57
+ # Returns: [{"database": "prod", "schema": "sales", "table": "orders"}]
58
+ """
59
+ try:
60
+ import sqlglot
61
+ from sqlglot import exp
62
+ except ImportError:
63
+ raise ImportError(
64
+ "sqlglot is required for SQL table reference extraction. "
65
+ "Install it with: pip install dagster-evidence[sql]"
66
+ ) from None
67
+
68
+ try:
69
+ parsed = sqlglot.parse(sql_query)
70
+ tables: list[dict[str, str | None]] = []
71
+
72
+ for statement in parsed:
73
+ if statement is None:
74
+ continue
75
+ for table in statement.find_all(exp.Table):
76
+ tables.append(
77
+ {
78
+ "database": table.catalog or default_database,
79
+ "schema": table.db or default_schema,
80
+ "table": table.name,
81
+ }
82
+ )
83
+
84
+ return tables
85
+ except Exception as e:
86
+ logger.debug(f"Failed to parse SQL: {e}")
87
+ return []
@@ -0,0 +1,120 @@
1
+ Metadata-Version: 2.4
2
+ Name: dagster-evidence
3
+ Version: 0.2.0
4
+ Summary: Dagster integration with evidence.dev
5
+ Requires-Python: <3.14,>=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: dagster>=1.12.10
8
+ Requires-Dist: pyyaml>=6.0
9
+ Provides-Extra: github-pages
10
+ Requires-Dist: gitpython>=3.1.0; extra == "github-pages"
11
+ Provides-Extra: sql
12
+ Requires-Dist: sqlglot>=26.0.0; extra == "sql"
13
+ Provides-Extra: duckdb
14
+ Requires-Dist: duckdb>=0.10.0; extra == "duckdb"
15
+ Provides-Extra: bigquery
16
+ Requires-Dist: google-cloud-bigquery>=3.0.0; extra == "bigquery"
17
+ Provides-Extra: gsheets
18
+ Requires-Dist: google-api-python-client>=2.0.0; extra == "gsheets"
19
+ Requires-Dist: google-auth>=2.0.0; extra == "gsheets"
20
+ Provides-Extra: all
21
+ Requires-Dist: gitpython>=3.1.0; extra == "all"
22
+ Requires-Dist: sqlglot>=26.0.0; extra == "all"
23
+ Requires-Dist: duckdb>=0.10.0; extra == "all"
24
+ Requires-Dist: google-cloud-bigquery>=3.0.0; extra == "all"
25
+ Requires-Dist: google-api-python-client>=2.0.0; extra == "all"
26
+ Requires-Dist: google-auth>=2.0.0; extra == "all"
27
+ Provides-Extra: test
28
+ Requires-Dist: pytest; extra == "test"
29
+ Requires-Dist: pytest-mock; extra == "test"
30
+
31
+ # dagster-evidence
32
+
33
+ Dagster integration with [Evidence](https://evidence.dev/), enabling you to orchestrate Evidence.dev dashboard projects as Dagster assets.
34
+
35
+ ![architecture](./docs/component-architecture.excalidraw.png)
36
+ ## Installation
37
+
38
+ ```bash
39
+ # Basic installation
40
+ uv add dagster-evidence
41
+
42
+ # With specific data source support
43
+ uv add "dagster-evidence[duckdb]" # DuckDB support
44
+ uv add "dagster-evidence[bigquery]" # BigQuery support
45
+ uv add "dagster-evidence[gsheets]" # Google Sheets support
46
+ uv add "dagster-evidence[github-pages]" # GitHub Pages deployment
47
+
48
+ # Install multiple extras
49
+ uv add "dagster-evidence[duckdb,bigquery,github-pages]"
50
+ ```
51
+
52
+ ## Features
53
+
54
+ - **Automatic asset discovery**: Automatically generates Dagster assets from Evidence project sources
55
+ - **Build and deployment**: Builds Evidence projects and deploys them to your hosting platform
56
+ - **Customizable translation**: Extend the translator to customize how Evidence sources map to Dagster assets
57
+ - **Type-safe configuration**: Pydantic-based configuration with YAML support
58
+ - **Source change detection**: Optional sensors to detect changes in upstream data sources
59
+
60
+ ## Supported Data Sources
61
+
62
+ - DuckDB
63
+ - MotherDuck
64
+ - BigQuery
65
+ - Google Sheets
66
+
67
+ Need additional source types? You can extend the translator to add custom sources, or contribute to the project!
68
+
69
+ ## Deployment Support
70
+
71
+ **Currently Implemented:**
72
+ - GitHub Pages: Deploy to GitHub Pages with automatic git push
73
+ - Custom commands: Run any custom deployment script or command
74
+
75
+ **Need another deployment target?** Open an issue and tag [@milicevica23](https://github.com/milicevica23) with your deployment requirements.
76
+
77
+ ## Quick Start
78
+
79
+ ### Component Configuration (YAML)
80
+
81
+ ```yaml
82
+ # defs.yaml
83
+ type: dagster_evidence.EvidenceProjectComponentV2
84
+ attributes:
85
+ evidence_project:
86
+ project_type: local
87
+ project_path: ./my-evidence-project
88
+ enable_source_assets_hiding: true
89
+ enable_source_sensors: false
90
+ project_deployment:
91
+ type: github_pages
92
+ github_repo: my-org/my-dashboard
93
+ branch: gh-pages
94
+ ```
95
+
96
+ ## Advanced Usage
97
+
98
+ ### Custom Translator
99
+
100
+ You can customize how Evidence sources are translated to Dagster assets:
101
+
102
+ ```python
103
+ from dagster_evidence import DagsterEvidenceTranslator, EvidenceSourceTranslatorData
104
+ import dagster as dg
105
+
106
+ class MyTranslator(DagsterEvidenceTranslator):
107
+ def get_asset_spec(self, data):
108
+ if isinstance(data, EvidenceSourceTranslatorData):
109
+ # Custom logic for source assets
110
+ return dg.AssetSpec(
111
+ key=dg.AssetKey(["custom", data.query.name]),
112
+ kinds={"evidence", data.source_content.connection.type},
113
+ )
114
+ return super().get_asset_spec(data)
115
+ ```
116
+
117
+
118
+ ## Contributing
119
+
120
+ Contributions are welcome! Please see the main repository's contribution guidelines.
@@ -0,0 +1,17 @@
1
+ dagster_evidence/__init__.py,sha256=vXUZVFLQwWH7ABbpnL5oVXwlRfwORIzRs8T49q8U-j0,903
2
+ dagster_evidence/resource.py,sha256=-9nIHmhEIUONnFIgZunDWmayNzkYBvA6BvlzQn2_R4w,2198
3
+ dagster_evidence/components/__init__.py,sha256=gZbG2q0F1d1DtFC-dfO0DEYzgABXFseDetpKhFbzEnc,582
4
+ dagster_evidence/components/deployments.py,sha256=CZijqpYK0rGX4oFra50W_a1OqblwyeJQaVavFbRz0tM,16784
5
+ dagster_evidence/components/evidence_project_v2.py,sha256=185YRveCapIXpukNsHWoj1ajVjL2fot50HQRz_pNUH4,6468
6
+ dagster_evidence/components/projects.py,sha256=o96ceqGPgLi8AA0bR0r3K2U4dEshVJBEATc8q7uenj8,23658
7
+ dagster_evidence/components/sources.py,sha256=Fqe7QA14Db4wK_jTIvQ0eIl_cUPZ04qBsyWb6fWFT6E,45501
8
+ dagster_evidence/components/translator.py,sha256=bFRY-XTMeZSZjmJczvNRS8mlzG7z5jiC1f3vt_KhOSg,6079
9
+ dagster_evidence/lib/__init__.py,sha256=pLil7ph_5C5j_WEVUgfU91u9y-cg7OaX4AKE-9elWNo,66
10
+ dagster_evidence/lib/evidence_project.py,sha256=4ZaqSgfUK6oeZ3dRGa3Zr5a2oUPSVXoKkJLWAHuNYnQ,2820
11
+ dagster_evidence/utils/__init__.py,sha256=RS1hWN41SPPAok4TtsIMnsW4jIwtUJGshaqqkGhURAg,136
12
+ dagster_evidence/utils/sql_parser.py,sha256=NnbvMMw_XUUF-eV3rScaRlhWrxquQM562EyRgiePL88,2821
13
+ dagster_evidence-0.2.0.dist-info/METADATA,sha256=BZ6d2SVDwJ1vV-2Y0mngzEJHBQIKTDCk0ruX0M3eO3Y,3974
14
+ dagster_evidence-0.2.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
15
+ dagster_evidence-0.2.0.dist-info/entry_points.txt,sha256=CZx5kJ7dwUo0ke4Vu0B-GaSxM8B1vlLIzXrT14ovUr0,114
16
+ dagster_evidence-0.2.0.dist-info/top_level.txt,sha256=YAZVTuvV8f7bWYwERV2uGnMQj93E4Vq3KHdsGPpN6IY,17
17
+ dagster_evidence-0.2.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,23 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: dagster-evidence
3
- Version: 0.1.7
4
- Summary: Dagster integration with evidence.dev
5
- Requires-Python: >=3.10
6
- Description-Content-Type: text/markdown
7
- Requires-Dist: dagster>=1.10.9
8
-
9
- # dagster-evidence
10
-
11
- `dagster-evidence` provides a Dagster Component that integrates with [evidence](https://evidence.dev/), an open-source tool for building data apps.
12
-
13
- ## Test
14
-
15
- ```sh
16
- make test
17
- ```
18
-
19
- ## Build
20
-
21
- ```sh
22
- make build
23
- ```
@@ -1,9 +0,0 @@
1
- dagster_evidence/__init__.py,sha256=zVzzLtXbOCxlxneYi8A5fwMlTyY41TSJyfjYuRPNqK8,349
2
- dagster_evidence/resource.py,sha256=U9lBlJQTXVPft504tai6rMVExuFnbEqq59EYqa_uXDY,2184
3
- dagster_evidence/lib/__init__.py,sha256=pLil7ph_5C5j_WEVUgfU91u9y-cg7OaX4AKE-9elWNo,66
4
- dagster_evidence/lib/evidence_project.py,sha256=6RVpYBbjJnmviudxIujEv5sL608ZohHnlokVuHYutW8,2806
5
- dagster_evidence-0.1.7.dist-info/METADATA,sha256=w29KYOFGXEL49St4U5nLjJEXkHQNmC17TSN9Xd9Ujys,433
6
- dagster_evidence-0.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
- dagster_evidence-0.1.7.dist-info/entry_points.txt,sha256=CZx5kJ7dwUo0ke4Vu0B-GaSxM8B1vlLIzXrT14ovUr0,114
8
- dagster_evidence-0.1.7.dist-info/top_level.txt,sha256=YAZVTuvV8f7bWYwERV2uGnMQj93E4Vq3KHdsGPpN6IY,17
9
- dagster_evidence-0.1.7.dist-info/RECORD,,