Skip to content

Commit 6d82e9b

Browse files
IdirLISNObada Haddad
authored andcommitted
dynamic pagination class + UI for submissions
1 parent de6200b commit 6d82e9b

3 files changed

Lines changed: 102 additions & 10 deletions

File tree

src/apps/api/pagination.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,64 @@ def get_paginated_response(self, data):
2222
'page_size': self.page_size,
2323
'results': data
2424
})
25+
26+
27+
class DynamicChoicePagination(PageNumberPagination):
28+
"""
29+
Pagination dynamique pour l'UI :
30+
- défaut : 50
31+
- valeurs autorisées côté client : 50, 100, 500, all
32+
- si page_size=all => on renvoie tous les objets (dans la limite de max_page_size)
33+
"""
34+
page_size = 50
35+
page_size_query_param = 'page_size'
36+
max_page_size = 1000
37+
_allowed_sizes = (50, 100, 500, 'all')
38+
39+
def get_page_size(self, request):
40+
raw = request.query_params.get(self.page_size_query_param)
41+
if raw is None:
42+
return self.page_size
43+
44+
raw_lower = str(raw).lower()
45+
if raw_lower == 'all':
46+
return None
47+
48+
try:
49+
val = int(raw)
50+
except (TypeError, ValueError):
51+
return self.page_size
52+
53+
if val in (50, 100, 500):
54+
return val
55+
return self.page_size
56+
57+
def paginate_queryset(self, queryset, request, view=None):
58+
raw = request.query_params.get(self.page_size_query_param)
59+
self.requested_page_size = raw if raw is not None else str(self.page_size)
60+
61+
if raw is not None and str(raw).lower() == 'all':
62+
try:
63+
total = queryset.count()
64+
except Exception:
65+
total = self.max_page_size
66+
self.page_size = total if (isinstance(total, int) and total > 0) else 1
67+
else:
68+
page_size = self.get_page_size(request)
69+
if page_size is None:
70+
page_size = self.page_size
71+
self.page_size = page_size
72+
73+
return super().paginate_queryset(queryset, request, view)
74+
75+
def get_paginated_response(self, data):
76+
page_size_value = self.requested_page_size if getattr(self, 'requested_page_size', None) is not None else self.page_size
77+
78+
return Response({
79+
'next': self.get_next_link(),
80+
'previous': self.get_previous_link(),
81+
'count': self.page.paginator.count,
82+
'page_size': page_size_value,
83+
'results': data,
84+
'allowed_page_sizes': [50, 100, 500, 'all'],
85+
})

src/apps/api/views/submissions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from django.core.files.base import ContentFile
1717

1818
from profiles.models import Organization, Membership
19-
from api.pagination import LargePagination
19+
from api.pagination import DynamicChoicePagination
2020
from tasks.models import Task
2121
from api.serializers.submissions import SubmissionCreationSerializer, SubmissionSerializer, SubmissionFilesSerializer, SubmissionDetailSerializer
2222
from competitions.models import Submission, SubmissionDetails, Phase, CompetitionParticipant
@@ -33,7 +33,7 @@ class SubmissionViewSet(ModelViewSet):
3333
filterset_fields = ('phase__competition', 'phase', 'status', 'is_soft_deleted')
3434
search_fields = ('data__data_file', 'description', 'name', 'owner__username')
3535
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES + [renderers.CSVRenderer]
36-
pagination_class = LargePagination
36+
pagination_class = DynamicChoicePagination
3737

3838
def check_object_permissions(self, request, obj):
3939
if self.action in ['submission_leaderboard_connection']:

src/static/riot/competitions/detail/submission_manager.tag

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,14 @@
204204
</button>
205205
</div>
206206

207-
<div style="display:flex; align-items:center; gap:8px; margin-left: auto;">
207+
<div style="display:flex; align-items:center; gap:8px;">
208+
<label>Per page</label>
209+
<select class="ui dropdown" value="{ page_size }" onchange="{ change_page_size.bind(this) }">
210+
<option value="50">50</option>
211+
<option value="100">100</option>
212+
<option value="500">500</option>
213+
<option value="all">all</option>
214+
</select>
208215

209216
<div style="margin-right: 10px; color: #8c8c8c;">
210217
<small>{ total_count || 0 } total</small>
@@ -239,6 +246,8 @@
239246
show_visualization="{opts.competition.enable_detailed_results}"
240247
submission="{child}"></submission-modal>
241248
</div>
249+
250+
242251
<div class="ui tab" style="height: 565px; overflow: auto;" data-tab="admin" if="{is_admin()}">
243252
<submission-scores leaderboards="{leaderboards}"></submission-scores>
244253
</div>
@@ -258,7 +267,7 @@
258267
self.show_is_soft_deleted = false
259268

260269
self.page = 1
261-
self.page_size = 20
270+
self.page_size = 50
262271
self.total_count = 0
263272
self.total_pages = 1
264273
self.next = null
@@ -325,9 +334,22 @@
325334
self.next = response.next || null
326335
self.previous = response.previous || null
327336
self.total_count = response.count || 0
328-
// prefer page_size from server if present
329-
self.page_size = response.page_size || self.page_size
330-
self.total_pages = Math.max(1, Math.ceil(self.total_count / self.page_size))
337+
if (response && typeof response.page_size !== 'undefined') {
338+
const rsp = String(response.page_size).toLowerCase()
339+
if (rsp === 'all') {
340+
self.page_size = 'all'
341+
} else {
342+
const n = Number(response.page_size)
343+
self.page_size = isNaN(n) ? self.page_size : n
344+
}
345+
}
346+
347+
if (String(self.page_size).toLowerCase() === 'all') {
348+
self.total_pages = 1
349+
} else {
350+
const ps = Number(self.page_size) || 1
351+
self.total_pages = Math.max(1, Math.ceil(self.total_count / ps))
352+
}
331353
} else {
332354
results = response || []
333355
self.next = null
@@ -377,9 +399,18 @@
377399
}
378400

379401
self.change_page_size = function (e) {
380-
const val = (e && e.target && e.target.value) ? parseInt(e.target.value, 10) : parseInt(self.page_size, 10)
381-
if (!val || val <= 0) return
382-
self.page_size = val
402+
const raw = (e && e.target && typeof e.target.value !== 'undefined') ? String(e.target.value).toLowerCase() : String(self.page_size).toLowerCase()
403+
404+
if (raw === 'all') {
405+
self.page_size = 'all'
406+
} else {
407+
const val = parseInt(raw, 10)
408+
if (isNaN(val) || val <= 0) return
409+
// n'autorise que 50,100,500
410+
if (![50, 100, 500].includes(val)) return
411+
self.page_size = val
412+
}
413+
383414
self.page = 1 // reset to first page when page size changes
384415
self.update_submissions()
385416
}

0 commit comments

Comments
 (0)