97 lines
3.6 KiB
Python
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],
|
|
})
|