patchcenter/app/routers/patch_history.py

97 lines
3.6 KiB
Python

"""Router Historique patching — vue de patch_history"""
from fastapi import APIRouter, Request, Depends, Query
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.templating import Jinja2Templates
from sqlalchemy import text
from ..dependencies import get_db, get_current_user
from ..config import APP_NAME
router = APIRouter()
templates = Jinja2Templates(directory="app/templates")
@router.get("/patching/historique", response_class=HTMLResponse)
async def patch_history_page(request: Request, db=Depends(get_db),
year: int = Query(None), week: int = Query(None),
hostname: str = Query(None), page: int = Query(1)):
user = get_current_user(request)
if not user:
return RedirectResponse(url="/login")
from datetime import datetime
if not year:
year = datetime.now().year
per_page = 100
offset = (page - 1) * per_page
# KPIs
kpis = {}
kpis["total"] = db.execute(text(
"SELECT COUNT(*) FROM patch_history WHERE EXTRACT(YEAR FROM date_patch)=:y"
), {"y": year}).scalar()
kpis["servers"] = db.execute(text(
"SELECT COUNT(DISTINCT server_id) FROM patch_history WHERE EXTRACT(YEAR FROM date_patch)=:y"
), {"y": year}).scalar()
kpis["patchables"] = db.execute(text(
"SELECT COUNT(*) FROM servers WHERE etat='Production' AND patch_os_owner='secops'"
)).scalar()
kpis["never"] = db.execute(text("""
SELECT COUNT(*) FROM servers s
WHERE s.etat='Production' AND s.patch_os_owner='secops'
AND NOT EXISTS (SELECT 1 FROM patch_history ph
WHERE ph.server_id=s.id AND EXTRACT(YEAR FROM ph.date_patch)=:y)
"""), {"y": year}).scalar()
kpis["coverage_pct"] = round((kpis["servers"] / kpis["patchables"] * 100), 1) if kpis["patchables"] else 0
# Par semaine
by_week = db.execute(text("""
SELECT TO_CHAR(date_patch, 'IW') as week_num,
COUNT(DISTINCT server_id) as servers
FROM patch_history
WHERE EXTRACT(YEAR FROM date_patch)=:y
GROUP BY TO_CHAR(date_patch, 'IW')
ORDER BY week_num
"""), {"y": year}).fetchall()
# Filtres
where = ["EXTRACT(YEAR FROM ph.date_patch)=:y"]
params = {"y": year, "limit": per_page, "offset": offset}
if week:
where.append("EXTRACT(WEEK FROM ph.date_patch)=:wk")
params["wk"] = week
if hostname:
where.append("s.hostname ILIKE :h")
params["h"] = f"%{hostname}%"
wc = " AND ".join(where)
total_filtered = db.execute(text(
f"SELECT COUNT(*) FROM patch_history ph JOIN servers s ON ph.server_id=s.id WHERE {wc}"
), params).scalar()
rows = db.execute(text(f"""
SELECT s.id as sid, s.hostname, s.os_family, s.etat,
ph.date_patch, ph.status, ph.notes,
z.name as zone
FROM patch_history ph
JOIN servers s ON ph.server_id = s.id
LEFT JOIN zones z ON s.zone_id = z.id
WHERE {wc}
ORDER BY ph.date_patch DESC
LIMIT :limit OFFSET :offset
"""), params).fetchall()
# Années dispo
years = db.execute(text("""
SELECT DISTINCT EXTRACT(YEAR FROM date_patch)::int as y
FROM patch_history ORDER BY y DESC
""")).fetchall()
return templates.TemplateResponse("patch_history.html", {
"request": request, "user": user, "app_name": APP_NAME,
"kpis": kpis, "by_week": by_week, "rows": rows,
"year": year, "week": week, "hostname": hostname,
"page": page, "per_page": per_page, "total_filtered": total_filtered,
"years": [y.y for y in years],
})