From cf4cb32c5e91d2c904bbe0f1959bb09299740f3f Mon Sep 17 00:00:00 2001 From: Ross Date: Mon, 11 May 2026 13:10:05 +0100 Subject: [PATCH] feat: Enhance media cleanup functionality with loading indicator and detailed results --- rad/tests/test_media_cleanup.py | 2 ++ rad/views.py | 26 +++++++++++------- templates/rad/media_cleanup.html | 27 ++++++++++++++++++- .../rad/partials/_media_cleanup_result.html | 1 + 4 files changed, 45 insertions(+), 11 deletions(-) diff --git a/rad/tests/test_media_cleanup.py b/rad/tests/test_media_cleanup.py index 1268f060..5d85955c 100644 --- a/rad/tests/test_media_cleanup.py +++ b/rad/tests/test_media_cleanup.py @@ -43,6 +43,7 @@ def test_media_cleanup_page_for_manager(client, cid_manager_user): response = client.get(reverse("media_cleanup")) assert response.status_code == 200 assert b"Unused Media Cleanup" in response.content + assert b"Cleanup in progress" in response.content @pytest.mark.django_db @@ -108,3 +109,4 @@ def test_media_cleanup_live_run_disables_dry_run(client, cid_manager_user, monke assert captured["kwargs"]["interactive"] is False assert b"cleanup ok" in response.content assert b"Recovered space" in response.content + assert b"removed" in response.content diff --git a/rad/views.py b/rad/views.py index 6fc423d1..e67915a5 100644 --- a/rad/views.py +++ b/rad/views.py @@ -109,21 +109,21 @@ import psutil -def _directory_size_bytes(path: str) -> int: - """Return recursive directory size in bytes, skipping unreadable files.""" +def _media_file_inventory(path: str) -> dict[str, int]: + """Return a mapping of file path to byte size for files under a directory.""" if not path or not os.path.exists(path): - return 0 + return {} - total = 0 + inventory: dict[str, int] = {} for root, _, files in os.walk(path): for filename in files: file_path = os.path.join(root, filename) try: - total += os.path.getsize(file_path) + inventory[file_path] = os.path.getsize(file_path) except OSError: continue - return total + return inventory def feedback_checker(user): @@ -474,11 +474,10 @@ def media_cleanup(request): command_kwargs["exclude"] = exclude error_message = "" - media_size_before = _directory_size_bytes(settings.MEDIA_ROOT) - media_size_after = media_size_before + media_inventory_before = _media_file_inventory(settings.MEDIA_ROOT) + media_inventory_after = media_inventory_before try: call_command("cleanup_unused_media", **command_kwargs) - media_size_after = _directory_size_bytes(settings.MEDIA_ROOT) except CommandError as exc: success = False error_message = str(exc) @@ -486,8 +485,14 @@ def media_cleanup(request): success = False error_message = str(exc) + media_inventory_after = _media_file_inventory(settings.MEDIA_ROOT) + + media_size_before = sum(media_inventory_before.values()) + media_size_after = sum(media_inventory_after.values()) + removed_files = set(media_inventory_before) - set(media_inventory_after) + output = out.getvalue().strip() - recovered_size = max(0, media_size_before - media_size_after) + recovered_size = sum(media_inventory_before[path] for path in removed_files) context.update( { "ran": True, @@ -502,6 +507,7 @@ def media_cleanup(request): "media_size_before": media_size_before, "media_size_after": media_size_after, "recovered_size": recovered_size, + "removed_file_count": len(removed_files), } ) diff --git a/templates/rad/media_cleanup.html b/templates/rad/media_cleanup.html index fa30ec33..09203466 100644 --- a/templates/rad/media_cleanup.html +++ b/templates/rad/media_cleanup.html @@ -1,5 +1,23 @@ {% extends 'base.html' %} +{% block css %} + {{ block.super }} + +{% endblock %} + {% block content %}
@@ -17,11 +35,13 @@
{% csrf_token %} @@ -85,6 +105,11 @@ Delete unused files
+ +
+ + Cleanup in progress. This can take a while for large media directories. +
diff --git a/templates/rad/partials/_media_cleanup_result.html b/templates/rad/partials/_media_cleanup_result.html index 07f84a11..3bb312b4 100644 --- a/templates/rad/partials/_media_cleanup_result.html +++ b/templates/rad/partials/_media_cleanup_result.html @@ -38,6 +38,7 @@
Recovered space
{{ recovered_size|filesizeformat }}
+
{{ removed_file_count|default:0 }} file{{ removed_file_count|default:0|pluralize }} removed