roosterwijs/backend/modules/huisstijl/views.py

125 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Logo-beheer met kwaliteitscheck.
GET /api/huisstijl/ -> huidig logo (alle ingelogde gebruikers).
PUT /api/huisstijl/ -> upload logo (alleen beheerders); voert een
kwaliteitscheck uit en geeft waarschuwingen terug.
DELETE /api/huisstijl/ -> logo verwijderen (alleen beheerders).
"""
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Huisstijl
from .serializers import HuisstijlSerializer
TOEGESTAAN = {"png", "jpg", "jpeg", "gif", "webp", "svg"}
MAX_BYTES = 3 * 1024 * 1024 # 3 MB
def _hex(rgb):
return "#%02x%02x%02x" % (rgb[0], rgb[1], rgb[2])
def analyseer_logo(f):
"""Beoordeel een geüpload logo. Geeft {ok, fouten, waarschuwingen, info}.
'ok' is False bij harde problemen (verkeerd bestandstype, onleesbaar).
Anders wordt het bestand opgeslagen, maar mogelijk met waarschuwingen
(bv. een niet-transparante / gevulde achtergrond, of lage resolutie).
"""
naam = (f.name or "").lower()
ext = naam.rsplit(".", 1)[-1] if "." in naam else ""
fouten, waarsch = [], []
if ext not in TOEGESTAAN:
fouten.append(f"Bestandstype '.{ext}' wordt niet ondersteund. Gebruik PNG, SVG, JPG, GIF of WebP.")
return {"ok": False, "fouten": fouten, "waarschuwingen": waarsch, "info": {}}
if getattr(f, "size", 0) and f.size > MAX_BYTES:
waarsch.append(f"Het bestand is groot ({f.size // 1024} KB); overweeg een kleiner logo.")
if ext == "svg":
return {"ok": True, "fouten": [], "waarschuwingen": waarsch,
"info": {"formaat": "SVG", "transparant": True, "breedte": None, "hoogte": None, "achtergrond": None}}
try:
from PIL import Image
except Exception:
# Zonder Pillow slaan we toch op, maar zonder beeldcontrole.
return {"ok": True, "fouten": [], "waarschuwingen": waarsch + ["Beeldcontrole niet beschikbaar."], "info": {"formaat": ext.upper()}}
try:
f.seek(0)
img = Image.open(f)
img.load()
except Exception:
return {"ok": False, "fouten": ["Kan de afbeelding niet lezen of het bestand is beschadigd."], "waarschuwingen": [], "info": {}}
breedte, hoogte = img.size
transparant = False
achtergrond = None
heeft_alpha = img.mode in ("RGBA", "LA") or (img.mode == "P" and "transparency" in img.info)
if heeft_alpha:
rgba = img.convert("RGBA")
hoeken = [rgba.getpixel((0, 0)), rgba.getpixel((breedte - 1, 0)),
rgba.getpixel((0, hoogte - 1)), rgba.getpixel((breedte - 1, hoogte - 1))]
if all(p[3] < 16 for p in hoeken):
transparant = True
else:
achtergrond = hoeken[0][:3]
waarsch.append("De hoeken zijn niet doorzichtig; het logo lijkt een gevulde achtergrond te hebben. "
"Op een gekleurde balk valt dat lelijk op — gebruik bij voorkeur een transparante PNG of SVG.")
else:
rgb = img.convert("RGB")
hoeken = [rgb.getpixel((0, 0)), rgb.getpixel((breedte - 1, 0)),
rgb.getpixel((0, hoogte - 1)), rgb.getpixel((breedte - 1, hoogte - 1))]
achtergrond = hoeken[0]
eensgezind = len(set(hoeken)) == 1
if eensgezind and min(achtergrond) > 245:
waarsch.append("Effen witte achtergrond zonder transparantie. Op een gekleurd vlak valt het witte blok op; "
"een transparante PNG of SVG is netter.")
elif eensgezind:
waarsch.append(f"Effen achtergrondkleur gedetecteerd ({_hex(achtergrond)}). Gebruik bij voorkeur een transparante PNG of SVG.")
else:
waarsch.append("Geen transparantie (bv. JPG). Voor een nette weergave op gekleurde vlakken is een transparante PNG of SVG beter.")
if breedte < 80 or hoogte < 80:
waarsch.append(f"Lage resolutie ({breedte}×{hoogte}px); dit kan korrelig printen.")
info = {
"formaat": img.format or ext.upper(),
"breedte": breedte,
"hoogte": hoogte,
"transparant": transparant,
"achtergrond": _hex(achtergrond) if achtergrond else None,
}
return {"ok": True, "fouten": [], "waarschuwingen": waarsch, "info": info}
@api_view(["GET", "PUT", "POST", "DELETE"])
def huisstijl(request):
obj = Huisstijl.actueel()
schrijven = request.method in ("PUT", "POST", "DELETE")
if schrijven and not (request.user and request.user.is_staff):
return Response({"detail": "Alleen beheerders kunnen het logo wijzigen."}, status=403)
if request.method == "DELETE":
if obj.logo:
obj.logo.delete(save=True)
return Response(HuisstijlSerializer(obj, context={"request": request}).data)
if request.method in ("PUT", "POST"):
f = request.FILES.get("logo")
if not f:
return Response({"detail": "Geen bestand ontvangen (veld 'logo')."}, status=400)
analyse = analyseer_logo(f)
if not analyse["ok"]:
return Response({"detail": " ".join(analyse["fouten"]), "kwaliteit": analyse}, status=400)
f.seek(0)
if obj.logo:
obj.logo.delete(save=False)
obj.logo = f
obj.save()
data = HuisstijlSerializer(obj, context={"request": request}).data
data["kwaliteit"] = analyse
return Response(data)
return Response(HuisstijlSerializer(obj, context={"request": request}).data)