poprawki w ux, poprawki w rotowaniu i jakosci zdjęć
This commit is contained in:
131
app.py
131
app.py
@@ -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",
|
||||
|
Reference in New Issue
Block a user