API FormSemestreDescription: images: upload, tests.

This commit is contained in:
ilona 2024-08-14 15:39:57 +02:00
parent 5a751cb6e7
commit 4bfd0858a8
4 changed files with 54 additions and 10 deletions

View File

@ -13,12 +13,14 @@
FormSemestre FormSemestre
""" """
import mimetypes import base64
import io
from operator import attrgetter, itemgetter from operator import attrgetter, itemgetter
from flask import g, make_response, request from flask import g, make_response, request
from flask_json import as_json from flask_json import as_json
from flask_login import current_user, login_required from flask_login import current_user, login_required
import PIL
import sqlalchemy as sa import sqlalchemy as sa
import app import app
from app import db, log from app import db, log
@ -820,8 +822,8 @@ def formsemestre_get_description(formsemestre_id: int):
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json @as_json
def formsemestre_edit_description(formsemestre_id: int): def formsemestre_edit_description(formsemestre_id: int):
"""Modifie description externe du formsemestre """Modifie description externe du formsemestre.
Les images peuvent êtres passées dans el json, encodées en base64.
formsemestre_id : l'id du formsemestre formsemestre_id : l'id du formsemestre
SAMPLES SAMPLES
@ -832,6 +834,10 @@ def formsemestre_edit_description(formsemestre_id: int):
args = request.get_json(force=True) # may raise 400 Bad Request args = request.get_json(force=True) # may raise 400 Bad Request
if not formsemestre.description: if not formsemestre.description:
formsemestre.description = FormSemestreDescription() formsemestre.description = FormSemestreDescription()
# Decode images (base64)
for key in ["image", "photo_ens"]:
if key in args:
args[key] = base64.b64decode(args[key])
formsemestre.description.from_dict(args) formsemestre.description.from_dict(args)
db.session.commit() db.session.commit()
return formsemestre.description.to_dict() return formsemestre.description.to_dict()
@ -868,11 +874,12 @@ def formsemestre_get_photo_ens(formsemestre_id: int):
return _image_response(formsemestre.description.photo_ens) return _image_response(formsemestre.description.photo_ens)
def _image_response(image_data): def _image_response(image_data: bytes):
# Guess the mimetype based on the image data # Guess the mimetype based on the image data
mimetype = mimetypes.guess_type("image")[0] try:
image = PIL.Image.open(io.BytesIO(image_data))
if not mimetype: mimetype = image.get_format_mimetype()
except PIL.UnidentifiedImageError:
# Default to binary stream if mimetype cannot be determined # Default to binary stream if mimetype cannot be determined
mimetype = "application/octet-stream" mimetype = "application/octet-stream"

View File

@ -30,9 +30,11 @@ Emmanuel Viennet, 2023
""" """
import datetime import datetime
import io
from flask import flash, redirect, render_template, url_for from flask import flash, redirect, render_template, url_for
from flask import current_app, g, request from flask import current_app, g, request
import PIL
from app import db, log from app import db, log
from app.decorators import ( from app.decorators import (
@ -319,6 +321,20 @@ def edit_formsemestre_description(formsemestre_id: int):
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
) )
) )
try:
_ = PIL.Image.open(io.BytesIO(image_data))
except PIL.UnidentifiedImageError:
flash(
f"Image invalide ({field}), doit être une image",
"danger",
)
return redirect(
url_for(
"notes.edit_formsemestre_description",
formsemestre_id=formsemestre.id,
scodoc_dept=g.scodoc_dept,
)
)
setattr(formsemestre_description, field, image_data) setattr(formsemestre_description, field, image_data)
db.session.commit() db.session.commit()

View File

@ -122,9 +122,14 @@ def GET(path: str, headers: dict = None, errmsg=None, dept=None, raw=False):
if reply.headers.get("Content-Type", None) == "application/json": if reply.headers.get("Content-Type", None) == "application/json":
return reply.json() # decode la reponse JSON return reply.json() # decode la reponse JSON
if reply.headers.get("Content-Type", None) in [ if reply.headers.get("Content-Type", None) in [
"image/jpg",
"image/png",
"application/pdf", "application/pdf",
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"image/gif",
"image/jpeg",
"image/png",
"image/webp",
]: ]:
retval = { retval = {
"Content-Type": reply.headers.get("Content-Type", None), "Content-Type": reply.headers.get("Content-Type", None),
@ -132,7 +137,7 @@ def GET(path: str, headers: dict = None, errmsg=None, dept=None, raw=False):
} }
return retval return retval
raise APIError( raise APIError(
"Unknown returned content {r.headers.get('Content-Type', None} !\n", f"Unknown returned content {reply.headers.get('Content-Type', None)} !\n",
status_code=reply.status_code, status_code=reply.status_code,
) )

View File

@ -16,6 +16,7 @@ Utilisation :
Lancer : Lancer :
pytest tests/api/test_api_formsemestre.py pytest tests/api/test_api_formsemestre.py
""" """
import base64
import json import json
import requests import requests
from types import NoneType from types import NoneType
@ -813,6 +814,9 @@ def test_formsemestre_description(api_admin_headers):
assert r["salle"] == "une salle" assert r["salle"] == "une salle"
assert r["dispositif"] == 1 assert r["dispositif"] == 1
assert r["wip"] is True assert r["wip"] is True
# La réponse ne contient pas les images, servies à part:
assert "image" not in r
assert "photo_ens" not in r
r = POST( r = POST(
f"/formsemestre/{formsemestre_id}/description/edit", f"/formsemestre/{formsemestre_id}/description/edit",
data={ data={
@ -828,3 +832,15 @@ def test_formsemestre_description(api_admin_headers):
assert r["salle"] == "" assert r["salle"] == ""
assert r["dispositif"] == 0 assert r["dispositif"] == 0
assert r["wip"] is False assert r["wip"] is False
# Upload image
with open("tests/ressources/images/papillon.jpg", "rb") as f:
img = f.read()
img_base64 = base64.b64encode(img).decode("utf-8")
r = POST(
f"/formsemestre/{formsemestre_id}/description/edit", data={"image": img_base64}
)
assert r["wip"] is False
r = GET(f"/formsemestre/{formsemestre_id}/description/image", raw=True)
assert r.status_code == 200
assert r.headers.get("Content-Type") == "image/jpeg"
assert r.content == img