diff --git a/generic/urls.py b/generic/urls.py index 24c82280..4b0c3aed 100755 --- a/generic/urls.py +++ b/generic/urls.py @@ -296,6 +296,11 @@ def generic_view_urls(generic_views: GenericViewBase): generic_views.question_review_next, name="question_review_next", ), + path( + "question/review/list", + generic_views.question_review_list, + name="question_review_list", + ), path("question//thumbnail/fail", generic_views.question_thumbnail_fail, name="series_thumbnail_fail"), path("question//thumbnail", generic_views.question_thumbnail, name="series_thumbnail"), path( diff --git a/generic/views.py b/generic/views.py index e6ed0df8..427a037c 100644 --- a/generic/views.py +++ b/generic/views.py @@ -3139,6 +3139,65 @@ class GenericViewBase: return render(request, f"{self.app_name}/question_review_complete.html", {"app_name": self.app_name}) + def question_review_list(self, request): + """Return a page listing questions that match the supplied filters. + + Accepts query parameters similar to question_review_next: category, status. + """ + + # Read filters + category = request.GET.get("category") or request.POST.get("category") + status = request.GET.get("status") or request.POST.get("status") or "ANY" + + # Build base queryset of questions for this app + qs = self.question_object.objects.all().order_by("pk") + + # Restrict by category if provided + if category: + try: + qs = qs.filter(category__id=int(category)) + except Exception: + pass + + # Apply permission filter similar to question_review_next + if not request.user.groups.filter(name=self.checker_group).exists(): + try: + qs = qs.filter(open_access=True) | qs.filter(author__id=request.user.id) + except Exception: + pass + + # Optionally filter by review status + results = [] + for question in qs: + try: + if question.authors_only and not ( + request.user.is_superuser or request.user in question.author.all() + ): + continue + except Exception: + pass + + ct = ContentType.objects.get_for_model(question) + latest = ( + QuestionReview.objects.filter(content_type=ct, object_id=question.pk) + .order_by("-created_on").first() + ) + + match = False + if status == "ANY": + match = True + elif status == "UNREVIEWED": + match = latest is None + else: + if latest is not None and latest.status == status: + match = True + + if match: + results.append(question) + + return render(request, f"{self.app_name}/question_review_list.html", {"questions": results, "app_name": self.app_name, "filters": {"category": category, "status": status}}) + + @method_decorator(user_passes_test(lambda u: u.is_superuser)) def user_answer_delete_multiple(self, request): print(request.POST["answer_ids"]) diff --git a/sbas/templates/sbas/question_review_list.html b/sbas/templates/sbas/question_review_list.html new file mode 100644 index 00000000..90f05b2f --- /dev/null +++ b/sbas/templates/sbas/question_review_list.html @@ -0,0 +1,44 @@ +{% extends 'sbas/base.html' %} + +{% block content %} +

Filtered questions

+ +

Showing questions matching the selected filters.

+ +
+ Active filters: + {% if filters.category %} + Category: {{ filters.category }} + {% else %} + Category: Any + {% endif %} + {% if filters.status %} + Status: {{ filters.status }} + {% else %} + Status: Any + {% endif %} +
+ + {% if questions %} + + {% else %} +
No questions match the selected filters.
+ {% endif %} + + + +{% endblock %} diff --git a/sbas/templates/sbas/question_review_start.html b/sbas/templates/sbas/question_review_start.html index 4bc4ce4c..0d54cd11 100644 --- a/sbas/templates/sbas/question_review_start.html +++ b/sbas/templates/sbas/question_review_start.html @@ -24,6 +24,9 @@ {% endif %} + + +
- - +
+

You can also view the list of questions matching the filters below.

+ +
+ + + {% endblock %} \ No newline at end of file