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