optymalizacje_kodu #8

Merged
gru merged 23 commits from optymalizacje_kodu into master 2025-07-31 10:55:39 +02:00
6 changed files with 116 additions and 11 deletions
Showing only changes of commit 50de359838 - Show all commits

31
app.py
View File

@@ -1143,7 +1143,8 @@ def main_page():
# ostatnia kwota (w tym przypadku max = suma z ostatniego zapisu)
latest_expenses_map = dict(
db.session.query(
Expense.list_id, func.coalesce(func.max(Expense.amount), 0)
Expense.list_id,
func.coalesce(func.sum(Expense.amount), 0)
)
.filter(Expense.list_id.in_(all_ids))
.group_by(Expense.list_id)
@@ -1907,21 +1908,21 @@ def admin_panel():
.group_by(Item.list_id)
.all()
)
stats_map = {
s.list_id: (s.total_count or 0, s.purchased_count or 0) for s in stats
}
# Pobranie ostatnich kwot dla wszystkich list w jednym zapytaniu
latest_expenses_map = dict(
db.session.query(
Expense.list_id, func.coalesce(func.max(Expense.amount), 0)
Expense.list_id,
func.coalesce(func.sum(Expense.amount), 0)
)
.filter(Expense.list_id.in_(all_ids))
.group_by(Expense.list_id)
.all()
)
enriched_lists = []
for l in all_lists:
total_count, purchased_count = stats_map.get(l.id, (0, 0))
@@ -2024,6 +2025,7 @@ def admin_panel():
)
@app.route("/admin/delete_list/<int:list_id>")
@login_required
@admin_required
@@ -2634,6 +2636,27 @@ def recalculate_filesizes_all():
return redirect(url_for("admin_receipts", id="all"))
@app.route("/admin/mass_edit_categories", methods=["GET", "POST"])
@login_required
@admin_required
def admin_mass_edit_categories():
lists = ShoppingList.query.options(joinedload(ShoppingList.categories)).order_by(ShoppingList.created_at.desc()).all()
categories = Category.query.order_by(Category.name.asc()).all()
if request.method == "POST":
for lst in lists:
selected_ids = request.form.getlist(f"categories_{lst.id}")
lst.categories.clear()
if selected_ids:
cats = Category.query.filter(Category.id.in_(selected_ids)).all()
lst.categories.extend(cats)
db.session.commit()
flash("Zaktualizowano kategorie dla wybranych list", "success")
return redirect(url_for("admin_mass_edit_categories"))
return render_template("admin/mass_edit_categories.html", lists=lists, categories=categories)
@app.route("/healthcheck")
def healthcheck():
header_token = request.headers.get("X-Internal-Check")

View File

@@ -0,0 +1,11 @@
document.addEventListener("DOMContentLoaded", function () {
document.querySelectorAll('select[multiple]').forEach(function (el) {
new TomSelect(el, {
plugins: ['remove_button'],
placeholder: 'Wybierz kategorie...',
create: false,
sortField: { field: "text", direction: "asc" },
dropdownParent: 'body'
});
});
});

View File

@@ -18,14 +18,18 @@
<div class="collapse navbar-collapse" id="adminNavbar">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="/admin/users">👥 Zarządzanie użytkownikami</a>
<a class="nav-link" href="{{ url_for('list_users') }}">👥 Zarządzanie użytkownikami</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/admin/receipts/all">📸 Wszystkie paragony</a>
<a class="nav-link" href="{{ url_for('admin_receipts', id='all') }}">📸 Wszystkie paragony</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/admin/products">🛍️ Produkty i sugestie</a>
<a class="nav-link" href="{{ url_for('list_products') }}">🛍️ Produkty i sugestie</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('admin_mass_edit_categories') }}">🗂 Masowa edycja kategorii</a>
</li>
</ul>
</div>
</div>
@@ -195,7 +199,7 @@
});
</script>
<script src="{{ url_for('static_bp.serve_js', filename='expenses.js') }}"></script>
{% endblock %}
<div class="info-bar-fixed">

View File

@@ -0,0 +1,67 @@
{% extends 'base.html' %}
{% block title %}Masowa edycja kategorii{% endblock %}
{% block content %}
<div class="d-flex justify-content-between align-items-center flex-wrap mb-4">
<h2 class="mb-2">🗂 Masowa edycja kategorii list</h2>
<div>
<a href="{{ url_for('admin_panel') }}" class="btn btn-outline-secondary">← Powrót do panelu</a>
</div>
</div>
<form method="post">
<div class="card bg-dark text-white mb-5">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-dark table-striped table-hover align-middle mb-0">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Nazwa listy</th>
<th scope="col">Właściciel</th>
<th scope="col">Data utworzenia</th>
<th scope="col">Kategorie</th>
</tr>
</thead>
<tbody>
{% for lst in lists %}
<tr>
<td>{{ lst.id }}</td>
<td>{{ lst.title }}</td>
<td>{{ lst.owner.username if lst.owner else "?" }}</td>
<td>{{ lst.created_at.strftime('%Y-%m-%d') }}</td>
<td style="min-width: 220px;">
<select name="categories_{{ lst.id }}" multiple
class="form-select bg-dark text-white border-secondary">
{% for cat in categories %}
<option value="{{ cat.id }}" {% if cat in lst.categories %}selected{% endif %}>
{{ cat.name }}
</option>
{% endfor %}
</select>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div class="mt-3">
<button type="submit" class="btn btn-success">💾 Zapisz zmiany</button>
</div>
</form>
{% endblock %}
{% block scripts %}
<script src="https://cdn.jsdelivr.net/npm/tom-select/dist/js/tom-select.complete.min.js"></script>
<script src="{{ url_for('static_bp.serve_js', filename='admin_mass_categories.js') }}"></script>
<style>
.ts-dropdown {
z-index: 9999 !important;
/* 🔹 dropdown zawsze na wierzchu */
}
</style>
{% endblock %}

View File

@@ -15,7 +15,7 @@
{% if '/admin/receipts' in request.path or '/edit_my_list' in request.path %}
<link href="{{ url_for('static_bp.serve_css_lib', filename='cropper.min.css') }}" rel="stylesheet">
{% endif %}
{% if '/edit_my_list' or '/admin/edit_list' in request.path %}
{% if '/edit_my_list' or '/admin/edit_list' in request.path or '/admin/mass_edit_categories' %}
<link href="{{ url_for('static_bp.serve_css_lib', filename='tom-select.bootstrap5.min.css') }}" rel="stylesheet">
{% endif %}
@@ -106,7 +106,7 @@
{% if '/admin/receipts' in request.path or '/edit_my_list' in request.path %}
<script src="{{ url_for('static_bp.serve_js_lib', filename='cropper.min.js') }}"></script>
{% endif %}
{% if '/edit_my_list' or '/admin/edit_list' in request.path %}
{% if '/edit_my_list' or '/admin/edit_list' or '/admin/mass_edit_categories' in request.path %}
<script src="{{ url_for('static_bp.serve_js_lib', filename='tom-select.complete.min.js') }}"></script>
{% endif %}

View File

@@ -100,7 +100,7 @@
<a href="{{ url_for('rotate_receipt_user', receipt_id=r.id) }}"
class="btn btn-sm btn-outline-warning w-100 mb-2">🔄 Obróć o 90°</a>
<a href="#" class="btn btn-sm btn-outline-secondary w-100 mb-2 disabled" data-bs-toggle="modal"
<a href="#" class="btn btn-sm btn-outline-secondary w-100 mb-2" data-bs-toggle="modal"
data-bs-target="#userCropModal" data-img-src="{{ url_for('uploaded_file', filename=r.filename) }}"
data-receipt-id="{{ r.id }}" data-crop-endpoint="{{ url_for('crop_receipt_user') }}">
✂️ Przytnij