Skip to content

Commit b8f962e

Browse files
Merge pull request #76 from Ritik574-coder/dbt_branch
Build end-to-end dbt warehouse with cleaned staging, marts, tests, an…
2 parents d6bfa79 + bb9c747 commit b8f962e

34 files changed

Lines changed: 2593 additions & 12 deletions

script/silver/dbt_project/dbt_project.yml

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,39 @@ version: '1.0.0'
99
profile: 'dbt_project'
1010

1111
# These configurations specify where dbt should look for different types of files.
12-
# The `model-paths` config, for example, states that models in this project can be
13-
# found in the "models/" directory. You probably won't need to change these!
1412
model-paths: ["models"]
1513
analysis-paths: ["analyses"]
1614
test-paths: ["tests"]
1715
seed-paths: ["seeds"]
1816
macro-paths: ["macros"]
1917
snapshot-paths: ["snapshots"]
2018

21-
clean-targets: # directories to be removed by `dbt clean`
19+
clean-targets:
2220
- "target"
2321
- "dbt_packages"
2422

25-
26-
# Configuring models
27-
# Full documentation: https://docs.getdbt.com/docs/configuring-models
28-
29-
# In this example config, we tell dbt to build all models in the example/
30-
# directory as views. These settings can be overridden in the individual model
31-
# files using the `{{ config(...) }}` macro.
23+
# Configuring models with Medallion Architecture layers
3224
models:
3325
dbt_project:
34-
silver:
35-
+materialized: table
26+
27+
# ─── Staging Layer (Bronze → Silver Raw Cleaning) ───
28+
staging:
29+
+materialized: view
30+
+schema: silver
31+
32+
# ─── Intermediate Layer (Business Transformations) ───
33+
intermediate:
34+
+materialized: view
35+
+schema: silver
36+
37+
# ─── Marts Layer (Gold Layer - Business Ready) ───
38+
marts:
39+
dimensions:
40+
+materialized: table
41+
+schema: gold
42+
facts:
43+
+materialized: table
44+
+schema: gold
45+
reporting:
46+
+materialized: view
47+
+schema: gold
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
-- Intermediate Model: int_customers_standardized
2+
-- Purpose: Further refine customer data with business-specific transformations
3+
4+
WITH staging AS (
5+
SELECT * FROM {{ ref('src_customers') }}
6+
)
7+
8+
SELECT
9+
customer_id,
10+
title,
11+
first_name,
12+
last_name,
13+
CONCAT(first_name, ' ', last_name) AS full_name,
14+
gender,
15+
date_of_birth,
16+
age,
17+
18+
CASE
19+
WHEN age BETWEEN 0 AND 17 THEN 'Under 18'
20+
WHEN age BETWEEN 18 AND 24 THEN '18-24'
21+
WHEN age BETWEEN 25 AND 34 THEN '25-34'
22+
WHEN age BETWEEN 35 AND 44 THEN '35-44'
23+
WHEN age BETWEEN 45 AND 54 THEN '45-54'
24+
WHEN age BETWEEN 55 AND 64 THEN '55-64'
25+
WHEN age >= 65 THEN '65+'
26+
ELSE 'Unknown'
27+
END AS age_group,
28+
29+
email,
30+
phone,
31+
address,
32+
city,
33+
state_abbr,
34+
state_full,
35+
zip_code,
36+
country,
37+
region,
38+
customer_segment,
39+
loyalty_points,
40+
is_active,
41+
account_created_date,
42+
preferred_channel,
43+
annual_income_usd,
44+
company,
45+
GETDATE() AS _loaded_at
46+
FROM staging
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-- Intermediate Model: int_employees_standardized
2+
3+
WITH staging AS (
4+
SELECT * FROM {{ ref('src_employees') }}
5+
)
6+
7+
SELECT
8+
employee_id,
9+
first_name,
10+
last_name,
11+
CONCAT(first_name, ' ', last_name) AS full_name,
12+
email,
13+
phone,
14+
job_title,
15+
department,
16+
store_id,
17+
store_name,
18+
store_city,
19+
hire_date,
20+
years_employed,
21+
annual_salary_usd,
22+
commission_rate_pct,
23+
is_active,
24+
performance_rating,
25+
manager_id,
26+
DATEDIFF(YEAR, hire_date, GETDATE()) AS calculated_tenure_years,
27+
GETDATE() AS _loaded_at
28+
FROM staging
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
-- Intermediate Model: int_inventory_summary
2+
3+
WITH staging AS (
4+
SELECT * FROM {{ ref('src_inventory') }}
5+
)
6+
7+
SELECT
8+
snapshot_date,
9+
product_id,
10+
product_name,
11+
sku,
12+
category,
13+
stock_on_hand,
14+
stock_reserved,
15+
stock_available,
16+
reorder_level,
17+
unit_cost,
18+
unit_price,
19+
inventory_value,
20+
CASE WHEN stock_available <= reorder_level THEN 'Yes' ELSE 'No' END AS needs_reorder,
21+
warehouse_location,
22+
store_id,
23+
GETDATE() AS _loaded_at
24+
FROM staging
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
-- Intermediate Model: int_products_standardized
2+
3+
WITH staging AS (
4+
SELECT * FROM {{ ref('src_products') }}
5+
)
6+
7+
SELECT
8+
product_id,
9+
sku,
10+
product_name,
11+
brand,
12+
category,
13+
sub_category,
14+
department,
15+
base_price_usd,
16+
cost_price_usd,
17+
gross_margin_pct,
18+
CASE WHEN base_price_usd > 0 THEN (base_price_usd - cost_price_usd) / base_price_usd * 100 ELSE NULL END AS calculated_margin_pct,
19+
weight_kg,
20+
is_available,
21+
stock_quantity,
22+
reorder_level,
23+
CASE WHEN stock_quantity <= reorder_level THEN 'Yes' ELSE 'No' END AS low_stock_flag,
24+
supplier_name,
25+
supplier_country,
26+
warranty_years,
27+
rating_avg,
28+
review_count,
29+
CASE
30+
WHEN rating_avg >= 4.5 THEN 'Excellent'
31+
WHEN rating_avg >= 3.5 THEN 'Good'
32+
WHEN rating_avg >= 2.5 THEN 'Average'
33+
WHEN rating_avg >= 1.0 THEN 'Poor'
34+
ELSE 'Not Rated'
35+
END AS rating_category,
36+
launched_date,
37+
product_url,
38+
GETDATE() AS _loaded_at
39+
FROM staging
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-- Intermediate Model: int_returns_summary
2+
3+
WITH staging AS (
4+
SELECT * FROM {{ ref('src_returns') }}
5+
)
6+
7+
SELECT
8+
return_id,
9+
original_txn_id,
10+
original_order_id,
11+
customer_id,
12+
customer_name,
13+
product_id,
14+
product_name,
15+
quantity_returned,
16+
return_date,
17+
return_reason,
18+
refund_amount,
19+
refund_method,
20+
return_channel,
21+
restocked,
22+
return_status,
23+
handled_by_emp_id,
24+
notes,
25+
DATEPART(YEAR, return_date) AS return_year,
26+
DATEPART(MONTH, return_date) AS return_month,
27+
GETDATE() AS _loaded_at
28+
FROM staging
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
-- Intermediate Model: int_reviews_summary
2+
3+
WITH staging AS (
4+
SELECT * FROM {{ ref('src_reviews') }}
5+
)
6+
7+
SELECT
8+
review_id,
9+
txn_id,
10+
customer_id,
11+
customer_name,
12+
product_id,
13+
product_name,
14+
rating,
15+
rating_text,
16+
review_date,
17+
verified_purchase,
18+
helpful_votes,
19+
review_channel,
20+
review_title,
21+
DATEPART(YEAR, review_date) AS review_year,
22+
DATEPART(MONTH, review_date) AS review_month,
23+
GETDATE() AS _loaded_at
24+
FROM staging
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
-- Intermediate Model: int_stores_standardized
2+
3+
WITH staging AS (
4+
SELECT * FROM {{ ref('src_stores') }}
5+
)
6+
7+
SELECT
8+
store_id,
9+
store_name,
10+
store_type,
11+
address,
12+
city,
13+
state,
14+
state_full,
15+
CONCAT(city, ', ', state) AS store_location,
16+
zip_code,
17+
country,
18+
region,
19+
district,
20+
phone,
21+
manager_name,
22+
opened_date,
23+
DATEDIFF(YEAR, opened_date, GETDATE()) AS years_operating,
24+
sq_footage,
25+
num_employees,
26+
annual_rent_usd,
27+
CASE WHEN sq_footage > 0 THEN TRY_CAST(num_employees AS DECIMAL(10,2)) / sq_footage * 1000 ELSE NULL END AS employees_per_1000_sqft,
28+
is_active,
29+
has_parking,
30+
has_cafe,
31+
GETDATE() AS _loaded_at
32+
FROM staging
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
-- Intermediate Model: int_transactions_enriched
2+
3+
WITH staging AS (
4+
5+
SELECT *
6+
FROM {{ ref('src_transactions') }}
7+
8+
)
9+
10+
SELECT
11+
12+
transaction_id,
13+
order_id,
14+
order_line_number,
15+
16+
order_date,
17+
order_year,
18+
order_month,
19+
order_month_name,
20+
order_quarter,
21+
order_day_of_week,
22+
23+
customer_id,
24+
product_id,
25+
26+
-- Sales Metrics
27+
TRY_CAST(quantity_ordered AS INT) AS quantity_ordered,
28+
29+
TRY_CAST(unit_list_price AS DECIMAL(18,2)) AS unit_list_price,
30+
31+
TRY_CAST(discount_pct AS DECIMAL(18,2)) AS discount_pct,
32+
33+
TRY_CAST(unit_selling_price AS DECIMAL(18,2)) AS unit_selling_price,
34+
35+
TRY_CAST(line_total_before_tax AS DECIMAL(18,2)) AS line_total_before_tax,
36+
37+
TRY_CAST(tax_rate_pct AS DECIMAL(18,2)) AS tax_rate_pct,
38+
39+
TRY_CAST(tax_amount AS DECIMAL(18,2)) AS tax_amount,
40+
41+
TRY_CAST(line_total_with_tax AS DECIMAL(18,2)) AS line_total_with_tax,
42+
43+
-- Derived Metrics
44+
(
45+
TRY_CAST(unit_selling_price AS DECIMAL(18,2))
46+
*
47+
TRY_CAST(quantity_ordered AS INT)
48+
) AS gross_sales_amount,
49+
50+
TRY_CAST(cost_price AS DECIMAL(18,2)) AS cost_price,
51+
52+
TRY_CAST(gross_profit AS DECIMAL(18,2)) AS gross_profit,
53+
54+
(
55+
TRY_CAST(line_total_with_tax AS DECIMAL(18,2))
56+
-
57+
TRY_CAST(cost_price AS DECIMAL(18,2))
58+
) AS net_profit,
59+
60+
CASE
61+
WHEN TRY_CAST(line_total_with_tax AS DECIMAL(18,2)) = 0 THEN NULL
62+
63+
ELSE ROUND(
64+
(
65+
(
66+
TRY_CAST(line_total_with_tax AS DECIMAL(18,2))
67+
-
68+
TRY_CAST(cost_price AS DECIMAL(18,2))
69+
)
70+
/
71+
TRY_CAST(line_total_with_tax AS DECIMAL(18,2))
72+
) * 100,
73+
2
74+
)
75+
76+
END AS profit_margin_pct,
77+
78+
-- Dimensions
79+
store_id,
80+
employee_id,
81+
82+
promo_id,
83+
promo_name,
84+
85+
LOWER(TRIM(sales_channel)) AS sales_channel,
86+
87+
payment_method,
88+
shipping_method,
89+
order_status,
90+
is_returned,
91+
92+
-- Audit Column
93+
GETDATE() AS _loaded_at
94+
95+
FROM staging

0 commit comments

Comments
 (0)