admin frontend elkezdése, járművek tisztázása, frontend fejleszts

This commit is contained in:
Roo
2026-06-15 18:52:38 +00:00
parent ef8df9608c
commit 213ba3b0f1
80 changed files with 11615 additions and 2304 deletions

View File

@@ -1,213 +1,15 @@
"""
Smart Odometer Service - Adminisztrátor által paraméterezhető kilométeróra becslés.
[DEPRECATED] Smart Odometer Service - VehicleOdometerState model removed.
A szolgáltatás a járművek kilométeróra állását becsüli a költségbejegyzések alapján,
figyelembe véve a rendszerparamétereket (ODOMETER_MIN_DAYS_FOR_AVG, ODOMETER_CONFIDENCE_THRESHOLD).
Ha az admin beállított manuális átlagot (manual_override_avg), akkor azt használja.
This service is deprecated. The VehicleOdometerState model has been removed
from the schema. Odometer tracking is now handled directly via the
OdometerReading table (audit trail) and Asset.current_mileage field.
The predictive odometer state estimation logic has been removed to keep
the entity model clean. Use OdometerReading records directly for
mileage audit trail purposes.
"""
from datetime import datetime, timedelta
from typing import Optional, Tuple
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func, and_
from sqlalchemy.orm import selectinload
from app.models.vehicle import VehicleOdometerState, VehicleCost
from app.models.system import SystemParameter
from app.models import VehicleModelDefinition
class OdometerService:
"""Kilométeróra becslési szolgáltatás adminisztrációs kontrollal."""
@staticmethod
async def get_system_param(db: AsyncSession, key: str, default_value):
"""Rendszerparaméter lekérése a system.system_parameters táblából."""
stmt = select(SystemParameter).where(
SystemParameter.key == key,
SystemParameter.scope_level == 'global',
SystemParameter.is_active == True
)
result = await db.execute(stmt)
param = result.scalars().first()
if param and 'value' in param.value:
return param.value['value']
return default_value
@staticmethod
async def update_vehicle_stats(db: AsyncSession, vehicle_id: int) -> Optional[VehicleOdometerState]:
"""
Frissíti a jármű kilométeróra statisztikáit.
Algoritmus:
1. Ha van manual_override_avg, használja azt.
2. Különben számol átlagot a vehicle.costs bejegyzésekből.
3. Figyelembe veszi az ODOMETER_MIN_DAYS_FOR_AVG paramétert.
4. Kiszámolja a confidence_score-t a minták száma alapján.
5. Frissíti vagy létrehozza a VehicleOdometerState rekordot.
"""
# Rendszerparaméterek lekérése
min_days = await OdometerService.get_system_param(db, 'ODOMETER_MIN_DAYS_FOR_AVG', 7)
confidence_threshold = await OdometerService.get_system_param(db, 'ODOMETER_CONFIDENCE_THRESHOLD', 0.5)
# Meglévő állapot lekérése
stmt = select(VehicleOdometerState).where(VehicleOdometerState.vehicle_id == vehicle_id)
result = await db.execute(stmt)
odometer_state = result.scalars().first()
# Költségbejegyzések lekérése dátum és odometer szerint rendezve
cost_stmt = select(VehicleCost).where(
VehicleCost.vehicle_id == vehicle_id,
VehicleCost.odometer.isnot(None)
).order_by(VehicleCost.date.asc())
cost_result = await db.execute(cost_stmt)
costs = cost_result.scalars().all()
if not costs:
# Nincs adat, alapértelmezett értékek
if odometer_state:
odometer_state.daily_avg_distance = 0
odometer_state.confidence_score = 0
odometer_state.estimated_current_odometer = odometer_state.last_recorded_odometer
else:
# Jármű alapadatok lekérése
vehicle_stmt = select(VehicleModelDefinition).where(VehicleModelDefinition.id == vehicle_id)
vehicle_result = await db.execute(vehicle_stmt)
vehicle = vehicle_result.scalars().first()
if not vehicle:
return None
odometer_state = VehicleOdometerState(
vehicle_id=vehicle_id,
last_recorded_odometer=0,
last_recorded_date=datetime.now(),
daily_avg_distance=0,
estimated_current_odometer=0,
confidence_score=0,
manual_override_avg=None
)
db.add(odometer_state)
await db.commit()
await db.refresh(odometer_state)
return odometer_state
# Utolsó rögzített adatok
last_cost = costs[-1]
last_recorded_odometer = last_cost.odometer
last_recorded_date = last_cost.date
# Manuális átlag ellenőrzése
if odometer_state and odometer_state.manual_override_avg is not None:
daily_avg = float(odometer_state.manual_override_avg)
confidence = 1.0 # Manuális beállítás esetén teljes bizalom
else:
# Átlag számítása a költségbejegyzésekből
valid_pairs = []
for i in range(1, len(costs)):
prev = costs[i-1]
curr = costs[i]
days_diff = (curr.date - prev.date).days
km_diff = curr.odometer - prev.odometer
if days_diff >= min_days and km_diff > 0:
daily_avg = km_diff / days_diff
valid_pairs.append((daily_avg, days_diff))
if valid_pairs:
# Súlyozott átlag (hosszabb időszakok nagyobb súllyal)
total_weighted = sum(avg * weight for avg, weight in valid_pairs)
total_days = sum(weight for _, weight in valid_pairs)
daily_avg = total_weighted / total_days if total_days > 0 else 0
# Confidence score: érvényes párok száma / összes lehetséges párok
confidence = min(len(valid_pairs) / max(len(costs) - 1, 1), 1.0)
else:
daily_avg = 0
confidence = 0
# Becsült jelenlegi kilométer
days_since_last = (datetime.now(last_recorded_date.tzinfo) - last_recorded_date).days
estimated_odometer = last_recorded_odometer + (daily_avg * max(days_since_last, 0))
# Állapot frissítése vagy létrehozása
if odometer_state:
odometer_state.last_recorded_odometer = last_recorded_odometer
odometer_state.last_recorded_date = last_recorded_date
odometer_state.daily_avg_distance = daily_avg
odometer_state.estimated_current_odometer = estimated_odometer
odometer_state.confidence_score = confidence
else:
odometer_state = VehicleOdometerState(
vehicle_id=vehicle_id,
last_recorded_odometer=last_recorded_odometer,
last_recorded_date=last_recorded_date,
daily_avg_distance=daily_avg,
estimated_current_odometer=estimated_odometer,
confidence_score=confidence,
manual_override_avg=None
)
db.add(odometer_state)
await db.commit()
await db.refresh(odometer_state)
return odometer_state
@staticmethod
async def get_estimated_odometer(db: AsyncSession, vehicle_id: int) -> Tuple[Optional[float], float]:
"""
Visszaadja a jármű becsült jelenlegi kilométeróra állását és a bizalom pontszámot.
Returns:
Tuple[estimated_odometer, confidence_score]
"""
stmt = select(VehicleOdometerState).where(VehicleOdometerState.vehicle_id == vehicle_id)
result = await db.execute(stmt)
odometer_state = result.scalars().first()
if not odometer_state:
# Ha nincs állapot, frissítsük
odometer_state = await OdometerService.update_vehicle_stats(db, vehicle_id)
if not odometer_state:
return None, 0.0
return odometer_state.estimated_current_odometer, odometer_state.confidence_score
@staticmethod
async def set_manual_override(db: AsyncSession, vehicle_id: int, daily_avg: Optional[float]) -> Optional[VehicleOdometerState]:
"""
Adminisztrátori manuális átlag beállítása.
Args:
daily_avg: Napi átlagos kilométer (km/nap). Ha None, törli a manuális beállítást.
"""
stmt = select(VehicleOdometerState).where(VehicleOdometerState.vehicle_id == vehicle_id)
result = await db.execute(stmt)
odometer_state = result.scalars().first()
if not odometer_state:
# Ha nincs állapot, hozzuk létre
odometer_state = VehicleOdometerState(
vehicle_id=vehicle_id,
last_recorded_odometer=0,
last_recorded_date=datetime.now(),
daily_avg_distance=0,
estimated_current_odometer=0,
confidence_score=0,
manual_override_avg=daily_avg
)
db.add(odometer_state)
else:
odometer_state.manual_override_avg = daily_avg
# Frissítsük a becslést a manuális átlaggal
if daily_avg is not None:
days_since_last = (datetime.now(odometer_state.last_recorded_date.tzinfo) - odometer_state.last_recorded_date).days
odometer_state.estimated_current_odometer = odometer_state.last_recorded_odometer + (daily_avg * max(days_since_last, 0))
odometer_state.confidence_score = 1.0
await db.commit()
await db.refresh(odometer_state)
return odometer_state
import logging
logger = logging.getLogger(__name__)
logger.warning("OdometerService is deprecated. VehicleOdometerState model has been removed.")