From 73179b8e68c6912b60adc58b00ecc887e4b0153f Mon Sep 17 00:00:00 2001
From: Jorran de Wit <jorrandewit@outlook.com>
Date: Tue, 15 Aug 2017 16:11:12 +0200
Subject: [PATCH] Remove HTML from Contributor model

---
 config.scss                                   |  0
 scipost/managers.py                           |  9 +++
 scipost/models.py                             | 68 +++----------------
 .../contributor_assignments_as_td.html        | 14 ++++
 .../scipost/Fellow_activity_overview.html     | 25 +++++--
 scipost/templates/scipost/personal_page.html  | 62 +++++++++--------
 scipost/urls.py                               |  6 +-
 scipost/views.py                              | 37 +++++-----
 submissions/managers.py                       | 20 +++++-
 submissions/models.py                         |  8 ++-
 .../admin/editorial_admin_summary.html        |  2 +-
 .../submissions/_submission_card_in_pool.html |  4 +-
 .../submissions/assign_submission.html        |  4 +-
 submissions/views.py                          |  4 +-
 14 files changed, 138 insertions(+), 125 deletions(-)
 create mode 100644 config.scss
 create mode 100644 scipost/templates/partials/scipost/contributor_assignments_as_td.html

diff --git a/config.scss b/config.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/scipost/managers.py b/scipost/managers.py
index 1a4a401fd..d108e255b 100644
--- a/scipost/managers.py
+++ b/scipost/managers.py
@@ -25,6 +25,9 @@ class ContributorManager(models.Manager):
     def awaiting_validation(self):
         return self.filter(user__is_active=False, status=CONTRIBUTOR_NEWLY_REGISTERED)
 
+    def fellows(self):
+        return self.filter(user__groups__name='Editorial College')
+
 
 class RegistrationInvitationManager(models.Manager):
     def pending_invited_fellows(self):
@@ -34,3 +37,9 @@ class RegistrationInvitationManager(models.Manager):
     def declined_invited_fellows(self):
         return self.filter(invitation_type=INVITATION_EDITORIAL_FELLOW,
                            responded=False, declined=True)
+
+
+class UnavailabilityPeriodManager(models.Manager):
+    def today(self):
+        today = datetime.date.today()
+        return self.filter(start__lte=today, end__gte=today)
diff --git a/scipost/models.py b/scipost/models.py
index a1e542d81..7d05b6a02 100644
--- a/scipost/models.py
+++ b/scipost/models.py
@@ -9,7 +9,6 @@ from django.contrib.postgres.fields import ArrayField
 from django.db import models
 from django.template import Template, Context
 from django.utils import timezone
-from django.urls import reverse
 
 from django_countries.fields import CountryField
 
@@ -20,7 +19,8 @@ from .constants import SCIPOST_DISCIPLINES, SCIPOST_SUBJECT_AREAS,\
                        INVITATION_CONTRIBUTOR, INVITATION_FORMAL,\
                        AUTHORSHIP_CLAIM_PENDING, AUTHORSHIP_CLAIM_STATUS
 from .fields import ChoiceArrayField
-from .managers import FellowManager, ContributorManager, RegistrationInvitationManager
+from .managers import FellowManager, ContributorManager, RegistrationInvitationManager,\
+                      UnavailabilityPeriodManager
 
 
 def get_sentinel_user():
@@ -73,9 +73,14 @@ class Contributor(models.Model):
     def get_absolute_url(self):
         return reverse('scipost:contributor_info', args=(self.id,))
 
+    @property
     def get_formal_display(self):
         return '%s %s %s' % (self.get_title_display(), self.user.first_name, self.user.last_name)
 
+    @property
+    def is_currently_available(self):
+        return not self.unavailability_periods.today().exists()
+
     def is_SP_Admin(self):
         return self.user.groups.filter(name='SciPost Administrators').exists()
 
@@ -85,15 +90,6 @@ class Contributor(models.Model):
     def is_VE(self):
         return self.user.groups.filter(name='Vetting Editors').exists()
 
-    def is_currently_available(self):
-        unav_periods = UnavailabilityPeriod.objects.filter(contributor=self)
-
-        today = datetime.date.today()
-        for unav in unav_periods:
-            if unav.start < today and unav.end > today:
-                return False
-        return True
-
     def get_activation_key(self):
         if not self._activation_key:
             self.generate_key()
@@ -117,57 +113,15 @@ class Contributor(models.Model):
             return ', '.join([subject_areas_dict[exp].lower() for exp in self.expertises])
         return ''
 
-    def assignments_summary_as_td(self):
-        assignments = self.editorialassignment_set.all()
-        nr_ongoing = assignments.filter(accepted=True, completed=False).count()
-        nr_last_12mo = assignments.filter(
-            date_created__gt=timezone.now() - timezone.timedelta(days=365)).count()
-        nr_accepted = assignments.filter(accepted=True).count()
-        nr_accepted_last_12mo = assignments.filter(
-            accepted=True, date_created__gt=timezone.now() - timezone.timedelta(days=365)).count()
-        nr_refused = assignments.filter(accepted=False).count()
-        nr_refused_last_12mo = assignments.filter(
-            accepted=False, date_created__gt=timezone.now() - timezone.timedelta(days=365)).count()
-        nr_ignored = assignments.filter(accepted=None).count()
-        nr_ignored_last_12mo = assignments.filter(
-            accepted=None, date_created__gt=timezone.now() - timezone.timedelta(days=365)).count()
-        nr_completed = assignments.filter(completed=True).count()
-        nr_completed_last_12mo = assignments.filter(
-            completed=True, date_created__gt=timezone.now() - timezone.timedelta(days=365)).count()
-
-        context = Context({
-            'nr_ongoing': nr_ongoing,
-            'nr_total': assignments.count(),
-            'nr_last_12mo': nr_last_12mo,
-            'nr_accepted': nr_accepted,
-            'nr_accepted_last_12mo': nr_accepted_last_12mo,
-            'nr_refused': nr_refused,
-            'nr_refused_last_12mo': nr_refused_last_12mo,
-            'nr_ignored': nr_ignored,
-            'nr_ignored_last_12mo': nr_ignored_last_12mo,
-            'nr_completed': nr_completed,
-            'nr_completed_last_12mo': nr_completed_last_12mo,
-        })
-        output = '<td>'
-        if self.expertises:
-            for expertise in self.expertises:
-                output += subject_areas_dict[expertise] + '<br/>'
-        output += ('</td>'
-                   '<td>{{ nr_ongoing }}</td>'
-                   '<td>{{ nr_last_12mo }} / {{ nr_total }}</td>'
-                   '<td>{{ nr_accepted_last_12mo }} / {{ nr_accepted }}</td>'
-                   '<td>{{ nr_refused_last_12mo }} / {{ nr_refused }}</td>'
-                   '<td>{{ nr_ignored_last_12mo }} / {{ nr_ignored }}</td>'
-                   '<td>{{ nr_completed_last_12mo }} / {{ nr_completed }}</td>\n')
-        template = Template(output)
-        return template.render(context)
-
 
 class UnavailabilityPeriod(models.Model):
-    contributor = models.ForeignKey('scipost.Contributor', on_delete=models.CASCADE)
+    contributor = models.ForeignKey('scipost.Contributor', on_delete=models.CASCADE,
+                                    related_name='unavailability_periods')
     start = models.DateField()
     end = models.DateField()
 
+    objects = UnavailabilityPeriodManager()
+
     class Meta:
         ordering = ['-start']
 
diff --git a/scipost/templates/partials/scipost/contributor_assignments_as_td.html b/scipost/templates/partials/scipost/contributor_assignments_as_td.html
new file mode 100644
index 000000000..4f6d8b211
--- /dev/null
+++ b/scipost/templates/partials/scipost/contributor_assignments_as_td.html
@@ -0,0 +1,14 @@
+{% load scipost_extras %}
+
+<td>
+    {% for expertise in contributor.expertises %}
+        {{expertise|get_specialization_display}}<br>
+    {% endfor %}
+</td>
+
+<td>{{contributor.editorial_assignments.ongoing.count}}</td>
+<td>{{contributor.editorial_assignments.last_year.count}} / {{contributor.editorial_assignments.count}}</td>
+<td>{{contributor.editorial_assignments.last_year.accepted.count}} / {{contributor.editorial_assignments.accepted.count}}</td>
+<td>{{contributor.editorial_assignments.last_year.refused.count}} / {{contributor.editorial_assignments.refused.count}}</td>
+<td>{{contributor.editorial_assignments.last_year.ignored.count}} / {{contributor.editorial_assignments.ignored.count}}</td>
+<td>{{contributor.editorial_assignments.last_year.completed.count}} / {{contributor.editorial_assignments.completed.count}}</td>
diff --git a/scipost/templates/scipost/Fellow_activity_overview.html b/scipost/templates/scipost/Fellow_activity_overview.html
index 3e2bacaa8..e0515b486 100644
--- a/scipost/templates/scipost/Fellow_activity_overview.html
+++ b/scipost/templates/scipost/Fellow_activity_overview.html
@@ -1,10 +1,15 @@
-{% extends 'scipost/base.html' %}
+{% extends 'scipost/_personal_page_base.html' %}
 
 {% block pagetitle %}: Overview{% endblock pagetitle %}
 
 {% load scipost_extras %}
 {% load submissions_extras %}
 
+{% block breadcrumb_items %}
+    {{block.super}}
+    <span class="breadcrumb-item">Overview of Fellow's activities</span>
+{% endblock %}
+
 {% block content %}
 
 <div class="row">
@@ -29,10 +34,12 @@
         </thead>
         <tbody>
           {% for fellow in fellows %}
-          <tr>
-    	<td><a href="{% url 'scipost:Fellow_activity_overview' Fellow_id=fellow.id %}">{{ fellow.user.last_name }}, {{ fellow.user.first_name }}</a></td>
-    	{{ fellow.assignments_summary_as_td }}
-          </tr>
+            <tr>
+                <td>
+                    <a href="{% url 'scipost:Fellow_activity_overview' %}?fellow={{fellow.id}}">{{ fellow.user.last_name }}, {{ fellow.user.first_name }}</a>
+                </td>
+                {% include 'partials/scipost/contributor_assignments_as_td.html' with contributor=fellow %}
+            </tr>
           {% endfor %}
         </tbody>
       </table>
@@ -56,12 +63,14 @@
                   <li class="list-group-item">
                       {% include 'submissions/_submission_card_contributor_content.html' with submission=assignment.submission %}
                   </li>
+              {% empty %}
+                  <li class="list-group-item">No completed assignments</li>
               {% endfor %}
             </ul>
         </div>
 
-        <div class="col-12">
-            <h3>Completed:</h3>
+        <div class="col-12 mt-4">
+            <h3>Completed</h3>
         </div>
 
         <div class="col-12">
@@ -70,6 +79,8 @@
                 <li class="list-group-item">
                     {% include 'submissions/_submission_card_contributor_content.html' with submission=assignment.submission %}
                 </li>
+            {% empty %}
+                <li class="list-group-item">No completed assignments</li>
             {% endfor %}
           </ul>
         </div>
diff --git a/scipost/templates/scipost/personal_page.html b/scipost/templates/scipost/personal_page.html
index 56cafa251..c7bd7000a 100644
--- a/scipost/templates/scipost/personal_page.html
+++ b/scipost/templates/scipost/personal_page.html
@@ -103,36 +103,42 @@
                     {% endif %}
                 </div>
                 <div class="col-md-6">
+                  {% if not request.user.contributor.is_currently_available %}
+                      <h3 class="text-warning">You are currently unavailable</h3>
+                      <p>Check your availability underneath if this should not be the case.</p>
+                      <hr>
+                  {% endif %}
+
                   {% if 'SciPost Administrators' in user_groups %}
-                  <h3>You are a SciPost Administrator.</h3>
+                      <h3>You are a SciPost Administrator.</h3>
                   {% endif %}
                   {% if 'Editorial Administrators' in user_groups %}
-                  <h3>You are a SciPost Editorial Administrator.</h3>
+                      <h3>You are a SciPost Editorial Administrator.</h3>
                   {% endif %}
                   {% if 'Advisory Board' in user_groups %}
-                  <h3>You are a member of the Advisory Board.</h3>
+                      <h3>You are a member of the Advisory Board.</h3>
                   {% endif %}
                   {% if 'Editorial College' in user_groups %}
-                  <h3>You are a member of the Editorial College.</h3>
-		  {% endif %}
-		  {% if 'Vetting Editors' in user_groups %}
-                  <h3>You are a SciPost Vetting Editor.</h3>
-		  {% endif %}
-		  {% if 'Registered Contributors' in user_groups %}
-                  <h3>You are a Registered Contributor.</h3>
-		  {% endif %}
-		  {% if 'Testers' in user_groups %}
-                  <h3>You are a SciPost Tester.</h3>
-		  {% endif %}
-		  {% if 'Ambassadors' in user_groups %}
-                  <h3>You are a SciPost Ambassador.</h3>
-		  {% endif %}
-		  {% if 'Junior Ambassadors' in user_groups %}
-                  <h3>You are a SciPost Junior Ambassador.</h3>
-		  {% endif %}
-		  {% if 'Production Officers' in user_groups %}
-		  <h3>You are a SciPost Production Officer.</h3>
-		  {% endif %}
+                      <h3>You are a member of the Editorial College.</h3>
+        		  {% endif %}
+        		  {% if 'Vetting Editors' in user_groups %}
+                          <h3>You are a SciPost Vetting Editor.</h3>
+        		  {% endif %}
+        		  {% if 'Registered Contributors' in user_groups %}
+                          <h3>You are a Registered Contributor.</h3>
+        		  {% endif %}
+        		  {% if 'Testers' in user_groups %}
+                          <h3>You are a SciPost Tester.</h3>
+        		  {% endif %}
+        		  {% if 'Ambassadors' in user_groups %}
+                          <h3>You are a SciPost Ambassador.</h3>
+        		  {% endif %}
+        		  {% if 'Junior Ambassadors' in user_groups %}
+                          <h3>You are a SciPost Junior Ambassador.</h3>
+        		  {% endif %}
+        		  {% if 'Production Officers' in user_groups %}
+        		  <h3>You are a SciPost Production Officer.</h3>
+        		  {% endif %}
 
                   <h3 class="mt-3">Update your personal data or password</h3>
 
@@ -145,12 +151,8 @@
 
             <hr>
             <div class="row">
-                <div class="col-12">
-                    <div class="card card-grey">
-                        <div class="card-body">
-                            <h2 class="card-title mb-0">Your Availability</h2>
-                        </div>
-                    </div>
+                <div class="col">
+                    <h2 class="highlight">Your Availability</h2>
                 </div>
             </div>
             <div class="row justify-content-center">
@@ -166,7 +168,7 @@
                 <div class="col-md-4 ml-md-5">
                     {% if unavailabilities %}
                         <h3>Your unavailability periods in our records</h3>
-                        <p class="text-muted">(YYYY-DD-MM)</p>
+                        <p class="text-muted">(YYYY-MM-DD)</p>
                         <table class="table">
                             <tr>
                                 <th>Start</th>
diff --git a/scipost/urls.py b/scipost/urls.py
index 8eaf7b40e..eb36c827b 100644
--- a/scipost/urls.py
+++ b/scipost/urls.py
@@ -168,11 +168,7 @@ urlpatterns = [
     # Editorial College #
     #####################
     url(r'^EdCol_by-laws$', views.EdCol_bylaws, name='EdCol_by-laws'),
-    url(r'^Fellow_activity_overview/(?P<Fellow_id>[0-9]+)$',
-        views.Fellow_activity_overview,
-        name='Fellow_activity_overview'),
-    url(r'^Fellow_activity_overview$',
-        views.Fellow_activity_overview,
+    url(r'^Fellow_activity_overview$', views.Fellow_activity_overview,
         name='Fellow_activity_overview'),
 
 
diff --git a/scipost/views.py b/scipost/views.py
index 22361d5ad..5d287072b 100644
--- a/scipost/views.py
+++ b/scipost/views.py
@@ -1299,22 +1299,27 @@ def EdCol_bylaws(request):
 
 
 @permission_required('scipost.can_view_pool', return_403=True)
-def Fellow_activity_overview(request, Fellow_id=None):
-    fellows = Contributor.objects.filter(
-        user__groups__name='Editorial College').order_by('user__last_name')
-    context = {'fellows': fellows}
-    if Fellow_id:
-        fellow = get_object_or_404(Contributor, pk=Fellow_id)
-        context['fellow'] = fellow
-
-        assignments_ongoing = (EditorialAssignment.objects.get_for_user_in_pool(request.user)
-                               .filter(accepted=True, completed=False, to=fellow)
-                               .order_by('-date_created'))
-        context['assignments_ongoing'] = assignments_ongoing
-
-        assignments_completed = (EditorialAssignment.objects.get_for_user_in_pool(request.user)
-                                 .filter(completed=True, to=fellow).order_by('-date_created'))
-        context['assignments_completed'] = assignments_completed
+def Fellow_activity_overview(request):
+    fellows = (Contributor.objects.fellows()
+               .prefetch_related('editorial_assignments')
+               .order_by('user__last_name'))
+    context = {
+        'fellows': fellows
+    }
+
+    if request.GET.get('fellow'):
+        try:
+            fellow = fellows.get(pk=request.GET['fellow'])
+            context['fellow'] = fellow
+
+            context['assignments_ongoing'] = (fellow.editorial_assignments
+                                              .ongoing()
+                                              .get_for_user_in_pool(request.user))
+            context['assignments_completed'] = (fellow.editorial_assignments
+                                                .completed()
+                                                .get_for_user_in_pool(request.user))
+        except Contributor.DoesNotExist:
+            pass
     return render(request, 'scipost/Fellow_activity_overview.html', context)
 
 
diff --git a/submissions/managers.py b/submissions/managers.py
index 3465c643a..e4b5b5f2a 100644
--- a/submissions/managers.py
+++ b/submissions/managers.py
@@ -144,12 +144,30 @@ class SubmissionEventQuerySet(models.QuerySet):
         return self.filter(created__gte=timezone.now() - datetime.timedelta(hours=hours))
 
 
-class EditorialAssignmentManager(models.Manager):
+class EditorialAssignmentQuerySet(models.QuerySet):
     def get_for_user_in_pool(self, user):
         return self.exclude(submission__authors=user.contributor)\
                 .exclude(Q(submission__author_list__icontains=user.last_name),
                          ~Q(submission__authors_false_claims=user.contributor))
 
+    def last_year(self):
+        return self.filter(date_created__gt=timezone.now() - timezone.timedelta(days=365))
+
+    def accepted(self):
+        return self.filter(accepted=True)
+
+    def refused(self):
+        return self.filter(accepted=False)
+
+    def ignored(self):
+        return self.filter(accepted=None)
+
+    def completed(self):
+        return self.filter(completed=True)
+
+    def ongoing(self):
+        return self.filter(completed=False).accepted()
+
 
 class EICRecommendationManager(models.Manager):
     def get_for_user_in_pool(self, user):
diff --git a/submissions/models.py b/submissions/models.py
index 4c69c9e70..f5a03da1d 100644
--- a/submissions/models.py
+++ b/submissions/models.py
@@ -15,7 +15,7 @@ from .constants import ASSIGNMENT_REFUSAL_REASONS, ASSIGNMENT_NULLBOOL,\
                        REPORT_STATUSES, STATUS_UNVETTED, SUBMISSION_EIC_RECOMMENDATION_REQUIRED,\
                        SUBMISSION_CYCLES, CYCLE_DEFAULT, CYCLE_SHORT, CYCLE_DIRECT_REC,\
                        EVENT_GENERAL, EVENT_TYPES, EVENT_FOR_AUTHOR, EVENT_FOR_EIC
-from .managers import SubmissionQuerySet, EditorialAssignmentManager, EICRecommendationManager,\
+from .managers import SubmissionQuerySet, EditorialAssignmentQuerySet, EICRecommendationManager,\
                       ReportQuerySet, SubmissionEventQuerySet, RefereeInvitationQuerySet
 from .utils import ShortSubmissionCycle, DirectRecommendationSubmissionCycle,\
                    GeneralSubmissionCycle
@@ -264,7 +264,11 @@ class EditorialAssignment(SubmissionRelatedObjectMixin, models.Model):
     date_created = models.DateTimeField(default=timezone.now)
     date_answered = models.DateTimeField(blank=True, null=True)
 
-    objects = EditorialAssignmentManager()
+    objects = EditorialAssignmentQuerySet.as_manager()
+
+    class Meta:
+        default_related_name = 'editorial_assignments'
+        ordering = ['-date_created']
 
     def __str__(self):
         return (self.to.user.first_name + ' ' + self.to.user.last_name + ' to become EIC of ' +
diff --git a/submissions/templates/partials/submissions/admin/editorial_admin_summary.html b/submissions/templates/partials/submissions/admin/editorial_admin_summary.html
index 60f10bf1a..5d8c75447 100644
--- a/submissions/templates/partials/submissions/admin/editorial_admin_summary.html
+++ b/submissions/templates/partials/submissions/admin/editorial_admin_summary.html
@@ -96,7 +96,7 @@
         {% if not submission.editor_in_charge %}
             <li>EIC Assignment requests:</li>
             <ul>
-                {% for assignment in submission.editorialassignment_set.all %}
+                {% for assignment in submission.editorial_assignments.all %}
                     {% include 'submissions/_assignment_info.html' with assignment=assignment %}
                 {% empty %}
                     <li>None found. <a href="{% url 'submissions:assign_submission' submission.arxiv_identifier_w_vn_nr %}">Send a first assignment request</a></li>
diff --git a/submissions/templates/submissions/_submission_card_in_pool.html b/submissions/templates/submissions/_submission_card_in_pool.html
index 05eb57d34..5425c1195 100644
--- a/submissions/templates/submissions/_submission_card_in_pool.html
+++ b/submissions/templates/submissions/_submission_card_in_pool.html
@@ -28,10 +28,10 @@
     {% endif %}
 
     {% if perms.scipost.can_assign_submissions %}
-        {% if submission.editorialassignment_set.all %}
+        {% if submission.editorial_assignments.exists %}
             <h4>EIC Assignment requests:</h4>
             <ul>
-              {% for assignment in submission.editorialassignment_set.all %}
+              {% for assignment in submission.editorial_assignments.all %}
                   {% include 'submissions/_assignment_info.html' with assignment=assignment %}
               {% endfor %}
             </ul>
diff --git a/submissions/templates/submissions/assign_submission.html b/submissions/templates/submissions/assign_submission.html
index 92020ea32..ce677a88f 100644
--- a/submissions/templates/submissions/assign_submission.html
+++ b/submissions/templates/submissions/assign_submission.html
@@ -39,11 +39,11 @@
 
 <div class="row">
     <div class="col-12">
-      {% if submission_to_assign.editorialassignment_set.all %}
+      {% if submission_to_assign.editorial_assignments.exists %}
           <h4>EIC Assignment requests already sent:</h4>
           <h3>If more than 5 Fellows have declined an assignment for a red-marked reason, the Submission should be rejected.</h3>
           <ul>
-            {% for assignment in sub.editorialassignment_set.all %}
+            {% for assignment in sub.editorial_assignments.all %}
                 {% include 'submissions/_assignment_info.html' with assignment=assignment %}
             {% endfor %}
           </ul>
diff --git a/submissions/views.py b/submissions/views.py
index 3294cb044..c4adc1fbd 100644
--- a/submissions/views.py
+++ b/submissions/views.py
@@ -439,7 +439,7 @@ def assign_submission_ack(request, arxiv_identifier_w_vn_nr):
         if form.is_valid():
             suggested_editor_in_charge = form.cleaned_data['editor_in_charge']
             # TODO: check for possible co-authorships, disqualifying this suggested EIC
-            if not suggested_editor_in_charge.is_currently_available():
+            if not suggested_editor_in_charge.is_currently_available:
                 errormessage = ('This Fellow is marked as currently unavailable. '
                                 'Please go back and select another one.')
                 return render(request, 'scipost/error.html', {'errormessage': errormessage})
@@ -774,7 +774,7 @@ def send_refereeing_invitation(request, arxiv_identifier_w_vn_nr, contributor_id
     submission = get_object_or_404(Submission.objects.get_pool(request.user),
                                    arxiv_identifier_w_vn_nr=arxiv_identifier_w_vn_nr)
     contributor = get_object_or_404(Contributor, pk=contributor_id)
-    if not contributor.is_currently_available():
+    if not contributor.is_currently_available:
         errormessage = ('This Contributor is marked as currently unavailable. '
                         'Please go back and select another referee.')
         return render(request, 'scipost/error.html', {'errormessage': errormessage})
-- 
GitLab