Initial commit: Robot ökoszisztéma v2.0 - Stabilizált jármű és szerviz robotok

This commit is contained in:
Kincses
2026-03-04 02:03:03 +01:00
commit 250f4f4b8f
7942 changed files with 449625 additions and 0 deletions

View File

@@ -0,0 +1,177 @@
from fastapi import FastAPI, HTTPException
from fastapi.responses import FileResponse
from pydantic import BaseModel, validator
from typing import Optional, List
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
from sqlalchemy import text
import os
from datetime import date
import uuid
from dotenv import load_dotenv
load_dotenv()
raw_url = os.getenv("DATABASE_URL")
if not raw_url:
raw_url = "postgresql://admin:PASSWORD_111@postgres-db:5432/service_finder"
fixed_url = raw_url.replace("postgresql://", "postgresql+asyncpg://").replace("/service_finder_db", "/service_finder")
engine = create_async_engine(fixed_url)
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
app = FastAPI(title="Service Finder API")
# --- MODELLEK ---
class IssueReport(BaseModel):
vehicle_id: int
description: str
is_critical: bool
class IssueResolve(BaseModel):
vehicle_id: int
# ÚJ: KÖLTSÉG MODELL
class CostCreate(BaseModel):
vehicle_id: int
cost_type: str # FUEL, SERVICE, INSURANCE, TAX, OTHER
amount: float
currency: str # HUF, EUR
date: date
mileage: int
description: Optional[str] = ""
# --- LOGOLÁS ---
async def create_audit_log(session, user_id, event, target_id, details, old_val=None, new_val=None):
await session.execute(text("""
INSERT INTO data.audit_logs (user_id, event_type, target_id, details, old_value, new_value)
VALUES (:uid, :evt, :tid, :det, :old, :new)
"""), {"uid": user_id, "evt": event, "tid": target_id, "det": details, "old": old_val, "new": new_val})
# --- VÉGPONTOK ---
# 1. KÖLTSÉG HOZZÁADÁSA (Okos Km frissítéssel!)
@app.post("/api/add_cost")
async def add_cost(data: CostCreate):
user_id = 1
async with AsyncSessionLocal() as session:
async with session.begin():
# Költség mentése
await session.execute(text("""
INSERT INTO data.costs (vehicle_id, user_id, cost_type, amount, currency, date, mileage_at_cost, description)
VALUES (:vid, :uid, :type, :amt, :curr, :date, :mil, :desc)
"""), {
"vid": data.vehicle_id, "uid": user_id, "type": data.cost_type,
"amt": data.amount, "curr": data.currency, "date": data.date,
"mil": data.mileage, "desc": data.description
})
# KM ÓRA AUTOMATIKUS FRISSÍTÉSE (Ha a megadott km nagyobb, mint a jelenlegi)
# Először lekérjük a jelenlegit
res = await session.execute(text("SELECT start_mileage FROM data.vehicle_history WHERE vehicle_id = :vid AND end_date IS NULL"), {"vid": data.vehicle_id})
current = res.scalar() or 0
if data.mileage > current:
await session.execute(text("""
UPDATE data.vehicle_history SET start_mileage = :mil
WHERE vehicle_id = :vid AND end_date IS NULL
"""), {"mil": data.mileage, "vid": data.vehicle_id})
# Opcionális: Logoljuk a km frissítést is
await create_audit_log(session, user_id, "MILEAGE_UPDATE", data.vehicle_id, f"Km frissítve költség rögzítésekor: {data.mileage}")
await create_audit_log(session, user_id, "ADD_COST", data.vehicle_id, f"{data.cost_type}: {data.amount} {data.currency}")
return {"status": "success"}
# 2. SZERVIZKÖNYV LEKÉRÉSE (Történet)
@app.get("/api/vehicle/{vehicle_id}/history")
async def get_vehicle_history(vehicle_id: int):
user_id = 1
async with AsyncSessionLocal() as session:
# Lekérjük a költségeket
res_costs = await session.execute(text("""
SELECT id, cost_type as type, amount, currency, date, mileage_at_cost as mileage, description, 'COST' as source
FROM data.costs WHERE vehicle_id = :vid ORDER BY date DESC
"""), {"vid": vehicle_id})
costs = res_costs.fetchall()
# Lekérjük az Audit Log eseményeket (Hiba, Javítás)
res_logs = await session.execute(text("""
SELECT id, event_type as type, 0 as amount, '' as currency, created_at::date as date, 0 as mileage, details as description, 'LOG' as source
FROM data.audit_logs WHERE target_id = :vid AND event_type IN ('ISSUE_REPORT', 'ISSUE_RESOLVED') ORDER BY created_at DESC
"""), {"vid": vehicle_id})
logs = res_logs.fetchall()
# Összefésüljük a két listát Pythonban és dátum szerint rendezzük
combined = []
for r in costs: combined.append(dict(r._mapping))
for r in logs: combined.append(dict(r._mapping))
# Rendezés dátum szerint csökkenőbe (legújabb elöl)
combined.sort(key=lambda x: x['date'], reverse=True)
return combined
# --- KORÁBBI VÉGPONTOK (Rövidítve a hely miatt, de ezek kellenek!) ---
@app.get("/api/my_vehicles")
async def get_my_garage():
user_id = 1
async with AsyncSessionLocal() as session:
query = text("""
SELECT v.id as vehicle_id, v.vin, v.current_plate, m.name as brand,
mo.model_name, mo.category, vh.start_mileage, vh.role,
v.status, v.current_issue
FROM data.vehicle_history vh
JOIN data.vehicles v ON vh.vehicle_id = v.id
JOIN ref.vehicle_models mo ON v.model_id = mo.id
JOIN ref.vehicle_makes m ON mo.make_id = m.id
WHERE vh.user_id = :uid AND vh.end_date IS NULL
ORDER BY vh.start_date DESC
""")
result = await session.execute(query, {"uid": user_id})
return [{"vehicle_id": r.vehicle_id, "brand": r.brand, "model": r.model_name, "plate": r.current_plate, "category": r.category, "role": r.role, "status": r.status, "current_issue": r.current_issue} for r in result.fetchall()]
@app.get("/api/vehicle/{vehicle_id}")
async def get_vehicle_details(vehicle_id: int):
user_id = 1
async with AsyncSessionLocal() as session:
q = text("""
SELECT v.id, v.vin, v.current_plate, v.production_year,
m.name as brand, mo.model_name, mo.category,
vh.role, vh.start_date, vh.start_mileage, u.default_currency,
v.status, v.current_issue
FROM data.vehicle_history vh
JOIN data.vehicles v ON vh.vehicle_id = v.id
JOIN ref.vehicle_models mo ON v.model_id = mo.id
JOIN ref.vehicle_makes m ON mo.make_id = m.id
JOIN data.users u ON vh.user_id = u.id
WHERE v.id = :vid AND vh.user_id = :uid AND vh.end_date IS NULL
""")
res = await session.execute(q, {"vid": vehicle_id, "uid": user_id})
car = res.fetchone()
if not car: raise HTTPException(status_code=404, detail="Nincs adat")
return {"id": car.id, "brand": car.brand, "model": car.model_name, "plate": car.current_plate, "vin": car.vin, "role": car.role, "start_date": car.start_date, "mileage": car.start_mileage, "currency": car.default_currency, "status": car.status, "current_issue": car.current_issue}
@app.post("/api/report_issue")
async def report_issue(data: IssueReport):
user_id = 1
async with AsyncSessionLocal() as session:
async with session.begin():
new_status = 'CRITICAL' if data.is_critical else 'WARNING'
await session.execute(text("UPDATE data.vehicles SET status = :st, current_issue = :desc WHERE id = :vid"), {"st": new_status, "desc": data.description, "vid": data.vehicle_id})
await create_audit_log(session, user_id, "ISSUE_REPORT", data.vehicle_id, details=data.description, old_val="OK", new_val=new_status)
return {"status": "success"}
@app.post("/api/resolve_issue")
async def resolve_issue(data: IssueResolve):
user_id = 1
async with AsyncSessionLocal() as session:
async with session.begin():
await session.execute(text("UPDATE data.vehicles SET status = 'OK', current_issue = NULL WHERE id = :vid"), {"vid": data.vehicle_id})
await create_audit_log(session, user_id, "ISSUE_RESOLVED", data.vehicle_id, details="Probléma megoldva", old_val="WARNING", new_val="OK")
return {"status": "success", "message": "Jármű státusza helyreállítva!"}
@app.get("/")
async def serve_frontend():
if os.path.exists("/app/frontend/index.html"): return FileResponse("/app/frontend/index.html")
return {"error": "Frontend hiányzik"}