Add image size display to series viewer: implement HTMX endpoint and partial template for total image size
This commit is contained in:
@@ -0,0 +1,13 @@
|
|||||||
|
<div class="d-flex align-items-center justify-content-between">
|
||||||
|
<div>
|
||||||
|
<div class="small text-muted">Total images size</div>
|
||||||
|
<div class="fw-bold">{{ size_human }}</div>
|
||||||
|
<div class="text-muted small">({{ size_bytes }} bytes)</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-end">
|
||||||
|
<button class="btn btn-sm btn-outline-secondary"
|
||||||
|
hx-get="{% url 'atlas:series_image_size' series.pk %}?refresh=1"
|
||||||
|
hx-target="#series-image-size"
|
||||||
|
hx-swap="innerHTML">Recalculate</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -170,6 +170,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-header">Images size</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div id="series-image-size"
|
||||||
|
hx-get="{% url 'atlas:series_image_size' series.pk %}"
|
||||||
|
hx-trigger="load"
|
||||||
|
hx-swap="innerHTML">
|
||||||
|
<p class="text-muted small">Loading size...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="card mb-3">
|
<div class="card mb-3">
|
||||||
<div class="card-header">Actions</div>
|
<div class="card-header">Actions</div>
|
||||||
<div class="card-body d-flex flex-column gap-2">
|
<div class="card-body d-flex flex-column gap-2">
|
||||||
|
|||||||
@@ -480,6 +480,11 @@ urlpatterns = [
|
|||||||
views.series_truncate_htmx,
|
views.series_truncate_htmx,
|
||||||
name="series_truncate",
|
name="series_truncate",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"series/<int:series_id>/image_size/",
|
||||||
|
views.series_image_size_htmx,
|
||||||
|
name="series_image_size",
|
||||||
|
),
|
||||||
path("series/<int:pk>/images/", views.series_images_partial, name="series_images"),
|
path("series/<int:pk>/images/", views.series_images_partial, name="series_images"),
|
||||||
path("series/<int:pk>/authors", views.SeriesAuthorUpdate.as_view(), name="series_authors"),
|
path("series/<int:pk>/authors", views.SeriesAuthorUpdate.as_view(), name="series_authors"),
|
||||||
path("series/<int:series_id>/finding/related", views.series_finding_related, name="series_finding_related"),
|
path("series/<int:series_id>/finding/related", views.series_finding_related, name="series_finding_related"),
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ from django.views.decorators.http import require_POST
|
|||||||
from django.views.decorators.http import require_http_methods
|
from django.views.decorators.http import require_http_methods
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
|
from django.template.defaultfilters import filesizeformat
|
||||||
|
|
||||||
# from django.contrib.auth.models import User
|
# from django.contrib.auth.models import User
|
||||||
from django.contrib.auth.decorators import login_required, user_passes_test
|
from django.contrib.auth.decorators import login_required, user_passes_test
|
||||||
@@ -235,6 +236,42 @@ def series_truncate_htmx(request, series_id, start, end):
|
|||||||
return HttpResponse(html)
|
return HttpResponse(html)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def series_image_size_htmx(request, series_id):
|
||||||
|
"""HTMX endpoint returning the total size of images in a series.
|
||||||
|
|
||||||
|
If `?refresh=1` is provided the size will be recalculated.
|
||||||
|
"""
|
||||||
|
series = get_object_or_404(Series, pk=series_id)
|
||||||
|
|
||||||
|
# Permission: reuse existing series permission check if available
|
||||||
|
try:
|
||||||
|
can_view = series.check_user_can_view(request.user)
|
||||||
|
except Exception:
|
||||||
|
# fallback to basic permission: allow authenticated users
|
||||||
|
can_view = True
|
||||||
|
|
||||||
|
if not can_view:
|
||||||
|
return HttpResponse("<div class=\"alert alert-danger\">Permission denied</div>")
|
||||||
|
|
||||||
|
refresh = request.GET.get("refresh") in ("1", "true", "True")
|
||||||
|
|
||||||
|
try:
|
||||||
|
size_bytes = series.get_total_image_size(refresh=refresh)
|
||||||
|
except Exception:
|
||||||
|
size_bytes = 0
|
||||||
|
|
||||||
|
human = filesizeformat(size_bytes)
|
||||||
|
|
||||||
|
html = render_to_string(
|
||||||
|
"atlas/partials/_series_image_size.html",
|
||||||
|
{"series": series, "size_bytes": size_bytes, "size_human": human, "refresh": refresh},
|
||||||
|
request=request,
|
||||||
|
)
|
||||||
|
|
||||||
|
return HttpResponse(html)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def case_search_partial(request):
|
def case_search_partial(request):
|
||||||
"""Return an HTMX partial listing cases matching the query parameter `q`.
|
"""Return an HTMX partial listing cases matching the query parameter `q`.
|
||||||
|
|||||||
Reference in New Issue
Block a user