Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.12
20 changes: 4 additions & 16 deletions AI_ASSIST.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,19 @@ Document one place you used an LLM during this assignment.

## The problem

<!-- TODO: describe the specific problem you asked an LLM about.
Example: "My safe_divide macro compiled but returned NULL for all rows even
when tip_amount and fare_amount were both non-zero." -->

TODO
When I tried to install `dbt_utils` with `dbt deps`, dbt failed with a Windows `WinError 32` file-lock error. The error said that the `dbt_packages/dbt_utils` folder was being used by another process, so dbt could not delete or replace it.

## The prompt

<!-- TODO: paste the exact prompt you sent to the LLM. -->

TODO
I am working on a dbt assignment on Windows. When I run `uv run dbt deps`, I get this error: `[WinError 32] The process cannot access the file because it is being used by another process: 'dbt_packages\\dbt_utils'`. I am using uv and dbt_utils version 1.4.1. How can I fix this without breaking my project?

## The response

<!-- TODO: summarise or paste what the LLM returned. -->

TODO
The LLM explained that this was not a dbt code problem, but a Windows file-lock problem. It suggested closing anything that might be using the `dbt_packages` folder, such as VS Code tabs, File Explorer, or a running `dbt docs serve` process. It also suggested deleting `dbt_packages` and `package-lock.yml`, then running `uv run dbt deps` again from PowerShell.

## Reflection

<!-- TODO: what did you change, keep, or discard after reviewing the LLM's answer?
Be specific: "I kept the NULLIF suggestion but changed the column alias from
'ratio' to 'tip_pct' to match the assignment schema." -->

TODO
I kept the advice to close possible locking processes and rerun the command from PowerShell. After removing the locked package folder and running `uv run dbt deps` again, dbt installed `dbt_utils` successfully. I did not change my models because the problem was related to the local Windows file system, not my SQL or dbt project logic.

---

Expand Down
6 changes: 3 additions & 3 deletions dbt_project.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name: 'nyc_taxi_borough_daily'
version: '1.0.0'
name: "nyc_taxi_borough_daily"
version: "1.0.0"
config-version: 2

# This project connects to the profile of the same name in profiles.yml.
profile: 'nyc_taxi_borough_daily'
profile: "nyc_taxi_borough_daily"

model-paths: ["models"]
macro-paths: ["macros"]
Expand Down
Binary file added docs/lineage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 2 additions & 5 deletions macros/safe_divide.sql
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
-- safe_divide(numerator, denominator)
-- Returns numerator / denominator, or NULL when denominator is 0 or NULL.
-- Use for tip_pct = tip_amount / fare_amount and similar ratio columns.


{% macro safe_divide(numerator, denominator) %}
-- TODO: implement the macro body.
-- Use NULLIF(denominator, 0) to avoid division-by-zero errors.
-- Return only the SQL expression (no SELECT, no semicolon).
NULL
({{ numerator }}::numeric / NULLIF({{ denominator }}::numeric, 0))
{% endmacro %}
38 changes: 26 additions & 12 deletions models/marts/_fct_daily_borough_stats.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,36 @@ 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).
# 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.
Daily borough-level taxi mart with one row per pickup borough and pickup date.
It is built from stg_trips joined to stg_zones. Rows with missing pickup_location_id
or negative fare_amount are removed in staging, and trips with pickup location IDs
that do not exist in stg_zones are dropped by the inner join. The avg_tip_pct warning
test can return rows where small borough/date groups have unusually high average tip ratios.
data_tests:
- dbt_utils.unique_combination_of_columns:
combination_of_columns:
- pickup_borough
- pickup_date

columns:
- name: pickup_borough
description: "TODO: explain what this column contains and where it comes from"
description: "Borough or TLC zone label where the trip was picked up, taken from the taxi zone lookup table."
data_tests:
- not_null

- name: pickup_date
description: "TODO: explain units and how it is derived"
description: "Calendar date of the trip pickup, derived from pickup_datetime. Unit: date."
data_tests:
- not_null

- name: trip_count
description: "TODO: explain what is counted (unit: number of trips)"
description: "Number of trips picked up in this borough on this date. Unit: trips."

- name: total_fare
description: "TODO: explain units (USD) and what fare_amount represents"
description: "Sum of fare_amount for trips picked up in this borough on this date. Unit: US dollars."

- name: avg_tip_pct
description: "TODO: explain the ratio (tip_amount / fare_amount)"
description: "Average tip ratio for trips in this borough on this date, calculated as tip_amount divided by fare_amount. Unit: ratio, not multiplied by 100."

- name: avg_trip_distance
description: "TODO: explain units (miles, from TLC source data)"
description: "Average recorded trip distance for trips picked up in this borough on this date. Unit: miles."
33 changes: 13 additions & 20 deletions models/marts/fct_daily_borough_stats.sql
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,18 @@ 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 AS pickup_borough,
t.pickup_datetime::date AS pickup_date,
COUNT(*) AS trip_count,
SUM(t.fare_amount) AS total_fare,
AVG(t.tip_pct) AS avg_tip_pct,
AVG(t.trip_distance) 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
z.borough,
t.pickup_datetime::date
6 changes: 3 additions & 3 deletions models/staging/_sources.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 # TODO: confirm this matches the schema where raw_trips and raw_zones live
tables:
- name: raw_trips
description: "TODO: one sentence on what this table contains and its grain"
description: "Raw NYC Green Taxi trip records. Each row represents one taxi trip from the loaded January 2024 dataset."
- name: raw_zones
description: "TODO: one sentence on what this table contains"
description: "Raw TLC taxi zone lookup table. Each row represents one taxi location zone and its borough."
20 changes: 11 additions & 9 deletions models/staging/_stg_trips.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@ version: 2

models:
- name: stg_trips
description: "TODO: state the grain (one row per ___) and what source this reads from"
description: "Staging model with one row per NYC Green Taxi trip, built from nyc_taxi.raw_trips. It keeps the columns needed for the borough daily mart and filters rows with missing pickup location or negative fare."
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: "Timestamp when the trip started. Used to derive the pickup date in the mart."
data_tests:
- not_null
- name: pickup_location_id
description: "TODO"
description: "TLC pickup zone identifier. Used to join trips to the zone lookup table."
data_tests:
- not_null
- name: fare_amount
description: "TODO"
description: "Base fare amount charged for the trip, in US dollars. Negative fares are removed in staging"
- name: tip_amount
description: "TODO"
description: "Tip amount paid for the trip, in US dollars"
- name: trip_distance
description: "TODO"
description: "Trip distance recorded by the taxi meter, in miles."
- name: tip_pct
description: "TODO"
description: "Tip ratio calculated as tip_amount divided by fare_amount. It is NULL when fare_amount is zero."
14 changes: 9 additions & 5 deletions models/staging/_stg_zones.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ version: 2

models:
- name: stg_zones
description: "TODO: state the grain and what source this reads from"
description: "Staging model with one row per TLC taxi zone, built from nyc_taxi.raw_zones."
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: "Unique TLC zone identifier. Used as the join key from trips to zones."
data_tests:
- not_null
- unique

- name: borough
description: "TODO"
description: "Borough or zone label for the TLC location, such as Manhattan, Brooklyn, Queens, Bronx, Staten Island, EWR, Unknown, or NaN."
data_tests:
- not_null
16 changes: 10 additions & 6 deletions models/staging/stg_trips.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
-- 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,
pickup_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
4 changes: 2 additions & 2 deletions models/staging/stg_zones.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
-- 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') }}
5 changes: 5 additions & 0 deletions package-lock.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
packages:
- name: dbt_utils
package: dbt-labs/dbt_utils
version: 1.4.1
sha1_hash: 8b27037b26f3f630c6661194d2470e720c49f6ee
8 changes: 3 additions & 5 deletions packages.yml
Original file line number Diff line number Diff line change
@@ -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.4.1
10 changes: 10 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[project]
name = "c55-data-week-10"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"dbt-core>=1.11.12",
"dbt-postgres>=1.10.2",
]
70 changes: 56 additions & 14 deletions reports/answers.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
# Business Question Answers

Queries run against `dev_<your_name>.fct_daily_borough_stats`.
Queries run against `dev_mohammedalfakih.fct_daily_borough_stats`.

## Q1: Highest total `total_fare` across the whole loaded dataset

**SQL:**

```sql
-- TODO: query fct_daily_borough_stats grouped by pickup_borough, sum total_fare, order DESC
SELECT
pickup_borough,
SUM(total_fare) AS total_fare_all_days
FROM dev_mohammedalfakih.fct_daily_borough_stats
GROUP BY pickup_borough
ORDER BY total_fare_all_days DESC
LIMIT 1;
```

**Result:** TODO
**Result:** | pickup_borough | total_fare_all_days |
| Manhattan | 493,955.62 |

**Interpretation:** TODO (one sentence)
**Interpretation:** Manhattan generated the highest total fare across the loaded dataset, with $493,955.62 in total fare revenue.

---

Expand All @@ -21,12 +28,19 @@ Queries run against `dev_<your_name>.fct_daily_borough_stats`.
**SQL:**

```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 overall_trip_count
FROM dev_mohammedalfakih.fct_daily_borough_stats
GROUP BY pickup_date
ORDER BY overall_trip_count DESC
LIMIT 1;
```

**Result:** TODO
**Result:** | pickup_date | overall_trip_count |
| 2024-01-17 | 2,221 |

**Interpretation:** TODO (one sentence)
**Interpretation:** January 17, 2024 had the highest total trip volume across all pickup boroughs, with 2,221 trips.

---

Expand All @@ -35,23 +49,51 @@ Queries run against `dev_<your_name>.fct_daily_borough_stats`.
**SQL:**

```sql
-- TODO: query fct_daily_borough_stats order by avg_tip_pct DESC LIMIT 5
SELECT
pickup_borough,
pickup_date,
avg_tip_pct
FROM dev_mohammedalfakih.fct_daily_borough_stats
ORDER BY avg_tip_pct DESC
LIMIT 5;
```

**Result:** TODO
**Result:** | pickup_borough | pickup_date | avg_tip_pct |

**Interpretation:** TODO — note whether any avg_tip_pct > 1 rows appear and what causes them
| Unknown | 2024-01-30 | 2.500125 |
| Unknown | 2024-01-07 | 1.3396296296 |
| Unknown | 2024-01-11 | 1.104495614 |
| Unknown | 2024-01-16 | 1 |
| Unknown | 2024-01-18 | 0.6111111111 |

---
**Interpretation:** The highest average tip percentage was for the Unknown borough on 2024-01-30, where the average tip was more than twice the fare; this matches the warning test and is likely caused by unusual trips in a small Unknown borough group rather than a model failure.

The warning-level singular test returned these rows where avg_tip_pct > 1:

| pickup_borough | pickup_date | avg_tip_pct |
| -------------- | ----------- | -----------: |
| Unknown | 2024-01-30 | 2.500125 |
| Unknown | 2024-01-07 | 1.3396296296 |
| Unknown | 2024-01-11 | 1.104495614 |

## These are data quality warnings, not build errors.

## Q4: Median daily `trip_count` for Manhattan vs Brooklyn

**SQL:**

```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_trip_count
FROM dev_mohammedalfakih.fct_daily_borough_stats
WHERE pickup_borough IN ('Manhattan', 'Brooklyn')
GROUP BY pickup_borough
ORDER BY pickup_borough;
```

**Result:** TODO
**Result:** | pickup_borough | median_daily_trip_count |
| Brooklyn | 248 |
| Manhattan | 1,169.5 |

**Interpretation:** TODO (one sentence on the ratio)
**Interpretation:** Manhattan’s median daily trip count was much higher than Brooklyn’s, at about 4.7 times as many trips per day.
Loading