From 29244d3cb226b6cff070e3ee870cce4cb043d819 Mon Sep 17 00:00:00 2001 From: Mohamad Bader almsaddi alzin Date: Sat, 18 Jul 2026 05:21:50 +0200 Subject: [PATCH] super commit --- .gitignore | 1 + AI_ASSIST.md | 74 +++++++++++++++++---- docs/lineage.png | Bin 0 -> 40083 bytes macros/safe_divide.sql | 14 ++-- models/marts/_fct_daily_borough_stats.yml | 32 ++++++--- models/marts/fct_daily_borough_stats.sql | 30 +++------ models/staging/_sources.yml | 6 +- models/staging/_stg_trips.yml | 28 +++++--- models/staging/_stg_zones.yml | 13 ++-- models/staging/stg_trips.sql | 17 +++-- models/staging/stg_zones.sql | 3 +- package-lock.yml | 5 ++ packages.yml | 8 +-- profiles.yml.example | 3 +- reports/answers.md | 40 ++++++++--- tests/assert_avg_tip_pct_within_bounds.sql | 7 +- 16 files changed, 188 insertions(+), 93 deletions(-) create mode 100644 docs/lineage.png create mode 100644 package-lock.yml diff --git a/.gitignore b/.gitignore index d083004..5f1af3c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ target/ dbt_packages/ logs/ profiles.yml +.user.yml # System files .DS_Store diff --git a/AI_ASSIST.md b/AI_ASSIST.md index 76f6a37..70472bb 100644 --- a/AI_ASSIST.md +++ b/AI_ASSIST.md @@ -4,31 +4,81 @@ Document one place you used an LLM during this assignment. ## The problem - +tried to run jinja syntax inside dbeaver -TODO ## The prompt - +select + pickup_datetime, + dropoff_datetime, + pickup_location_id, + dropoff_location_id, + fare_amount, + tip_amount, + trip_distance, + {{ safe_divide('tip_amount', 'fare_amount') }} as tip_pct +from {{ source('nyc_taxi', 'raw_trips') }} +where pickup_location_id is not null +and fare_amount > 0 +SQL Error [42601]: ERROR: syntax error at or near "{" + Position: 164 + +Error position: line: 9 pos: 163 + -TODO ## The response - +The error you are seeing happens because your SQL client (like DBeaver, pgAdmin, or Datagrip) is trying to execute this query directly against your database, but it doesn't understand the `{{ ... }}` syntax. + +Those double curly braces are **Jinja templates**, which are used by tools like **dbt (Data Build Tool)**. Your database just sees them as a syntax error. + +Here is how to fix this depending on what you are trying to do: + +### Scenario 1: You are working in a dbt project + +If this is part of a dbt project, you cannot run this raw script directly in a standard SQL editor. + +* **The Fix:** Run the model through your terminal using `dbt run` or `dbt compile`. +* If you are using an IDE extension (like dbt Power User in VS Code) or dbt Cloud, use their specific **"Preview"** or **"Compile"** buttons instead of your database client's standard "Execute" button. + +### Scenario 2: You just want to run this quick query in your SQL client + +If you aren't using dbt, or you just want to test this query right now in your SQL tool, you need to replace the Jinja code with standard SQL. + +Here is the clean SQL version of your query: + +```sql +select + pickup_datetime, + dropoff_datetime, + pickup_location_id, + dropoff_location_id, + fare_amount, + tip_amount, + trip_distance, + -- Replaced safe_divide macro with standard SQL handling for division by zero + case + when fare_amount = 0 then 0 + else tip_amount / fare_amount + end as tip_pct +-- Replaced the source() macro with your actual schema and table name +from nyc_taxi.raw_trips +where pickup_location_id is not null +and fare_amount > 0; + +``` + +> **Note:** Since you already have `where fare_amount > 0` at the bottom of your query, a simple `tip_amount / fare_amount` would technically be safe from division-by-zero errors in this specific run, but using the `CASE` statement mimics what `safe_divide` does behind the scenes! + -TODO ## Reflection - +realized i should remove jinja and got the actual dbeaver query from target>compiled after running dbt compile + -TODO --- diff --git a/docs/lineage.png b/docs/lineage.png new file mode 100644 index 0000000000000000000000000000000000000000..63af118e49666b1865ce2dd565f3c4b5bf8bb4bd GIT binary patch literal 40083 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 0 then round(({{ numerator }} / {{ denominator }})::numeric, 4) + else null + end +{% endmacro %} \ No newline at end of file diff --git a/models/marts/_fct_daily_borough_stats.yml b/models/marts/_fct_daily_borough_stats.yml index 15bba90..716bc4e 100644 --- a/models/marts/_fct_daily_borough_stats.yml +++ b/models/marts/_fct_daily_borough_stats.yml @@ -2,23 +2,35 @@ version: 2 models: - name: fct_daily_borough_stats - description: > - TODO: state the grain (one row per ___), the source lineage - (built from ___ and ___), and at least one known caveat - (e.g. rows dropped in staging, any WARN-severity tests). + description: "One row per completed NYC green taxi trip in January 2024, with + pickup/dropoff zone attributes folded in (OBT-style mart). Queried + directly by dashboards and ad-hoc analysis. + + **Grain:** one row per trip. + **Source:** `public.raw_trips` joined to `public.raw_zones` on + `pickup_location_id` and `dropoff_location_id`. + **Not included:** trips where `pickup_location_id` is NULL (dropped + in `stg_trips`); duplicate rows from the TLC source are kept as-is + and surfaced by `dbt_utils.unique_combination_of_columns`." # TODO: Task 5 -- add the compound uniqueness test on the mart's primary # key (pickup_borough, pickup_date). You need the dbt_utils package for # this: declare it in packages.yml and run `dbt deps` first. + tests: + - dbt_utils.unique_combination_of_columns: + combination_of_columns: + - pickup_borough + - pickup_date + severity: warn columns: - name: pickup_borough - description: "TODO: explain what this column contains and where it comes from" + description: "TLC borough where the trip started" - name: pickup_date - description: "TODO: explain units and how it is derived" + description: "the day the trip started (date only, no time)" - name: trip_count - description: "TODO: explain what is counted (unit: number of trips)" + description: "total trips that started in this borough on this day" - name: total_fare - description: "TODO: explain units (USD) and what fare_amount represents" + description: "total revenue from trips that started in this borough on this day in USD" - name: avg_tip_pct - description: "TODO: explain the ratio (tip_amount / fare_amount)" + description: "average tip percentage for trips that started in this borough on this day, expressed as a decimal (e.g. 0.15 = 15%)" - name: avg_trip_distance - description: "TODO: explain units (miles, from TLC source data)" + description: "average distance of trips that started in this borough on this day, in miles" diff --git a/models/marts/fct_daily_borough_stats.sql b/models/marts/fct_daily_borough_stats.sql index bf47070..7c56b93 100644 --- a/models/marts/fct_daily_borough_stats.sql +++ b/models/marts/fct_daily_borough_stats.sql @@ -14,25 +14,15 @@ zones AS ( ) SELECT - -- TODO: join trips to zones on pickup_location_id = location_id. - -- Use an INNER JOIN: a few trips have a pickup_location_id with no matching - -- zone (e.g. 999). INNER JOIN drops those so pickup_borough is never NULL and - -- can serve as part of the mart's primary key (your not_null test needs this). - -- TODO: aggregate to grain (pickup_borough, pickup_date) - -- Required output columns: - -- pickup_borough TEXT - z.borough - -- pickup_date DATE - pickup_datetime::date - -- trip_count BIGINT - count(*) - -- total_fare NUMERIC - sum(fare_amount) - -- avg_tip_pct NUMERIC - avg(tip_pct) - -- avg_trip_distance NUMERIC - avg(trip_distance) - NULL AS pickup_borough, - NULL AS pickup_date, - NULL AS trip_count, - NULL AS total_fare, - NULL AS avg_tip_pct, - NULL AS avg_trip_distance + + z.borough::text AS pickup_borough, + t.pickup_datetime::date AS pickup_date, + COUNT(*) AS trip_count, + SUM(t.fare_amount)::numeric(10,2) AS total_fare, + AVG(t.tip_pct)::numeric(10,2) AS avg_tip_pct, + AVG(t.trip_distance)::numeric(10,2) AS avg_trip_distance FROM trips t --- TODO: add JOIN to zones here --- TODO: add GROUP BY here +INNER JOIN zones z + ON t.pickup_location_id = z.location_id +GROUP BY pickup_borough, pickup_date diff --git a/models/staging/_sources.yml b/models/staging/_sources.yml index 4bbee9c..e5bfc3c 100644 --- a/models/staging/_sources.yml +++ b/models/staging/_sources.yml @@ -2,9 +2,9 @@ version: 2 sources: - name: nyc_taxi - schema: nyc_taxi # TODO: confirm this matches the schema where raw_trips and raw_zones live + schema: nyc_taxi tables: - name: raw_trips - description: "TODO: one sentence on what this table contains and its grain" + description: "One row per green taxi trip for January 2024 (~57K rows)." - name: raw_zones - description: "TODO: one sentence on what this table contains" + description: "NYC taxi zone lookup (265 rows mapping location IDs to boroughs)." diff --git a/models/staging/_stg_trips.yml b/models/staging/_stg_trips.yml index 14829a8..5a947ef 100644 --- a/models/staging/_stg_trips.yml +++ b/models/staging/_stg_trips.yml @@ -2,19 +2,29 @@ version: 2 models: - name: stg_trips - description: "TODO: state the grain (one row per ___) and what source this reads from" + description: "Cleaned green taxi trips, one row per trip. This reads from the raw_trips source." columns: - name: pickup_datetime - description: "TODO" - # TODO: Task 5 -- add not_null tests on every column used as a join or - # group-by key. See the chapter's dbt Tests section for the syntax. + description: "when the trip started" + tests: + - not_null - name: pickup_location_id - description: "TODO" + description: "TLC zone id where the trip started" + tests: + - not_null + - relationships: + to: ref('stg_zones') + field: location_id + config: + severity: warn - name: fare_amount - description: "TODO" + description: "cost of the trip in USD" - name: tip_amount - description: "TODO" + description: "tip paid for the trip" + - name: trip_distance - description: "TODO" + description: "distance of the trip in miles" + - name: tip_pct - description: "TODO" + description: "percentage of tip relative to trip cost" + diff --git a/models/staging/_stg_zones.yml b/models/staging/_stg_zones.yml index c4b785d..13c591d 100644 --- a/models/staging/_stg_zones.yml +++ b/models/staging/_stg_zones.yml @@ -2,11 +2,14 @@ version: 2 models: - name: stg_zones - description: "TODO: state the grain and what source this reads from" + description: "One row per TLC taxi zone (265 zones total)" columns: - name: location_id - description: "TODO" - # TODO: Task 5 -- this column is the join key. Which two generic tests - # guarantee a clean one-to-many join from stg_trips? + description: TLC zone ID. + tests: + - unique + - not_null - name: borough - description: "TODO" + description: NYC borough (Manhattan, Brooklyn, Queens, Bronx, Staten Island, EWR, Unknown). + tests: + - not_null \ No newline at end of file diff --git a/models/staging/stg_trips.sql b/models/staging/stg_trips.sql index b5f7b81..b613728 100644 --- a/models/staging/stg_trips.sql +++ b/models/staging/stg_trips.sql @@ -3,11 +3,14 @@ -- Downstream: fct_daily_borough_stats joins this to stg_zones. SELECT - -- TODO: select the columns you need for the mart: - -- pickup_datetime, pickup_location_id, fare_amount, tip_amount, trip_distance - -- - -- TODO: add tip_pct using {{ safe_divide('tip_amount', 'fare_amount') }} - -- - -- TODO: filter out rows where pickup_location_id IS NULL or fare_amount < 0 - + pickup_datetime, + dropoff_datetime, + pickup_location_id, + dropoff_location_id, + fare_amount, + tip_amount, + trip_distance, + {{ safe_divide('tip_amount', 'fare_amount') }} as tip_pct FROM {{ source('nyc_taxi', 'raw_trips') }} +WHERE pickup_location_id IS NOT NULL + AND fare_amount >= 0 \ No newline at end of file diff --git a/models/staging/stg_zones.sql b/models/staging/stg_zones.sql index cc34d23..a44c667 100644 --- a/models/staging/stg_zones.sql +++ b/models/staging/stg_zones.sql @@ -2,6 +2,7 @@ -- Exposes location_id and borough for use as a lookup in the mart. SELECT - -- TODO: select location_id and borough from {{ source('nyc_taxi', 'raw_zones') }} + location_id, + borough FROM {{ source('nyc_taxi', 'raw_zones') }} diff --git a/package-lock.yml b/package-lock.yml new file mode 100644 index 0000000..1ce78fc --- /dev/null +++ b/package-lock.yml @@ -0,0 +1,5 @@ +packages: + - name: dbt_utils + package: dbt-labs/dbt_utils + version: 1.4.1 +sha1_hash: e6424ba9e5a22487e47f023803aa4f0411946808 diff --git a/packages.yml b/packages.yml index bbb2357..71fdb1a 100644 --- a/packages.yml +++ b/packages.yml @@ -1,5 +1,3 @@ -# TODO: Task 5 -- declare the dbt-labs/dbt_utils package here, then run -# `dbt deps` to install it. You need it for the compound uniqueness test -# on the mart. See https://hub.getdbt.com/dbt-labs/dbt_utils/latest/ -# for the package block syntax. -packages: [] +packages: + - package: dbt-labs/dbt_utils + version: [">=1.1.0", "<2.0.0"] \ No newline at end of file diff --git a/profiles.yml.example b/profiles.yml.example index 82c6ae0..f5102c0 100644 --- a/profiles.yml.example +++ b/profiles.yml.example @@ -8,8 +8,9 @@ nyc_taxi_borough_daily: user: "{{ env_var('PG_USER') }}" password: "{{ env_var('PG_PASSWORD') }}" dbname: "{{ env_var('PG_DBNAME', 'postgres') }}" - schema: "dev_" # TODO: replace with your first name (the schema you already own) + schema: "dev_bader" threads: 1 + sslmode: require # Copy this file to profiles.yml (same directory), fill in your name, and ensure # PG_HOST, PG_USER, PG_PASSWORD, and PG_DBNAME are set in your environment. diff --git a/reports/answers.md b/reports/answers.md index 8fb8cf2..dea1e8c 100644 --- a/reports/answers.md +++ b/reports/answers.md @@ -8,11 +8,17 @@ Queries run against `dev_.fct_daily_borough_stats`. ```sql -- TODO: query fct_daily_borough_stats grouped by pickup_borough, sum total_fare, order DESC +select +pickup_borough, +SUM(total_fare) as total_fares +from dev_bader.fct_daily_borough_stats +group by pickup_borough +order by total_fares desc ``` -**Result:** TODO +**Result:** Manhatten 493955.62 -**Interpretation:** TODO (one sentence) +**Interpretation:** highest total revenue per borough is manhatten --- @@ -22,11 +28,18 @@ Queries run against `dev_.fct_daily_borough_stats`. ```sql -- TODO: query fct_daily_borough_stats grouped by pickup_date, sum trip_count, order DESC LIMIT 1 +select +pickup_date, +SUM(trip_count) as total_trips +from dev_bader.fct_daily_borough_stats +group by pickup_date +order by total_trips desc +limit 1 ``` -**Result:** TODO +**Result:** 17-01-2024 -**Interpretation:** TODO (one sentence) +**Interpretation:** highest trips count day is 17-01-2024 --- @@ -35,12 +48,15 @@ Queries run against `dev_.fct_daily_borough_stats`. **SQL:** ```sql --- TODO: query fct_daily_borough_stats order by avg_tip_pct DESC LIMIT 5 +select * +from dev_bader.fct_daily_borough_stats +order by avg_tip_pct desc +limit 5 ``` -**Result:** TODO +**Result:** unknown borough -**Interpretation:** TODO — note whether any avg_tip_pct > 1 rows appear and what causes them +**Interpretation:** shows highest average tip precentages per borough/date. any average tips precentages above 1 do show, because we didnt add where tip_pct > 1 when building mart and we used a warn assert test instead which keeps them in the data --- @@ -50,8 +66,14 @@ Queries run against `dev_.fct_daily_borough_stats`. ```sql -- TODO: use percentile_cont(0.5) WITHIN GROUP (ORDER BY trip_count) filtered by borough +SELECT + pickup_borough, + percentile_cont(0.5) WITHIN GROUP (ORDER BY trip_count) AS median_daily +FROM dev_bader.fct_daily_borough_stats +WHERE pickup_borough IN ('Manhattan', 'Brooklyn') +GROUP BY pickup_borough; ``` -**Result:** TODO +**Result:** 248 for brooklyn and 1169.5 for manhatten -**Interpretation:** TODO (one sentence on the ratio) +**Interpretation:** manhatten has almost 4.7 times the trip count volume as brooklyn diff --git a/tests/assert_avg_tip_pct_within_bounds.sql b/tests/assert_avg_tip_pct_within_bounds.sql index b0fab32..054736d 100644 --- a/tests/assert_avg_tip_pct_within_bounds.sql +++ b/tests/assert_avg_tip_pct_within_bounds.sql @@ -15,5 +15,8 @@ -- TODO: write the SELECT here. -- Query {{ ref('fct_daily_borough_stats') }} and return rows where avg_tip_pct > 1. -SELECT NULL AS pickup_borough, NULL AS pickup_date, NULL AS avg_tip_pct -WHERE FALSE -- TODO: replace with the real query + +{{ config(severity='warn') }} +select pickup_borough, pickup_date, avg_tip_pct +from {{ ref('fct_daily_borough_stats') }} +where avg_tip_pct > 1 \ No newline at end of file