Skip to content

Commit 4993124

Browse files
brownjuly2003-codeJuliaEdomclaude
authored
feat(demo): repin serving demo store to the kitchen-appliance legend (B3) (#140)
Replaces the four demo tables' grocery/electronics-era placeholder values (Wireless Headphones, Running Shoes, USD amounts) with RUB-denominated kitchen-appliance data matching docs/generator-spec.md §9-10: 10 products (one per SKU category, one out-of-stock bestseller for the oversell story), 8 orders (5 marketplace 1.5-3k RUB + 1 D2C ~4k + 2 wholesale ~48k/~76k RUB), 5 users (2 dealer + 3 retail), 6 sessions remapped to the retail-only D2C telemetry story. Mirrored identically across duckdb_backend.py and clickhouse_backend.py so both serving backends tell the same demo story. Propagates RUB as the revenue/avg_order_value metric unit (catalog.py + regenerated config/contracts/metric.*.yaml, since both metrics sum/average orders_v2.total_amount), updates the order.yaml currency description, and fixes search-index snippet text and the handful of tests/docs that asserted the old literal names, prices, and unit strings. Co-authored-by: JuliaEdom <uedomskikh@gmail.com> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
1 parent a2572cd commit 4993124

16 files changed

Lines changed: 89 additions & 89 deletions

‎config/contracts/metric.avg_order_value.v1.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ fields:
66
- name: value
77
type: float
88
required: true
9-
unit: USD
9+
unit: RUB
1010
- name: unit
1111
type: string
1212
required: true

‎config/contracts/metric.revenue.v1.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ fields:
66
- name: value
77
type: float
88
required: true
9-
unit: USD
9+
unit: RUB
1010
- name: unit
1111
type: string
1212
required: true

‎contracts/entities/order.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ fields:
77
user_id: Customer identifier
88
status: "Current status: pending, confirmed, shipped, delivered, cancelled"
99
total_amount: Order total in the order currency
10-
currency: ISO 4217 alpha-3 currency code (USD, EUR, GBP)
10+
currency: ISO 4217 alpha-3 currency code (RUB primary; AED/KZT on branch-currency reads)
1111
created_at: Order creation timestamp
1212
relationships:
1313
user: user_id

‎docs/api-reference.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ console.log(Object.keys(catalog.entities));
233233
**Response 200**
234234

235235
```json
236-
{"entities":{"order":{"primary_key":"order_id"}},"metrics":{"revenue":{"unit":"USD"}}}
236+
{"entities":{"order":{"primary_key":"order_id"}},"metrics":{"revenue":{"unit":"RUB"}}}
237237
```
238238

239239
**Errors:** `401`, `429`.
@@ -313,7 +313,7 @@ console.log(metric.value, metric.unit);
313313
**Response 200**
314314

315315
```json
316-
{"metric_name":"revenue","value":984.91,"unit":"USD","window":"1h","components":null}
316+
{"metric_name":"revenue","value":12750.00,"unit":"RUB","window":"1h","components":null}
317317
```
318318

319319
**Errors:** `401`, `404`, `422` for invalid historical anchor, `503` if metric computation cannot run.

‎scripts/generate_contracts.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ class ContractSpec:
148148
),
149149
field_overrides={
150150
"value": {
151-
"unit": "USD",
151+
"unit": "RUB",
152152
},
153153
},
154154
source_events=ORDER_SOURCE_EVENTS,
@@ -192,7 +192,7 @@ class ContractSpec:
192192
),
193193
field_overrides={
194194
"value": {
195-
"unit": "USD",
195+
"unit": "RUB",
196196
},
197197
},
198198
source_events=ORDER_SOURCE_EVENTS,

‎src/serving/backends/clickhouse_backend.py‎

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -451,16 +451,16 @@ def ts(delta: timedelta) -> str:
451451
[
452452
# demo seed data uses trusted config and generated timestamps
453453
f"INSERT INTO {self._database}.products_current VALUES", # nosec B608
454-
"('PROD-001', 'Wireless Headphones', 'electronics', 79.99, 1, 142),",
455-
"('PROD-002', 'Running Shoes', 'footwear', 129.99, 1, 58),",
456-
"('PROD-003', 'Coffee Maker', 'kitchen', 49.99, 1, 203),",
457-
"('PROD-004', 'Mechanical Keyboard', 'electronics', 149.99, 1, 37),",
458-
"('PROD-005', 'Yoga Mat', 'fitness', 34.99, 1, 315),",
459-
"('PROD-006', 'Backpack', 'accessories', 89.99, 1, 94),",
460-
"('PROD-007', 'Water Bottle', 'fitness', 24.99, 1, 421),",
461-
"('PROD-008', 'Desk Lamp', 'home', 44.99, 0, 0),",
462-
"('PROD-009', 'Bluetooth Speaker', 'electronics', 59.99, 1, 167),",
463-
"('PROD-010', 'Sunglasses', 'accessories', 119.99, 1, 72)",
454+
"('PROD-001', 'Electric Kettle 1.7L 2200W', 'kettles', 2190.00, 0, 0),",
455+
"('PROD-002', 'Air Fryer Grill 5.5L', 'grills', 5490.00, 1, 58),",
456+
"('PROD-003', 'Immersion Blender Set 800W', 'blenders', 2490.00, 1, 203),",
457+
"('PROD-004', 'Stand Mixer 5L Planetary', 'mixers', 6990.00, 1, 37),",
458+
"('PROD-005', 'Drip Coffee Maker 1.2L', 'coffee', 3490.00, 1, 94),",
459+
"('PROD-006', 'Waffle Maker Double', 'multibakers', 2290.00, 1, 142),",
460+
"('PROD-007', 'Mini Chopper 500ml', 'choppers', 1490.00, 1, 315),",
461+
"('PROD-008', 'Cold-Press Juicer', 'juicers', 4490.00, 1, 72),",
462+
"('PROD-009', 'Digital Kitchen Scale 5kg', 'scales', 990.00, 1, 421),",
463+
"('PROD-010', 'Vacuum Sealer Compact', 'vacuum-dry', 3290.00, 1, 167)",
464464
]
465465
),
466466
expect_json=False,
@@ -471,14 +471,14 @@ def ts(delta: timedelta) -> str:
471471
[
472472
# demo seed data uses trusted config and generated timestamps
473473
f"INSERT INTO {self._database}.orders_v2 VALUES", # nosec B608
474-
f"('ORD-20260404-1001', 'USR-10001', 'delivered', 159.98, 'USD', '{ts(timedelta(hours=2))}'),",
475-
f"('ORD-20260404-1002', 'USR-10002', 'shipped', 129.99, 'USD', '{ts(timedelta(minutes=90))}'),",
476-
f"('ORD-20260404-1003', 'USR-10001', 'confirmed', 249.97, 'USD', '{ts(timedelta(hours=1))}'),",
477-
f"('ORD-20260404-1004', 'USR-10003', 'pending', 79.99, 'USD', '{ts(timedelta(minutes=45))}'),",
478-
f"('ORD-20260404-1005', 'USR-10004', 'delivered', 89.99, 'USD', '{ts(timedelta(minutes=30))}'),",
479-
f"('ORD-20260404-1006', 'USR-10002', 'cancelled', 34.99, 'USD', '{ts(timedelta(minutes=20))}'),",
480-
f"('ORD-20260404-1007', 'USR-10005', 'confirmed', 179.98, 'USD', '{ts(timedelta(minutes=15))}'),",
481-
f"('ORD-20260404-1008', 'USR-10003', 'pending', 59.99, 'USD', '{ts(timedelta(minutes=5))}')",
474+
f"('ORD-20260404-1001', 'USR-10001', 'delivered', 76400.00, 'RUB', '{ts(timedelta(hours=2))}'),",
475+
f"('ORD-20260404-1002', 'USR-10002', 'shipped', 48100.00, 'RUB', '{ts(timedelta(minutes=90))}'),",
476+
f"('ORD-20260404-1003', 'USR-10003', 'confirmed', 2650.00, 'RUB', '{ts(timedelta(hours=1))}'),",
477+
f"('ORD-20260404-1004', 'USR-10003', 'pending', 1890.00, 'RUB', '{ts(timedelta(minutes=45))}'),",
478+
f"('ORD-20260404-1005', 'USR-10004', 'delivered', 2290.00, 'RUB', '{ts(timedelta(minutes=30))}'),",
479+
f"('ORD-20260404-1006', 'USR-10004', 'cancelled', 1590.00, 'RUB', '{ts(timedelta(minutes=20))}'),",
480+
f"('ORD-20260404-1007', 'USR-10005', 'confirmed', 2990.00, 'RUB', '{ts(timedelta(minutes=15))}'),",
481+
f"('ORD-20260404-1008', 'USR-10005', 'pending', 3990.00, 'RUB', '{ts(timedelta(minutes=5))}')",
482482
]
483483
),
484484
expect_json=False,
@@ -489,11 +489,11 @@ def ts(delta: timedelta) -> str:
489489
[
490490
# demo seed data uses trusted config and generated timestamps
491491
f"INSERT INTO {self._database}.users_enriched VALUES", # nosec B608
492-
f"('USR-10001', 15, 2340.50, '{ts(timedelta(days=180))}', '{ts(timedelta(hours=1))}', 'electronics'),",
493-
f"('USR-10002', 8, 890.20, '{ts(timedelta(days=90))}', '{ts(timedelta(minutes=20))}', 'footwear'),",
494-
f"('USR-10003', 3, 210.00, '{ts(timedelta(days=30))}', '{ts(timedelta(minutes=5))}', 'electronics'),",
495-
f"('USR-10004', 22, 4100.75, '{ts(timedelta(days=365))}', '{ts(timedelta(minutes=30))}', 'accessories'),",
496-
f"('USR-10005', 1, 179.98, '{ts(timedelta(days=1))}', '{ts(timedelta(minutes=15))}', 'electronics')",
492+
f"('USR-10001', 34, 1200000.00, '{ts(timedelta(days=365))}', '{ts(timedelta(hours=2))}', 'grills'),",
493+
f"('USR-10002', 15, 460000.00, '{ts(timedelta(days=270))}', '{ts(timedelta(minutes=90))}', 'coffee'),",
494+
f"('USR-10003', 4, 8900.00, '{ts(timedelta(days=60))}', '{ts(timedelta(minutes=45))}', 'choppers'),",
495+
f"('USR-10004', 6, 15800.00, '{ts(timedelta(days=120))}', '{ts(timedelta(minutes=20))}', 'blenders'),",
496+
f"('USR-10005', 3, 28500.00, '{ts(timedelta(days=10))}', '{ts(timedelta(minutes=5))}', 'vacuum-dry')",
497497
]
498498
),
499499
expect_json=False,
@@ -504,8 +504,8 @@ def ts(delta: timedelta) -> str:
504504
[
505505
# demo seed data uses trusted config and generated timestamps
506506
f"INSERT INTO {self._database}.sessions_aggregated VALUES", # nosec B608
507-
f"('SES-a1b2c3', 'USR-10001', '{ts(timedelta(hours=2))}', '{ts(timedelta(minutes=100))}', 1200, 14, 6, 'checkout', 1),",
508-
f"('SES-d4e5f6', 'USR-10002', '{ts(timedelta(minutes=90))}', '{ts(timedelta(minutes=70))}', 1200, 8, 4, 'add_to_cart', 0),",
507+
f"('SES-a1b2c3', 'USR-10005', '{ts(timedelta(hours=2))}', '{ts(timedelta(minutes=100))}', 1200, 14, 6, 'checkout', 1),",
508+
f"('SES-d4e5f6', 'USR-10004', '{ts(timedelta(minutes=90))}', '{ts(timedelta(minutes=70))}', 1200, 8, 4, 'add_to_cart', 0),",
509509
f"('SES-g7h8i9', NULL, '{ts(timedelta(minutes=60))}', '{ts(timedelta(minutes=58))}', 120, 2, 2, 'bounce', 0),",
510510
f"('SES-j1k2l3', 'USR-10003', '{ts(timedelta(minutes=45))}', '{ts(timedelta(minutes=20))}', 1500, 11, 5, 'checkout', 1),",
511511
f"('SES-m4n5o6', 'USR-10004', '{ts(timedelta(minutes=30))}', '{ts(timedelta(minutes=15))}', 900, 6, 3, 'product_view', 0),",

‎src/serving/backends/duckdb_backend.py‎

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ def initialize_demo_data(self) -> None:
126126
user_id VARCHAR,
127127
status VARCHAR,
128128
total_amount DECIMAL(10,2),
129-
currency VARCHAR DEFAULT 'USD',
129+
currency VARCHAR DEFAULT 'RUB',
130130
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
131131
)
132132
""")
@@ -192,59 +192,59 @@ def initialize_demo_data(self) -> None:
192192

193193
self._conn.execute("""
194194
INSERT INTO products_current VALUES
195-
('PROD-001', 'Wireless Headphones', 'electronics', 79.99, TRUE, 142),
196-
('PROD-002', 'Running Shoes', 'footwear', 129.99, TRUE, 58),
197-
('PROD-003', 'Coffee Maker', 'kitchen', 49.99, TRUE, 203),
198-
('PROD-004', 'Mechanical Keyboard', 'electronics', 149.99, TRUE, 37),
199-
('PROD-005', 'Yoga Mat', 'fitness', 34.99, TRUE, 315),
200-
('PROD-006', 'Backpack', 'accessories', 89.99, TRUE, 94),
201-
('PROD-007', 'Water Bottle', 'fitness', 24.99, TRUE, 421),
202-
('PROD-008', 'Desk Lamp', 'home', 44.99, FALSE, 0),
203-
('PROD-009', 'Bluetooth Speaker', 'electronics', 59.99, TRUE, 167),
204-
('PROD-010', 'Sunglasses', 'accessories', 119.99, TRUE, 72)
195+
('PROD-001', 'Electric Kettle 1.7L 2200W', 'kettles', 2190.00, FALSE, 0),
196+
('PROD-002', 'Air Fryer Grill 5.5L', 'grills', 5490.00, TRUE, 58),
197+
('PROD-003', 'Immersion Blender Set 800W', 'blenders', 2490.00, TRUE, 203),
198+
('PROD-004', 'Stand Mixer 5L Planetary', 'mixers', 6990.00, TRUE, 37),
199+
('PROD-005', 'Drip Coffee Maker 1.2L', 'coffee', 3490.00, TRUE, 94),
200+
('PROD-006', 'Waffle Maker Double', 'multibakers', 2290.00, TRUE, 142),
201+
('PROD-007', 'Mini Chopper 500ml', 'choppers', 1490.00, TRUE, 315),
202+
('PROD-008', 'Cold-Press Juicer', 'juicers', 4490.00, TRUE, 72),
203+
('PROD-009', 'Digital Kitchen Scale 5kg', 'scales', 990.00, TRUE, 421),
204+
('PROD-010', 'Vacuum Sealer Compact', 'vacuum-dry', 3290.00, TRUE, 167)
205205
""")
206206

207207
self._conn.execute("""
208208
INSERT INTO orders_v2 VALUES
209209
('ORD-20260404-1001', 'USR-10001', 'delivered',
210-
159.98, 'USD', NOW() - INTERVAL '2 hours'),
210+
76400.00, 'RUB', NOW() - INTERVAL '2 hours'),
211211
('ORD-20260404-1002', 'USR-10002', 'shipped',
212-
129.99, 'USD', NOW() - INTERVAL '90 minutes'),
213-
('ORD-20260404-1003', 'USR-10001', 'confirmed',
214-
249.97, 'USD', NOW() - INTERVAL '1 hour'),
212+
48100.00, 'RUB', NOW() - INTERVAL '90 minutes'),
213+
('ORD-20260404-1003', 'USR-10003', 'confirmed',
214+
2650.00, 'RUB', NOW() - INTERVAL '1 hour'),
215215
('ORD-20260404-1004', 'USR-10003', 'pending',
216-
79.99, 'USD', NOW() - INTERVAL '45 minutes'),
216+
1890.00, 'RUB', NOW() - INTERVAL '45 minutes'),
217217
('ORD-20260404-1005', 'USR-10004', 'delivered',
218-
89.99, 'USD', NOW() - INTERVAL '30 minutes'),
219-
('ORD-20260404-1006', 'USR-10002', 'cancelled',
220-
34.99, 'USD', NOW() - INTERVAL '20 minutes'),
218+
2290.00, 'RUB', NOW() - INTERVAL '30 minutes'),
219+
('ORD-20260404-1006', 'USR-10004', 'cancelled',
220+
1590.00, 'RUB', NOW() - INTERVAL '20 minutes'),
221221
('ORD-20260404-1007', 'USR-10005', 'confirmed',
222-
179.98, 'USD', NOW() - INTERVAL '15 minutes'),
223-
('ORD-20260404-1008', 'USR-10003', 'pending',
224-
59.99, 'USD', NOW() - INTERVAL '5 minutes')
222+
2990.00, 'RUB', NOW() - INTERVAL '15 minutes'),
223+
('ORD-20260404-1008', 'USR-10005', 'pending',
224+
3990.00, 'RUB', NOW() - INTERVAL '5 minutes')
225225
""")
226226

227227
self._conn.execute("""
228228
INSERT INTO users_enriched VALUES
229-
('USR-10001', 15, 2340.50, NOW() - INTERVAL '180 days',
230-
NOW() - INTERVAL '1 hour', 'electronics'),
231-
('USR-10002', 8, 890.20, NOW() - INTERVAL '90 days',
232-
NOW() - INTERVAL '20 minutes', 'footwear'),
233-
('USR-10003', 3, 210.00, NOW() - INTERVAL '30 days',
234-
NOW() - INTERVAL '5 minutes', 'electronics'),
235-
('USR-10004', 22, 4100.75, NOW() - INTERVAL '365 days',
236-
NOW() - INTERVAL '30 minutes', 'accessories'),
237-
('USR-10005', 1, 179.98, NOW() - INTERVAL '1 day',
238-
NOW() - INTERVAL '15 minutes', 'electronics')
229+
('USR-10001', 34, 1200000.00, NOW() - INTERVAL '365 days',
230+
NOW() - INTERVAL '2 hours', 'grills'),
231+
('USR-10002', 15, 460000.00, NOW() - INTERVAL '270 days',
232+
NOW() - INTERVAL '90 minutes', 'coffee'),
233+
('USR-10003', 4, 8900.00, NOW() - INTERVAL '60 days',
234+
NOW() - INTERVAL '45 minutes', 'choppers'),
235+
('USR-10004', 6, 15800.00, NOW() - INTERVAL '120 days',
236+
NOW() - INTERVAL '20 minutes', 'blenders'),
237+
('USR-10005', 3, 28500.00, NOW() - INTERVAL '10 days',
238+
NOW() - INTERVAL '5 minutes', 'vacuum-dry')
239239
""")
240240

241241
self._conn.execute("""
242242
INSERT INTO sessions_aggregated VALUES
243-
('SES-a1b2c3', 'USR-10001',
243+
('SES-a1b2c3', 'USR-10005',
244244
NOW() - INTERVAL '2 hours',
245245
NOW() - INTERVAL '100 minutes',
246246
1200, 14, 6, 'checkout', TRUE),
247-
('SES-d4e5f6', 'USR-10002',
247+
('SES-d4e5f6', 'USR-10004',
248248
NOW() - INTERVAL '90 minutes',
249249
NOW() - INTERVAL '70 minutes',
250250
1200, 8, 4, 'add_to_cart', FALSE),

‎src/serving/semantic_layer/catalog.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def _register_defaults(self) -> None:
6363
"WHERE status != 'cancelled' "
6464
"AND created_at >= NOW() - INTERVAL '{window}'"
6565
),
66-
unit="USD",
66+
unit="RUB",
6767
contract_version=self.contract_registry.latest_contract_version("metric.revenue"),
6868
source_events=["order.created", "order.updated", "order.cancelled"],
6969
source_table="orders_v2",
@@ -103,7 +103,7 @@ def _register_defaults(self) -> None:
103103
"WHERE status != 'cancelled' "
104104
"AND created_at >= NOW() - INTERVAL '{window}'"
105105
),
106-
unit="USD",
106+
unit="RUB",
107107
contract_version=self.contract_registry.latest_contract_version(
108108
"metric.avg_order_value"
109109
),

‎src/serving/semantic_layer/search_index.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,13 +267,13 @@ def _entity_snippet(self, entity: EntityDefinition, payload: dict) -> str:
267267
stock_status = "in stock" if payload.get("in_stock") else "out of stock"
268268
return (
269269
f"Product {payload.get('name')} ({payload.get('product_id')}) "
270-
f"category {payload.get('category')} price {payload.get('price')} USD "
270+
f"category {payload.get('category')} price {payload.get('price')} RUB "
271271
f"{stock_status}"
272272
)
273273
if entity.name == "user":
274274
return (
275275
f"User {payload.get('user_id')} with {payload.get('total_orders')} orders "
276-
f"spent {payload.get('total_spent')} USD "
276+
f"spent {payload.get('total_spent')} RUB "
277277
f"prefers {payload.get('preferred_category')}"
278278
)
279279
if entity.name == "session":

‎tests/integration/test_batch.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def test_batch_returns_results_for_mixed_request_types(client):
102102
assert "_last_updated" not in entity_result["data"]
103103

104104
assert metric_result["status"] == "ok"
105-
assert metric_result["data"]["unit"] == "USD"
105+
assert metric_result["data"]["unit"] == "RUB"
106106
assert metric_result["data"]["value"] >= 0
107107

108108
assert query_result["status"] == "ok"

0 commit comments

Comments
 (0)