added script for calculating auto useage

This commit is contained in:
2025-11-04 22:45:12 -05:00
parent 45a27b01f3
commit b8bb71900d
3 changed files with 208 additions and 2 deletions

205
app/routers/fixstuff.py Normal file
View File

@@ -0,0 +1,205 @@
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder
from database import session
from sqlalchemy import func
from datetime import date
from decimal import Decimal
from app.models.auto import Auto_Delivery, Tickets_Auto_Delivery, Auto_Temp
from app.models.delivery import Delivery
# Constants from fuel_estimator
HOT_WATER_DAILY_USAGE = Decimal('2.0')
K_FACTOR_SMOOTHING_WEIGHT = Decimal('0.7')
router = APIRouter(
prefix="/fixstuff",
tags=["fixstuff"],
responses={404: {"description": "Not found"}},
)
@router.get("/lastdelivered", status_code=200)
def fix_customer_last_delivered():
"""
Updates the last_fill date in the auto_delivery table for each customer
by finding the most recent completed delivery (ticket with non-NULL fill_date)
from the auto_tickets table, matched by account_number.
Returns statistics and a list of changes made.
"""
auto_deliveries = session.query(Auto_Delivery).all()
changes = []
total_customers = len(auto_deliveries)
tickets_found = 0
updates_made = 0
for ad in auto_deliveries:
latest_ticket = session.query(Tickets_Auto_Delivery).filter(
Tickets_Auto_Delivery.account_number == ad.account_number,
Tickets_Auto_Delivery.fill_date.isnot(None)
).order_by(Tickets_Auto_Delivery.fill_date.desc()).first()
if latest_ticket:
tickets_found += 1
if ad.last_fill != latest_ticket.fill_date:
updates_made += 1
old_date = ad.last_fill
ad.last_fill = latest_ticket.fill_date
changes.append({
"id": ad.id,
"customer_full_name": ad.customer_full_name,
"before_date": str(old_date) if old_date else None,
"new_date": str(latest_ticket.fill_date)
})
session.add(ad)
session.commit()
result = {
"total_customers": total_customers,
"tickets_found": tickets_found,
"updates_made": updates_made,
"changes": changes
}
return JSONResponse(content=jsonable_encoder(result))
@router.get("/estimate_gallons/{update_db}", status_code=200)
def estimate_customer_gallons(update_db: int):
"""
Estimates current gallons for each customer based on delivery history and weather.
update_db: 0 for estimation only (no DB changes), 1 for estimation with DB updates.
No tickets: assume 100 gallons. Single delivery: use weather for 2000 sq ft home.
Multiple deliveries: use historical average. Includes address and scaling factor.
When update_db=1, updates estimated_gallons_left and house_factor in database.
"""
auto_deliveries = session.query(Auto_Delivery).all()
estimates = []
for ad in auto_deliveries:
tickets = session.query(Tickets_Auto_Delivery).filter(
Tickets_Auto_Delivery.account_number == ad.account_number,
Tickets_Auto_Delivery.fill_date.isnot(None)
).order_by(Tickets_Auto_Delivery.fill_date).all()
# Get tank size and hot water setting
tank_size = Decimal(ad.tank_size) if ad.tank_size else Decimal('275')
# Adjust effective tank capacity (not filled to 100%)
if tank_size == 275:
effective_tank = Decimal('250')
elif tank_size == 330:
effective_tank = Decimal('300')
else:
effective_tank = tank_size
hot_water = ad.hot_water_summer == 1
calculated_scaling = None # For DB update
if not tickets:
estimated_gallons = Decimal('100')
else:
last_fill = tickets[-1].fill_date
estimated_gallons_left = effective_tank
today = date.today()
if len(tickets) == 1:
# Single delivery: use weather data for 2000 sq ft home, only heat when temp <=70
if last_fill < today:
# Get daily weather data
temp_days = session.query(Auto_Temp).filter(
Auto_Temp.todays_date > last_fill,
Auto_Temp.todays_date <= today
).all()
heating_usage = Decimal('0')
hot_water_usage = Decimal('0')
house_factor_2000_sqft = Decimal('0.005') # gallons per degree day
for temp in temp_days:
degree_day = max(0, 65 - float(temp.temp_avg))
heating_usage += house_factor_2000_sqft * Decimal(degree_day)
if hot_water:
hot_water_usage += HOT_WATER_DAILY_USAGE
total_usage = heating_usage + hot_water_usage
estimated_gallons_left = max(Decimal('0'), estimated_gallons_left - total_usage)
else:
# Multiple deliveries: calculate house_factor (gallons per degree day)
daily_heating_usages = []
avg_degree_per_days = []
for i in range(len(tickets) - 1):
prev_ticket = tickets[i]
next_ticket = tickets[i + 1]
days = (next_ticket.fill_date - prev_ticket.fill_date).days
if days > 0:
# Calculate degree days for this interval from temp_avg
interval_temps = session.query(Auto_Temp).filter(
Auto_Temp.todays_date > prev_ticket.fill_date,
Auto_Temp.todays_date <= next_ticket.fill_date
).all()
total_degree_days = sum(max(0, 65 - float(temp.temp_avg)) for temp in interval_temps)
total_degree_days = Decimal(total_degree_days)
avg_degree_per_day = total_degree_days / days
total_hot_water = HOT_WATER_DAILY_USAGE * days
gallons_heating = prev_ticket.gallons_delivered - total_hot_water
if gallons_heating > 0 and total_degree_days > 0:
daily_heating = gallons_heating / days
daily_heating_usages.append(daily_heating)
avg_degree_per_days.append(avg_degree_per_day)
if daily_heating_usages and avg_degree_per_days:
average_daily_heating = sum(daily_heating_usages) / len(daily_heating_usages)
average_degree_days_per_day = sum(avg_degree_per_days) / len(avg_degree_per_days)
house_factor = average_daily_heating / average_degree_days_per_day
calculated_scaling = house_factor
else:
house_factor = Decimal('0.005') # Default
calculated_scaling = house_factor
# Calculate usage from last_fill to today using temperature-dependent heating
if last_fill < today:
temp_days = session.query(Auto_Temp).filter(
Auto_Temp.todays_date > last_fill,
Auto_Temp.todays_date <= today
).all()
heating_usage = Decimal('0')
hot_water_usage = Decimal('0')
for temp in temp_days:
degree_day = max(0, 65 - float(temp.temp_avg))
heating_usage += house_factor * Decimal(degree_day)
if hot_water:
hot_water_usage += HOT_WATER_DAILY_USAGE
total_usage = heating_usage + hot_water_usage
estimated_gallons_left = max(Decimal('0'), estimated_gallons_left - total_usage)
estimated_gallons = estimated_gallons_left
# Update database if requested
if update_db == 1:
ad.estimated_gallons_left = estimated_gallons
if calculated_scaling is not None:
ad.house_factor = calculated_scaling
session.add(ad)
last_5 = tickets[-5:] if tickets else []
scaling_factor = float(ad.house_factor) if ad.house_factor else None
estimates.append({
"id": ad.id,
"customer_full_name": ad.customer_full_name,
"account_number": ad.account_number,
"address": ad.customer_address,
"estimated_gallons": float(estimated_gallons),
"scaling_factor": scaling_factor,
"last_5_deliveries": [
{
"fill_date": str(t.fill_date),
"gallons_delivered": float(t.gallons_delivered),
"price_per_gallon": float(t.price_per_gallon),
"total_amount_customer": float(t.total_amount_customer)
} for t in last_5
]
})
if update_db == 1:
session.commit()
return JSONResponse(content=jsonable_encoder(estimates))

View File

@@ -1,5 +1,5 @@
from fastapi import FastAPI from fastapi import FastAPI
from app.routers import main, delivery, confirm from app.routers import main, delivery, confirm, fixstuff
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
import os import os
from config import load_config from config import load_config
@@ -14,6 +14,7 @@ app = FastAPI()
app.include_router(main.router) app.include_router(main.router)
app.include_router(delivery.router) app.include_router(delivery.router)
app.include_router(confirm.router) app.include_router(confirm.router)
app.include_router(fixstuff.router)
# print(ApplicationConfig.origins) # print(ApplicationConfig.origins)