major claude changes

This commit is contained in:
2026-01-28 21:54:58 -05:00
parent 6316309184
commit ac4354716b
15 changed files with 269 additions and 95 deletions

42
app/auth.py Normal file
View File

@@ -0,0 +1,42 @@
import re
import logging
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.orm import Session
from database import get_db
from app.models.auth import Auth_User
logger = logging.getLogger(__name__)
security = HTTPBearer()
def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(security),
db: Session = Depends(get_db)
) -> Auth_User:
"""
Validates the Bearer token and returns the authenticated user.
Raises HTTPException 401 if token is invalid or user not found.
"""
token = credentials.credentials
user = db.query(Auth_User).filter(Auth_User.api_key == token).first()
if not user:
logger.warning("Authentication failed: invalid API key")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication token",
headers={"WWW-Authenticate": "Bearer"},
)
if user.active != 1:
logger.warning(f"Authentication failed: user {user.username} is inactive")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="User account is inactive",
headers={"WWW-Authenticate": "Bearer"},
)
return user

20
app/models/auth.py Normal file
View File

@@ -0,0 +1,20 @@
from sqlalchemy import Column, Integer, String, Text, TIMESTAMP
from database import Base
class Auth_User(Base):
__tablename__ = 'auth_users'
__table_args__ = {"schema": "public"}
id = Column(Integer, primary_key=True, autoincrement=True)
uuid = Column(String(32))
api_key = Column(Text)
username = Column(String(40))
password_hash = Column(Text)
member_since = Column(TIMESTAMP)
email = Column(String(350))
last_seen = Column(TIMESTAMP)
admin = Column(Integer)
admin_role = Column(Integer)
confirmed = Column(Integer)
active = Column(Integer, default=1)

View File

@@ -1,5 +1,8 @@
import logging
from fastapi import APIRouter, Request, HTTPException
from datetime import date
logger = logging.getLogger(__name__)
from database import session
from decimal import Decimal
@@ -52,7 +55,7 @@ async def update_auto(autoid: int, request: Request):
return {"ok": True, "message": "Delivery confirmed and customer factor refined successfully."}
except Exception as e:
print(str(e))
logger.error(str(e))
session.rollback()
raise HTTPException(status_code=500, detail=f"An internal error occurred: {str(e)}")

View File

@@ -1,10 +1,15 @@
from fastapi import APIRouter
import logging
from fastapi import APIRouter, Depends
from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder
from database import session
from app.models.auto import Auto_Delivery, Tickets_Auto_Delivery
from app.models.delivery import Delivery
from app.models.auth import Auth_User
from app.auth import get_current_user
logger = logging.getLogger(__name__)
@@ -17,8 +22,8 @@ router = APIRouter(
@router.get("/all/customers", status_code=200)
def get_delivery_customers():
def get_delivery_customers(current_user: Auth_User = Depends(get_current_user)):
logger.info(f"GET /delivery/all/customers - User: {current_user.username}")
automatics = (
session.query(Auto_Delivery)
.filter(Auto_Delivery.auto_status.in_([1, 3]))
@@ -27,10 +32,11 @@ def get_delivery_customers():
)
return JSONResponse(content=jsonable_encoder(automatics), status_code=200)
@router.get("/driver/{driver_employee_id}", status_code=200)
def get_delivery_for_specific_driver(driver_employee_id: int):
def get_delivery_for_specific_driver(driver_employee_id: int, current_user: Auth_User = Depends(get_current_user)):
logger.info(f"GET /delivery/driver/{driver_employee_id} - User: {current_user.username}")
automatics = (
session.query(Delivery)
.filter(Delivery.driver_employee_id == driver_employee_id)
@@ -40,9 +46,10 @@ def get_delivery_for_specific_driver(driver_employee_id: int):
)
return JSONResponse(content=jsonable_encoder(automatics), status_code=200)
@router.get("/delivery/{ticket_id}", status_code=200)
def get_delivery_by_openticket(ticket_id):
def get_delivery_by_openticket(ticket_id, current_user: Auth_User = Depends(get_current_user)):
logger.info(f"GET /delivery/delivery/{ticket_id} - User: {current_user.username}")
get_delivery = (
session.query(Auto_Delivery)
.filter(Auto_Delivery.id == ticket_id)
@@ -50,10 +57,11 @@ def get_delivery_by_openticket(ticket_id):
)
return JSONResponse(content=jsonable_encoder(get_delivery), status_code=200)
@router.get("/finddelivery/{ticket_id}", status_code=200)
def get_delivery_by_openticket(ticket_id):
def get_delivery_by_findticket(ticket_id, current_user: Auth_User = Depends(get_current_user)):
logger.info(f"GET /delivery/finddelivery/{ticket_id} - User: {current_user.username}")
get_delivery = (
session.query(Auto_Delivery)
.filter(Auto_Delivery.open_ticket_id == ticket_id)
@@ -61,11 +69,12 @@ def get_delivery_by_openticket(ticket_id):
)
return JSONResponse(content=jsonable_encoder(get_delivery), status_code=200)
@router.get("/autoticket/{delivery_id_order}", status_code=200)
def get_auto_by_ticket(delivery_id_order):
def get_auto_by_ticket(delivery_id_order, current_user: Auth_User = Depends(get_current_user)):
logger.info(f"GET /delivery/autoticket/{delivery_id_order} - User: {current_user.username}")
get_delivery = (
session.query(Tickets_Auto_Delivery)
.filter(Tickets_Auto_Delivery.id == delivery_id_order)
@@ -73,11 +82,11 @@ def get_auto_by_ticket(delivery_id_order):
)
return JSONResponse(content=jsonable_encoder(get_delivery), status_code=200)
@router.get("/all/profile/{customer_id}", status_code=200)
def get_autos_customers(customer_id):
def get_autos_customers(customer_id, current_user: Auth_User = Depends(get_current_user)):
logger.info(f"GET /delivery/all/profile/{customer_id} - User: {current_user.username}")
get_delivery = (
session.query(Tickets_Auto_Delivery)
.filter(Tickets_Auto_Delivery.customer_id == customer_id)
@@ -89,8 +98,8 @@ def get_autos_customers(customer_id):
return JSONResponse(content=jsonable_encoder(get_delivery), status_code=200)
@router.get("/all/profile/profile/{customer_id}", status_code=200)
def get_autos_customers(customer_id):
def get_autos_customers_extended(customer_id, current_user: Auth_User = Depends(get_current_user)):
logger.info(f"GET /delivery/all/profile/profile/{customer_id} - User: {current_user.username}")
get_delivery = (
session.query(Tickets_Auto_Delivery)
.filter(Tickets_Auto_Delivery.customer_id == customer_id)
@@ -103,8 +112,8 @@ def get_autos_customers(customer_id):
@router.get("/auto/customer/{customer_id}", status_code=200)
def get_auto_delivery_by_customer(customer_id):
def get_auto_delivery_by_customer(customer_id, current_user: Auth_User = Depends(get_current_user)):
logger.info(f"GET /delivery/auto/customer/{customer_id} - User: {current_user.username}")
get_auto_delivery = (
session.query(Auto_Delivery)
.filter(Auto_Delivery.customer_id == customer_id)
@@ -115,7 +124,8 @@ def get_auto_delivery_by_customer(customer_id):
@router.put("/update_status/{auto_id}", status_code=200)
def update_auto_status(auto_id: int):
def update_auto_status(auto_id: int, current_user: Auth_User = Depends(get_current_user)):
logger.info(f"PUT /delivery/update_status/{auto_id} - User: {current_user.username}")
update_status = (
session.query(Auto_Delivery)
.filter(Auto_Delivery.id == auto_id)

View File

@@ -1,3 +1,4 @@
import logging
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder
@@ -9,6 +10,8 @@ from decimal import Decimal
from app.models.auto import Auto_Delivery, Tickets_Auto_Delivery, Auto_Temp
from app.models.delivery import Delivery
logger = logging.getLogger(__name__)
# Constants from fuel_estimator
HOT_WATER_DAILY_USAGE = Decimal('1.0')
K_FACTOR_SMOOTHING_WEIGHT = Decimal('0.7')
@@ -33,6 +36,7 @@ def fix_customer_last_delivered():
Returns statistics and a list of changes made.
"""
logger.info("GET /fixstuff/lastdelivered - Fixing customer last delivered dates")
auto_deliveries = session.query(Auto_Delivery).all()
changes = []
total_customers = len(auto_deliveries)
@@ -76,6 +80,7 @@ def estimate_customer_gallons(update_db: int):
Multiple deliveries: use historical average. Includes address and scaling factor.
When update_db=1, updates estimated_gallons_left and house_factor in database.
"""
logger.info(f"GET /fixstuff/estimate_gallons/{update_db} - Estimating customer gallons (update_db={update_db})")
auto_deliveries = session.query(Auto_Delivery).all()
estimates = []
for ad in auto_deliveries:

View File

@@ -1,3 +1,4 @@
import logging
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder
@@ -10,6 +11,8 @@ from app.models.customer import Customer_Customer, Customer_estimate_gallons
from app.models.delivery import Delivery
from app.models.auto import Auto_Temp
logger = logging.getLogger(__name__)
# Constants from fuel_estimator
HOT_WATER_DAILY_USAGE = Decimal('1.0')
K_FACTOR_SMOOTHING_WEIGHT = Decimal('0.7')
@@ -42,6 +45,7 @@ def fix_customer_last_delivered():
Returns statistics and a list of changes made.
"""
logger.info("GET /fixstuff_customer/lastdelivered - Fixing customer last delivered dates")
session.rollback() # Reset any aborted transaction state
customer_estimates = session.query(Customer_estimate_gallons).all()
changes = []
@@ -87,6 +91,7 @@ def estimate_customer_gallons(update_db: int):
Multiple deliveries: use historical average. Includes address and scaling factor.
When update_db=1, updates estimated_gallons_left and house_factor in database.
"""
logger.info(f"GET /fixstuff_customer/estimate_gallons/{update_db} - Estimating customer gallons (update_db={update_db})")
session.rollback() # Reset any aborted transaction state
# Check if weather data is available
@@ -235,6 +240,7 @@ def estimate_customer_gallons_specific(customer_id: int):
Estimates current gallons for a specific regular customer based on delivery history and weather.
Returns estimation data for the specified customer only.
"""
logger.info(f"GET /fixstuff_customer/estimate_gallons/customer/{customer_id} - Estimating gallons for specific customer")
session.rollback() # Reset any aborted transaction state
# Check if weather data is available
@@ -386,6 +392,7 @@ def populate_customer_estimates():
Returns statistics on records created.
"""
logger.info("GET /fixstuff_customer/populate_estimates - Populating customer estimates")
session.rollback() # Reset any aborted transaction state
# Get all regular customers (customer_automatic == 0)

View File

@@ -1,6 +1,9 @@
import logging
from fastapi import APIRouter, HTTPException
from database import session
logger = logging.getLogger(__name__)
from app.script.fuel_estimator import FuelEstimator
from app.script.temp_getter import fetch_and_store_daily_temp
from app.script.fuel_estimator_customer import FuelEstimatorCustomer
@@ -45,7 +48,7 @@ def update_all_customer_fuel_levels_auto():
except Exception as e:
session.rollback()
# Log the exception e
print(str(e))
logger.error(str(e))
return {"ok": False, "message": "An internal error occurred."}
@@ -63,5 +66,5 @@ def update_all_customer_fuel_levels_normal():
except Exception as e:
session.rollback()
# Log the exception e
print(str(e))
logger.error(str(e))
return {"ok": False, "message": "An internal error occurred."}

View File

@@ -1,8 +1,11 @@
import logging
from sqlalchemy.orm import Session
from sqlalchemy import func
from datetime import date, timedelta
from decimal import Decimal
logger = logging.getLogger(__name__)
# Import your existing database models
from app.models.auto import Auto_Delivery, Auto_Temp, Auto_Update, Tickets_Auto_Delivery
@@ -58,7 +61,7 @@ class FuelEstimator:
if delivery_count <= 1:
# Customers with 0 or 1 delivery should have house_factor = 0.12 (initial average)
if customer.house_factor != Decimal('0.12'):
print(f"Correcting house_factor for customer {customer.customer_id} from {customer.house_factor} to 0.12 (1 or fewer deliveries)")
logger.info(f"Correcting house_factor for customer {customer.customer_id} from {customer.house_factor} to 0.12 (1 or fewer deliveries)")
customer.house_factor = Decimal('0.12')
corrected = True
# For customers with 2+ deliveries, keep their calculated factor (no correction needed)
@@ -74,13 +77,13 @@ class FuelEstimator:
# 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.")
logger.info(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.")
logger.info(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 = 0.
@@ -92,10 +95,10 @@ class FuelEstimator:
).all()
if not auto_customers:
print("No active automatic delivery customers found.")
logger.info("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...")
logger.info(f"Staging daily fuel update for {len(auto_customers)} customers...")
corrections_made = 0
@@ -124,7 +127,7 @@ class FuelEstimator:
new_update_log = Auto_Update(last_updated=today)
self.session.add(new_update_log)
print("Daily update staged. Awaiting commit.")
logger.info("Daily update staged. Awaiting commit.")
message = f"Successfully staged updates for {len(auto_customers)} customers."
if corrections_made > 0:
message += f" Corrected house factors for {corrections_made} customers."
@@ -141,11 +144,11 @@ class FuelEstimator:
).first()
if not customer:
print(f"Customer {ticket.customer_id} not found.")
logger.info(f"Customer {ticket.customer_id} not found.")
return
if not customer.last_fill:
print(f"Setting initial K-Factor for new customer {ticket.customer_id} with only one delivery.")
logger.info(f"Setting initial K-Factor for new customer {ticket.customer_id} with only one delivery.")
customer.house_factor = self._estimate_initial_house_factor(customer)
self._update_tank_after_fill(customer, ticket)
return
@@ -154,7 +157,7 @@ class FuelEstimator:
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.")
logger.info(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
@@ -172,7 +175,7 @@ class FuelEstimator:
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.")
logger.info(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
@@ -181,13 +184,13 @@ class FuelEstimator:
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}")
logger.info(f"Refining K-Factor for Customer ID {customer.customer_id}:")
logger.info(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.")
logger.info(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 update customer tank status after a fill-up or partial delivery."""

View File

@@ -1,8 +1,11 @@
import logging
from sqlalchemy.orm import Session
from sqlalchemy import func
from datetime import date, timedelta
from decimal import Decimal
logger = logging.getLogger(__name__)
# Import your existing database models
from app.models.customer import Customer_estimate_gallons, Customer_Update
from app.models.delivery import Delivery
@@ -60,7 +63,7 @@ class FuelEstimatorCustomer:
if delivery_count <= 1:
# Customers with 0 or 1 delivery should have house_factor = 0.12 (initial average)
if customer.house_factor != Decimal('0.12'):
print(f"Correcting house_factor for customer {customer.customer_id} from {customer.house_factor} to 0.12 (1 or fewer deliveries)")
logger.info(f"Correcting house_factor for customer {customer.customer_id} from {customer.house_factor} to 0.12 (1 or fewer deliveries)")
customer.house_factor = Decimal('0.12')
corrected = True
# For customers with 2+ deliveries, keep their calculated factor (no correction needed)
@@ -76,13 +79,13 @@ class FuelEstimatorCustomer:
# 1. Check if the update has already run today
if self.session.query(Customer_Update).filter(Customer_Update.last_updated == today).first():
print(f"Daily update for {today} has already been completed.")
logger.info(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.")
logger.info(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 = 0.
@@ -94,10 +97,10 @@ class FuelEstimatorCustomer:
).all()
if not customer_estimates:
print("No active regular delivery customers found.")
logger.info("No active regular delivery customers found.")
return {"ok": True, "message": "No active customers to update."}
print(f"Staging daily fuel update for {len(customer_estimates)} customers...")
logger.info(f"Staging daily fuel update for {len(customer_estimates)} customers...")
corrections_made = 0
@@ -126,7 +129,7 @@ class FuelEstimatorCustomer:
new_update_log = Customer_Update(last_updated=today)
self.session.add(new_update_log)
print("Daily update staged. Awaiting commit.")
logger.info("Daily update staged. Awaiting commit.")
message = f"Successfully staged updates for {len(customer_estimates)} customers."
if corrections_made > 0:
message += f" Corrected house factors for {corrections_made} customers."
@@ -143,11 +146,11 @@ class FuelEstimatorCustomer:
).first()
if not customer:
print(f"Customer {delivery.customer_id} not found.")
logger.info(f"Customer {delivery.customer_id} not found.")
return
if not customer.last_fill:
print(f"Setting initial K-Factor for new customer {delivery.customer_id} with only one delivery.")
logger.info(f"Setting initial K-Factor for new customer {delivery.customer_id} with only one delivery.")
customer.house_factor = self._estimate_initial_house_factor(customer)
self._update_tank_after_fill(customer, delivery)
return
@@ -156,7 +159,7 @@ class FuelEstimatorCustomer:
end_date = delivery.when_delivered
if start_date >= end_date:
print(f"Cannot refine K-Factor for customer {delivery.customer_id}: New fill date is not after the last one. Resetting tank only.")
logger.info(f"Cannot refine K-Factor for customer {delivery.customer_id}: New fill date is not after the last one. Resetting tank only.")
self._update_tank_after_fill(customer, delivery)
return
@@ -174,7 +177,7 @@ class FuelEstimatorCustomer:
gallons_for_heating = delivery.gallons_delivered - total_hot_water_usage
if gallons_for_heating <= 0 or total_hdd == 0:
print(f"Cannot calculate new K-Factor for customer {delivery.customer_id}. (HDD: {total_hdd}, Heating Gallons: {gallons_for_heating}). Resetting tank only.")
logger.info(f"Cannot calculate new K-Factor for customer {delivery.customer_id}. (HDD: {total_hdd}, Heating Gallons: {gallons_for_heating}). Resetting tank only.")
self._update_tank_after_fill(customer, delivery)
return
@@ -183,13 +186,13 @@ class FuelEstimatorCustomer:
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}")
logger.info(f"Refining K-Factor for Customer ID {customer.customer_id}:")
logger.info(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, delivery)
print(f"K-Factor and tank status for Customer {customer.customer_id} staged for update.")
logger.info(f"K-Factor and tank status for Customer {customer.customer_id} staged for update.")
def _update_tank_after_fill(self, customer: Customer_estimate_gallons, delivery: Delivery):
"""Helper to update customer tank status after a fill-up or partial delivery."""

View File

@@ -1,7 +1,10 @@
import logging
from datetime import date
import requests
from decimal import Decimal
logger = logging.getLogger(__name__)
# Import your database model and the session from your database configuration
from app.models.auto import Auto_Temp
from database import session
@@ -22,11 +25,11 @@ def fetch_and_store_daily_temp() -> bool:
# 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.")
logger.info(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...")
logger.info(f"Fetching temperature for {today} from OpenWeatherMap...")
try:
# API key and location
api_key = '21648d8c8d1a4ae495ace0b7810b4d36'
@@ -63,11 +66,11 @@ def fetch_and_store_daily_temp() -> bool:
# 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}.")
logger.info(f"Successfully fetched and staged temperature for {today}.")
return True
except Exception as e:
print(f"An error occurred while fetching weather data: {e}")
logger.info(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