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
123 changes: 114 additions & 9 deletions week11-streamlit/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,120 @@ def run_query(sql: str) -> pd.DataFrame:
return pd.read_sql(sql, conn)


payment_options_query = f"""
SELECT DISTINCT
CASE
WHEN payment_type = 1 THEN 'Credit card'
WHEN payment_type = 2 THEN 'Cash'
WHEN payment_type = 3 THEN 'No charge'
WHEN payment_type = 4 THEN 'Dispute'
WHEN payment_type = 5 THEN 'Unknown'
WHEN payment_type = 6 THEN 'Voided trip'
WHEN payment_type IS NULL THEN 'Not Recorded'
ELSE 'Other'
END AS payment_type_label
FROM {DB_SCHEMA}.vw_fact_trips;
"""

payment_df = run_query(payment_options_query)
payment_options = ["All"] + sorted(
[val for val in payment_df["payment_type_label"].unique() if val is not None]
)
selected_payment = st.sidebar.selectbox("Filter by Payment Type", payment_options)

st.subheader("Headline KPIs")
kpi_query = f"""
WITH base_trips AS (
SELECT
*,
CASE
WHEN payment_type = 1 THEN 'Credit card'
WHEN payment_type = 2 THEN 'Cash'
WHEN payment_type = 3 THEN 'No charge'
WHEN payment_type = 4 THEN 'Dispute'
WHEN payment_type = 5 THEN 'Unknown'
WHEN payment_type = 6 THEN 'Voided trip'
WHEN payment_type IS NULL THEN 'Not Recorded'
ELSE 'Other'
END AS payment_type_label
FROM {DB_SCHEMA}.vw_fact_trips
)
SELECT
COUNT(*) AS total_trips,
AVG(trip_distance) AS avg_trip_distance,
AVG(
CASE
WHEN trip_distance > 0 THEN fare_amount / trip_distance
ELSE 0
END
) AS avg_fare_per_mile
FROM base_trips
WHERE 1=1
"""

if selected_payment != "All":
kpi_query += f" AND payment_type_label = '{selected_payment}'"

kpi_df = run_query(kpi_query)
total_trips = kpi_df["total_trips"].iloc[0] or 0
avg_trip_distance = kpi_df["avg_trip_distance"].iloc[0] or 0
avg_fare_per_mile = kpi_df["avg_fare_per_mile"].iloc[0] or 0

col1, col2, col3 = st.columns(3)
with col1:
st.metric(label="Total Trips", value=f"{total_trips:,}")
with col2:
st.metric(label="Avg Trip Distance", value=f"{avg_trip_distance:.2f} miles")
with col3:
st.metric(label="Avg Fare per Mile", value=f"${avg_fare_per_mile:.2f}")

# TODO: query total trip count, average trip_distance, and average
# fare_per_mile from {DB_SCHEMA}.fct_trips through run_query(), then
# render three tiles side by side with st.columns(3) and .metric().
# This is deliberately not the total-trips/avg-fare/total-revenue trio
# from the chapter: trip_distance and fare_per_mile are different columns,
# so copying the chapter's SQL verbatim will not answer this.
raise NotImplementedError(
"TODO: implement the headline KPIs panel (total trips, avg trip "
"distance, avg fare per mile) from fct_trips."
st.subheader("Trip Count by Hour of Day")

hour_query = f"""
WITH base_trips AS (
SELECT
*,
CASE
WHEN payment_type = 1 THEN 'Credit card'
WHEN payment_type = 2 THEN 'Cash'
WHEN payment_type = 3 THEN 'No charge'
WHEN payment_type = 4 THEN 'Dispute'
WHEN payment_type = 5 THEN 'Unknown'
WHEN payment_type = 6 THEN 'Voided trip'
WHEN payment_type IS NULL THEN 'Not Recorded'
ELSE 'Other'
END AS payment_type_label
FROM {DB_SCHEMA}.vw_fact_trips
)
SELECT
EXTRACT(HOUR FROM pickup_datetime) AS hour_of_day,
COUNT(*) AS trip_count
FROM base_trips
WHERE 1=1
"""

if selected_payment != "All":
hour_query += f" AND payment_type_label = '{selected_payment}'"

hour_query += " GROUP BY 1 ORDER BY 1;"

hour_df = run_query(hour_query)

st.line_chart(data=hour_df.set_index("hour_of_day"), y="trip_count")

st.subheader("Data Freshness Status")
freshness_query = f"""
SELECT
COUNT(*) AS total_rows,
MAX(pickup_datetime) AS latest_pickup
FROM {DB_SCHEMA}.vw_fact_trips;
"""
fresh_df = run_query(freshness_query)
total_rows = fresh_df["total_rows"].iloc[0] or 0
latest_pickup = fresh_df["latest_pickup"].iloc[0]

col_f1, col_f2 = st.columns(2)
with col_f1:
st.metric(label="Total Database Rows", value=f"{total_rows:,}")
with col_f2:
st.metric(label="Latest Pickup Datetime", value=str(latest_pickup))
66 changes: 66 additions & 0 deletions week11-streamlit/metric_definitions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Metric definitions

Five fields per metric: Name, Description, Calculation, Data source, Refresh frequency. One block per panel. Copy this file to `metric_definitions.md` inside your `week11-streamlit/` folder and fill it in.

## Metabase panels

<!-- One block per Question: trip count by payment type, average fare per
mile by dropoff borough, average trip duration by hour of day. -->

### Panel 1: Trip Count by Payment Type

- **Name**: Trip Count by Payment Type
- **Description**: Displays the total number of taxi trips grouped by their payment method (e.g., Credit card, Cash, Dispute, etc.) to understand customer payment preferences. Missing or null values are classified explicitly as "Not Recorded" or "Unknown".
- **Calculation**: $$\text{Trip Count} = \text{COUNT}(*)$$Categorized using a CASE WHEN statement mapping payment_type codes (1 to 6) to their official descriptions, grouping by this category, and sorting in descending order.
- **Data source**: dev_mareh.vw_fact_trips
- **Refresh frequency**: Daily (or upon ETL pipeline execution)

### Panel 2: Average Fare per Mile by Dropoff Borough

- **Name**: Average Fare per Mile by Dropoff Borough
- **Description**: Visualizes the average cost of a taxi ride per mile based on where passengers are dropped off (Dropoff Borough). This helps identify which destinations yield the highest fare rates relative to the distance traveled.
- **Calculation**: $$\text{Average Fare per Mile} = \text{AVG}\left(\frac{\text{fare\_amount}}{\text{trip\_distance}}\right)$$Note: A conditional check ($\text{trip\_distance} > 0$) is implemented to prevent division-by-zero errors.
- **Data source**: Joined tables dev_mareh.vw_fact_trips (fact) and dev_mareh.vw_dim_zones (dimension) on location IDs.
- **Refresh frequency**: Daily (or upon ETL pipeline execution)

### Panel 3: Average Trip Duration by Hour of Day

- **Name**: Average Trip Duration by Hour of Day
- **Description**: A line chart illustrating how the average duration of taxi trips (in minutes) fluctuates across different hours of the day (from 0 to 23). This highlights peak traffic congestion hours and off-peak travel times.
- **Calculation**: $$\text{Trip Duration (Minutes)} = \frac{\text{EXTRACT}(\text{EPOCH FROM } (\text{dropoff\_datetime} - \text{pickup\_datetime}))}{60}$$
$$\text{Average Trip Duration} = \text{AVG}(\text{Trip Duration})$$Grouped by $\text{EXTRACT}(\text{HOUR FROM } \text{pickup\_datetime})$ and sorted chronologically.
- **Data source**: dev_mareh.vw_fact_trips
- **Refresh frequency**: Daily (or upon ETL pipeline execution)

## Streamlit panels

## Streamlit panels

### Panel 1: Headline KPIs

- **Name**: Headline KPIs (Total Trips, Avg Trip Distance, Avg Fare per Mile)
- **Description**: Shows three main numbers to understand how many rides happened, how long they were on average, and the average cost for each mile.
- **Calculation**:
- `total_trips`: `COUNT(*)` (Total number of rides).
- `avg_trip_distance`: `AVG(trip_distance)` (Average distance of a single ride, measured in miles).
- `avg_fare_per_mile`: `AVG(CASE WHEN trip_distance > 0 THEN fare_amount / trip_distance ELSE 0 END)` (Calculates the cost per mile for each ride first, then finds the average of those rates. It safely ignores rides with zero distance).
- **Data source**: `dev_mareh.vw_fact_trips`
- **Refresh frequency**: Daily / Whenever the database is updated.

### Panel 2: Trip Count by Hour of Day

- **Name**: Hourly Trip Distribution
- **Description**: A line chart that shows the number of rides for every hour of the day (from 0 to 23) to see when the busiest times are.
- **Calculation**: `COUNT(*)` grouped by the hour of the ride start time using `EXTRACT(HOUR FROM pickup_datetime)`.
- **Data source**: `dev_mareh.vw_fact_trips`
- **Refresh frequency**: Daily / Whenever the database is updated.

### Panel 3: Data Freshness Status

- **Name**: Data Freshness and Row Count
- **Description**: Shows the total number of rows in the database and the time of the very last ride to make sure our data is up-to-date and not missing anything.
- **Calculation**:
- `total_rows`: `COUNT(*)` (Total rows in the table).
- `latest_pickup`: `MAX(pickup_datetime)` (The timestamp of the newest ride recorded).
- **Data source**: `dev_mareh.vw_fact_trips`
- **Refresh frequency**: Daily / Whenever the database is updated.
Loading