crop dla userów i przeniesienie listy na inny miesiac

This commit is contained in:
Mateusz Gruszczyński
2025-07-28 13:20:23 +02:00
parent 9e3068a722
commit 643757e45e
9 changed files with 334 additions and 111 deletions

145
app.py
View File

@@ -64,13 +64,17 @@ app = Flask(__name__)
app.config.from_object(Config)
# Konfiguracja nagłówków bezpieczeństwa z .env
csp_policy = {
"default-src": "'self'",
"script-src": "'self' 'unsafe-inline'",
"style-src": "'self' 'unsafe-inline'",
"img-src": "'self' data:",
"connect-src": "'self'",
} if app.config.get("ENABLE_CSP", True) else None
csp_policy = (
{
"default-src": "'self'",
"script-src": "'self' 'unsafe-inline'",
"style-src": "'self' 'unsafe-inline'",
"img-src": "'self' data:",
"connect-src": "'self'",
}
if app.config.get("ENABLE_CSP", True)
else None
)
permissions_policy = {"browsing-topics": "()"} if app.config.get("ENABLE_PP") else None
@@ -424,9 +428,38 @@ def generate_new_receipt_filename(list_id):
return f"list_{list_id}_{timestamp}_{random_part}.webp"
def handle_crop_receipt(receipt_id, file):
if not receipt_id or not file:
return {"success": False, "error": "Brak danych"}
receipt = Receipt.query.get_or_404(receipt_id)
old_path = os.path.join(app.config["UPLOAD_FOLDER"], receipt.filename)
try:
new_filename = generate_new_receipt_filename(receipt.list_id)
new_path = os.path.join(app.config["UPLOAD_FOLDER"], new_filename)
save_resized_image(file, new_path)
if os.path.exists(old_path):
os.remove(old_path)
receipt.filename = os.path.basename(new_path)
db.session.commit()
recalculate_filesizes(receipt.id)
return {"success": True}
except Exception as e:
return {"success": False, "error": str(e)}
def get_expenses_aggregated_by_list_created_at(
user_only=False, admin=False, show_all=False,
range_type="monthly", start_date=None, end_date=None, user_id=None
user_only=False,
admin=False,
show_all=False,
range_type="monthly",
start_date=None,
end_date=None,
user_id=None,
):
"""
Wspólna logika: sumujemy najnowszy wydatek z każdej listy,
@@ -455,8 +488,7 @@ def get_expenses_aggregated_by_list_created_at(
except Exception:
return {"error": "Błędne daty", "labels": [], "expenses": []}
lists_query = lists_query.filter(
ShoppingList.created_at >= dt_start,
ShoppingList.created_at < dt_end
ShoppingList.created_at >= dt_start, ShoppingList.created_at < dt_end
)
lists = lists_query.all()
@@ -464,8 +496,7 @@ def get_expenses_aggregated_by_list_created_at(
data = []
for sl in lists:
latest_exp = (
Expense.query
.filter_by(list_id=sl.id)
Expense.query.filter_by(list_id=sl.id)
.order_by(Expense.added_at.desc())
.first()
)
@@ -1002,15 +1033,31 @@ def edit_my_list(list_id):
abort(403, description="Nie jesteś właścicielem tej listy.")
if request.method == "POST":
# Obsługa zmiany miesiąca utworzenia listy
move_to_month = request.form.get("move_to_month")
if move_to_month:
try:
year, month = map(int, move_to_month.split("-"))
new_created_at = datetime(year, month, 1, tzinfo=timezone.utc)
l.created_at = new_created_at
db.session.commit()
flash(
f"Zmieniono datę utworzenia listy na {new_created_at.strftime('%Y-%m-%d')}",
"success",
)
return redirect(url_for("edit_my_list", list_id=list_id))
except ValueError:
flash("Nieprawidłowy format miesiąca", "danger")
return redirect(url_for("edit_my_list", list_id=list_id))
# Pozostała aktualizacja pól
new_title = request.form.get("title", "").strip()
is_public = "is_public" in request.form
is_temporary = "is_temporary" in request.form
is_archived = "is_archived" in request.form
expires_date = request.form.get("expires_date")
expires_time = request.form.get("expires_time")
# Walidacja tytułu
if not new_title:
flash("Podaj poprawny tytuł", "danger")
return redirect(url_for("edit_my_list", list_id=list_id))
@@ -1020,7 +1067,6 @@ def edit_my_list(list_id):
l.is_temporary = is_temporary
l.is_archived = is_archived
# Obsługa daty wygaśnięcia
if expires_date and expires_time:
try:
combined = f"{expires_date} {expires_time}"
@@ -1160,7 +1206,7 @@ def view_list(list_id):
expenses=expenses,
total_expense=total_expense,
is_share=False,
is_owner=is_owner
is_owner=is_owner,
)
@@ -1254,7 +1300,7 @@ def user_expenses_data():
range_type=range_type,
start_date=start_date,
end_date=end_date,
user_id=current_user.id
user_id=current_user.id,
)
if "error" in result:
return jsonify({"error": result["error"]}), 400
@@ -1545,6 +1591,22 @@ def analyze_receipts_for_list(list_id):
return jsonify({"results": results, "total": round(total, 2)})
@app.route("/user_crop_receipt", methods=["POST"])
@login_required
def crop_receipt_user():
receipt_id = request.form.get("receipt_id")
file = request.files.get("cropped_image")
receipt = Receipt.query.get_or_404(receipt_id)
list_obj = ShoppingList.query.get_or_404(receipt.list_id)
if list_obj.owner_id != current_user.id and not current_user.is_admin:
return jsonify(success=False, error="Brak dostępu"), 403
result = handle_crop_receipt(receipt_id, file)
return jsonify(result)
@app.route("/admin")
@login_required
@admin_required
@@ -1775,8 +1837,11 @@ def admin_receipts(id):
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_")
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)
@@ -1794,7 +1859,7 @@ def admin_receipts(id):
"admin/receipts.html",
receipts=receipts,
orphan_files=stale_files,
orphan_files_count=len(stale_files)
orphan_files_count=len(stale_files),
)
@@ -1995,6 +2060,18 @@ def edit_list(list_id):
flash("Niepoprawna kwota", "danger")
return redirect(url_for("edit_list", list_id=list_id))
created_month = request.form.get("created_month")
if created_month:
try:
year, month = map(int, created_month.split("-"))
l.created_at = datetime(year, month, 1, tzinfo=timezone.utc)
except ValueError:
flash(
"Nieprawidłowy format miesiąca (przeniesienie daty utworzenia)",
"danger",
)
return redirect(url_for("edit_list", list_id=list_id))
db.session.add(l)
db.session.commit()
flash("Zapisano zmiany listy", "success")
@@ -2232,31 +2309,11 @@ def demote_user(user_id):
@app.route("/admin/crop_receipt", methods=["POST"])
@login_required
@admin_required
def crop_receipt():
def crop_receipt_admin():
receipt_id = request.form.get("receipt_id")
file = request.files.get("cropped_image")
if not receipt_id or not file:
return jsonify(success=False, error="Brak danych")
receipt = Receipt.query.get_or_404(receipt_id)
old_path = os.path.join(app.config["UPLOAD_FOLDER"], receipt.filename)
try:
new_filename = generate_new_receipt_filename(receipt.list_id)
new_path = os.path.join(app.config["UPLOAD_FOLDER"], new_filename)
save_resized_image(file, new_path)
if os.path.exists(old_path):
os.remove(old_path)
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))
result = handle_crop_receipt(receipt_id, file)
return jsonify(result)
@app.route("/admin/recalculate_filesizes")