diff --git a/app/models/delivery.py b/app/models/delivery.py index a94a671..7f5d218 100644 --- a/app/models/delivery.py +++ b/app/models/delivery.py @@ -25,7 +25,7 @@ class Delivery(Base): customer_filled = Column(INTEGER) delivery_status = Column(INTEGER) - when_ordered = Column(DATE(), ) + when_ordered = Column(DATE()) when_delivered = Column(DATE()) expected_delivery_date = Column(DATE(), default=None) automatic = Column(INTEGER) diff --git a/app/routers/confirm.py b/app/routers/confirm.py index da8394b..ea7d37f 100644 --- a/app/routers/confirm.py +++ b/app/routers/confirm.py @@ -1,15 +1,14 @@ - -from fastapi import APIRouter, Request -from datetime import date +from fastapi import APIRouter, Request, HTTPException +from datetime import date from database import session -from pyowm import OWM from decimal import Decimal -from app.models.auto import Auto_Delivery, Tickets_Auto_Delivery + +# Import your models +from app.models.auto import Auto_Delivery, Tickets_Auto_Delivery from app.models.pricing import Pricing_Oil_Oil - - -from app.script.update_auto import calc_home_factor +# Import the new estimator class +from app.script.fuel_estimator import FuelEstimator router = APIRouter( prefix="/confirm", @@ -20,121 +19,98 @@ router = APIRouter( @router.put("/auto/update/{autoid}") async def update_auto(autoid: int, request: Request): + """ + Confirms a delivery, updates the ticket, and triggers the self-correcting + K-Factor (house_factor) refinement. + """ + try: + request_body = await request.json() + gallons_delivered = Decimal(request_body['gallons_delivered']) - request_body = await request.json() - gallons_delivered = request_body['gallons_delivered'] - gallons_delivered = Decimal(gallons_delivered) + auto_customer = session.query(Auto_Delivery).filter(Auto_Delivery.id == autoid).first() + if not auto_customer: + raise HTTPException(status_code=404, detail="Auto Delivery profile not found") - get_auto_delivery = (session - .query(Auto_Delivery) - .filter(Auto_Delivery.id == autoid) - .first()) + if auto_customer.open_ticket_id is None: + raise HTTPException(status_code=400, detail="No open ticket found for this auto-delivery. Cannot confirm delivery.") + + ticket = session.query(Tickets_Auto_Delivery).filter(Tickets_Auto_Delivery.id == auto_customer.open_ticket_id).first() + if not ticket: + raise HTTPException(status_code=404, detail=f"Open ticket with ID {auto_customer.open_ticket_id} not found.") - gallons_put_in_home = Decimal(gallons_delivered) + ticket.gallons_delivered = gallons_delivered + ticket.total_amount_customer = ticket.price_per_gallon * gallons_delivered + estimator = FuelEstimator(session=session) + estimator.refine_factor_after_delivery(ticket=ticket) - customer_home_factor = get_auto_delivery.house_factor + session.add(ticket) + session.add(auto_customer) + session.commit() - new_home_factor = calc_home_factor(gallons_put_in_home = gallons_put_in_home, - current_house_factor=customer_home_factor) - - gallons_left_buffer = int(get_auto_delivery.tank_size) - 30 - - - get_auto_delivery.house_factor = new_home_factor - get_auto_delivery.tank_height = 'Full' - get_auto_delivery.last_fill = date.today() - get_auto_delivery.estimated_gallons_left = gallons_left_buffer - get_auto_delivery.estimated_gallons_left_prev_day = gallons_left_buffer - get_auto_delivery.auto_status = 1 - get_auto_delivery.days_since_last_fill = 0 - - # Update the associated ticket if it exists - if get_auto_delivery.open_ticket_id is not None: - get_ticket = (session - .query(Tickets_Auto_Delivery) - .filter(Tickets_Auto_Delivery.id == get_auto_delivery.open_ticket_id) - .first()) - if get_ticket: - get_ticket.gallons_delivered = gallons_delivered - get_ticket.total_amount_customer = get_ticket.price_per_gallon * gallons_delivered - session.add(get_ticket) - - session.add(get_auto_delivery) - - session.commit() - - return ({"ok": True}), 200 - + return {"ok": True, "message": "Delivery confirmed and customer factor refined successfully."} + except Exception as e: + session.rollback() + raise HTTPException(status_code=500, detail=f"An internal error occurred: {str(e)}") @router.post("/auto/create/{autoid}") async def create_auto_ticket(autoid: int, request: Request): - - + """ + Creates a new delivery ticket and links it to the auto-delivery customer profile. + """ request_body = await request.json() - gallons_delivered = request_body['gallons_delivered'] + gallons_delivered = request_body['gallons_delivered'] # Estimated gallons for pre-auth gallons_delivered = Decimal(gallons_delivered) payment_type = request_body.get('payment_type') payment_card_id = request_body.get('payment_card_id') payment_status = request_body.get('payment_status') - - get_auto_delivery = (session - .query(Auto_Delivery) - .filter(Auto_Delivery.id == autoid) - .first()) - get_todays_price = (session.query(Pricing_Oil_Oil) - .order_by(Pricing_Oil_Oil.id.desc()) - .first()) - gallons_put_in_home = Decimal(gallons_delivered) + get_auto_delivery = session.query(Auto_Delivery).filter(Auto_Delivery.id == autoid).first() + get_todays_price = session.query(Pricing_Oil_Oil).order_by(Pricing_Oil_Oil.id.desc()).first() + todays_price = Decimal(get_todays_price.price_for_customer) - total_amount = gallons_put_in_home * todays_price + total_amount = gallons_delivered * todays_price create_new_ticket = Tickets_Auto_Delivery( - customer_id = get_auto_delivery.customer_id, - account_number = get_auto_delivery.account_number, - customer_town = get_auto_delivery.customer_town, - customer_state = get_auto_delivery.customer_state, - customer_address = get_auto_delivery.customer_address, - customer_zip =get_auto_delivery.customer_zip, - customer_full_name = get_auto_delivery.customer_full_name, - oil_prices_id = get_todays_price.id, - gallons_delivered = gallons_delivered, - price_per_gallon = get_todays_price.price_for_customer, - total_amount_customer = total_amount, - fill_date = date.today(), - payment_type = payment_type, - payment_card_id = payment_card_id, - payment_status = payment_status, + customer_id=get_auto_delivery.customer_id, + account_number=get_auto_delivery.account_number, + customer_town=get_auto_delivery.customer_town, + customer_state=get_auto_delivery.customer_state, + customer_address=get_auto_delivery.customer_address, + customer_zip=get_auto_delivery.customer_zip, + customer_full_name=get_auto_delivery.customer_full_name, + oil_prices_id=get_todays_price.id, + gallons_delivered=gallons_delivered, + price_per_gallon=get_todays_price.price_for_customer, + total_amount_customer=total_amount, + fill_date=date.today(), + payment_type=payment_type, + payment_card_id=payment_card_id, + payment_status=payment_status, ) - session.add(create_new_ticket) - session.flush() # Generate the ID for create_new_ticket + session.flush() - # Update the auto_delivery with the open ticket id get_auto_delivery.open_ticket_id = create_new_ticket.id session.add(get_auto_delivery) - session.commit() # Commit all changes in one transaction + session.commit() - return ({ - "ok": True, - "auto_ticket_id":create_new_ticket.id - }), 200 + return ({"ok": True, "auto_ticket_id":create_new_ticket.id}), 200 @router.put("/auto/close_ticket/{ticket_id}") async def close_ticket(ticket_id: int): """ - Close an auto ticket by updating payment_status to 3 and setting open_ticket_id to None on the auto delivery. + Closes an auto ticket by updating payment_status and unlinking from the auto delivery profile. """ ticket = session.query(Tickets_Auto_Delivery).filter(Tickets_Auto_Delivery.id == ticket_id).first() if ticket: - ticket.payment_status = 3 + ticket.payment_status = 3 # Assuming 3 means closed/paid session.add(ticket) delivery = session.query(Auto_Delivery).filter(Auto_Delivery.open_ticket_id == ticket_id).first() @@ -149,8 +125,7 @@ async def close_ticket(ticket_id: int): @router.delete("/auto/delete_ticket/{ticket_id}") async def delete_ticket(ticket_id: int): """ - Delete an auto ticket and remove its reference from the associated auto delivery. - Used when authorization fails and the ticket needs to be cleaned up. + Deletes an auto ticket and unlinks its reference, used for cleanup (e.g., failed payment). """ ticket = session.query(Tickets_Auto_Delivery).filter(Tickets_Auto_Delivery.id == ticket_id).first() if ticket: @@ -165,13 +140,11 @@ async def delete_ticket(ticket_id: int): return {"ok": True}, 200 - - - @router.post("/auto/create/nopreauth/{autoid}") async def create_auto_ticket_no_preauth(autoid: int, request: Request): - - + """ + Creates a new delivery ticket without pre-authorization details. + """ request_body = await request.json() gallons_delivered = request_body['gallons_delivered'] gallons_delivered = Decimal(gallons_delivered) @@ -179,43 +152,31 @@ async def create_auto_ticket_no_preauth(autoid: int, request: Request): payment_card_id = 0 payment_status = 3 - - get_auto_delivery = (session - .query(Auto_Delivery) - .filter(Auto_Delivery.id == autoid) - .first()) - get_todays_price = (session.query(Pricing_Oil_Oil) - .order_by(Pricing_Oil_Oil.id.desc()) - .first()) - gallons_put_in_home = Decimal(gallons_delivered) + get_auto_delivery = session.query(Auto_Delivery).filter(Auto_Delivery.id == autoid).first() + get_todays_price = session.query(Pricing_Oil_Oil).order_by(Pricing_Oil_Oil.id.desc()).first() + todays_price = Decimal(get_todays_price.price_for_customer) - total_amount = gallons_put_in_home * todays_price + total_amount = gallons_delivered * todays_price create_new_ticket = Tickets_Auto_Delivery( - customer_id = get_auto_delivery.customer_id, - account_number = get_auto_delivery.account_number, - customer_town = get_auto_delivery.customer_town, - customer_state = get_auto_delivery.customer_state, - customer_address = get_auto_delivery.customer_address, - customer_zip =get_auto_delivery.customer_zip, - customer_full_name = get_auto_delivery.customer_full_name, - oil_prices_id = get_todays_price.id, - gallons_delivered = gallons_delivered, - price_per_gallon = get_todays_price.price_for_customer, - total_amount_customer = total_amount, - fill_date = date.today(), - payment_type = payment_type, - payment_card_id = payment_card_id, - payment_status = payment_status, + customer_id=get_auto_delivery.customer_id, + account_number=get_auto_delivery.account_number, + customer_town=get_auto_delivery.customer_town, + customer_state=get_auto_delivery.customer_state, + customer_address=get_auto_delivery.customer_address, + customer_zip=get_auto_delivery.customer_zip, + customer_full_name=get_auto_delivery.customer_full_name, + oil_prices_id=get_todays_price.id, + gallons_delivered=gallons_delivered, + price_per_gallon=get_todays_price.price_for_customer, + total_amount_customer=total_amount, + fill_date=date.today(), + payment_type=payment_type, + payment_card_id=payment_card_id, + payment_status=payment_status, ) - session.add(create_new_ticket) - - - session.commit() # Commit all changes in one transaction - - return ({ - "ok": True, - "auto_ticket_id":create_new_ticket.id - }), 200 \ No newline at end of file + session.commit() + + return ({"ok": True, "auto_ticket_id":create_new_ticket.id}), 200 \ No newline at end of file diff --git a/app/routers/delivery.py b/app/routers/delivery.py index 6551ba5..8560008 100644 --- a/app/routers/delivery.py +++ b/app/routers/delivery.py @@ -1,8 +1,7 @@ -from fastapi import APIRouter, Request, Depends +from fastapi import APIRouter from fastapi.responses import JSONResponse from fastapi.encoders import jsonable_encoder -from database import session, get_db -from sqlalchemy.orm import Session +from database import session from app.models.auto import Auto_Delivery, Tickets_Auto_Delivery from app.models.delivery import Delivery @@ -113,6 +112,6 @@ def update_auto_status(auto_id: int): if update_status: update_status.auto_status = 3 - db.commit() + session.commit() return {"message": "Auto status updated to 3"} return {"error": "Auto delivery not found"} diff --git a/app/routers/main.py b/app/routers/main.py index 8b15c2b..545d6dc 100644 --- a/app/routers/main.py +++ b/app/routers/main.py @@ -1,13 +1,8 @@ -from fastapi import APIRouter, Request -from datetime import datetime, date +from fastapi import APIRouter, HTTPException from database import session -from pyowm import OWM - -from app.models.auto import Auto_Delivery, Auto_Temp, Auto_Update -from app.models.delivery import Delivery - - +from app.script.fuel_estimator import FuelEstimator +from app.script.temp_getter import fetch_and_store_daily_temp router = APIRouter( prefix="/main", @@ -15,150 +10,40 @@ router = APIRouter( responses={404: {"description": "Not found"}}, ) -def Average(lst): - return sum(lst) / len(lst) + +@router.get("/temp", status_code=200) +def update_temp_manually(): + """ + Manually triggers the fetch and storage of today's temperature. + This is useful for testing or for manual intervention if the cron job fails. + """ + try: + success = fetch_and_store_daily_temp() + if success: + session.commit() + return {"ok": True, "message": "Temperature updated or already exists."} + else: + # The function already rolled back, so just return an error + return HTTPException(status_code=500, detail="Failed to fetch temperature from the weather API.") + except Exception as e: + session.rollback() + raise HTTPException(status_code=500, detail=f"An unexpected server error occurred: {str(e)}") + @router.get("/update", status_code=200) -def update_auto_customers(): +def update_all_customer_fuel_levels(): + """ + This endpoint triggers the daily update for all customers. + It should be called once per day by a cron job or scheduler. + """ try: - - see_if_autos_updated = (session.query(Auto_Update) - .filter(Auto_Update.last_updated == date.today()) - .first() - ) - except: + estimator = FuelEstimator() + result = estimator.run_daily_update() + return result + except Exception as e: session.rollback() - see_if_autos_updated = None - - if see_if_autos_updated is not None: - return ({"ok": True}), 200 - else: - - create_new_update = Auto_Update( - last_updated = date.today() - ) - today_temp = (session - .query(Auto_Temp) - .order_by(Auto_Temp.id.desc()) - .first()) - - # get all automatic customers - auto_customers = (session - .query(Auto_Delivery) - .filter(Auto_Delivery.last_fill != None) - .order_by(Auto_Delivery.last_updated.desc()) - .limit(25)) - - for f in auto_customers: - - # figure out days since last fill - d1 = date.today() - - d0 = f.last_fill - - delta = d1 - d0 - f.days_since_last_fill = delta.days - # figure out how much temperature effects oil - today_temptemp_avg = int(today_temp.temp_avg) - if today_temptemp_avg >= 65.01: - use_day = 1 - elif 50.01 <= today_temptemp_avg <= 65: - use_day = 2 - elif 35.01 <= today_temptemp_avg <= 50: - use_day = 3 - elif 30.01 <= today_temptemp_avg <= 35: - use_day = 4 - elif 25.01 <= today_temptemp_avg <= 30: - use_day = 5 - elif 20.01 <= today_temptemp_avg <= 25: - use_day = 7 - elif 15.01 <= today_temptemp_avg <= 20: - use_day = 8 - elif 10.01 <= today_temptemp_avg <= 15: - use_day = 9 - elif 5.01 <= today_temptemp_avg <= 10: - use_day = 10 - elif 0.01 <= today_temptemp_avg <= 5: - use_day = 12 - elif -20 <= today_temptemp_avg <= -1: - use_day = 15 - else: - use_day = 0 - - # times temp factory by house factor - - gallon_use_today = f.house_factor * use_day + # Log the exception e + return {"ok": False, "message": "An internal error occurred."} - # get previous day gallons left - f.estimated_gallons_left_prev_day = f.estimated_gallons_left - - # get estimated gallons left - get_gallons_left = f.estimated_gallons_left_prev_day - gallon_use_today - - - - f.estimated_gallons_left = get_gallons_left - f.last_updated = date.today() - session.add(create_new_update) - session.add(f) - session.commit() - - - return ({"ok": True}), 200 - - -@router.get("/temp", status_code=200) -def update_temp(): - try: - see_if_temp_exists = (session.query(Auto_Temp) - .filter(Auto_Temp.todays_date == date.today()) - .first()) - except: - see_if_temp_exists = None - if see_if_temp_exists is not None: - return ({"ok": True}), 200 - else: - try: - temps = [] - - owm = OWM('21648d8c8d1a4ae495ace0b7810b4d36') - mgr = owm.weather_manager() - - # Search for current weather in London (Great Britain) and get details - observation = mgr.weather_at_place('Worcester, US') - w = observation.weather - - temp_dict_fahrenheit = w.temperature() # a dict in Kelvin units (default when no temperature units provided) - temp_dict_fahrenheit['temp_min'] - temp_dict_fahrenheit['temp_max'] - temp_dict_fahrenheit = w.temperature('fahrenheit') - - low_temp = temp_dict_fahrenheit['temp_min'] - high_temp = temp_dict_fahrenheit['temp_max'] - temps.append(temp_dict_fahrenheit['temp_max']) - temps.append(temp_dict_fahrenheit['temp_min']) - - get_avg = Average(temps) - rounded_temp = round(get_avg) - - dday = (65 - ((low_temp + high_temp) /2)) - - add_new_temp = Auto_Temp( - temp = temp_dict_fahrenheit['temp'], - temp_max = temp_dict_fahrenheit['temp_max'], - temp_min = temp_dict_fahrenheit['temp_min'], - temp_avg = rounded_temp, - degree_day = dday, - todays_date = date.today() - ) - - session.add(add_new_temp) - session.commit() - - return ({"ok": True}), 200 - - - except: - return ({"ok": True}), 200 \ No newline at end of file diff --git a/app/script/fuel_estimator.py b/app/script/fuel_estimator.py new file mode 100644 index 0000000..21132ce --- /dev/null +++ b/app/script/fuel_estimator.py @@ -0,0 +1,153 @@ +from sqlalchemy.orm import Session +from sqlalchemy import func +from datetime import date, timedelta +from decimal import Decimal + +# Import your existing database models +from app.models.auto import Auto_Delivery, Auto_Temp, Auto_Update, Tickets_Auto_Delivery + +# --- Constants for the Model --- +# This is a baseline daily usage for homes that use oil for hot water. +# A typical value is 0.5 to 1.0 gallons per day. Adjust as needed. +HOT_WATER_DAILY_USAGE = Decimal('0.7') + +# This determines how quickly the K-Factor adjusts. +# 0.7 means 70% weight is given to the historical factor and 30% to the new one. +# This prevents wild swings from a single unusual delivery period. +K_FACTOR_SMOOTHING_WEIGHT = Decimal('0.7') + + +class FuelEstimator: + def __init__(self, session: Session): + self.session = session + + def _get_weather_for_date(self, target_date: date) -> Auto_Temp | None: + """Helper to fetch weather data for a specific date.""" + return self.session.query(Auto_Temp).filter(Auto_Temp.todays_date == target_date).first() + + def run_daily_update(self): + """ + Main function to run once per day. It updates the estimated fuel level + for all active automatic delivery customers. The calling function must commit the session. + """ + today = date.today() + + # 1. Check if the update has already run today + if self.session.query(Auto_Update).filter(Auto_Update.last_updated == today).first(): + print(f"Daily update for {today} has already been completed.") + return {"ok": True, "message": "Update already run today."} + + # 2. Get today's weather data (specifically the Heating Degree Days) + todays_weather = self._get_weather_for_date(today) + if not todays_weather: + print(f"Error: Weather data for {today} not found. Cannot run update.") + return {"ok": False, "message": f"Weather data for {today} not found."} + + # Degree days can't be negative for this calculation. If it's warm, HDD is 0. + degree_day = Decimal(max(0, todays_weather.degree_day)) + + # 3. Get all active automatic customers + auto_customers = self.session.query(Auto_Delivery).filter( + Auto_Delivery.auto_status == 1 # Assuming 1 means active + ).all() + + if not auto_customers: + print("No active automatic delivery customers found.") + return {"ok": True, "message": "No active customers to update."} + + print(f"Staging daily fuel update for {len(auto_customers)} customers...") + + # 4. Loop through each customer and update their fuel level + for customer in auto_customers: + heating_usage = customer.house_factor * degree_day + + hot_water_usage = Decimal('0.0') + if customer.hot_water_summer == 1: + hot_water_usage = HOT_WATER_DAILY_USAGE + + gallons_used_today = heating_usage + hot_water_usage + + customer.estimated_gallons_left_prev_day = customer.estimated_gallons_left + new_estimated_gallons = customer.estimated_gallons_left - gallons_used_today + customer.estimated_gallons_left = max(Decimal('0.0'), new_estimated_gallons) + customer.last_updated = today + if customer.days_since_last_fill is not None: + customer.days_since_last_fill += 1 + + # 5. Log that today's update is complete + new_update_log = Auto_Update(last_updated=today) + self.session.add(new_update_log) + + print("Daily update staged. Awaiting commit.") + return {"ok": True, "message": f"Successfully staged updates for {len(auto_customers)} customers."} + + def refine_factor_after_delivery(self, ticket: Tickets_Auto_Delivery): + """ + This is the self-correction logic. It recalculates and refines the customer's + K-Factor (house_factor) after a delivery. The calling function must commit the session. + """ + customer = self.session.query(Auto_Delivery).filter( + Auto_Delivery.customer_id == ticket.customer_id + ).first() + + if not customer or not customer.last_fill: + print(f"Cannot refine K-Factor: Customer {ticket.customer_id} not found or has no previous fill date. Resetting tank only.") + self._update_tank_after_fill(customer, ticket) + return + + start_date = customer.last_fill + end_date = ticket.fill_date + + if start_date >= end_date: + print(f"Cannot refine K-Factor for customer {ticket.customer_id}: New fill date is not after the last one. Resetting tank only.") + self._update_tank_after_fill(customer, ticket) + return + + total_hdd_result = self.session.query(func.sum(Auto_Temp.degree_day)).filter( + Auto_Temp.todays_date > start_date, + Auto_Temp.todays_date <= end_date, + Auto_Temp.degree_day > 0 + ).scalar() + + total_hdd = Decimal(total_hdd_result or 0) + + total_hot_water_usage = Decimal('0.0') + if customer.hot_water_summer == 1: + num_days = (end_date - start_date).days + total_hot_water_usage = Decimal(num_days) * HOT_WATER_DAILY_USAGE + + gallons_for_heating = ticket.gallons_delivered - total_hot_water_usage + if gallons_for_heating <= 0 or total_hdd == 0: + print(f"Cannot calculate new K-Factor for customer {ticket.customer_id}. (HDD: {total_hdd}, Heating Gallons: {gallons_for_heating}). Resetting tank only.") + self._update_tank_after_fill(customer, ticket) + return + + new_k_factor = gallons_for_heating / total_hdd + + current_k_factor = customer.house_factor + smoothed_k_factor = (current_k_factor * K_FACTOR_SMOOTHING_WEIGHT) + (new_k_factor * (Decimal('1.0') - K_FACTOR_SMOOTHING_WEIGHT)) + + print(f"Refining K-Factor for Customer ID {customer.customer_id}:") + print(f" - Old K-Factor: {current_k_factor:.4f}, New Smoothed K-Factor: {smoothed_k_factor:.4f}") + + customer.house_factor = smoothed_k_factor + self._update_tank_after_fill(customer, ticket) + + print(f"K-Factor and tank status for Customer {customer.customer_id} staged for update.") + + def _update_tank_after_fill(self, customer: Auto_Delivery, ticket: Tickets_Auto_Delivery): + """Helper to reset customer tank status after a fill-up.""" + customer.last_fill = ticket.fill_date + customer.days_since_last_fill = 0 + + # A "fill-up" means the tank is full. This is critical for accuracy. + if customer.tank_size and Decimal(customer.tank_size) > 0: + customer.estimated_gallons_left = Decimal(customer.tank_size) + else: + # Default to a common tank size if not specified, e.g., 275 + customer.estimated_gallons_left = Decimal('275.0') + + # The previous day's value should match the new full value on a fill day. + customer.estimated_gallons_left_prev_day = customer.estimated_gallons_left + customer.last_updated = date.today() + customer.auto_status = 1 # Reactivate the customer \ No newline at end of file diff --git a/app/script/temp_getter.py b/app/script/temp_getter.py new file mode 100644 index 0000000..b0765f7 --- /dev/null +++ b/app/script/temp_getter.py @@ -0,0 +1,73 @@ +from datetime import date +import requests +from decimal import Decimal + +# Import your database model and the session from your database configuration +from app.models.auto import Auto_Temp +from database import session + +def Average(lst): + """Calculates the average of a list of numbers.""" + return sum(lst) / len(lst) + +def fetch_and_store_daily_temp() -> bool: + """ + Fetches the current day's weather for Worcester, MA, calculates the + Heating Degree Day (HDD), and stores it in the database if it doesn't already exist. + + Returns: + bool: True if the temperature is successfully fetched and stored (or already exists), + False if an error occurs. + """ + # 1. Check if the temperature for today already exists in the database + today = date.today() + if session.query(Auto_Temp).filter(Auto_Temp.todays_date == today).first(): + print(f"Temperature for {today} already exists in the database. Skipping fetch.") + return True + + # 2. If it doesn't exist, fetch it from the API + print(f"Fetching temperature for {today} from OpenWeatherMap...") + try: + # API key and location + api_key = '21648d8c8d1a4ae495ace0b7810b4d36' + location = 'Worcester,US' + + # Make request to OpenWeatherMap Current Weather API with imperial units for Fahrenheit + url = f"http://api.openweathermap.org/data/2.5/weather?q={location}&appid={api_key}&units=imperial" + response = requests.get(url) + response.raise_for_status() # Raise an error for bad status codes + + data = response.json() + + # Extract temperatures in Fahrenheit + main = data.get('main', {}) + current_temp = Decimal(main.get('temp', 0)) + low_temp = Decimal(main.get('temp_min', 0)) + high_temp = Decimal(main.get('temp_max', 0)) + + # Calculate average temperature and Heating Degree Days (HDD) + avg_temp = (low_temp + high_temp) / 2 + + # HDD is based on a baseline of 65°F. It cannot be negative for heating calculations. + degree_day = max(Decimal(0), 65 - avg_temp) + + # 3. Create a new database object + add_new_temp = Auto_Temp( + temp=current_temp, + temp_max=high_temp, + temp_min=low_temp, + temp_avg=round(avg_temp, 2), + degree_day=round(degree_day), + todays_date=today + ) + + # 4. Add the new record to the session (it will be committed by the calling function) + session.add(add_new_temp) + print(f"Successfully fetched and staged temperature for {today}.") + return True + + except Exception as e: + print(f"An error occurred while fetching weather data: {e}") + # Make sure to rollback the session in case of a partial failure + session.rollback() + return False diff --git a/app/script/update_auto.py b/app/script/update_auto.py deleted file mode 100644 index e88678e..0000000 --- a/app/script/update_auto.py +++ /dev/null @@ -1,26 +0,0 @@ -from decimal import Decimal -from database import session - - - - -def calc_home_factor(gallons_put_in_home, current_house_factor): - - - if 200.01 <= gallons_put_in_home <= 500: - customer_home_factor = Decimal(current_house_factor) + Decimal(1) - elif 170.01 <= gallons_put_in_home <= 200: - customer_home_factor = Decimal(current_house_factor) + Decimal(1.5) - elif 150.01 <= gallons_put_in_home <= 170: - customer_home_factor = Decimal(current_house_factor) + Decimal(1.25) - elif 120.01 <= gallons_put_in_home <= 150: - customer_home_factor = Decimal(current_house_factor) - Decimal(.25) - elif 90.01 <= gallons_put_in_home <= 120: - customer_home_factor = Decimal(current_house_factor) - Decimal(.50) - elif 0.01 <= gallons_put_in_home <= 90: - customer_home_factor = Decimal(current_house_factor) - Decimal(.75) - else: - customer_home_factor = Decimal(current_house_factor) - if customer_home_factor <= 0: - customer_home_factor = Decimal(.25) - return customer_home_factor \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index c934fb0..4f04b95 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,5 +2,4 @@ fastapi uvicorn[standard] psycopg2-binary sqlalchemy -pyowm -setuptools \ No newline at end of file +requests