diff --git a/db/backfill_county_id.sql b/db/backfill_county_id.sql new file mode 100644 index 0000000..c64e175 --- /dev/null +++ b/db/backfill_county_id.sql @@ -0,0 +1,63 @@ +-- Backfill county_id for existing oil_prices records based on state+zone mapping +-- Run this ONCE after adding the county_id column to oil_prices + +-- First, add the column if it doesn't exist +ALTER TABLE oil_prices ADD COLUMN IF NOT EXISTS county_id INTEGER; + +-- Connecticut +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'New London' AND state = 'CT') WHERE LOWER(state) = 'connecticut' AND zone = 1 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Windham' AND state = 'CT') WHERE LOWER(state) = 'connecticut' AND zone = 2 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'New Haven' AND state = 'CT') WHERE LOWER(state) = 'connecticut' AND zone = 3 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Middlesex' AND state = 'CT') WHERE LOWER(state) = 'connecticut' AND zone = 4 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'New Haven' AND state = 'CT') WHERE LOWER(state) = 'connecticut' AND zone = 5 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Hartford' AND state = 'CT') WHERE LOWER(state) = 'connecticut' AND zone = 6 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Litchfield' AND state = 'CT') WHERE LOWER(state) = 'connecticut' AND zone = 7 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Fairfield' AND state = 'CT') WHERE LOWER(state) = 'connecticut' AND zone = 8 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Tolland' AND state = 'CT') WHERE LOWER(state) = 'connecticut' AND zone = 9 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Litchfield' AND state = 'CT') WHERE LOWER(state) = 'connecticut' AND zone = 10 AND county_id IS NULL; + +-- Massachusetts +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Suffolk' AND state = 'MA') WHERE LOWER(state) = 'massachusetts' AND zone = 1 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Middlesex' AND state = 'MA') WHERE LOWER(state) = 'massachusetts' AND zone = 2 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Norfolk' AND state = 'MA') WHERE LOWER(state) = 'massachusetts' AND zone = 3 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Plymouth' AND state = 'MA') WHERE LOWER(state) = 'massachusetts' AND zone = 4 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Middlesex' AND state = 'MA') WHERE LOWER(state) = 'massachusetts' AND zone = 5 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Bristol' AND state = 'MA') WHERE LOWER(state) = 'massachusetts' AND zone = 6 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Barnstable' AND state = 'MA') WHERE LOWER(state) = 'massachusetts' AND zone = 7 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Essex' AND state = 'MA') WHERE LOWER(state) = 'massachusetts' AND zone = 8 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Essex' AND state = 'MA') WHERE LOWER(state) = 'massachusetts' AND zone = 9 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Worcester' AND state = 'MA') WHERE LOWER(state) = 'massachusetts' AND zone = 10 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Worcester' AND state = 'MA') WHERE LOWER(state) = 'massachusetts' AND zone = 11 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Hampshire' AND state = 'MA') WHERE LOWER(state) = 'massachusetts' AND zone = 12 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Hampden' AND state = 'MA') WHERE LOWER(state) = 'massachusetts' AND zone = 13 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Franklin' AND state = 'MA') WHERE LOWER(state) = 'massachusetts' AND zone = 14 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Berkshire' AND state = 'MA') WHERE LOWER(state) = 'massachusetts' AND zone = 15 AND county_id IS NULL; + +-- New Hampshire +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Coos' AND state = 'NH') WHERE LOWER(state) = 'newhampshire' AND zone = 1 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Strafford' AND state = 'NH') WHERE LOWER(state) = 'newhampshire' AND zone = 2 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Merrimack' AND state = 'NH') WHERE LOWER(state) = 'newhampshire' AND zone = 3 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Grafton' AND state = 'NH') WHERE LOWER(state) = 'newhampshire' AND zone = 4 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Cheshire' AND state = 'NH') WHERE LOWER(state) = 'newhampshire' AND zone = 5 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Hillsborough' AND state = 'NH') WHERE LOWER(state) = 'newhampshire' AND zone = 6 AND county_id IS NULL; + +-- Rhode Island +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Newport' AND state = 'RI') WHERE LOWER(state) = 'rhodeisland' AND zone = 1 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Providence' AND state = 'RI') WHERE LOWER(state) = 'rhodeisland' AND zone = 2 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Washington' AND state = 'RI') WHERE LOWER(state) = 'rhodeisland' AND zone = 3 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Kent' AND state = 'RI') WHERE LOWER(state) = 'rhodeisland' AND zone = 4 AND county_id IS NULL; + +-- Maine +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Cumberland' AND state = 'ME') WHERE LOWER(state) = 'maine' AND zone = 1 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Kennebec' AND state = 'ME') WHERE LOWER(state) = 'maine' AND zone = 2 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Androscoggin' AND state = 'ME') WHERE LOWER(state) = 'maine' AND zone = 3 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'York' AND state = 'ME') WHERE LOWER(state) = 'maine' AND zone = 4 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Knox' AND state = 'ME') WHERE LOWER(state) = 'maine' AND zone = 5 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Penobscot' AND state = 'ME') WHERE LOWER(state) = 'maine' AND zone = 6 AND county_id IS NULL; +UPDATE oil_prices SET county_id = (SELECT id FROM county WHERE name = 'Washington' AND state = 'ME') WHERE LOWER(state) = 'maine' AND zone = 7 AND county_id IS NULL; + +-- Verify +SELECT state, zone, county_id, COUNT(*) as record_count +FROM oil_prices +GROUP BY state, zone, county_id +ORDER BY state, zone; diff --git a/schema.sql b/schema.sql index 7d2a1c7..8739256 100755 --- a/schema.sql +++ b/schema.sql @@ -58,6 +58,22 @@ CREATE TABLE listings ( last_edited TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); +-- Oil prices (scraped from NewEnglandOil.com / MaineOil.com) +CREATE TABLE oil_prices ( + id SERIAL PRIMARY KEY, + state VARCHAR(100), + zone INTEGER, + name VARCHAR(255), + price DOUBLE PRECISION, + date VARCHAR(20), + scrapetimestamp TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + company_id INTEGER, + county_id INTEGER +); + +-- If the table already exists, add county_id +-- ALTER TABLE oil_prices ADD COLUMN county_id INTEGER; + -- If the table already exists, add the new columns -- ALTER TABLE listings ADD COLUMN price_per_gallon_cash DOUBLE PRECISION; -- ALTER TABLE listings ADD COLUMN note TEXT; diff --git a/src/main.rs b/src/main.rs index 3ddceac..5ce3acf 100755 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ use crate::auth::auth::{auth_middleware, login, register, logout}; use crate::data::data::get_user; use crate::state::data::{get_counties_by_state, get_county_by_id}; use crate::listing::data::{get_listings, get_listing_by_id, get_listings_by_county, create_listing, update_listing, delete_listing}; +use crate::oil_prices::data::get_oil_prices_by_county; use axum::middleware; use sqlx::PgPool; use std::sync::Arc; @@ -18,6 +19,7 @@ mod data; mod state; mod company; mod listing; +mod oil_prices; #[tokio::main] async fn main() { @@ -82,7 +84,8 @@ async fn main() { .route("/categories", axum::routing::get(crate::company::category::get_all_categories)) .route("/state/:state_abbr", axum::routing::get(get_counties_by_state)) .route("/state/:state_abbr/:county_id", axum::routing::get(get_county_by_id)) - .route("/listings/county/:county_id", axum::routing::get(get_listings_by_county)); + .route("/listings/county/:county_id", axum::routing::get(get_listings_by_county)) + .route("/oil-prices/county/:county_id", axum::routing::get(get_oil_prices_by_county)); let app = public_routes .merge(protected_routes) diff --git a/src/oil_prices/data.rs b/src/oil_prices/data.rs new file mode 100644 index 0000000..4d8a95c --- /dev/null +++ b/src/oil_prices/data.rs @@ -0,0 +1,34 @@ +use axum::{ + extract::{Path, State}, + http::StatusCode, + Json, +}; +use crate::auth::structs::AppState; +use crate::oil_prices::structs::OilPrice; +use serde_json::json; + +pub async fn get_oil_prices_by_county( + State(app_state): State, + Path(county_id): Path, +) -> Result>, (StatusCode, Json)> { + tracing::info!(county_id = county_id, "Fetching oil prices by county"); + match sqlx::query_as::<_, OilPrice>( + "SELECT id, state, zone, name, price, date, scrapetimestamp, county_id FROM oil_prices WHERE county_id = $1 ORDER BY price ASC" + ) + .bind(county_id) + .fetch_all(&*app_state.db) + .await + { + Ok(prices) => { + tracing::info!(county_id = county_id, count = prices.len(), "Oil prices retrieved"); + Ok(Json(prices)) + }, + Err(e) => { + tracing::error!(county_id = county_id, error = %e, "Failed to fetch oil prices"); + Err(( + StatusCode::INTERNAL_SERVER_ERROR, + Json(json!({"error": format!("Failed to fetch oil prices: {}", e)})) + )) + }, + } +} diff --git a/src/oil_prices/mod.rs b/src/oil_prices/mod.rs new file mode 100644 index 0000000..0499a0b --- /dev/null +++ b/src/oil_prices/mod.rs @@ -0,0 +1,2 @@ +pub mod structs; +pub mod data; diff --git a/src/oil_prices/structs.rs b/src/oil_prices/structs.rs new file mode 100644 index 0000000..e104d31 --- /dev/null +++ b/src/oil_prices/structs.rs @@ -0,0 +1,15 @@ +use serde::{Deserialize, Serialize}; +use sqlx::FromRow; +use chrono::NaiveDateTime; + +#[derive(Debug, Serialize, Deserialize, FromRow)] +pub struct OilPrice { + pub id: i32, + pub state: Option, + pub zone: Option, + pub name: Option, + pub price: Option, + pub date: Option, + pub scrapetimestamp: Option, + pub county_id: Option, +}