poprawki w ux, poprawki w rotowaniu i jakosci zdjęć

This commit is contained in:
Mateusz Gruszczyński
2025-07-27 23:03:09 +02:00
parent 5c6e2f6540
commit a5025b94ff
2 changed files with 99 additions and 79 deletions

131
app.py
View File

@@ -116,6 +116,14 @@ failed_login_attempts = defaultdict(deque)
MAX_ATTEMPTS = 10
TIME_WINDOW = 60 * 60
WEBP_SAVE_PARAMS = {
"format": "WEBP",
"lossless": True, # lub False jeśli chcesz używać quality
"method": 6,
# "quality": 95, # tylko jeśli lossless=False
}
db = SQLAlchemy(app)
socketio = SocketIO(app, async_mode="eventlet")
login_manager = LoginManager(app)
@@ -328,8 +336,7 @@ def save_resized_image(file, path):
image.info.clear()
new_path = path.rsplit(".", 1)[0] + ".webp"
# image.save(new_path, format="WEBP", quality=100, method=0)
image.save(new_path, format="WEBP", lossless=True, method=6)
image.save(new_path, **WEBP_SAVE_PARAMS)
except Exception as e:
raise ValueError(f"Błąd podczas przetwarzania obrazu: {e}")
@@ -390,7 +397,7 @@ def rotate_receipt_by_id(receipt_id):
new_filename = generate_new_receipt_filename(receipt.list_id)
new_path = os.path.join(app.config["UPLOAD_FOLDER"], new_filename)
rotated.save(new_path, format="WEBP", quality=100)
rotated.save(new_path, **WEBP_SAVE_PARAMS)
os.remove(old_path)
receipt.filename = new_filename
@@ -486,6 +493,35 @@ def get_expenses_aggregated_by_list_created_at(
return {"labels": labels, "expenses": expenses}
def recalculate_filesizes(receipt_id: int = None):
updated = 0
not_found = 0
unchanged = 0
if receipt_id is not None:
receipt = db.session.get(Receipt, receipt_id)
receipts = [receipt] if receipt else []
else:
receipts = db.session.execute(db.select(Receipt)).scalars().all()
for r in receipts:
if not r:
continue
filepath = os.path.join(app.config["UPLOAD_FOLDER"], r.filename)
if os.path.exists(filepath):
real_size = os.path.getsize(filepath)
if r.filesize != real_size:
r.filesize = real_size
updated += 1
else:
unchanged += 1
else:
not_found += 1
db.session.commit()
return updated, unchanged, not_found
############# OCR ###########################
@@ -1431,6 +1467,7 @@ def rotate_receipt_user(receipt_id):
try:
rotate_receipt_by_id(receipt_id)
recalculate_filesizes(receipt_id)
flash("Obrócono paragon", "success")
except FileNotFoundError:
flash("Plik nie istnieje", "danger")
@@ -1730,6 +1767,15 @@ def admin_receipts(id):
try:
if id == "all":
receipts = Receipt.query.order_by(Receipt.uploaded_at.desc()).all()
# Szukaj sierot tylko dla "all"
upload_folder = app.config["UPLOAD_FOLDER"]
all_db_filenames = set(r.filename for r in receipts)
files_on_disk = set(os.listdir(upload_folder))
stale_files = [
f for f in files_on_disk
if f.endswith(".webp") and f not in all_db_filenames and f.startswith("list_")
]
else:
list_id = int(id)
receipts = (
@@ -1737,20 +1783,11 @@ def admin_receipts(id):
.order_by(Receipt.uploaded_at.desc())
.all()
)
stale_files = [] # brak sierot
except ValueError:
flash("Nieprawidłowe ID listy.", "danger")
return redirect(url_for("admin_panel"))
# Przeszukaj folder upload pod kątem „sierot”
upload_folder = app.config["UPLOAD_FOLDER"]
db_filenames = set(r.filename for r in receipts)
all_db_filenames = set(r.filename for r in Receipt.query.all()) # Wszystko z bazy
files_on_disk = set(os.listdir(upload_folder))
stale_files = [
f for f in files_on_disk
if f.endswith(".webp") and f not in all_db_filenames and f.startswith("list_")
]
# Przekaż do template: receipts (z bazy) i orphan_files (sieroty)
return render_template(
"admin/receipts.html",
receipts=receipts,
@@ -1759,34 +1796,13 @@ def admin_receipts(id):
)
@app.route("/admin/delete_orphan_receipt_file/<filename>")
@login_required
@admin_required
def delete_orphan_receipt_file(filename):
upload_folder = app.config["UPLOAD_FOLDER"]
safe_filename = os.path.basename(filename)
file_path = os.path.join(upload_folder, safe_filename)
# Dowolnego pliku nie kasujemy jeśli jest w bazie (Receipt.filename)
if Receipt.query.filter_by(filename=safe_filename).first():
flash("Nie możesz usunąć pliku powiązanego z bazą!", "danger")
return redirect(url_for("admin_receipts", id="all"))
if not os.path.exists(file_path):
flash("Plik już nie istnieje.", "warning")
else:
try:
os.remove(file_path)
flash(f"Usunięto plik: {safe_filename}", "success")
except Exception as e:
flash(f"Błąd przy usuwaniu pliku: {e}", "danger")
return redirect(url_for("admin_receipts", id="all"))
@app.route("/admin/rotate_receipt/<int:receipt_id>")
@login_required
@admin_required
def rotate_receipt(receipt_id):
try:
rotate_receipt_by_id(receipt_id)
recalculate_filesizes(receipt_id)
flash("Obrócono paragon", "success")
except FileNotFoundError:
flash("Plik nie istnieje", "danger")
@@ -1797,9 +1813,27 @@ def rotate_receipt(receipt_id):
@app.route("/admin/delete_receipt/<int:receipt_id>")
@app.route("/admin/delete_receipt/orphan/<path:filename>")
@login_required
@admin_required
def delete_receipt(receipt_id):
def delete_receipt(receipt_id=None, filename=None):
if filename: # tryb orphan
safe_filename = os.path.basename(filename)
if Receipt.query.filter_by(filename=safe_filename).first():
flash("Nie można usunąć pliku powiązanego z bazą!", "danger")
else:
file_path = os.path.join(app.config["UPLOAD_FOLDER"], safe_filename)
if os.path.exists(file_path):
try:
os.remove(file_path)
flash(f"Usunięto plik: {safe_filename}", "success")
except Exception as e:
flash(f"Błąd przy usuwaniu pliku: {e}", "danger")
else:
flash("Plik już nie istnieje.", "warning")
return redirect(url_for("admin_receipts", id="all"))
# tryb z rekordem w bazie
try:
delete_receipt_by_id(receipt_id)
flash("Paragon usunięty", "success")
@@ -1826,6 +1860,8 @@ def rename_receipt(receipt_id):
try:
os.rename(old_path, new_path)
receipt.filename = new_filename
db.session.flush()
recalculate_filesizes(receipt.id)
db.session.commit()
flash("Zmieniono nazwę pliku", "success")
except Exception as e:
@@ -2215,7 +2251,7 @@ def crop_receipt():
receipt.filename = os.path.basename(new_path)
db.session.commit()
recalculate_filesizes(receipt.id)
return jsonify(success=True)
except Exception as e:
return jsonify(success=False, error=str(e))
@@ -2224,25 +2260,8 @@ def crop_receipt():
@app.route("/admin/recalculate_filesizes")
@login_required
@admin_required
def recalculate_filesizes():
updated = 0
not_found = 0
unchanged = 0
receipts = Receipt.query.all()
for r in receipts:
filepath = os.path.join(app.config["UPLOAD_FOLDER"], r.filename)
if os.path.exists(filepath):
real_size = os.path.getsize(filepath)
if r.filesize != real_size:
r.filesize = real_size
updated += 1
else:
unchanged += 1
else:
not_found += 1
db.session.commit()
def recalculate_filesizes_all():
updated, unchanged, not_found = recalculate_filesizes()
flash(
f"Zaktualizowano: {updated}, bez zmian: {unchanged}, brak pliku: {not_found}",
"success",