Skip to content

Commit ab4ca6c

Browse files
committed
Move test fixtures to tests/fixtures/ and fix example scripts
Moved test fixture YAML/LookML files from examples/ to tests/fixtures/: - All adapter test fixtures (cube, lookml, metricflow, hex, omni, rill, superset, sidemantic, sql) - Updated all test imports to use tests/fixtures/ paths - Updated export_example.py to reference new fixture location - multi_format_demo/ remains in examples/ as a user-facing demo Fixed broken example scripts with PEP 723 dependencies: - comprehensive_demo.py: Added sidemantic dep, fixed QueryRewriter import, updated to use 'metrics' virtual table - comprehensive_example.py: Added sidemantic dep - symmetric_aggregates_example.py: Added sidemantic and pandas deps - streamlit_dashboard.py: Added sidemantic dep Added test coverage for unreferenced fixture files: - test_lookml_bq_thelook_derived_table - test_lookml_bq_thelook_user_facts - test_import_omni_model_relationships All example scripts now run successfully with uv run.
1 parent 336e3a1 commit ab4ca6c

71 files changed

Lines changed: 218 additions & 160 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

examples/comprehensive_demo.py

Lines changed: 19 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -14,29 +14,21 @@
1414
"""
1515
# /// script
1616
# dependencies = [
17+
# "sidemantic",
1718
# "duckdb",
18-
# "sqlglot",
19-
# "pydantic",
20-
# "pyyaml"
2119
# ]
2220
# requires-python = ">=3.12"
2321
# ///
2422

25-
# Add parent directory to path so we can import sidemantic
26-
import sys
27-
from pathlib import Path
28-
29-
sys.path.insert(0, str(Path(__file__).parent.parent))
30-
3123
import duckdb
32-
from sidemantic.sql.rewriter import SemanticSQLRewriter
3324

3425
from sidemantic.core.dimension import Dimension
3526
from sidemantic.core.metric import Metric
3627
from sidemantic.core.model import Model
3728
from sidemantic.core.relationship import Relationship
3829
from sidemantic.core.semantic_graph import SemanticGraph
3930
from sidemantic.sql.generator import SQLGenerator
31+
from sidemantic.sql.query_rewriter import QueryRewriter
4032

4133

4234
def print_section(title: str):
@@ -198,16 +190,16 @@ def main():
198190
# =========================================================================
199191
print_section("4. SQL Rewriter: Write SQL Against Semantic Layer")
200192

201-
rewriter = SemanticSQLRewriter(graph)
193+
rewriter = QueryRewriter(graph)
202194

203195
# Example 1: Simple query without GROUP BY
204196
print("\nExample 1: Query without GROUP BY")
205197
user_sql = """
206198
SELECT
207-
status,
199+
orders.status,
208200
total_revenue,
209201
avg_order_value
210-
FROM semantic_layer.orders
202+
FROM metrics
211203
"""
212204
print(f"\nUser writes:\n{user_sql}")
213205

@@ -217,52 +209,41 @@ def main():
217209
results = conn.execute(rewritten).fetchall()
218210
print_results(results, "Revenue Metrics by Status")
219211

220-
# Example 2: Join semantic layer with regular table
221-
print("\nExample 2: Join semantic layer with regular table")
222-
223-
# Create a regular table
224-
conn.execute("""
225-
CREATE TABLE IF NOT EXISTS promotions AS
226-
SELECT 'completed' AS status, 'Completion Bonus' AS promo_name, 0.10 AS discount
227-
UNION ALL
228-
SELECT 'pending', 'Pending Discount', 0.05
229-
""")
212+
# Example 2: Query from single model
213+
print("\nExample 2: Query from single model")
230214

231215
user_sql = """
232216
SELECT
233-
o.status,
234-
o.total_revenue,
235-
p.promo_name,
236-
p.discount
237-
FROM semantic_layer.orders AS o
238-
JOIN promotions AS p ON o.status = p.status
217+
orders.status,
218+
orders.amount AS revenue,
219+
orders.order_count
220+
FROM orders
239221
"""
240222
print(f"\nUser writes:\n{user_sql}")
241223

242224
rewritten = rewriter.rewrite(user_sql)
243225
print_sql(rewritten)
244226

245227
results = conn.execute(rewritten).fetchall()
246-
print_results(results, "Revenue + Promotions")
228+
print_results(results, "Revenue by status from orders model")
247229

248-
# Example 3: Cross-model join (using Rails relationships)
249-
print("\nExample 3: Cross-model join using Rails-like relationships")
230+
# Example 3: Cross-model metrics
231+
print("\nExample 3: Cross-model metrics using 'metrics' virtual table")
250232

251233
user_sql = """
252234
SELECT
253-
c.name,
254-
o.status,
255-
o.total_revenue
256-
FROM semantic_layer.customers AS c
257-
JOIN semantic_layer.orders AS o ON c.customer_id = o.customer_id
235+
customers.region,
236+
total_revenue,
237+
avg_order_value
238+
FROM metrics
258239
"""
259240
print(f"\nUser writes:\n{user_sql}")
260241

261242
rewritten = rewriter.rewrite(user_sql)
262243
print_sql(rewritten)
263244

264245
results = conn.execute(rewritten).fetchall()
265-
print_results(results, "Customer Revenue (cross-model join)")
246+
print_results(results, "Revenue by customer region")
266247

267248
# =========================================================================
268249
# 5. Generate reusable views

examples/comprehensive_example.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python3
22
# /// script
33
# dependencies = [
4+
# "sidemantic",
45
# "duckdb",
56
# ]
67
# ///

examples/export_example.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
# Load from native Sidemantic YAML
99
print("Loading native Sidemantic YAML...")
10-
sl = SemanticLayer.from_yaml("examples/sidemantic/orders.yml")
10+
sl = SemanticLayer.from_yaml("tests/fixtures/sidemantic/orders.yml")
1111
print(f"Loaded {len(sl.list_models())} models and {len(sl.list_metrics())} metrics")
1212

1313
# Export to Cube format

examples/streamlit_dashboard.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python3
22
# /// script
33
# dependencies = [
4+
# "sidemantic",
45
# "duckdb",
56
# "streamlit",
67
# "plotly",

examples/symmetric_aggregates_example.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#!/usr/bin/env python3
22
# /// script
33
# dependencies = [
4+
# "sidemantic",
45
# "duckdb",
6+
# "pandas",
57
# ]
68
# ///
79
"""Example demonstrating symmetric aggregates for fan-out joins.

tests/adapters/test_cube.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
def test_cube_adapter():
1111
"""Test Cube adapter with example YAML."""
1212
adapter = CubeAdapter()
13-
graph = adapter.parse(Path("examples/cube/orders.yml"))
13+
graph = adapter.parse(Path("tests/fixtures/cube/orders.yml"))
1414

1515
# Check models were imported
1616
assert "orders" in graph.models
@@ -48,7 +48,7 @@ def test_cube_adapter():
4848
def test_cube_adapter_join_discovery():
4949
"""Test that Cube adapter enables join discovery."""
5050
adapter = CubeAdapter()
51-
graph = adapter.parse(Path("examples/cube/orders.yml"))
51+
graph = adapter.parse(Path("tests/fixtures/cube/orders.yml"))
5252

5353
# Check that relationships were imported
5454
orders = graph.get_model("orders")
@@ -60,7 +60,7 @@ def test_cube_adapter_join_discovery():
6060
def test_cube_adapter_pre_aggregations():
6161
"""Test Cube adapter with pre-aggregations."""
6262
adapter = CubeAdapter()
63-
graph = adapter.parse(Path("examples/cube/orders_with_preagg.yml"))
63+
graph = adapter.parse(Path("tests/fixtures/cube/orders_with_preagg.yml"))
6464

6565
orders = graph.get_model("orders")
6666
assert orders is not None
@@ -85,7 +85,7 @@ def test_cube_adapter_pre_aggregations():
8585
def test_cube_adapter_multi_cube():
8686
"""Test Cube adapter with multiple related cubes."""
8787
adapter = CubeAdapter()
88-
graph = adapter.parse(Path("examples/cube/ecommerce_multi_cube.yml"))
88+
graph = adapter.parse(Path("tests/fixtures/cube/ecommerce_multi_cube.yml"))
8989

9090
# Check all models were imported
9191
assert "orders" in graph.models
@@ -118,7 +118,7 @@ def test_cube_adapter_multi_cube():
118118
def test_cube_adapter_segments():
119119
"""Test Cube adapter segment parsing with SQL transformations."""
120120
adapter = CubeAdapter()
121-
graph = adapter.parse(Path("examples/cube/ecommerce_multi_cube.yml"))
121+
graph = adapter.parse(Path("tests/fixtures/cube/ecommerce_multi_cube.yml"))
122122

123123
# Test orders segments
124124
orders = graph.get_model("orders")
@@ -141,7 +141,7 @@ def test_cube_adapter_segments():
141141
def test_cube_adapter_drill_members():
142142
"""Test Cube adapter drill member parsing."""
143143
adapter = CubeAdapter()
144-
graph = adapter.parse(Path("examples/cube/ecommerce_multi_cube.yml"))
144+
graph = adapter.parse(Path("tests/fixtures/cube/ecommerce_multi_cube.yml"))
145145

146146
orders = graph.get_model("orders")
147147
count_metric = orders.get_metric("count")
@@ -157,7 +157,7 @@ def test_cube_adapter_drill_members():
157157
def test_cube_saas_analytics():
158158
"""Test Cube adapter with SaaS analytics example."""
159159
adapter = CubeAdapter()
160-
graph = adapter.parse(Path("examples/cube/saas_analytics.yml"))
160+
graph = adapter.parse(Path("tests/fixtures/cube/saas_analytics.yml"))
161161

162162
# Check all models were imported
163163
assert "users" in graph.models
@@ -236,7 +236,7 @@ def test_cube_saas_analytics():
236236
def test_cube_retail_analytics():
237237
"""Test Cube adapter with retail analytics example."""
238238
adapter = CubeAdapter()
239-
graph = adapter.parse(Path("examples/cube/retail_analytics.yml"))
239+
graph = adapter.parse(Path("tests/fixtures/cube/retail_analytics.yml"))
240240

241241
# Check all models were imported
242242
assert "products" in graph.models
@@ -306,7 +306,7 @@ def test_cube_retail_analytics():
306306
def test_cube_web_analytics():
307307
"""Test Cube adapter with web analytics example."""
308308
adapter = CubeAdapter()
309-
graph = adapter.parse(Path("examples/cube/web_analytics.yml"))
309+
graph = adapter.parse(Path("tests/fixtures/cube/web_analytics.yml"))
310310

311311
# Check all models were imported
312312
assert "page_views" in graph.models
@@ -381,7 +381,7 @@ def test_cube_web_analytics():
381381
def test_cube_financial_analytics():
382382
"""Test Cube adapter with financial analytics example."""
383383
adapter = CubeAdapter()
384-
graph = adapter.parse(Path("examples/cube/financial_analytics.yml"))
384+
graph = adapter.parse(Path("tests/fixtures/cube/financial_analytics.yml"))
385385

386386
# Check all models were imported
387387
assert "transactions" in graph.models
@@ -437,7 +437,7 @@ def test_cube_financial_analytics():
437437
def test_cube_logistics_shipping():
438438
"""Test Cube adapter with logistics/shipping example."""
439439
adapter = CubeAdapter()
440-
graph = adapter.parse(Path("examples/cube/logistics_shipping.yml"))
440+
graph = adapter.parse(Path("tests/fixtures/cube/logistics_shipping.yml"))
441441

442442
# Check all models were imported
443443
assert "shipments" in graph.models
@@ -499,7 +499,7 @@ def test_cube_logistics_shipping():
499499
def test_cube_iot_sensors():
500500
"""Test Cube adapter with IoT/sensor data example."""
501501
adapter = CubeAdapter()
502-
graph = adapter.parse(Path("examples/cube/iot_sensors.yml"))
502+
graph = adapter.parse(Path("tests/fixtures/cube/iot_sensors.yml"))
503503

504504
# Check all models were imported
505505
assert "sensor_readings" in graph.models
@@ -563,7 +563,7 @@ def test_cube_iot_sensors():
563563
def test_cube_healthcare_patients():
564564
"""Test Cube adapter with healthcare analytics example."""
565565
adapter = CubeAdapter()
566-
graph = adapter.parse(Path("examples/cube/healthcare_patients.yml"))
566+
graph = adapter.parse(Path("tests/fixtures/cube/healthcare_patients.yml"))
567567

568568
# Check all models were imported
569569
assert "patients" in graph.models

tests/adapters/test_cube_roundtrip.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
def test_import_real_cube_example():
1818
"""Test importing a real Cube.js schema file."""
1919
adapter = CubeAdapter()
20-
graph = adapter.parse("examples/cube/orders.yml")
20+
graph = adapter.parse("tests/fixtures/cube/orders.yml")
2121

2222
# Verify models loaded
2323
assert "orders" in graph.models
@@ -60,7 +60,7 @@ def test_cube_to_sidemantic_to_cube_roundtrip():
6060
"""Test that Cube -> Sidemantic -> Cube preserves structure."""
6161
# Import from Cube
6262
cube_adapter = CubeAdapter()
63-
graph = cube_adapter.parse("examples/cube/orders.yml")
63+
graph = cube_adapter.parse("tests/fixtures/cube/orders.yml")
6464

6565
# Export back to Cube
6666
with tempfile.NamedTemporaryFile(mode="w", suffix=".yml", delete=False) as f:
@@ -96,7 +96,7 @@ def test_cube_to_metricflow_conversion():
9696
"""Test converting Cube format to MetricFlow format."""
9797
# Import from Cube
9898
cube_adapter = CubeAdapter()
99-
graph = cube_adapter.parse("examples/cube/orders.yml")
99+
graph = cube_adapter.parse("tests/fixtures/cube/orders.yml")
100100

101101
# Export to MetricFlow
102102
mf_adapter = MetricFlowAdapter()
@@ -134,7 +134,7 @@ def test_query_imported_cube_example():
134134
from sidemantic import SemanticLayer
135135

136136
adapter = CubeAdapter()
137-
graph = adapter.parse("examples/cube/orders.yml")
137+
graph = adapter.parse("tests/fixtures/cube/orders.yml")
138138

139139
layer = SemanticLayer()
140140
layer.graph = graph
@@ -165,7 +165,7 @@ def test_query_with_time_dimension_cube():
165165
from sidemantic import SemanticLayer
166166

167167
adapter = CubeAdapter()
168-
graph = adapter.parse("examples/cube/orders.yml")
168+
graph = adapter.parse("tests/fixtures/cube/orders.yml")
169169

170170
layer = SemanticLayer()
171171
layer.graph = graph
@@ -181,7 +181,7 @@ def test_roundtrip_real_cube_example():
181181
adapter = CubeAdapter()
182182

183183
# Import original
184-
graph1 = adapter.parse("examples/cube/orders.yml")
184+
graph1 = adapter.parse("tests/fixtures/cube/orders.yml")
185185

186186
# Export
187187
with tempfile.NamedTemporaryFile(mode="w", suffix=".yml", delete=False) as f:
@@ -217,7 +217,7 @@ def test_cube_to_lookml_conversion():
217217
"""Test converting Cube format to LookML format."""
218218
# Import from Cube
219219
cube_adapter = CubeAdapter()
220-
graph = cube_adapter.parse("examples/cube/orders.yml")
220+
graph = cube_adapter.parse("tests/fixtures/cube/orders.yml")
221221

222222
# Export to LookML
223223
lookml_adapter = LookMLAdapter()
@@ -253,7 +253,7 @@ def test_cube_to_hex_conversion():
253253
"""Test converting Cube format to Hex format."""
254254
# Import from Cube
255255
cube_adapter = CubeAdapter()
256-
graph = cube_adapter.parse("examples/cube/orders.yml")
256+
graph = cube_adapter.parse("tests/fixtures/cube/orders.yml")
257257

258258
# Export to Hex
259259
hex_adapter = HexAdapter()
@@ -285,7 +285,7 @@ def test_cube_to_rill_conversion():
285285
"""Test converting Cube format to Rill format."""
286286
# Import from Cube
287287
cube_adapter = CubeAdapter()
288-
graph = cube_adapter.parse("examples/cube/orders.yml")
288+
graph = cube_adapter.parse("tests/fixtures/cube/orders.yml")
289289

290290
# Export to Rill
291291
rill_adapter = RillAdapter()
@@ -314,7 +314,7 @@ def test_cube_to_superset_conversion():
314314
superset_adapter = SupersetAdapter()
315315

316316
# Import from Cube
317-
graph = cube_adapter.parse("examples/cube/orders.yml")
317+
graph = cube_adapter.parse("tests/fixtures/cube/orders.yml")
318318

319319
# Export to Superset
320320
with tempfile.TemporaryDirectory() as tmpdir:
@@ -334,7 +334,7 @@ def test_cube_to_omni_conversion():
334334
omni_adapter = OmniAdapter()
335335

336336
# Import from Cube
337-
graph = cube_adapter.parse("examples/cube/orders.yml")
337+
graph = cube_adapter.parse("tests/fixtures/cube/orders.yml")
338338

339339
# Export to Omni
340340
with tempfile.TemporaryDirectory() as tmpdir:

0 commit comments

Comments
 (0)