diff --git a/AI_ASSIST.md b/AI_ASSIST.md index 8585a41..91c4c55 100644 --- a/AI_ASSIST.md +++ b/AI_ASSIST.md @@ -8,19 +8,35 @@ Document one place you used an LLM during this assignment. Example: "My Streamlit KPI panel kept re-querying Postgres on every sidebar interaction even though I wrapped run_query in @st.cache_data." --> -TODO +While building the Streamlit dashboard, my "Trips by Hour of Day" chart was not displaying correctly. I wanted to check whether my SQL query and the data format passed to st.line_chart() were correct. ## The prompt -TODO +I have this Streamlit code for a line chart. The query runs successfully, but I want to make sure the data is in the correct format for st.line_chart(). Is there anything I should change? + +st.subheader("Trips by Hour of Day") + +hour_query = f""" +SELECT + EXTRACT(HOUR FROM pickup_datetime) AS pickup_hour, + COUNT(*) AS trip_count +FROM {DB_SCHEMA}.fct_trips +{payment_filter} +GROUP BY pickup_hour +ORDER BY pickup_hour; +""" + +hour_df = run_query(hour_query) + +st.line_chart(hour_df) ## The response -TODO +The LLM explained that st.line_chart() works best when the x-axis is used as the DataFrame index. It suggested converting pickup_hour to an integer and setting it as the index before creating the chart. ## Reflection @@ -28,7 +44,7 @@ TODO Be specific: "I kept the cache_data suggestion but changed ttl from 60 to 300 to match the mart's once-a-day refresh cadence." --> -TODO +I kept the suggestion to convert pickup_hour to an integer and used it as the DataFrame index before calling st.line_chart(). After making these changes, the chart displayed correctly and the hours appeared in the correct order. --- diff --git a/README.md b/README.md index 91de124..07583fa 100644 --- a/README.md +++ b/README.md @@ -53,9 +53,12 @@ Fill in `week11-streamlit/metric_definitions.md`: a five-field definition (name, 3) Paste your 5-minute presentation recording link (keep it PRIVATE). --> - Metabase dashboard (in the **Week 11 Submissions** collection): TODO -- Screenshots / PDF export: TODO +https://metabase-hyf.politepebble-abd3ebc2.westeurope.azurecontainerapps.io/dashboard/42-nyc-taxi-analytics-baraah +- Screenshots / PDF export: TODO Metabase_dashboard_baraah.png - Presentation recording (private, hosted in the Azure `student-submissions` container): TODO +https://hyfstoragedev.blob.core.windows.net/student-submissions/week-11/Baraah.mp4?sp=r&st=2026-07-16T15:33:03Z&se=2026-07-22T23:48:03Z&skoid=8bfd6dc9-8735-4bc5-8362-05ebddd6526b&sktid=07a14c4e-d88c-42f7-83b3-13af7e57ff3d&skt=2026-07-16T15:33:03Z&ske=2026-07-22T23:48:03Z&sks=b&skv=2026-02-06&spr=https&sv=2026-02-06&sr=b&sig=GRcWIO6iheJqLzk0s8zeJNoQLaZaSRw%2FGOD9scBdsYQ%3D + > ⚠️ **Keep the recording private.** It shows your screen and voice. Never make it public and never commit the `.mp4` (git history is forever). Check the frame for passwords, `.env` contents, or connection strings before uploading. Host the recording in Azure: upload the `.mp4` to the shared `student-submissions` blob container (teachers get read access, nothing is public) and put the read-only link in your PR. The container is shared, so name your file after yourself: `week-11/.mp4` (e.g. `week-11/jane-doe.mp4`). See "Host the recording on Azure Blob Storage" in the Week 11 Assignment chapter for the `az` CLI and Portal steps. diff --git a/week11-streamlit/Metabase_dashboard_baraah.png b/week11-streamlit/Metabase_dashboard_baraah.png new file mode 100644 index 0000000..92f5b65 Binary files /dev/null and b/week11-streamlit/Metabase_dashboard_baraah.png differ diff --git a/week11-streamlit/Streamlit_dashboard_baraah.png b/week11-streamlit/Streamlit_dashboard_baraah.png new file mode 100644 index 0000000..2c0b8b5 Binary files /dev/null and b/week11-streamlit/Streamlit_dashboard_baraah.png differ diff --git a/week11-streamlit/app.py b/week11-streamlit/app.py index cf06bcb..a77b57d 100644 --- a/week11-streamlit/app.py +++ b/week11-streamlit/app.py @@ -18,7 +18,7 @@ load_dotenv() # reads .env file if present POSTGRES_URL = os.environ["POSTGRES_URL"] -DB_SCHEMA = os.environ.get("DB_SCHEMA", "dev_yourname") +DB_SCHEMA = os.environ.get("DB_SCHEMA", "dev_baraah") st.set_page_config(page_title="NYC Taxi Metrics", layout="wide") st.title("NYC Taxi Metrics") @@ -30,6 +30,27 @@ def run_query(sql: str) -> pd.DataFrame: with engine.connect() as conn: return pd.read_sql(sql, conn) +st.sidebar.header("Filters") + +payment_query = f""" +SELECT DISTINCT payment_type_label +FROM {DB_SCHEMA}.fct_trips +ORDER BY payment_type_label; +""" + +payment_df = run_query(payment_query) + +payment_options = ["All"] + payment_df["payment_type_label"].tolist() + +selected_payment = st.sidebar.selectbox( + "Payment Type", + payment_options +) + +if selected_payment == "All": + payment_filter = "" +else: + payment_filter = f"WHERE payment_type_label = '{selected_payment}'" st.subheader("Headline KPIs") @@ -39,7 +60,88 @@ def run_query(sql: str) -> pd.DataFrame: # 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." +# Query headline KPIs from fct_trips +kpi_query = f""" +SELECT + COUNT(*) AS total_trips, + AVG(trip_distance) AS avg_trip_distance, + AVG(fare_per_mile) AS avg_fare_per_mile +FROM {DB_SCHEMA}.fct_trips +{payment_filter}; +""" + +kpi_df = run_query(kpi_query) + +total_trips = int(kpi_df["total_trips"].iloc[0]) +avg_trip_distance = kpi_df["avg_trip_distance"].iloc[0] +avg_fare_per_mile = kpi_df["avg_fare_per_mile"].iloc[0] + + +col1, col2, col3 = st.columns(3) + +with col1: + st.metric( + "Total Trips", + f"{total_trips:,}" + ) + +with col2: + st.metric( + "Average Trip Distance", + f"{avg_trip_distance:.2f} miles" + ) + +with col3: + st.metric( + "Average Fare per Mile", + f"${avg_fare_per_mile:.2f}" + ) + +st.subheader("Trips by Hour of Day") + +hour_query = f""" +SELECT + EXTRACT(HOUR FROM pickup_datetime) AS pickup_hour, + COUNT(*) AS trip_count +FROM {DB_SCHEMA}.fct_trips +{payment_filter} +GROUP BY pickup_hour +ORDER BY pickup_hour; +""" + +hour_df = run_query(hour_query) + +hour_df["pickup_hour"] = hour_df["pickup_hour"].astype(int) + +st.line_chart( + hour_df.set_index("pickup_hour")["trip_count"] ) + +st.subheader("Data Freshness") + +freshness_query = f""" +SELECT + COUNT(*) AS row_count, + MAX(pickup_datetime) AS latest_pickup_datetime +FROM {DB_SCHEMA}.fct_trips +{payment_filter}; +""" + +freshness_df = run_query(freshness_query) + +row_count = int(freshness_df["row_count"].iloc[0]) +latest_pickup = freshness_df["latest_pickup_datetime"].iloc[0] + +col1, col2 = st.columns(2) + +with col1: + st.metric( + "Rows in fct_trips", + f"{row_count:,}" + ) + +with col2: + st.metric( + "Latest Pickup Datetime", + str(latest_pickup) + ) diff --git a/week11-streamlit/metric_definitions.md b/week11-streamlit/metric_definitions.md new file mode 100644 index 0000000..fc72016 --- /dev/null +++ b/week11-streamlit/metric_definitions.md @@ -0,0 +1,68 @@ +# 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 + + + +### Panel 1: Trip Count by Payment Type + +- **Name**: trip_count_by_payment_type +- **Description**: Total number of taxi trips grouped by payment type. This metric shows which payment methods are most commonly used by passengers. +- **Calculation**: COUNT(*) grouped by payment_type_label. +- **Data source**: dev_baraah.fct_trips (dbt mart) +- **Refresh frequency**: Rebuilt once per day. + +### Panel 2: Average Fare per Mile by Dropoff Borough + +- **Name**: avg_fare_per_mile_by_dropoff_borough +- **Description**: Average fare earned per mile for taxi trips grouped by dropoff borough. This metric compares fare efficiency across boroughs. +- **Calculation**: AVG(fare_per_mile) grouped by dropoff_borough. +- **Data source**: dev_baraah.fct_trips (dbt mart) +- **Refresh frequency**: Rebuilt once per day. + +### Panel 3: Average Trip Duration by Hour of Day + +- **Name**: avg_trip_duration_by_hour +- **Description**: Average trip duration in minutes grouped by pickup hour. This metric helps identify how trip duration changes throughout the day. +- **Calculation**: AVG(EXTRACT(EPOCH FROM (dropoff_datetime - pickup_datetime)) / 60) grouped by EXTRACT(HOUR FROM pickup_datetime). +- **Data source**: dev_baraah.fct_trips (dbt mart) +- **Refresh frequency**: Rebuilt once per day. + +## Streamlit panels + + + +### Panel 1: Headline KPIs + +- **Name**: taxi_headline_kpis +- **Description**: Displays the total trips, average trip distance, and average fare per mile. +- **Calculation**: COUNT(*), AVG(trip_distance), and AVG(fare_per_mile). +- **Data source**: dev_baraah.fct_trips (dbt mart) +- **Refresh frequency**: Rebuilt once per day. + + +### Panel 2: Trips by Hour of Day + +- **Name**: trips_by_hour +- **Description**: Shows the number of taxi trips for each pickup hour. +- **Calculation**: COUNT(*) grouped by EXTRACT(HOUR FROM pickup_datetime). +- **Data source**: dev_baraah.fct_trips (dbt mart) +- **Refresh frequency**: Rebuilt once per day. + +### Panel 3: Data Freshness + +- **Name**: taxi_data_freshness +- **Description**: Shows the total number of rows and the latest pickup datetime to verify that the data is current. +- **Calculation**: COUNT(*) and MAX(pickup_datetime). +- **Data source**: dev_baraah.fct_trips (dbt mart) +- **Refresh frequency**: Rebuilt once per day. + + + +