v1.0.0 — GSPARC Mezzouna API initiale
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
"""Routes de l'application GSPARC Mezzouna."""
|
||||
from fastapi import APIRouter
|
||||
from app.routes.auth_routes import router as auth_router
|
||||
from app.routes.vehicles import router as vehicles_router
|
||||
from app.routes.fuel import router as fuel_router
|
||||
from app.routes.anomalies import router as anomalies_router
|
||||
from app.routes.dashboard import router as dashboard_router
|
||||
|
||||
# Router principal qui regroupe tout
|
||||
api_router = APIRouter()
|
||||
api_router.include_router(dashboard_router)
|
||||
api_router.include_router(vehicles_router)
|
||||
api_router.include_router(fuel_router)
|
||||
api_router.include_router(anomalies_router)
|
||||
@@ -0,0 +1,34 @@
|
||||
"""Routes — anomalies."""
|
||||
from fastapi import APIRouter, Depends, Request, Form
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
from app.auth import require_auth
|
||||
from app.baserow import list_rows, update_row
|
||||
from app.templates import templates
|
||||
from app.config import TABLE_ANOMALIES, TABLE_VEHICULES
|
||||
|
||||
router = APIRouter(prefix="/anomalies")
|
||||
|
||||
|
||||
@router.get("/", response_class=HTMLResponse)
|
||||
async def liste_anomalies(request: Request, user=Depends(require_auth)):
|
||||
anomalies = await list_rows(TABLE_ANOMALIES, size=100)
|
||||
vehicules = await list_rows(TABLE_VEHICULES)
|
||||
return templates.TemplateResponse("anomalies.html", {
|
||||
"request": request,
|
||||
"anomalies": anomalies,
|
||||
"vehicules": {v["رقم_الماتريكول"]: v["النوع"] for v in vehicules},
|
||||
})
|
||||
|
||||
|
||||
@router.post("/{row_id}/resolve")
|
||||
async def resoudre_anomalie(
|
||||
request: Request,
|
||||
row_id: int,
|
||||
action: str = Form(""),
|
||||
user=Depends(require_auth),
|
||||
):
|
||||
await update_row(TABLE_ANOMALIES, row_id, {
|
||||
"تمت_المعالجة": True,
|
||||
"إجراء_التصحيح": action,
|
||||
})
|
||||
return RedirectResponse(url="/anomalies", status_code=303)
|
||||
@@ -0,0 +1,25 @@
|
||||
"""Routes — authentification (login/logout pages)."""
|
||||
from fastapi import APIRouter, Form, Request
|
||||
from fastapi.responses import RedirectResponse, HTMLResponse
|
||||
from app.auth import login_user, logout_user
|
||||
from app.templates import templates
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/login", response_class=HTMLResponse)
|
||||
async def login_page(request: Request, error: str = ""):
|
||||
return templates.TemplateResponse("login.html", {"request": request, "error": error})
|
||||
|
||||
|
||||
@router.post("/login")
|
||||
async def login(request: Request, username: str = Form(...), password: str = Form(...)):
|
||||
if login_user(request, username, password):
|
||||
return RedirectResponse(url="/", status_code=303)
|
||||
return templates.TemplateResponse("login.html", {"request": request, "error": "اسم المستخدم أو كلمة المرور غير صحيحة"}, status_code=401)
|
||||
|
||||
|
||||
@router.get("/logout")
|
||||
async def logout(request: Request):
|
||||
logout_user(request)
|
||||
return RedirectResponse(url="/login")
|
||||
@@ -0,0 +1,44 @@
|
||||
"""Routes — tableau de bord principal."""
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from app.auth import require_auth
|
||||
from app.baserow import list_rows
|
||||
from app.templates import templates
|
||||
from app.config import TABLE_VEHICULES, TABLE_APPROVISIONNEMENTS, TABLE_ANOMALIES
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/", response_class=HTMLResponse)
|
||||
async def dashboard(request: Request, user=Depends(require_auth)):
|
||||
vehicules = await list_rows(TABLE_VEHICULES)
|
||||
appros = await list_rows(TABLE_APPROVISIONNEMENTS, size=50)
|
||||
anomalies = await list_rows(TABLE_ANOMALIES, size=20)
|
||||
|
||||
# Stats globales
|
||||
total_appros = len(appros)
|
||||
total_anomalies = len([a for a in anomalies if not a.get("تمت_المعالجة")])
|
||||
total_vehicules = len(vehicules)
|
||||
|
||||
# Consommation moyenne par véhicule
|
||||
stats_vehicules = []
|
||||
for v in vehicules:
|
||||
matricule = v.get("رقم_الماتريكول", "")
|
||||
appros_v = [a for a in appros if a.get("رقم_الماتريكول") == matricule]
|
||||
conso_values = [a.get("الاستهلاك_100كم") for a in appros_v if a.get("الاستهلاك_100كم")]
|
||||
conso_moy = round(sum(conso_values) / len(conso_values), 2) if conso_values else 0
|
||||
stats_vehicules.append({
|
||||
"matricule": matricule,
|
||||
"type": v.get("النوع", ""),
|
||||
"nb_appros": len(appros_v),
|
||||
"conso_moyenne": conso_moy,
|
||||
"nb_anomalies": len([a for a in anomalies if a.get("رقم_الماتريكول") == matricule and not a.get("تمت_المعالجة")]),
|
||||
})
|
||||
|
||||
return templates.TemplateResponse("dashboard.html", {
|
||||
"request": request,
|
||||
"total_v": total_vehicules,
|
||||
"total_appro": total_appros,
|
||||
"total_anomalies": total_anomalies,
|
||||
"stats_vehicules": stats_vehicules,
|
||||
})
|
||||
@@ -0,0 +1,88 @@
|
||||
"""Routes — approvisionnements en carburant."""
|
||||
from fastapi import APIRouter, Depends, Request, Form, UploadFile, File
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
from app.auth import require_auth
|
||||
from app.baserow import list_rows, create_row, update_row, get_row
|
||||
from app.templates import templates
|
||||
from app.config import TABLE_VEHICULES, TABLE_APPROVISIONNEMENTS, TABLE_ANOMALIES
|
||||
from app.business import calculer_kilometrage, calculer_consommation, detecter_anomalies
|
||||
from app.ocr import extract_text, parse_receipt_ocr
|
||||
|
||||
router = APIRouter(prefix="/fuel")
|
||||
|
||||
|
||||
@router.get("/", response_class=HTMLResponse)
|
||||
async def formulaire(request: Request, user=Depends(require_auth)):
|
||||
vehicules = await list_rows(TABLE_VEHICULES)
|
||||
appros = await list_rows(TABLE_APPROVISIONNEMENTS, size=50)
|
||||
return templates.TemplateResponse("fuel_form.html", {
|
||||
"request": request,
|
||||
"vehicules": vehicules,
|
||||
"appros": appros,
|
||||
})
|
||||
|
||||
|
||||
@router.post("/add")
|
||||
async def ajouter_approvisionnement(
|
||||
request: Request,
|
||||
matricule: str = Form(...),
|
||||
date: str = Form(...),
|
||||
produit: str = Form("Gasoual Normal"),
|
||||
quantite: float = Form(...),
|
||||
prix_litre: float = Form(...),
|
||||
valeur: float = Form(...),
|
||||
compteur_avant: float = Form(None),
|
||||
compteur_apres: float = Form(None),
|
||||
no_recu: str = Form(""),
|
||||
user=Depends(require_auth),
|
||||
):
|
||||
# Calcul automatique
|
||||
km_parcouru = calculer_kilometrage(compteur_avant or 0, compteur_apres or 0)
|
||||
conso_100 = calculer_consommation(quantite, km_parcouru) if km_parcouru > 0 else None
|
||||
|
||||
data = {
|
||||
"التاريخ": date,
|
||||
"رقم_الماتريكول": matricule,
|
||||
"المنتج": produit,
|
||||
"الكمية_باللتر": quantite,
|
||||
"السعر_الليتر": prix_litre,
|
||||
"القيمة_الجملية": valeur,
|
||||
"العداد_قبل": compteur_avant,
|
||||
"العداد_بعد": compteur_apres,
|
||||
"الكلم_المقطوع": km_parcouru,
|
||||
"الاستهلاك_100كم": conso_100,
|
||||
"رقم_الايصال": no_recu,
|
||||
}
|
||||
|
||||
# Détection d'anomalies
|
||||
appro_precedents = await list_rows(TABLE_APPROVISIONNEMENTS, size=100)
|
||||
dernier = next((a for a in appro_precedents if a.get("رقم_الماتريكول") == matricule), None)
|
||||
codes = detecter_anomalies(data, dernier)
|
||||
|
||||
if codes:
|
||||
data["شذوذ"] = True
|
||||
data["نوع_الشذوذ"] = ", ".join(codes)
|
||||
|
||||
await create_row(TABLE_APPROVISIONNEMENTS, data)
|
||||
return RedirectResponse(url="/fuel", status_code=303)
|
||||
|
||||
|
||||
@router.post("/ocr-upload")
|
||||
async def upload_recu_ocr(
|
||||
request: Request,
|
||||
file: UploadFile = File(...),
|
||||
user=Depends(require_auth),
|
||||
):
|
||||
"""Upload d'un reçu + OCR automatique."""
|
||||
content = await file.read()
|
||||
text = await extract_text(content, file.filename or "receipt.jpg")
|
||||
parsed = parse_receipt_ocr(text)
|
||||
|
||||
vehicules = await list_rows(TABLE_VEHICULES)
|
||||
return templates.TemplateResponse("fuel_form.html", {
|
||||
"request": request,
|
||||
"vehicules": vehicules,
|
||||
"appros": [],
|
||||
"ocr_text": text,
|
||||
"ocr_parsed": parsed,
|
||||
})
|
||||
@@ -0,0 +1,62 @@
|
||||
"""Routes — véhicules."""
|
||||
from fastapi import APIRouter, Depends, Request, Form
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
from app.auth import require_auth
|
||||
from app.baserow import list_rows, create_row, update_row, delete_row, get_row
|
||||
from app.templates import templates
|
||||
from app.config import TABLE_VEHICULES
|
||||
|
||||
router = APIRouter(prefix="/vehicules")
|
||||
|
||||
|
||||
@router.get("/", response_class=HTMLResponse)
|
||||
async def liste_vehicules(request: Request, user=Depends(require_auth)):
|
||||
vehicules = await list_rows(TABLE_VEHICULES)
|
||||
return templates.TemplateResponse("vehicules.html", {"request": request, "vehicules": vehicules})
|
||||
|
||||
|
||||
@router.post("/add")
|
||||
async def ajouter_vehicule(
|
||||
request: Request,
|
||||
matricule: str = Form(...),
|
||||
type_v: str = Form(...),
|
||||
marque: str = Form(""),
|
||||
capacite: float = Form(None),
|
||||
notes: str = Form(""),
|
||||
user=Depends(require_auth),
|
||||
):
|
||||
await create_row(TABLE_VEHICULES, {
|
||||
"رقم_الماتريكول": matricule,
|
||||
"النوع": type_v,
|
||||
"العلامة": marque,
|
||||
"حمولة_الخزان": capacite,
|
||||
"ملاحظات": notes,
|
||||
})
|
||||
return RedirectResponse(url="/vehicules", status_code=303)
|
||||
|
||||
|
||||
@router.post("/{row_id}/delete")
|
||||
async def supprimer_vehicule(request: Request, row_id: int, user=Depends(require_auth)):
|
||||
await delete_row(TABLE_VEHICULES, row_id)
|
||||
return RedirectResponse(url="/vehicules", status_code=303)
|
||||
|
||||
|
||||
@router.post("/{row_id}/edit")
|
||||
async def modifier_vehicule(
|
||||
request: Request,
|
||||
row_id: int,
|
||||
matricule: str = Form(...),
|
||||
type_v: str = Form(...),
|
||||
marque: str = Form(""),
|
||||
capacite: float = Form(None),
|
||||
notes: str = Form(""),
|
||||
user=Depends(require_auth),
|
||||
):
|
||||
await update_row(TABLE_VEHICULES, row_id, {
|
||||
"رقم_الماتريكول": matricule,
|
||||
"النوع": type_v,
|
||||
"العلامة": marque,
|
||||
"حمولة_الخزان": capacite,
|
||||
"ملاحظات": notes,
|
||||
})
|
||||
return RedirectResponse(url="/vehicules", status_code=303)
|
||||
Reference in New Issue
Block a user