From 2974209527b0367f384dc8f55ff679ee41247d26 Mon Sep 17 00:00:00 2001
From: George Katsikas <giorgakis.katsikas@gmail.com>
Date: Thu, 7 Dec 2023 15:47:08 +0100
Subject: [PATCH] add veto form to nominations

---
 scipost_django/colleges/forms.py              | 60 +++++++++++++++++++
 .../_hx_nomination_details_contents.html      | 27 ++++++---
 .../colleges/_hx_nomination_veto.html         | 10 ++++
 .../colleges/_hx_nomination_veto_btn.html     | 18 ++++++
 scipost_django/colleges/urls.py               |  5 ++
 scipost_django/colleges/views.py              | 39 ++++++++++++
 6 files changed, 152 insertions(+), 7 deletions(-)
 create mode 100644 scipost_django/colleges/templates/colleges/_hx_nomination_veto.html
 create mode 100644 scipost_django/colleges/templates/colleges/_hx_nomination_veto_btn.html

diff --git a/scipost_django/colleges/forms.py b/scipost_django/colleges/forms.py
index ac2d09774..8f4ab75e2 100644
--- a/scipost_django/colleges/forms.py
+++ b/scipost_django/colleges/forms.py
@@ -35,6 +35,7 @@ from .models import (
     FellowshipNominationComment,
     FellowshipNominationDecision,
     FellowshipNominationVotingRound,
+    FellowshipNominationEvent,
     FellowshipInvitation,
 )
 from .constants import (
@@ -1088,6 +1089,65 @@ class FellowshipNominationVotingRoundStartForm(forms.ModelForm):
             )
 
 
+class FellowshipNominationVetoForm(forms.Form):
+    edadmin_comments = forms.CharField(
+        label="Comments for editorial administration",
+        widget=forms.Textarea(attrs={"rows": 4}),
+        required=True,
+    )
+
+    fellow_comments = forms.CharField(
+        label="Comments for voting Fellows",
+        widget=forms.Textarea(attrs={"rows": 4}),
+        required=True,
+    )
+
+    def __init__(self, *args, **kwargs):
+        self.fellow = kwargs.pop("fellow", None)
+        self.nomination = kwargs.pop("nomination", None)
+        super().__init__(*args, **kwargs)
+
+        self.helper = FormHelper()
+        self.helper.attrs = {
+            "hx-post": reverse(
+                "colleges:_hx_nomination_veto",
+                kwargs={"nomination_id": self.nomination.id},
+            ),
+            "hx-target": "closest .veto-btn-container",
+            "hx-swap": "outerHTML",
+        }
+
+        self.helper.layout = Layout(
+            Div(
+                Div(Field("edadmin_comments"), css_class="col"),
+                Div(Field("fellow_comments"), css_class="col"),
+                Div(
+                    ButtonHolder(Submit("submit", "Veto", css_class="btn btn-dark")),
+                    css_class="col-auto d-flex align-items-end",
+                ),
+                css_class="row mb-0",
+            ),
+        )
+
+    def save(self):
+        self.nomination.vetoes.add(self.fellow)
+        self.nomination.save()
+
+        # Fellow's comments are added as a regular comment
+        FellowshipNominationComment.objects.create(
+            nomination=self.nomination,
+            by=self.fellow.contributor,
+            text=self.cleaned_data["fellow_comments"],
+        )
+
+        # EdAdmin's comments are added as an event
+        FellowshipNominationEvent.objects.create(
+            nomination=self.nomination,
+            by=self.fellow.contributor,
+            description=f"Vetoed with justification: {self.cleaned_data['edadmin_comments']}",
+        )
+
+
 ###############
 # Invitations #
 ###############
diff --git a/scipost_django/colleges/templates/colleges/_hx_nomination_details_contents.html b/scipost_django/colleges/templates/colleges/_hx_nomination_details_contents.html
index 0f5c52223..534e31b62 100644
--- a/scipost_django/colleges/templates/colleges/_hx_nomination_details_contents.html
+++ b/scipost_django/colleges/templates/colleges/_hx_nomination_details_contents.html
@@ -7,16 +7,29 @@
     <div id="profile-{{ nomination.profile.id }}-specialties"
          class="border border-danger mb-4 d-none-empty"></div>
 
-    {% if nomination.nominator_comments %}
-      <div class="col-12 mb-3">
-        <div class="card">
-          <div class="card-header">Nominator motivation</div>
-          <div class="card-body">{{ nomination.nominated_by.profile.full_name }}: {{ nomination.nominator_comments }}</div>
+    <div class="col mb-3">
+      <div class="card">
+        <div class="card-header">Nominator motivation</div>
+        <div class="card-body">
+
+          {% if nomination.nominator_comments %}
+            <strong>{{ nomination.nominated_by.profile.full_name }}:</strong> {{ nomination.nominator_comments }}
+          {% else %}
+            No motivation provided.
+          {% endif %}
+
         </div>
       </div>
+    </div>
+ 
+
+    {% if not is_ed_admin %}
+      {% include "colleges/_hx_nomination_veto_btn.html" %}
     {% endif %}
 
-    <div class="col-12 col-md mb-3">
+  </div>
+  <div class="row mb-0">
+    <div class="col-12 col-md-6 mb-3">
       <div class="card">
         <div class="card-header">Details</div>
         <div class="card-body p-0">
@@ -65,7 +78,7 @@
         </div>
       </div>
     </div>
-    <div class="col-12 col-md mb-3">
+    <div class="col-12 col-md-6 mb-3">
       <div class="card">
         <div class="card-header">Publications in SciPost Journals</div>
         <div class="card-body">
diff --git a/scipost_django/colleges/templates/colleges/_hx_nomination_veto.html b/scipost_django/colleges/templates/colleges/_hx_nomination_veto.html
new file mode 100644
index 000000000..5d999d68c
--- /dev/null
+++ b/scipost_django/colleges/templates/colleges/_hx_nomination_veto.html
@@ -0,0 +1,10 @@
+{% load crispy_forms_tags %}
+
+<div class="veto-btn-container col-12 border border-dark p-2">
+  <h3>Exercise your right to veto:</h3>
+  <p>
+    Use this form to veto a nomination by providing some justification for your decision.
+    Comments provided to fellows will be visible to all other fellows, while those to editorial administration will remain private.
+  </p>
+  {% crispy form %}
+</div>
diff --git a/scipost_django/colleges/templates/colleges/_hx_nomination_veto_btn.html b/scipost_django/colleges/templates/colleges/_hx_nomination_veto_btn.html
new file mode 100644
index 000000000..d72e65026
--- /dev/null
+++ b/scipost_django/colleges/templates/colleges/_hx_nomination_veto_btn.html
@@ -0,0 +1,18 @@
+<div class="col-auto d-flex flex-column justify-content-between mb-3 veto-btn-container">
+
+  {% if fellow in nomination.vetoes.all %}
+    <div class="bg-dark p-2 text-center">
+      <span class="fs-5 text-white">Nomination
+        <br />
+      Vetoed</span>
+    </div>
+  {% else %}
+    <div class="fs-5 text-muted align-self-center">Something wrong?</div>
+    <button class="btn btn-sm btn-dark"
+            hx-get="{% url "colleges:_hx_nomination_veto" nomination_id=nomination.id %}"
+            hx-target="closest .veto-btn-container">
+      <span class="text-white">Veto</span>
+    </button>
+  {% endif %}
+
+</div>
diff --git a/scipost_django/colleges/urls.py b/scipost_django/colleges/urls.py
index 3627fbf17..4435ae8d3 100644
--- a/scipost_django/colleges/urls.py
+++ b/scipost_django/colleges/urls.py
@@ -216,6 +216,11 @@ urlpatterns = [
                                 views._hx_nomination_comments,
                                 name="_hx_nomination_comments",
                             ),
+                            path(
+                                "_hx_veto",
+                                views._hx_nomination_veto,
+                                name="_hx_nomination_veto",
+                            ),
                         ]
                     ),
                 ),
diff --git a/scipost_django/colleges/views.py b/scipost_django/colleges/views.py
index 1e033d68a..b0d0aea1e 100644
--- a/scipost_django/colleges/views.py
+++ b/scipost_django/colleges/views.py
@@ -43,6 +43,7 @@ from .constants import (
 from .forms import (
     CollegeChoiceForm,
     FellowshipNominationSearchForm,
+    FellowshipNominationVetoForm,
     FellowshipNominationVotingRoundStartForm,
     FellowshipSearchForm,
     FellowshipDynSelForm,
@@ -770,12 +771,14 @@ def _hx_nomination_round_remove_voter(request, round_id, voter_id):
 def _hx_nomination_details_contents(request, nomination_id):
     """For (re)loading the details if modified."""
     nomination = get_object_or_404(FellowshipNomination, pk=nomination_id)
+    fellow = request.user.contributor.session_fellowship(request)
     start_email_sent = nomination.events.filter(
         description__contains="start email sent",
     ).exists()
     context = {
         "nomination": nomination,
         "start_email_sent": start_email_sent,
+        "fellow": fellow,
     }
     return render(request, "colleges/_hx_nomination_details_contents.html", context)
 
@@ -942,6 +945,42 @@ def _hx_nomination_vote(request, round_id):
     return render(request, "colleges/_hx_nomination_vote.html", context)
 
 
+@login_required
+@user_passes_test(is_edadmin_or_advisory_or_active_regular_or_senior_fellow)
+def _hx_nomination_veto(request, nomination_id):
+    fellow = request.user.contributor.session_fellowship(request)
+    nomination = get_object_or_404(FellowshipNomination, pk=nomination_id)
+
+    # Check that vetoer is a sneior fellow is in the same college as the nomination
+    if (
+        not fellow.contributor.fellowships.filter(college=nomination.college)
+        .senior()
+        .active()
+        .exists()
+    ):
+        return HTMXResponse(
+            """You are not an active senior fellow of this college.""",
+            tag="danger",
+        )
+
+    form = FellowshipNominationVetoForm(
+        request.POST or None, fellow=fellow, nomination=nomination
+    )
+
+    if form.is_valid():
+        form.save()
+        return render(
+            request,
+            "colleges/_hx_nomination_veto_btn.html",
+            {
+                "nomination": nomination,
+                "fellow": fellow,
+            },
+        )
+
+    return render(request, "colleges/_hx_nomination_veto.html", {"form": form})
+
+
 @login_required
 @user_passes_test(is_edadmin)
 def _hx_voting_round_start_form(request, round_id):
-- 
GitLab