Skip to content

Commit 604c74c

Browse files
feat(synthetic_data_generator)!: orders schema matches Snowflake RAW.ORDERS spec
BREAKING: rewrites _generate_orders to emit a column shape compatible with the canonical Snowflake RAW.ORDERS table definition used across the Snowflake demo set. Existing consumers that depend on the old shape will need to migrate (audited; only 2 consumers had real shape dependencies — both updated in the paired CLI-repo commit). Column-by-column changes: order_id str 'ORD' + 10-digit (was 8-digit) customer_id str 'CUST' + 6-digit (unchanged) order_date datetime datetime object — was string (strftime) Snowflake's write_pandas maps datetime → TIMESTAMP_NTZ cleanly without any string-parsing fallback category str lowercase 8-value enum (was Title Case 7-value): electronics, clothing, books, home, sports, toys, beauty, food (was: Electronics, Clothing, Home & Garden, Sports, Books, Toys, Food) num_items int 1..10 (was 1..5) subtotal float uniform 10..1000 (was sum-of-N-uniforms shape) shipping float uniform 0..30 (was discrete [0, 5.99, 9.99, 14.99]) tax float unchanged formula (subtotal * 0.08) total float unchanged formula (subtotal + shipping + tax) status str 5-value enum (was 4 — adds "paid"): pending, paid, shipped, delivered, cancelled region str NEW — NA, EU, APAC, LATAM + date fallback: last 30 days with hours/minutes/seconds randomness (was last 365 days, date-only randomness) Why: the existing implementation was emitting a string timestamp and Title-Case categories that didn't match either the Snowflake demo's TIMESTAMP_NTZ column type or the canonical lowercase category values used downstream. The missing region column blocked any region-based demo aggregation. The 1..5 num_items range was unrealistic for an e-commerce shape. Audit of 33 downstream demos using schema_type: orders found two real breakage sites (fixed in the companion CLI-repo commit): - setup_data_quality_checks_demo.sh: Pandera isin([...]) check on Title-Case categories + 4-value status enum + missing region - setup_azure_synapse_serverless_demo.sh: stale comment listing Title-Case categories Other 31 consumers pass orders through to writers / notebooks without referencing the column shape — no migration needed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b66ce5c commit 604c74c

3 files changed

Lines changed: 51 additions & 22 deletions

File tree

assets/ai/synthetic_data_generator/component.py

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -574,47 +574,76 @@ def _generate_customers(n: int, target_date: Optional[datetime] = None) -> pd.Da
574574

575575

576576
def _generate_orders(n: int, target_date: Optional[datetime] = None) -> pd.DataFrame:
577-
"""Generate order data.
577+
"""Generate synthetic e-commerce orders matching the Snowflake RAW.ORDERS schema.
578+
579+
Column shape (lowercase here; ``dataframe_to_snowflake`` / ``write_pandas``
580+
upper-cases on write, so this is compatible with::
581+
582+
CREATE TABLE RAW.ORDERS (
583+
ORDER_ID VARCHAR, -- 'ORD' + 10-digit zero-padded
584+
CUSTOMER_ID VARCHAR, -- 'CUST' + 6-digit zero-padded
585+
ORDER_DATE TIMESTAMP_NTZ,
586+
CATEGORY VARCHAR, -- lowercase
587+
NUM_ITEMS NUMBER,
588+
SUBTOTAL NUMBER(18,2),
589+
SHIPPING NUMBER(10,2),
590+
TAX NUMBER(10,2),
591+
TOTAL NUMBER(18,2),
592+
STATUS VARCHAR,
593+
REGION VARCHAR
594+
)
578595
579596
Args:
580-
n: Number of orders to generate
581-
target_date: If provided, all orders will be placed on this date
597+
n: Number of orders to generate.
598+
target_date: If provided, every order's ``order_date`` falls within that
599+
day (uniform random hours/minutes/seconds). Without it, dates are
600+
sampled uniformly over the last 30 days.
601+
602+
``order_date`` is emitted as a ``datetime`` object (not a formatted
603+
string) so Snowflake's ``write_pandas`` maps it to TIMESTAMP_NTZ
604+
cleanly without any string-parsing fallback.
582605
"""
583-
product_categories = ["Electronics", "Clothing", "Home & Garden", "Sports", "Books", "Toys", "Food"]
584-
statuses = ["pending", "shipped", "delivered", "cancelled"]
606+
categories = ["electronics", "clothing", "books", "home", "sports", "toys", "beauty", "food"]
607+
statuses = ["pending", "paid", "shipped", "delivered", "cancelled"]
608+
regions = ["NA", "EU", "APAC", "LATAM"]
585609

586610
data = []
587611
for i in range(n):
588-
order_id = f"ORD{i+1:08d}"
612+
order_id = f"ORD{i+1:010d}"
589613
customer_id = f"CUST{random.randint(1, 1000):06d}"
590614

591-
# Use target_date if partitioned, otherwise random date
592615
if target_date:
593-
# Add random hours/minutes within the day
594616
order_date = target_date + timedelta(
595617
hours=random.randint(0, 23),
596618
minutes=random.randint(0, 59),
597-
seconds=random.randint(0, 59)
619+
seconds=random.randint(0, 59),
598620
)
599621
else:
600-
order_date = datetime.now() - timedelta(days=random.randint(0, 365))
622+
order_date = datetime.now() - timedelta(
623+
days=random.randint(0, 30),
624+
hours=random.randint(0, 23),
625+
minutes=random.randint(0, 59),
626+
seconds=random.randint(0, 59),
627+
)
601628

602-
num_items = random.randint(1, 5)
603-
item_total = sum(random.uniform(10, 200) for _ in range(num_items))
604-
shipping = random.choice([0, 5.99, 9.99, 14.99])
605-
tax = item_total * 0.08
629+
num_items = random.randint(1, 10)
630+
subtotal = round(random.uniform(10.0, 1000.0), 2)
631+
shipping = round(random.uniform(0.0, 30.0), 2)
632+
tax = round(subtotal * 0.08, 2)
633+
total = round(subtotal + shipping + tax, 2)
606634

607635
data.append({
608636
"order_id": order_id,
609637
"customer_id": customer_id,
610-
"order_date": order_date.strftime("%Y-%m-%d %H:%M:%S"),
611-
"category": random.choice(product_categories),
638+
"order_date": order_date, # datetime object → TIMESTAMP_NTZ
639+
"category": random.choice(categories),
612640
"num_items": num_items,
613-
"subtotal": round(item_total, 2),
641+
"subtotal": subtotal,
614642
"shipping": shipping,
615-
"tax": round(tax, 2),
616-
"total": round(item_total + shipping + tax, 2),
617-
"status": random.choice(statuses)
643+
"tax": tax,
644+
"total": total,
645+
"status": random.choice(statuses),
646+
"region": random.choice(regions),
618647
})
619648

620649
return pd.DataFrame(data)

dagster_community_components/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import importlib.util
2222
from pathlib import Path
2323

24-
__version__ = "0.6.6"
24+
__version__ = "0.7.0"
2525

2626
_PACKAGE_ROOT = Path(__file__).resolve().parent
2727

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "dagster-community-components"
7-
version = "0.6.6"
7+
version = "0.7.0"
88
description = "Community-maintained Dagster components — ingestion, transforms, IO managers, sensors, sinks, resources, and more."
99
readme = "README.md"
1010
requires-python = ">=3.10"

0 commit comments

Comments
 (0)