Coverage for src / mesh / views / views_reviewer.py: 61%
175 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-07-09 13:14 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-07-09 13:14 +0000
1from typing import Any
3from django.contrib import messages
4from django.contrib.auth.mixins import LoginRequiredMixin
5from django.http import HttpResponseRedirect, JsonResponse
6from django.shortcuts import get_object_or_404
7from django.template.loader import render_to_string
8from django.urls import reverse, reverse_lazy
9from django.utils.decorators import method_decorator
10from django.utils.translation import gettext_lazy as _
11from django.views.decorators.csrf import csrf_exempt
12from django.views.generic.base import TemplateView
13from django.views.generic.edit import CreateView, DeleteView, UpdateView
14from ptf.views.components.breadcrumb import get_base_breadcrumb
16from mesh.crud.reviewer_list import create_suggested_reviewer, get_all_reviewers
17from mesh.models.orm.submission_models import Submission
18from mesh.models.orm.suggestion_model import Suggestion
19from mesh.models.orm.user_models import SuggestedReviewer, User
20from mesh.views.views_base import MeshObjectMixin
22from .forms.reviewer_forms import SimpleReviewerForm, SuggestedReviewerForm
23from .utils import get_suggestion
26def busy(reviewer):
27 return reviewer.pending > 0
30class ReviewerListView(LoginRequiredMixin, CreateView):
31 """
32 View for the list of reviewers and suggested reviewers.
33 A reviewer is a user with an account (which might have been created by an editor with Assign Reviewer),
34 or a SuggestedReviewer (a person which does not have an account yet)
35 This view can be called for all the reviewers in the system, or to create a shortlist for a submission
36 """
38 model = SuggestedReviewer
39 template_name = "mesh/reviewer/reviewer_list_page.html"
40 form_class = SuggestedReviewerForm
42 def setup(self, request, *args, **kwargs):
43 super().setup(request, *args, **kwargs)
44 if not self.request.current_role.can_access_journal_sections():
45 raise PermissionError
47 def get_success_url(self):
48 return reverse("mesh:reviewer_list")
50 def set_success_message(self):
51 # messages.success(self.request, "Le fascicule a été modifié")
52 pass
54 def get_form_kwargs(self):
55 kwargs = super().get_form_kwargs()
56 kwargs["current_user"] = self.request.user
57 return kwargs
59 def get_context_data(self, **kwargs) -> dict[str, Any]:
60 context = super().get_context_data(**kwargs)
61 context["reviewers"] = get_all_reviewers()
62 context["reviewer_form_action"] = reverse("mesh:reviewer_list")
64 breadcrumb = get_base_breadcrumb()
65 breadcrumb.add_item(title=_("Reviewers"), url=reverse("mesh:reviewer_list"))
66 context["breadcrumb"] = breadcrumb
68 context["page_title"] = _("Reviewers")
69 return context
71 def form_valid(self, form):
72 create_suggested_reviewer(form)
73 return HttpResponseRedirect(self.get_success_url())
75 def form_invalid(self, form):
76 for key, value in form.errors.items():
77 messages.error(self.request, str(value[0]))
78 return super().form_invalid(form)
81class ReviewerDeleteView(LoginRequiredMixin, DeleteView):
82 model = SuggestedReviewer
83 success_url = reverse_lazy("mesh:reviewer_list")
85 def setup(self, request, *args, **kwargs):
86 super().setup(request, *args, **kwargs)
87 if not self.request.current_role.can_access_journal_sections():
88 raise PermissionError
91class ReviewerEditAPIView(LoginRequiredMixin, UpdateView):
92 """
93 Modal to add a new (suggested) reviewer, used in the ReviewerListView
94 """
96 model = SuggestedReviewer
97 template_name = "mesh/reviewer/suggested_reviewer_modal.html"
98 form_class = SuggestedReviewerForm
99 success_url = reverse_lazy("mesh:home")
101 def setup(self, request, *args, **kwargs):
102 super().setup(request, *args, **kwargs)
103 if not self.request.current_role.can_access_journal_sections():
104 raise PermissionError
106 def get_context_data(self, **kwargs) -> dict[str, Any]:
107 context = super().get_context_data(**kwargs)
108 context["row_number"] = self.kwargs.get("row_number", -1)
109 context["user_type"] = self.kwargs.get("user_type", "reviewer")
110 return context
112 def get(self, request, *args, **kwargs):
113 user_type = self.kwargs.get("user_type", "reviewer")
114 if user_type == "user":
115 self.form_class = SimpleReviewerForm
116 self.model = User
118 response = super().get(request, *args, **kwargs)
119 return response
121 def post(self, request, *args, **kwargs):
122 user_type = self.kwargs.get("user_type", "reviewer")
123 if user_type == "user":
124 self.form_class = SimpleReviewerForm
125 self.model = User
127 super().post(request, *args, **kwargs)
128 if user_type == "user":
129 reviewer = get_object_or_404(User, pk=kwargs["pk"])
130 else:
131 reviewer = get_object_or_404(SuggestedReviewer, pk=kwargs["pk"])
132 row_number = self.kwargs.get("row_number", -1)
134 the_messages = messages.get_messages(request)
135 message_html = (
136 render_to_string(
137 "mesh/messages.html", {"messages": messages.get_messages(request)}, request=request
138 )
139 if len(the_messages) > 0
140 else ""
141 )
143 return JsonResponse(
144 {
145 "row_data": [
146 reviewer.first_name,
147 reviewer.last_name,
148 reviewer.email,
149 reviewer.keywords,
150 ],
151 "row_number": row_number,
152 "messages": message_html,
153 }
154 )
156 def form_valid(self, form):
157 return super().form_valid(form)
159 def form_invalid(self, form):
160 for key, value in form.errors.items():
161 messages.error(self.request, str(value[0]))
162 return super().form_invalid(form)
165def add_suggestion(submission, suggested_user, suggested_reviewer, suggest_to_avoid=False):
166 qs = submission.suggestions_for_reviewer.order_by("-seq")
167 seq = qs.first().seq + 1 if qs.exists() else 0
169 suggestion = Suggestion(
170 submission=submission,
171 suggested_user=suggested_user,
172 suggested_reviewer=suggested_reviewer,
173 suggest_to_avoid=suggest_to_avoid,
174 seq=seq,
175 )
176 suggestion.save()
178 return suggestion
181def add_suggestion_from_person(submission, person_info, suggest_to_avoid):
182 if submission is not None and person_info is not None and person_info["email"] != "":
183 suggested_user = suggested_reviewer = None
184 qs = User.objects.filter(email=person_info["email"])
185 if qs.exists():
186 suggested_user = qs.first()
187 else:
188 qs = SuggestedReviewer.objects.filter(email=person_info["email"])
189 if qs.exists():
190 suggested_reviewer = qs.first()
191 else:
192 suggested_reviewer = SuggestedReviewer(
193 first_name=person_info["first_name"],
194 last_name=person_info["last_name"],
195 email=person_info["email"],
196 )
197 suggested_reviewer.save()
199 add_suggestion(
200 submission=submission,
201 suggested_user=suggested_user,
202 suggested_reviewer=suggested_reviewer,
203 suggest_to_avoid=suggest_to_avoid,
204 )
207@method_decorator([csrf_exempt], name="dispatch")
208class SuggestionView(MeshObjectMixin, ReviewerListView):
209 """
210 Page to add/delete reviewers AND to add/remove reviewers to a shortlist (= Suggestion)
211 """
213 template_name = "mesh/reviewer/suggestion_page.html"
214 submission: Submission
216 def setup(self, request, *args, **kwargs):
217 super().setup(request, *args, **kwargs)
218 if not self.request.current_role.can_access_journal_sections():
219 raise PermissionError
220 self.submission = self.get_submission()
222 def get_success_url(self):
223 return reverse("mesh:submission_shortlist", kwargs={"submission_pk": self.submission.pk})
225 def get_context_data(self, **kwargs):
226 context = super().get_context_data(**kwargs)
227 # if "submission" in request.POST:
228 # self.submission = Submission.objects.get(pk=int(kwargs.get"submission"]))
230 context["submission"] = self.submission
231 context["selected_reviewers"] = get_suggestion(self.submission)
232 context["reviewer_form_action"] = reverse(
233 "mesh:submission_shortlist", kwargs={"submission_pk": self.submission.pk}
234 )
235 return context
237 def form_valid(self, form):
238 create_suggested_reviewer(form)
239 return HttpResponseRedirect(self.get_success_url())
242@method_decorator([csrf_exempt], name="dispatch")
243class SuggestionAPIView(MeshObjectMixin, TemplateView):
244 """
245 API to add/remove users to a shortlist. It ends up adding or removing a Suggestion.
246 """
248 template_name = "mesh/reviewer/shortlist_content.html"
250 def setup(self, request, *args, **kwargs):
251 super().setup(request, *args, **kwargs)
252 if not self.request.current_role.can_access_journal_sections():
253 raise PermissionError
254 self.submission = self.get_submission()
255 self.user_type = self.kwargs.get("user_type", "reviewer")
257 def add_suggestion(self):
258 if not self.qs.exists():
259 suggested_user = suggested_reviewer = None
261 if self.user_type == "user":
262 suggested_user = self.reviewer
263 else:
264 suggested_reviewer = self.reviewer
266 add_suggestion(
267 submission=self.submission,
268 suggested_user=suggested_user,
269 suggested_reviewer=suggested_reviewer,
270 )
272 def remove_suggestion(self):
273 suggestion = self.qs.first()
274 if suggestion:
275 suggestion.delete()
277 def post(self, request, *args, **kwargs):
278 if self.user_type == "user":
279 self.reviewer = get_object_or_404(User, pk=kwargs["user_pk"])
280 self.qs = Suggestion.objects.filter(
281 submission=self.submission, suggested_user=self.reviewer
282 )
283 else:
284 self.reviewer = get_object_or_404(SuggestedReviewer, pk=kwargs["user_pk"])
285 self.qs = Suggestion.objects.filter(
286 submission=self.submission, suggested_reviewer=self.reviewer
287 )
289 if self.kwargs.get("action", "add") == "add":
290 self.add_suggestion()
291 else:
292 self.remove_suggestion()
294 return super().get(request, *args, **kwargs)
296 def get_context_data(self, **kwargs):
297 context = super().get_context_data(**kwargs)
298 context["submission"] = self.submission
299 context["reviewer"] = self.reviewer
300 context["selected_reviewers"] = get_suggestion(self.submission)
302 return context