Unpaginate staged uploads and make big upload batches visible
The upload board partitions /api/uploads/ into Pending / Visual similarity / Auto-uploaded, but the endpoint was paginated at 48 — a 69-file batch silently lost 21 entries, and the similarity sweep (which reads the same list back after uploading) only ever saw the first page. The staged-upload list is now unpaginated: it is a transient per-user set, still limited to the caller's rows and the uploader role. The page takes a plain array. Watching progress with dozens of files was also poor: - the queue uploads three files at a time instead of strictly one at a time; - the Uploads section now shows a batch bar and 'n/m uploaded · x%' next to the count, so the overall progress never scrolls out of sight; - entries are ordered active-first (uploading, queued, failed, done) so the file being uploaded is always at the top of the grid; - tiles are larger (4 columns at lg instead of 5); - the header reads 'Uploading n/m…' and 'Checking n file(s) against IQDB…' instead of a bare spinner. Tests: staged-upload list unpaginated past 48, per-user, uploader-only (3 new; full suite 49 green). Live-checked the bare-array response.
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
"""The staged-upload list is a complete set, not a page.
|
||||
|
||||
The upload board partitions it into Pending / Visual similarity / Auto-uploaded
|
||||
columns; with the standard 48-item pagination a 69-file batch silently lost 21
|
||||
entries from the board (and from the similarity sweep that reads it back).
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.authtoken.models import Token
|
||||
|
||||
from apps.library.models import TempUpload
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class TempUploadListTests(TestCase):
|
||||
def setUp(self):
|
||||
self.uploader = User.objects.create_user(
|
||||
username="upload-user", password="upload-pass-123456"
|
||||
)
|
||||
self.uploader.role = "uploader"
|
||||
self.uploader.save(update_fields=["role"])
|
||||
self.other = User.objects.create_user(
|
||||
username="upload-other", password="upload-pass-123456"
|
||||
)
|
||||
self.other.role = "uploader"
|
||||
self.other.save(update_fields=["role"])
|
||||
|
||||
def api_client(self, user):
|
||||
client = Client()
|
||||
client.defaults["HTTP_AUTHORIZATION"] = (
|
||||
f"Token {Token.objects.create(user=user).key}"
|
||||
)
|
||||
return client
|
||||
|
||||
def test_list_is_not_paginated_past_48(self):
|
||||
TempUpload.objects.bulk_create(
|
||||
[
|
||||
TempUpload(
|
||||
user=self.uploader,
|
||||
original_filename=f"file_{index:03d}.png",
|
||||
md5=hashlib.md5(f"file-{index}".encode()).hexdigest(),
|
||||
size=index,
|
||||
)
|
||||
for index in range(69)
|
||||
]
|
||||
)
|
||||
response = self.api_client(self.uploader).get("/api/uploads/")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
data = response.json()
|
||||
self.assertIsInstance(data, list, "the list must not be paginated")
|
||||
self.assertEqual(len(data), 69)
|
||||
|
||||
def test_list_only_contains_the_callers_uploads(self):
|
||||
TempUpload.objects.create(
|
||||
user=self.uploader,
|
||||
original_filename="mine.png",
|
||||
md5=hashlib.md5(b"mine").hexdigest(),
|
||||
size=1,
|
||||
)
|
||||
TempUpload.objects.create(
|
||||
user=self.other,
|
||||
original_filename="theirs.png",
|
||||
md5=hashlib.md5(b"theirs").hexdigest(),
|
||||
size=1,
|
||||
)
|
||||
mine = self.api_client(self.uploader).get("/api/uploads/").json()
|
||||
self.assertEqual([row["original_filename"] for row in mine], ["mine.png"])
|
||||
|
||||
def test_anonymous_cannot_list(self):
|
||||
self.assertEqual(Client().get("/api/uploads/").status_code, 401)
|
||||
Reference in New Issue
Block a user