forked from ScoDoc/ScoDoc
API FormSemestreDescription: images: upload, tests.
This commit is contained in:
parent
5a751cb6e7
commit
4bfd0858a8
@ -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"
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user