From a75fb1d20dbe371181d4bddde1339f18782495c0 Mon Sep 17 00:00:00 2001
From: "J.-S. Caux" <J.S.Caux@uva.nl>
Date: Sun, 10 Apr 2016 21:52:25 +0200
Subject: [PATCH] Building up editorial workflow: EditorialCommunications

---
 submissions/admin.py                          |  6 +++
 submissions/forms.py                          | 11 +++++
 submissions/models.py                         | 40 +++++++++++++++---
 .../templates/submissions/communication.html  | 42 +++++++++++++++++++
 .../templates/submissions/editorial_page.html | 19 +++++++--
 submissions/urls.py                           |  3 ++
 submissions/views.py                          | 28 +++++++++++++
 7 files changed, 140 insertions(+), 9 deletions(-)
 create mode 100644 submissions/templates/submissions/communication.html

diff --git a/submissions/admin.py b/submissions/admin.py
index d454de7f0..a26d956b4 100644
--- a/submissions/admin.py
+++ b/submissions/admin.py
@@ -25,3 +25,9 @@ class ReportAdmin(admin.ModelAdmin):
     search_fields = ['author__user__username']
 
 admin.site.register(Report, ReportAdmin)
+
+
+class EditorialCommunicationAdmin(admin.ModelAdmin):
+    search_fields = ['submission', 'referee', 'text']
+
+admin.site.register(EditorialCommunication, EditorialCommunicationAdmin)
diff --git a/submissions/forms.py b/submissions/forms.py
index 559cc35e3..ff2c02384 100644
--- a/submissions/forms.py
+++ b/submissions/forms.py
@@ -118,3 +118,14 @@ class VetReportForm(forms.Form):
         super(VetReportForm, self).__init__(*args, **kwargs)
         self.fields['email_response_field'].widget.attrs.update({'placeholder': 'Optional: give a textual justification (will be emailed to the Report\'s author)', 'rows': 3})
 
+
+###################
+# Communications #
+###################
+
+class EditorialCommunicationForm(forms.Form):
+    text = forms.CharField(widget=forms.Textarea(), label='')
+
+    def __init__(self, *args, **kwargs):
+        super(EditorialCommunicationForm, self).__init__(*args, **kwargs)
+        self.fields['text'].widget.attrs.update({'rows': 5, 'cols': 50, 'placeholder': 'Write your message in this box.'})
diff --git a/submissions/models.py b/submissions/models.py
index 1c1bb9171..414d7d1f4 100644
--- a/submissions/models.py
+++ b/submissions/models.py
@@ -63,7 +63,7 @@ class Submission(models.Model):
     latest_activity = models.DateTimeField(default=timezone.now)
 
     def __str__ (self):
-        return self.title
+        return self.title[:30]
 
     @property
     def reporting_deadline_has_passed(self):
@@ -185,17 +185,16 @@ class RefereeInvitation(models.Model):
         output += 'invited ' + self.date_invited.strftime('%Y-%m-%d %H:%M') + ', '
         if self.accepted is not None:
             if self.accepted:
-                output += 'accepted '
+                output += 'task accepted '
             else:
-                output += 'declined ' 
+                output += 'task declined ' 
             output += self.date_responded.strftime('%Y-%m-%d %H:%M')
         else:
             output += 'response pending'
-        output += '; task fulfilled: '
         if self.fulfilled:
-            output += 'True'
+            output += '; Report has been delivered'
         else:
-            output += 'False'
+            output += '; (no Report yet)'
         return mark_safe(output)
     
 
@@ -319,3 +318,32 @@ class Report(models.Model):
         output += self.print_contents()
         output += '<h3>Recommendation: ' + report_rec_dict[self.recommendation] + '</h3>'
         return mark_safe(output)
+
+
+###########################
+# EditorialCorrespondence #
+###########################
+
+ED_CORR_CHOICES = (
+    ('EtoA', 'Editor-in-charge to Author'),
+    ('AtoE', 'Author to Editor-in-charge'),
+    ('EtoR', 'Editor-in-charge to Referee'),
+    ('RtoE', 'Referee to Editor-in-Charge'),
+    ('EtoS', 'Editor-in-charge to SciPost Editorial Administration'),
+    ('StoE', 'SciPost Editorial Administration to Editor-in-charge'),
+    )
+ed_corr_choices_dict = dict(ED_CORR_CHOICES)
+
+class EditorialCommunication(models.Model):
+    """ 
+    Each individual communication between Editor-in-charge 
+    to and from Referees and Authors becomes an instance of this class.
+    """
+    submission = models.ForeignKey(Submission)
+    referee = models.ForeignKey(Contributor, related_name='referee_in_correspondence', blank=True, null=True)
+    type = models.CharField(max_length=4, choices=ED_CORR_CHOICES)
+    timestamp = models.DateTimeField(default=timezone.now)
+    text = models.TextField()
+
+    def __str__ (self):
+        return self.type + ' for submission ' + self.submission.title[:30] + ' by ' + self.submission.author_list[:30]
diff --git a/submissions/templates/submissions/communication.html b/submissions/templates/submissions/communication.html
new file mode 100644
index 000000000..3471f3acb
--- /dev/null
+++ b/submissions/templates/submissions/communication.html
@@ -0,0 +1,42 @@
+{% extends 'scipost/base.html' %}
+
+{% block pagetitle %}: communication{% endblock pagetitle %}
+
+{% block headsup %}
+
+{% load scipost_extras %}
+
+{% endblock headsup %}
+
+{% block bodysup %}
+
+<!-- Temporary strip -->
+{% if user.is_authenticated %}
+
+<section>
+  <div class="flex-greybox">
+    <h1>Send a Communication</h1>
+  </div>
+  {% if type == 'EtoA' %}
+  <p>to the submitting Author of Submission</p>
+  {% elif type == 'AtoE' or type == 'RtoE' or type == 'StoE' %}
+  <p>to the Editor-in-charge of Submission</p>
+  {% elif type == 'EtoR' %}
+  <p>to Referee of Submission</p>
+  {% elif type == 'EtoS' %}
+  <p>to SciPost Editorial Administrators</p>
+  {% endif %}
+  <ul>{{ submission.header_as_li|safe }}</ul>
+
+  <br/>
+  <form action="{% url 'submissions:communication' submission_id=submission.id type=type %}" method="post">
+    {% csrf_token %}
+    {{ form }}
+    <input type="submit" value="Send communication"/>
+  </form>
+
+</section>
+
+{% endif %} <!-- Temporary strip -->
+
+{% endblock bodysup %}
diff --git a/submissions/templates/submissions/editorial_page.html b/submissions/templates/submissions/editorial_page.html
index d39d82d72..90e9afca8 100644
--- a/submissions/templates/submissions/editorial_page.html
+++ b/submissions/templates/submissions/editorial_page.html
@@ -43,8 +43,10 @@
   <h3>Actions:</h3>
   <ul>
     <li><a href="{% url 'submissions:select_referee' submission_id=submission.id %}">Select an additional referee</a></li>
-    <li>Extend the reporting deadline
-      (currently {{ submission.reporting_deadline }})
+    <li>Extend the refereeing deadline (currently {{ submission.reporting_deadline }}) by 
+      <a href="{% url 'submissions:extend_refereeing_deadline' submission_id=submission.id days=2 %}">2 days</a>,
+      <a href="{% url 'submissions:extend_refereeing_deadline' submission_id=submission.id days=7 %}">1 week</a> or
+      <a href="{% url 'submissions:extend_refereeing_deadline' submission_id=submission.id days=14 %}">2 weeks</a>
       {% if submission.reporting_deadline_has_passed %}
       <b style="color: red">THE REPORTING DEADLINE HAS PASSED</b>
       {% endif %}
@@ -52,7 +54,18 @@
     {% if not submission.reporting_deadline_has_passed %}
     <li><a href="{% url 'submissions:close_refereeing_round' submission_id=submission.id %}">Close the refereeing round</a></li>
     {% endif %}
-    <li>Request author response to Reports and Comments</li>
+    <li><a href="{% url 'submissions:communication' submission_id=submission.id type='EtoA' %}">Communicate with the submitting Author</a></li>
+    <li>Communicate with a Referee:
+    <ul>
+      {% for invitation in ref_invitations %}
+      {% if invitation.referee %}
+      <li><a href="{% url 'submissions:communication' submission_id=submission.id type='EtoR' referee_id=invitation.referee.id %}">
+	  {{ invitation.first_name }} {{ invitation.last_name }}</a></li>
+      {% endif %}
+      {% endfor %}
+    </ul>
+    </li>
+    <li><a href="{% url 'submissions:communication' submission_id=submission.id type='EtoS' %}">Communicate with SciPost Editorial Administration</a></li>
     <li>Formulate an Editorial Recommendation and send it to Editorial College for ratification</li>
     <li>Inform authors of Editorial Decisions</li>
   </ul>
diff --git a/submissions/urls.py b/submissions/urls.py
index 219e41d37..e2e14c362 100644
--- a/submissions/urls.py
+++ b/submissions/urls.py
@@ -22,7 +22,10 @@ urlpatterns = [
     url(r'^send_refereeing_invitation/(?P<submission_id>[0-9]+)/(?P<contributor_id>[0-9]+)$', views.send_refereeing_invitation, name='send_refereeing_invitation'),
     url(r'^accept_or_decline_ref_invitations$', views.accept_or_decline_ref_invitations, name='accept_or_decline_ref_invitations'),
     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'^extend_refereeing_deadline/(?P<submission_id>[0-9]+)/(?P<days>[0-9]+)$', views.extend_refereeing_deadline, name='extend_refereeing_deadline'),
     url(r'^close_refereeing_round/(?P<submission_id>[0-9]+)$', views.close_refereeing_round, name='close_refereeing_round'),
+    url(r'^communication/(?P<submission_id>[0-9]+)/(?P<type>[a-zA-Z]{4,})$', views.communication, name='communication'),
+    url(r'^communication/(?P<submission_id>[0-9]+)/(?P<type>[a-zA-Z]{4,})/(?P<referee_id>[0-9]+)$', views.communication, name='communication'),
     # Reports
     url(r'^submit_report/(?P<submission_id>[0-9]+)$', views.submit_report, name='submit_report'),
     url(r'^submit_report_ack$', TemplateView.as_view(template_name='submissions/submit_report_ack.html'), name='submit_report_ack'),
diff --git a/submissions/views.py b/submissions/views.py
index 44f22698b..8160a1b78 100644
--- a/submissions/views.py
+++ b/submissions/views.py
@@ -331,6 +331,14 @@ def accept_or_decline_ref_invitation_ack(request, invitation_id):
     return render(request, 'submissions/accept_or_decline_ref_invitation_ack.html', context)
 
 
+@permission_required('scipost.can_take_charge_of_submissions', raise_exception=True)
+def extend_refereeing_deadline(request, submission_id, days):
+    submission = get_object_or_404 (Submission, pk=submission_id)
+    submission.reporting_deadline += datetime.timedelta(days=int(days))
+    submission.save()
+    return redirect(reverse('submissions:editorial_page', kwargs={'submission_id': submission_id}))
+    
+
 @permission_required('scipost.can_take_charge_of_submissions', raise_exception=True)
 def close_refereeing_round(request, submission_id):
     submission = get_object_or_404 (Submission, pk=submission_id)
@@ -341,6 +349,26 @@ def close_refereeing_round(request, submission_id):
     return redirect(reverse('submissions:editorial_page', kwargs={'submission_id': submission_id}))
 
 
+def communication(request, submission_id, type, referee_id=None):
+    submission = get_object_or_404 (Submission, pk=submission_id)
+    if request.method == 'POST':
+        form = EditorialCommunicationForm(request.POST)
+        if form.is_valid():
+            communication = EditorialCommunication(submission=submission,
+                                                   type=type,
+                                                   timestamp=timezone.now(),
+                                                   text=form.cleaned_data['text'])
+            communication.save()
+            if type == 'EtoA' or type == 'EtoR' or type == 'EtoS':
+                return redirect(reverse('submissions:editorial_page', kwargs={'submission_id': submission_id}))
+            elif type == 'AtoE' or type == 'RtoE' or type == 'StoE':
+                return redirect(request, reverse('scipost:personal_page'))
+    else:
+        form = EditorialCommunicationForm()
+    context = {'submission': submission, 'type': type, 'form': form}
+    return render(request, 'submissions/communication.html', context)
+
+        
 ###########
 # Reports
 ###########
-- 
GitLab