diff --git a/the_works/templates/views/titelbild.html b/the_works/templates/views/titelbild.html
index ba6a1e0..7802fc2 100644
--- a/the_works/templates/views/titelbild.html
+++ b/the_works/templates/views/titelbild.html
@@ -67,6 +67,7 @@
+ max. Dateigröße: 2 MB
diff --git a/the_works/views/titelbild.py b/the_works/views/titelbild.py
index 0e3532c..85e0b2e 100644
--- a/the_works/views/titelbild.py
+++ b/the_works/views/titelbild.py
@@ -17,11 +17,6 @@ def all():
return render_template("views/titelbild.html", titelbilder=map(lambda t: t._asdict_with_urls(), db.session.scalars(select(Titelbild))))
-#@bp.route("/titelbild/read/")
-#def read(id):
-# return
-
-
@bp.route("/titelbild/image/")
def image(id):
titelbild = db.session.get(Titelbild, id)
@@ -42,68 +37,86 @@ def thumbnail(id):
#raise ValueError(message="requested thumbnail not found")
-# wrapper function; the actual magic happens in create_from_filestorage
@bp.route("/titelbild/create", methods=["POST"])
def create():
- id = create_from_filestorage(request.files["form_Titelbild"])
- if id:
- flash("Eintrag erfolgreich hinzugefügt")
- else:
- flash("Eintrag ist bereits in Datenbank vorhanden")
- return redirect(url_for("titelbild.all"), code=303)
-
-
-# take a FileStorage object with an image and, if it does not yet exist in the DB, create a new Titelbild record around it; return record id
-def create_from_filestorage(f):
- if f.filename == "":
+ if request.files["form_Titelbild"].filename == "":
raise TypeError(message="FileStorage object expected")
- blob = f.read()
- filesize = len(blob)
- print(f"filesize of {f.filename} is {filesize}")
+ blob = request.files["form_Titelbild"].read()
- # use BytesIO as a wrapper around the byte stream
- with BytesIO(blob) as bytes_like:
- if _check_duplicate(bytes_like):
- return False
+ with BytesIO(blob) as bytes_io:
+ # check if image is already in DB
+# would use hashlib.file_digest() from the standard library instead of a makeshift function, but it's not available in Python 3.10
+#if db.session.scalar(select(Titelbild).where(Titelbild.sha256 == hashlib.file_digest(bytes_like, "sha256"))):
+ sha256 = makeshift_digest(bytes_io)
+ if db.session.scalar(select(Titelbild).where(Titelbild.sha256 == sha256)):
+ flash("Eintrag ist bereits in Datenbank vorhanden")
+ return redirect(url_for("titelbild.all"), code=303)
- # open image in memory
- bytes_like.seek(0)
- img = Image.open(bytes_like)
-
- # prepare values for database entry
- bytes_like.seek(0)
- titelbild = Titelbild(
- Mimetype = img.get_format_mimetype(),
- Dateiname = secure_filename(f.filename),
- Dateigroesse = filesize,
- Breite = img.width,
- Hoehe = img.height,
- Bild = bytes_like.getvalue(),
-# I'd rather use hashlib.file_digest() instead of a makeshift function, but alas, it's not available in Python 3.10
- #sha256 = hashlib.file_digest(bytes_like, "sha256")
- sha256 = makeshift_digest(bytes_like)
- )
-
- # add thumbnail to table entry
- tn_format = img.format
- img.thumbnail([128, 128])
+ # get Image and thumbnail
+ bytes_io.seek(0)
+ img = Image.open(bytes_io)
+ w, h, img_format = img.width, img.height, img.format
+ img.thumbnail(dimensions)
tn = BytesIO()
- img.save(tn, format=tn_format)
- titelbild.Thumbnail = tn.getvalue()
+ img.save(tn, format=img_format)
+
+ # build new record
+ titelbild = Titelbild(
+ Dateiname = request.files["form_Titelbild"].filename,
+ Dateigroesse = len(blob),
+ Mimetype = img.get_format_mimetype(),
+ Breite = w,
+ Hoehe = h,
+ Bild = bytes_io.getvalue(),
+ Thumbnail = tn.getvalue(),
+ sha256 = sha256
+ )
# add record to DB
db.session.add(titelbild)
db.session.flush()
- id = titelbild.ID
+ if titelbild.ID:
+ flash("Eintrag erfolgreich hinzugefügt")
+ else:
+ flash("Fehler beim Eintragen in die Datenbank")
db.session.commit()
-
- # return assigned ID
- return id
+ return redirect(url_for("titelbild.all"), code=303)
@bp.route("/titelbild/update/", methods=["POST"])
def update(id):
- # not written yet
+ if request.files["form_Titelbild"].filename == "":
+ raise TypeError(message="FileStorage object expected")
+ blob = request.files["form_Titelbild"].read()
+
+ with BytesIO(blob) as bytes_io:
+ # make sure image does not exist in database yet (including the record to be updated )
+ sha256 = makeshift_digest(bytes_io)
+ if db.session.scalar(select(Titelbild).where(Titelbild.sha256 == sha256)):
+ flash("Eintrag ist bereits in Datenbank vorhanden")
+ return redirect(url_for("titelbild.all"), code=303)
+
+ # get Image and thumbnail
+ bytes_io.seek(0)
+ img = Image.open(bytes_io)
+ w, h, img_format = img.width, img.height, img.format
+ img.thumbnail([128, 128])
+ tn = BytesIO()
+ img.save(tn, format=img_format)
+
+ # update existing record
+ current_tb = db.session.get(Titelbild, id)
+ current_tb.Dateiname = request.files["form_Titelbild"].filename
+ current_tb.Dateigroesse = len(blob)
+ current_tb.Mimetype = img.get_format_mimetype()
+ current_tb.Breite = w
+ current_tb.Hoehe = h
+ current_tb.Bild = bytes_io.getvalue()
+ current_tb.Thumbnail = tn.getvalue()
+ current_tb.sha256 = sha256
+
+ db.session.commit()
+ flash("Eintrag erfolgreich geändert")
return redirect(url_for("titelbild.all"), code=303)
@@ -116,19 +129,6 @@ def delete(id):
return redirect(url_for("titelbild.all"))
-
-# returns id of record with same checksum or False if there is none
-def _check_duplicate(bytes_like):
- bytes_like.seek(0)
- # would use hashlib.file_digest() from the standard library instead of a makeshift function, but it's not available in Python 3.10
- #duplicate = db.session.scalar(select(Titelbild).where(Titelbild.sha256 == hashlib.file_digest(bytes_like, "sha256")))
- duplicate = db.session.scalar(select(Titelbild).where(Titelbild.sha256 == makeshift_digest(bytes_like)))
- if duplicate:
- return duplicate.ID
- else:
- return False
-
-
# code adapted from https://stackoverflow.com/a/76937680
def makeshift_digest(bytes_io):
h = hashlib.sha256()
@@ -138,3 +138,13 @@ def makeshift_digest(bytes_io):
h.update(mv[:n])
return h.hexdigest()
+
+# takes a BytesIO object containing an image and creates a Pillow thumbnail from it
+def _process_image_bytes(bytes_io, dimensions=[128, 128]):
+ bytes_io.seek(0)
+ img = Image.open(bytes_io)
+ tn_format = img.format
+ img.thumbnail(dimensions)
+ tn = BytesIO()
+ img.save(tn, format=tn_format)
+ return (tn.getvalue(), tn_format)
diff --git a/the_works/views/werk.py b/the_works/views/werk.py
index c8f2d28..8cfd565 100644
--- a/the_works/views/werk.py
+++ b/the_works/views/werk.py
@@ -2,10 +2,11 @@ from flask import Blueprint, render_template, request, redirect, flash, url_for
from sqlalchemy import select, insert, update, delete
from the_works.database import db
from the_works.models import Werk, Reihe, Verlag, Werksform, Werk_Genre, Genre, Werk_Herausgeber, Herausgeber, Titelbild
-from the_works.views import titelbild as cover
+from the_works.views import titelbild as tb
bp = Blueprint("werk", __name__)
+
@bp.route("/werk")
@bp.route("/werk/all")
def all():
@@ -13,7 +14,7 @@ def all():
rows = db.session.execute(select(Werk, Reihe, Verlag, Werksform).join(Werk.reihe, isouter=True).join(Werk.verlag, isouter=True).join(Werk.werksform, isouter=True))
# condense result into list of dicts
werke = []
- for row in rows:
+ for row in rows:
werke.append({
"id": row.Werk.ID,
"Titel": row.Werk.Titel,
@@ -27,7 +28,6 @@ def all():
"ISBN_10": row.Werk.ISBN_10 or "",
"ISSN": row.Werk.ISSN or "",
"Preis": row.Werk.Preis or "",
- # Titelbild: return URL for the thumbnail
"Titelbild": url_for("titelbild.thumbnail", id=row.Werk.Titelbild) if row.Werk.Titelbild else "",
"Klappentext": row.Werk.Klappentext or "",
"Anmerkungen": row.Werk.Anmerkungen or "",
@@ -36,24 +36,24 @@ def all():
})
return render_template("views/werk.html", werke=werke)
+
@bp.route("/werk/read/")
def read(id):
+ # prepare Titelbilder as dict including URLs for thumbnail and full pic
+ titelbilder = map(lambda t: t._asdict_with_urls(), db.session.scalars(select(Titelbild)))
+
# id of zero -> return empty data
if id == 0:
- return render_template("views/werk_detail.html", werk={"ID": 0, "Erscheinungsdatum": ""}, reihen=db.session.scalars(select(Reihe)), verlage=db.session.scalars(select(Verlag)), werksformen=db.session.scalars(select(Werksform)), genres=db.session.scalars(select(Genre)), hrsg=db.session.scalars(select(Herausgeber)), titelbild=None)
+ return render_template("views/werk_detail.html", werk={"ID": 0, "Erscheinungsdatum": ""}, reihen=db.session.scalars(select(Reihe)), verlage=db.session.scalars(select(Verlag)), werksformen=db.session.scalars(select(Werksform)), genres=db.session.scalars(select(Genre)), hrsg=db.session.scalars(select(Herausgeber)), titelbilder=titelbilder)
+
# all other ids -> read existing entry from DB and return as dict
werk = db.session.get(Werk, id)
if not werk:
- return "Werk not found", 404
+ raise ValueError(f"Werk with ID {id} not found")
werk = werk._asdict()
- # prepare Titelbild as dict including URLs for thumbnail and full pic
- if werk["Titelbild"]:
- titelbild = db.session.get(Titelbild, werk["Titelbild"])._asdict()
- titelbild["Bild"] = url_for("titelbild.image", id=titelbild["ID"])
- titelbild["Thumbnail"] = url_for("titelbild.thumbnail", id=titelbild["ID"])
- else:
- titelbild = None
- return render_template("views/werk_detail.html", werk=werk, reihen=db.session.scalars(select(Reihe)), verlage=db.session.scalars(select(Verlag)), werksformen=db.session.scalars(select(Werksform)), genres=db.session.scalars(select(Genre)), hrsg=db.session.scalars(select(Herausgeber)), titelbild=titelbild)
+
+ return render_template("views/werk_detail.html", werk=werk, reihen=db.session.scalars(select(Reihe)), verlage=db.session.scalars(select(Verlag)), werksformen=db.session.scalars(select(Werksform)), genres=db.session.scalars(select(Genre)), hrsg=db.session.scalars(select(Herausgeber)), titelbilder=titelbilder)
+
@bp.route("/werk/create", methods=["POST"])
def create():
@@ -64,12 +64,12 @@ def create():
Verlag = request.form["form_Verlag"] or None,
Reihe = request.form["form_Reihe"] or None,
Reihennummer = request.form["form_Reihennummer"] or None,
- Erscheinungsdatum = get_datum(request.form["form_Erscheinungsjahr"], request.form["form_Erscheinungsmonat"], request.form["form_Erscheinungstag"]),
+ Erscheinungsdatum = _get_datum(request.form["form_Erscheinungsjahr"], request.form["form_Erscheinungsmonat"], request.form["form_Erscheinungstag"]),
ISBN_13 = request.form["form_ISBN_13"] or None,
ISBN_10 = request.form["form_ISBN_10"] or None,
ISSN = request.form["form_ISSN"] or None,
Preis = request.form["form_Preis"] or None,
- Titelbild = cover.create(request.files["form_Titelbild"]) if request.files["form_Titelbild"].filename else None,
+ Titelbild = request.form["form_Titelbild"] or None,
Klappentext = request.form["form_Klappentext"] or None,
Anmerkungen = request.form["form_Anmerkungen"] or None
)
@@ -95,18 +95,15 @@ def update(id):
werk.Verlag = request.form["form_Verlag"] or None
werk.Reihe = request.form["form_Reihe"] or None
werk.Reihennummer = request.form["form_Reihennummer"] or None
- werk.Erscheinungsdatum = get_datum(request.form["form_Erscheinungsjahr"], request.form["form_Erscheinungsmonat"], request.form["form_Erscheinungstag"])
+ werk.Erscheinungsdatum = _get_datum(request.form["form_Erscheinungsjahr"], request.form["form_Erscheinungsmonat"], request.form["form_Erscheinungstag"])
werk.ISBN_13 = request.form["form_ISBN_13"] or None
werk.ISBN_10 = request.form["form_ISBN_10"] or None
werk.ISSN = request.form["form_ISSN"] or None
werk.Preis = request.form["form_Preis"] or None
+ werk.Titelbild = request.form["form_Titelbild"] or None
werk.Klappentext = request.form["form_Klappentext"] or None
werk.Anmerkungen = request.form["form_Anmerkungen"] or None
- # update Titelbild
- if request.form["form_Titelbild_haschanged"]:
- werk.Titelbild = cover.create(request.files["form_Titelbild"]) if request.files["form_Titelbild"].filename else None
-
# update associated values: Genre
form_set = set(map(lambda g: int(g), request.form.getlist("form_Genre")))
for g in set(werk.genres) - form_set:
@@ -125,7 +122,8 @@ def update(id):
db.session.commit()
flash("Eintrag erfolgreich geändert")
return redirect(url_for("werk.all"))
-
+
+
@bp.route("/werk/delete/")
def delete(id):
werk = db.session.get(Werk, id)
@@ -134,7 +132,8 @@ def delete(id):
flash("Eintrag erfolgreich gelöscht")
return redirect(url_for("werk.all"))
-def get_datum(jahr, monat, tag):
+
+def _get_datum(jahr, monat, tag):
if tag != "":
return "-".join([jahr, monat.zfill(2), tag.zfill(2)])
elif monat != "":