From b3d8b12c80e813ad039c78a362c2aef174dc254e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-S=C3=A9bastien=20Caux?= <git@jscaux.org>
Date: Fri, 28 Jan 2022 22:33:10 +0100
Subject: [PATCH] Basic nomination form, but dal is bugging

---
 scipost_django/colleges/forms.py              | 72 ++++++++++++++++++-
 scipost_django/colleges/models/nomination.py  |  8 +++
 .../templates/colleges/nominations.html       | 11 +++
 scipost_django/colleges/views.py              | 14 +++-
 4 files changed, 102 insertions(+), 3 deletions(-)

diff --git a/scipost_django/colleges/forms.py b/scipost_django/colleges/forms.py
index 81f8ad6bc..294d50619 100644
--- a/scipost_django/colleges/forms.py
+++ b/scipost_django/colleges/forms.py
@@ -283,6 +283,67 @@ class PotentialFellowshipEventForm(forms.ModelForm):
 # Nominations #
 ###############
 
+class FellowshipNominationForm(forms.Form):
+
+    college = forms.ModelChoiceField(
+        queryset=College.objects.all(),
+    )
+    nominee = forms.ModelChoiceField(
+        queryset=Profile.objects.all(),
+        widget=autocomplete.ModelSelect2(url='/profiles/profile-autocomplete'),
+    )
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.helper = FormHelper()
+        self.helper.layout = Layout(
+            Div(
+                Div(FloatingField('college'), css_class='col-lg-6'),
+                Div(FloatingField('nominee'), css_class='col-lg-6'),
+                css_class='row'
+            )
+        )
+
+    def clean_nominee(self):
+        """
+        Requirements:
+
+        - no current Fellowship exists
+        - no current FellowshipNomination exists
+        - no 'not elected' decision in last 2 years
+        - no invitation was turned down in the last 2 years
+        """
+        nominee = self.cleaned_data['nominee']
+        if Fellowship.objects.active().regular_or_senior().filter(
+                contributor__profile=nominee).exists():
+            self.add_error('This Profile is associated to an active Fellowship.')
+        latest_nomination = FellowshipNomination.objects.filter(
+            profile=self.cleaned_data.get('nominee')).first()
+        try:
+            if (latest_nomination.decision.fixed_on +
+                datetime.timedelta(days=730)) > timezone.now():
+                if latest_nomination.decision.elected:
+                    try:
+                        if latest_nomination.invitation.declined:
+                            self.add_error('Invitation declined less that 2 years ago. '
+                                           'Wait to try again.')
+                        else:
+                            self.add_error('Already elected, invitation in process.')
+                    except AttributeError:
+                        self.add_error('Already elected, invitation pending.')
+
+                self.add_error('Election failed less that 2 years ago. Must wait.')
+        except AttributeError: # no decision yet
+            self.add_error('This Profile is associated to an ongoing Nomination process.')
+        return nominee
+
+    def clean(self):
+        data = super().clean()
+        if data['college'].acad_field != data['nominee'].acad_field:
+            self.add_error('Mismatch between college.acad_field and nominee.acad_field.')
+        return data
+
+
 class FellowshipNominationSearchForm(forms.Form):
     """Filter a FellowshipNomination queryset using basic search fields."""
 
@@ -305,6 +366,10 @@ class FellowshipNominationSearchForm(forms.Form):
         label='Name (through Profile)',
         required=False
     )
+    # name = forms.CharField(
+    #     max_length=128,
+    #     required=False
+    # )
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
@@ -316,7 +381,8 @@ class FellowshipNominationSearchForm(forms.Form):
                 css_class='row'
             ),
             Div(
-                Div(FloatingField('profile'), css_class='col-lg-6'),
+                Div(FloatingField('profile'), css_id='search-profile', css_class='col-lg-6'),
+                #Div(FloatingField('name'), css_class='col-lg-6'),
                 css_class='row'
             ),
         )
@@ -325,6 +391,10 @@ class FellowshipNominationSearchForm(forms.Form):
         if self.cleaned_data.get('profile'):
             nominations = FellowshipNomination.objects.filter(
                 profile=self.cleaned_data.get('profile'))
+        # if self.cleaned_data.get('name'):
+        #     nominations = FellowshipNomination.objects.filter(
+        #         Q(profile__last_name__icontains=self.cleaned_data.get('profile')) |
+        #         Q(profile__first_name__icontains=self.cleaned_data.get('profile')))
         else:
             nominations = FellowshipNomination.objects.all()
         if self.cleaned_data.get('college'):
diff --git a/scipost_django/colleges/models/nomination.py b/scipost_django/colleges/models/nomination.py
index 65f7be47a..31c9298d5 100644
--- a/scipost_django/colleges/models/nomination.py
+++ b/scipost_django/colleges/models/nomination.py
@@ -163,6 +163,10 @@ class FellowshipNominationDecision(models.Model):
     def __str__(self):
         return f'Decision for {nomination}: {self.get_outcome_display()}'
 
+    @property
+    def elected(self):
+        return self.outcome == self.OUTCOME_ELECTED
+
 
 class FellowshipInvitation(models.Model):
 
@@ -206,3 +210,7 @@ class FellowshipInvitation(models.Model):
 
     def __str__(self):
         return f'Invitation for {nomination}'
+
+    @property
+    def declined(self):
+        return self.response == self.RESPONSE_DECLINED
diff --git a/scipost_django/colleges/templates/colleges/nominations.html b/scipost_django/colleges/templates/colleges/nominations.html
index e3840f52b..8a75ad627 100644
--- a/scipost_django/colleges/templates/colleges/nominations.html
+++ b/scipost_django/colleges/templates/colleges/nominations.html
@@ -15,6 +15,17 @@
 
   <h1 class="highlight">Fellowship Nominations</h1>
 
+  <div class="card">
+    <div class="card-header">
+      Nominate somebody for a Fellowship
+    </div>
+    <div class="card-body">
+      <form>
+	{% crispy nomination_form %}
+      </form>
+    </div>
+  </div>
+
   <div class="card">
     <div class="card-header">
       Search / filter
diff --git a/scipost_django/colleges/views.py b/scipost_django/colleges/views.py
index df3c2e387..7c56720c8 100644
--- a/scipost_django/colleges/views.py
+++ b/scipost_django/colleges/views.py
@@ -34,7 +34,7 @@ from .forms import (
     SubmissionAddFellowshipForm,
     FellowshipRemoveProceedingsForm, FellowshipAddProceedingsForm,
     PotentialFellowshipForm, PotentialFellowshipStatusForm, PotentialFellowshipEventForm,
-    FellowshipNominationSearchForm,
+    FellowshipNominationForm, FellowshipNominationSearchForm,
 )
 from .models import College, Fellowship, PotentialFellowship, PotentialFellowshipEvent
 
@@ -534,7 +534,8 @@ def nominations(request):
     List Nominations.
     """
     context = {
-        'form': FellowshipNominationSearchForm()
+        'form': FellowshipNominationSearchForm(),
+        'nomination_form': FellowshipNominationForm()
     }
     return render(request, 'colleges/nominations.html', context)
 
@@ -552,3 +553,12 @@ def _hx_nominations(request):
     page_obj = paginator.get_page(page_nr)
     context = { 'page_obj': page_obj }
     return render(request, 'colleges/_hx_nominations.html', context)
+
+
+# @login_required
+# @user_passes_test(is_edadmin_or_active_regular_or_senior_fellow)
+# def _hx_nominate(request):
+#     form = FellowshipNominationForm(request.POST or None)
+#     if form.is_valid():
+#         nomination = form.save()
+#         return render(reverse('colleges/_hx_nominations.html
-- 
GitLab