webp support

This commit is contained in:
Mateusz Gruszczyński
2025-07-20 16:50:26 +02:00
parent 1f609b6dba
commit 470cd32745
6 changed files with 222 additions and 88 deletions

136
app.py
View File

@@ -142,6 +142,14 @@ class Expense(db.Model):
receipt_filename = db.Column(db.String(255), nullable=True)
list = db.relationship("ShoppingList", backref="expenses", lazy=True)
class Receipt(db.Model):
id = db.Column(db.Integer, primary_key=True)
list_id = db.Column(db.Integer, db.ForeignKey("shopping_list.id"), nullable=False)
filename = db.Column(db.String(255), nullable=False)
uploaded_at = db.Column(db.DateTime, default=datetime.utcnow)
shopping_list = db.relationship("ShoppingList", backref="receipts", lazy=True)
with app.app_context():
db.create_all()
@@ -215,11 +223,12 @@ def allowed_file(filename):
def get_list_details(list_id):
shopping_list = ShoppingList.query.get_or_404(list_id)
items = Item.query.filter_by(list_id=list_id).order_by(Item.position.asc()).all()
receipt_pattern = f"list_{list_id}"
all_files = os.listdir(app.config["UPLOAD_FOLDER"])
receipt_files = [f for f in all_files if receipt_pattern in f]
expenses = Expense.query.filter_by(list_id=list_id).all()
total_expense = sum(e.amount for e in expenses)
receipts = Receipt.query.filter_by(list_id=list_id).all()
receipt_files = [r.filename for r in receipts]
return shopping_list, items, receipt_files, expenses, total_expense
@@ -243,16 +252,15 @@ def enrich_list_data(l):
l.total_expense = sum(e.amount for e in expenses)
return l
def save_resized_image(file, path):
image = Image.open(file)
image.thumbnail((2000, 2000))
if image.format == "HEIF":
path = path.rsplit(".", 1)[0] + ".jpg"
image = image.convert("RGB")
image.save(path, format="JPEG")
else:
image.save(path)
new_path = path.rsplit(".", 1)[0] + ".webp"
image = image.convert("RGB")
image.save(new_path, format="WEBP", quality=85)
def redirect_with_flash(
message: str, category: str = "info", endpoint: str = "main_page"
@@ -927,54 +935,46 @@ def upload_receipt(list_id):
return redirect(request.referrer) """
@app.route("/upload_receipt/<int:list_id>", methods=["POST"])
def upload_receipt(list_id):
if "receipt" not in request.files:
if (
request.is_json
or request.headers.get("X-Requested-With") == "XMLHttpRequest"
):
return jsonify({"success": False, "message": "Brak pliku"}), 400
flash("Brak pliku", "danger")
return redirect(request.referrer)
return _receipt_error("Brak pliku")
file = request.files["receipt"]
if file.filename == "":
if (
request.is_json
or request.headers.get("X-Requested-With") == "XMLHttpRequest"
):
return jsonify({"success": False, "message": "Nie wybrano pliku"}), 400
flash("Nie wybrano pliku", "danger")
return redirect(request.referrer)
return _receipt_error("Nie wybrano pliku")
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
full_filename = f"list_{list_id}_{filename}"
file_path = os.path.join(app.config["UPLOAD_FOLDER"], full_filename)
base_name = f"list_{list_id}_{filename.rsplit('.', 1)[0]}"
webp_filename = base_name + ".webp"
file_path = os.path.join(app.config["UPLOAD_FOLDER"], webp_filename)
save_resized_image(file, file_path)
if (
request.is_json
or request.headers.get("X-Requested-With") == "XMLHttpRequest"
):
url = url_for("uploaded_file", filename=full_filename)
new_receipt = Receipt(list_id=list_id, filename=webp_filename)
db.session.add(new_receipt)
db.session.commit()
if request.is_json or request.headers.get("X-Requested-With") == "XMLHttpRequest":
url = url_for("uploaded_file", filename=webp_filename)
socketio.emit("receipt_added", {"url": url}, to=str(list_id))
return jsonify({"success": True, "url": url})
flash("Wgrano paragon", "success")
return redirect(request.referrer)
return _receipt_error("Niedozwolony format pliku")
def _receipt_error(message):
if request.is_json or request.headers.get("X-Requested-With") == "XMLHttpRequest":
return jsonify({"success": False, "message": "Niedozwolony format pliku"}), 400
flash("Niedozwolony format pliku", "danger")
return jsonify({"success": False, "message": message}), 400
flash(message, "danger")
return redirect(request.referrer)
@app.route("/uploads/<filename>")
def uploaded_file(filename):
response = send_from_directory(app.config["UPLOAD_FOLDER"], filename)
@@ -1196,29 +1196,32 @@ def delete_user(user_id):
return redirect(url_for("list_users"))
import os
@app.route("/admin/receipts/<id>")
@login_required
@admin_required
def admin_receipts(id):
all_files = os.listdir(app.config["UPLOAD_FOLDER"])
image_files = [f for f in all_files if allowed_file(f)]
if id == "all":
filtered_files = image_files
else:
try:
try:
if id == "all":
receipts = Receipt.query.order_by(Receipt.uploaded_at.desc()).all()
else:
list_id = int(id)
receipt_prefix = f"list_{list_id}_"
filtered_files = [f for f in image_files if f.startswith(receipt_prefix)]
except ValueError:
flash("Nieprawidłowe ID listy.", "danger")
return redirect(url_for("admin_panel"))
receipts = (
Receipt.query.filter_by(list_id=list_id)
.order_by(Receipt.uploaded_at.desc())
.all()
)
except ValueError:
flash("Nieprawidłowe ID listy.", "danger")
return redirect(url_for("admin_panel"))
for r in receipts:
path = os.path.join(app.config["UPLOAD_FOLDER"], r.filename)
r.filesize = os.path.getsize(path) if os.path.exists(path) else 0
return render_template("admin/receipts.html", receipts=receipts)
return render_template(
"admin/receipts.html",
image_files=filtered_files,
upload_folder=app.config["UPLOAD_FOLDER"],
)
@app.route("/admin/delete_receipt/<filename>")
@@ -1226,16 +1229,31 @@ def admin_receipts(id):
@admin_required
def delete_receipt(filename):
file_path = os.path.join(app.config["UPLOAD_FOLDER"], filename)
removed_file = False
removed_db = False
# Usuń plik z dysku
if os.path.exists(file_path):
os.remove(file_path)
flash("Plik usunięty", "success")
else:
flash("Plik nie istnieje", "danger")
removed_file = True
# Usuń rekord z bazy
receipt = Receipt.query.filter_by(filename=filename).first()
if receipt:
db.session.delete(receipt)
db.session.commit()
removed_db = True
# Komunikat
if removed_file or removed_db:
flash("Paragon usunięty", "success")
else:
flash("Paragon nie istnieje", "danger")
# Powrót
next_url = request.args.get("next")
if next_url:
return redirect(next_url)
return redirect(url_for("admin_receipts"))
return redirect(next_url or url_for("admin_receipts", id="all"))
@app.route("/admin/delete_selected_lists", methods=["POST"])
@@ -1273,9 +1291,7 @@ def edit_list(list_id):
db.session.query(Item).filter_by(list_id=list_id).order_by(Item.id.desc()).all()
)
receipt_pattern = f"list_{list_id}_"
all_files = os.listdir(app.config["UPLOAD_FOLDER"])
receipts = [f for f in all_files if f.startswith(receipt_pattern)]
receipts = Receipt.query.filter_by(list_id=list_id).order_by(Receipt.uploaded_at.desc()).all()
if request.method == "POST":
action = request.form.get("action")