From 018fb9f8f50c89861d822b85f7e0ab0708ecfa35 Mon Sep 17 00:00:00 2001
From: "J.-S. Caux" <J.S.Caux@uva.nl>
Date: Thu, 15 Sep 2016 22:12:17 +0200
Subject: [PATCH] Add cancel ref invitation facility

---
 scipost/views.py                              |  4 +--
 submissions/models.py                         |  5 ++-
 .../templates/submissions/editorial_page.html | 29 +++++++++-------
 .../submissions/submission_detail.html        |  2 +-
 submissions/urls.py                           |  2 ++
 submissions/utils.py                          | 34 +++++++++++++++++++
 submissions/views.py                          | 21 +++++++++++-
 7 files changed, 79 insertions(+), 18 deletions(-)

diff --git a/scipost/views.py b/scipost/views.py
index 03d0ebe97..ec693b5a8 100644
--- a/scipost/views.py
+++ b/scipost/views.py
@@ -378,7 +378,7 @@ def vet_registration_request_ack(request, contributor_id):
                 pending_ref_inv_exists = True
                 try:
                     pending_ref_inv = RefereeInvitation.objects.get(
-                        invitation_key=contributor.invitation_key)
+                        invitation_key=contributor.invitation_key, cancelled=False)
                     pending_ref_inv.referee = contributor
                     pending_ref_inv.save()
                 except RefereeInvitation.DoesNotExist:
@@ -591,7 +591,7 @@ def personal_page(request):
             nr_thesislink_requests_to_vet = ThesisLink.objects.filter(vetted=False).count()
             nr_authorship_claims_to_vet = AuthorshipClaim.objects.filter(status='0').count()
         nr_ref_inv_to_consider = RefereeInvitation.objects.filter(
-            referee=contributor, accepted=None).count()
+            referee=contributor, accepted=None, cancelled=False).count()
         pending_ref_tasks = RefereeInvitation.objects.filter(
             referee=contributor, accepted=True, fulfilled=False)
         # Verify if there exist objects authored by this contributor, 
diff --git a/submissions/models.py b/submissions/models.py
index 6a0483549..7daf4528f 100644
--- a/submissions/models.py
+++ b/submissions/models.py
@@ -436,6 +436,7 @@ class RefereeInvitation(models.Model):
     refusal_reason = models.CharField(max_length=3, choices=ASSIGNMENT_REFUSAL_REASONS, 
                                       blank=True, null=True)
     fulfilled = models.BooleanField(default=False) # True if a Report has been submitted
+    cancelled = models.BooleanField(default=False) # True if EIC has deactivated invitation
 
     def __str__(self):
         return (self.first_name + ' ' + self.last_name + ' to referee ' + 
@@ -464,7 +465,9 @@ class RefereeInvitation(models.Model):
         context = Context({'first_name': self.first_name, 'last_name': self.last_name,
                            'date_invited': self.date_invited.strftime('%Y-%m-%d %H:%M')})
         output = '<td>{{ first_name }} {{ last_name }}</td><td>invited <br/>{{ date_invited }}</td><td>'
-        if self.accepted is not None:
+        if self.cancelled:
+            output += '<strong style="color: red;">cancelled</strong>'
+        elif self.accepted is not None:
             if self.accepted:
                 output += '<strong style="color: green">task accepted</strong> '
             else:
diff --git a/submissions/templates/submissions/editorial_page.html b/submissions/templates/submissions/editorial_page.html
index 3a162ca79..1e45d8ed1 100644
--- a/submissions/templates/submissions/editorial_page.html
+++ b/submissions/templates/submissions/editorial_page.html
@@ -76,7 +76,7 @@
 	{% for invitation in ref_invitations %}
 	<tr>
 	  {{ invitation.summary_as_tds }}
-	  {% if not invitation.accepted == False %}
+	  {% if not invitation.accepted == False and not invitation.cancelled %}
 	  <td>
 	    {% if invitation.referee %}
 	    <a href="{% url 'submissions:communication' arxiv_identifier_w_vn_nr=submission.arxiv_identifier_w_vn_nr comtype='EtoR' referee_id=invitation.referee.id %}">Write a communication</a>
@@ -85,21 +85,24 @@
 	    {% endif %}
 	  </td>
 	  <td>
-	  {% if not invitation.fulfilled %}
-	  <a href="{% url 'submissions:ref_invitation_reminder' arxiv_identifier_w_vn_nr=submission.arxiv_identifier_w_vn_nr invitation_id=invitation.id %}">Send reminder email</a>
-	  {% else %}
-	  <strong style="color: green">Report has been delivered</strong>
-	  {% endif %}
+	    {% if not invitation.fulfilled %}
+	    <a href="{% url 'submissions:ref_invitation_reminder' arxiv_identifier_w_vn_nr=submission.arxiv_identifier_w_vn_nr invitation_id=invitation.id %}">Send reminder email</a>
+	    {% else %}
+	    <strong style="color: green">Report has been delivered</strong>
+	    {% endif %}
 	  </td>
-	  {% else %}
-	  <td></td><td></td>
-	  {% endif %}
 	  <td>
-	  {% if invitation.nr_reminders > 0 %}
-	  (nr reminders sent: {{ invitation.nr_reminders }}, <br/>last on {{ invitation.date_last_reminded }})
-	  {% else %}
-	  &nbsp;
+	    {% if invitation.nr_reminders > 0 %}
+	    (nr reminders sent: {{ invitation.nr_reminders }}, <br/>last on {{ invitation.date_last_reminded }})
+	    {% endif %}
+	  </td>
+	  <td>
+	    {% if not invitation.fulfilled %}
+	    <a href="{% url 'submissions:cancel_ref_invitation' arxiv_identifier_w_vn_nr=submission.arxiv_identifier_w_vn_nr invitation_id=invitation.id %}">Cancel invitation</a>
+	    {% endif %}
 	  </td>
+	  {% else %}
+	  <td></td><td></td><td></td><td></td>
 	  {% endif %}
 	</tr>
 	{% endfor %}
diff --git a/submissions/templates/submissions/submission_detail.html b/submissions/templates/submissions/submission_detail.html
index d4b75979a..6122a406a 100644
--- a/submissions/templates/submissions/submission_detail.html
+++ b/submissions/templates/submissions/submission_detail.html
@@ -84,7 +84,7 @@
     {% if submission.open_for_reporting %}
     {% if perms.scipost.can_referee and not is_author and not is_author_unchecked %}
     <li><h3><a href="{% url 'submissions:submit_report' arxiv_identifier_w_vn_nr=submission.arxiv_identifier_w_vn_nr %}">Contribute a Report</a></h3>
-      <div class="reportingDeadline">Deadline for reporting: {{ submission.reporting_deadline }}</div></li>
+      <div class="reportingDeadline">Deadline for reporting: {{ submission.reporting_deadline|date:"Y-m-d" }}</div></li>
     {% elif is_author_unchecked %}
     <li><h3>Contribute a Report [deactivated]: the system flagged you as a potential author of this Submission.
 	Please go to your <a href="{% url 'scipost:personal_page' %}">personal page</a>
diff --git a/submissions/urls.py b/submissions/urls.py
index 8383c3911..0cb3e2984 100644
--- a/submissions/urls.py
+++ b/submissions/urls.py
@@ -48,6 +48,8 @@ urlpatterns = [
     url(r'^accept_or_decline_ref_invitation/(?P<invitation_id>[0-9]+)$', 
         views.accept_or_decline_ref_invitation_ack, name='accept_or_decline_ref_invitation_ack'),
     url(r'^ref_invitation_reminder/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})/(?P<invitation_id>[0-9]+)$', views.ref_invitation_reminder, name='ref_invitation_reminder'),
+    url(r'^cancel_ref_invitation/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})/(?P<invitation_id>[0-9]+)$', 
+        views.cancel_ref_invitation, name='cancel_ref_invitation'),
     url(r'^extend_refereeing_deadline/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})/(?P<days>[0-9]+)$', 
         views.extend_refereeing_deadline, name='extend_refereeing_deadline'),
     url(r'^close_refereeing_round/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})$', 
diff --git a/submissions/utils.py b/submissions/utils.py
index 49bb998f1..14652908e 100644
--- a/submissions/utils.py
+++ b/submissions/utils.py
@@ -282,6 +282,40 @@ class SubmissionUtils(object):
             ['submissions@scipost.org'],
             reply_to=['submissions@scipost.org'])
         emailmessage.send(fail_silently=False)
+
+
+    @classmethod
+    def send_ref_cancellation_email(cls):
+        """
+        This method is used to inform a referee that his/her services are no longer required.
+        It is called from the cancel_ref_invitation method in submissions/views.py.
+        """
+        email_text = ('Dear ' + title_dict[cls.invitation.title] + ' ' + cls.invitation.last_name + ',\n\n' 
+                      'On behalf of the Editor-in-charge '
+                      + title_dict[cls.invitation.submission.editor_in_charge.title] + ' '
+                      + cls.invitation.submission.editor_in_charge.user.last_name
+                      + ', we would like to inform you that your report on\n\n'
+                      + cls.invitation.submission.title + ' by ' 
+                      + cls.invitation.submission.author_list
+                      + '\n\nis no longer required.')
+        email_text += ('\n\nWe very much hope we can count on your expertise '
+                       'at some other point in the future,'
+                       '\n\nMany thanks for your time,\n\nThe SciPost Team')
+        if cls.invitation.referee is None:
+            email_text += ('\n\nP.S.: We would also like to renew '
+                           'our invitation to become a Contributor on SciPost '
+                           '(our records show that you are not yet registered); '
+                           'your partially pre-filled registration form is still available at\n\n'
+                           'https://scipost.org/invitation/' + cls.invitation.invitation_key + '\n\n'
+                           'after which your registration will be activated, giving you full access to '
+                           'the portal\'s facilities (in particular allowing you to provide referee reports).')
+        emailmessage = EmailMessage(
+            'SciPost: report no longer needed', email_text,
+            'SciPost Submissions <submissions@scipost.org>',
+            [cls.invitation.email_address],
+            ['submissions@scipost.org'],
+            reply_to=['submissions@scipost.org'])
+        emailmessage.send(fail_silently=False)
     
 
     @classmethod
diff --git a/submissions/views.py b/submissions/views.py
index 8bce676c1..dfdd9bc5e 100644
--- a/submissions/views.py
+++ b/submissions/views.py
@@ -839,6 +839,25 @@ def accept_or_decline_ref_invitation_ack(request, invitation_id):
     return render(request, 'submissions/accept_or_decline_ref_invitation_ack.html', context)
 
 
+
+@login_required
+@permission_required_or_403('can_take_editorial_actions', 
+                            (Submission, 'arxiv_identifier_w_vn_nr', 'arxiv_identifier_w_vn_nr'))
+def cancel_ref_invitation(request, arxiv_identifier_w_vn_nr, invitation_id):
+    """
+    This method is used by the Editor-in-charge from the editorial_page
+    to remove a referee for the list of invited ones.
+    It can be used for registered as well as unregistered referees.
+    """
+    invitation = get_object_or_404 (RefereeInvitation, pk=invitation_id)
+    invitation.cancelled=True
+    invitation.save()
+    SubmissionUtils.load({'invitation': invitation})
+    SubmissionUtils.send_ref_cancellation_email()
+    return redirect(reverse('submissions:editorial_page', 
+                            kwargs={'arxiv_identifier_w_vn_nr': arxiv_identifier_w_vn_nr}))    
+
+
 @login_required
 @permission_required_or_403('can_take_editorial_actions', 
                             (Submission, 'arxiv_identifier_w_vn_nr', 'arxiv_identifier_w_vn_nr'))
@@ -996,7 +1015,7 @@ def submit_report(request, arxiv_identifier_w_vn_nr):
                            and not (request.user.contributor in submission.authors_false_claims.all())
                            and (request.user.last_name in submission.author_list))
     errormessage = None
-    if timezone.now() > submission.reporting_deadline:
+    if timezone.now() > submission.reporting_deadline + datetime.timedelta(days=1):
         errormessage = ('The reporting deadline has passed. You cannot submit'
                         ' a Report anymore.')
     if is_author:
-- 
GitLab