From d5f389f365b86202c185d24acb60a05960abacee Mon Sep 17 00:00:00 2001 From: "J.-S. Caux" <J.S.Caux@uva.nl> Date: Mon, 16 May 2016 15:58:00 +0200 Subject: [PATCH] Work on editorial process --- commentaries/views.py | 2 +- journals/views.py | 2 +- scipost/templates/scipost/personal_page.html | 2 +- scipost/utils.py | 3 - scipost/views.py | 18 ++- submissions/models.py | 61 ++++++-- .../accept_or_decline_assignments.html | 2 +- .../submissions/assign_submissions.html | 18 ++- .../templates/submissions/communication.html | 12 +- .../templates/submissions/editorial_page.html | 6 +- submissions/templates/submissions/queue.html | 23 +++ .../submissions/submission_detail.html | 6 +- .../submissions/submit_manuscript.html | 15 +- submissions/urls.py | 8 +- submissions/views.py | 144 +++++++++++++++--- 15 files changed, 257 insertions(+), 65 deletions(-) create mode 100644 submissions/templates/submissions/queue.html diff --git a/commentaries/views.py b/commentaries/views.py index 6c7df474b..a3ba89cff 100644 --- a/commentaries/views.py +++ b/commentaries/views.py @@ -281,7 +281,7 @@ def vet_commentary_request_ack(request, commentary_id): email_text = ('Dear ' + title_dict[commentary.requested_by.title] + ' ' + commentary.requested_by.user.last_name + ', \n\nThe Commentary Page you have requested, concerning publication with title ' + commentary.pub_title + ' by ' + commentary.author_list + ', has not been activated for the following reason: ' + - commentary_refusal_dict(form.cleaned_data['refusal_reason']) + '.\n\nThank you for your interest, \nThe SciPost Team.') + commentary_refusal_dict[form.cleaned_data['refusal_reason']] + '.\n\nThank you for your interest, \nThe SciPost Team.') if form.cleaned_data['email_response_field']: email_text += '\n\nFurther explanations: ' + form.cleaned_data['email_response_field'] emailmessage = EmailMessage('SciPost Commentary Page activated', email_text, 'SciPost commentaries <commentaries@scipost.org>', diff --git a/journals/views.py b/journals/views.py index 008a9398c..1686d6528 100644 --- a/journals/views.py +++ b/journals/views.py @@ -3,7 +3,7 @@ from django.utils import timezone from django.shortcuts import get_object_or_404, render from django.contrib.auth import authenticate, login, logout from django.contrib.auth.models import User -from django.core.mail import send_mail +from django.core.mail import EmailMessage from django.core.urlresolvers import reverse from django.http import HttpResponse, HttpResponseRedirect from django.views.decorators.csrf import csrf_protect diff --git a/scipost/templates/scipost/personal_page.html b/scipost/templates/scipost/personal_page.html index 2c69feda4..b1846ff96 100644 --- a/scipost/templates/scipost/personal_page.html +++ b/scipost/templates/scipost/personal_page.html @@ -247,7 +247,7 @@ {% for task in pending_ref_tasks %} <li>{{ task.submission }}, due {{ task.submission.reporting_deadline }}. <a href="{% url 'submissions:submit_report' submission_id=task.submission.id %}">Submit your Report</a>. - <a href="{% url 'submissions:communication' submission_id=task.submission.id type='RtoE' %}">Write to the Editor-in-charge</a>. + <a href="{% url 'submissions:communication' submission_id=task.submission.id comtype='RtoE' %}">Write to the Editor-in-charge</a>. </li> {% endfor %} </ul> diff --git a/scipost/utils.py b/scipost/utils.py index c208505cc..12b6dca5c 100644 --- a/scipost/utils.py +++ b/scipost/utils.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - import datetime import hashlib import random diff --git a/scipost/views.py b/scipost/views.py index e214c2a04..7dba8f300 100644 --- a/scipost/views.py +++ b/scipost/views.py @@ -420,7 +420,7 @@ def personal_page(request): nr_reg_to_vet = Contributor.objects.filter(user__is_active=True, status=0).count() nr_reg_awaiting_validation = Contributor.objects.filter( user__is_active=False, key_expires__gte=now, key_expires__lte=intwodays, status=0).count() - nr_submissions_to_assign = Submission.objects.filter(assigned=False).count() + nr_submissions_to_assign = Submission.objects.filter(status__in=['unassigned']).count() nr_assignments_to_consider = 0 active_assignments = None if is_MEC(request.user): @@ -440,8 +440,12 @@ def personal_page(request): nr_ref_inv_to_consider = RefereeInvitation.objects.filter(referee=contributor, accepted=None).count() pending_ref_tasks = RefereeInvitation.objects.filter(referee=contributor, accepted=True, fulfilled=False) # Verify if there exist objects authored by this contributor, whose authorship hasn't been claimed yet - own_submissions = Submission.objects.filter(authors__in=[contributor]) - own_commentaries = Commentary.objects.filter(authors__in=[contributor]) + own_submissions = (Submission.objects + .filter(Q(authors__in=[contributor]) | Q(submitted_by=contributor)) + .order_by('-submission_date')) + own_commentaries = (Commentary.objects + .filter(authors__in=[contributor]) + .order_by('-latest_activity')) own_thesislinks = ThesisLink.objects.filter(author_as_cont__in=[contributor]) nr_submission_authorships_to_claim = (Submission.objects .filter(author_list__contains=contributor.user.last_name) @@ -461,8 +465,12 @@ def personal_page(request): .exclude(author_claims__in=[contributor]) .exclude(author_false_claims__in=[contributor]) .count()) - own_comments = Comment.objects.filter(author=contributor,is_author_reply=False).order_by('-date_submitted') - own_authorreplies = Comment.objects.filter(author=contributor,is_author_reply=True).order_by('-date_submitted') + own_comments = (Comment.objects + .filter(author=contributor,is_author_reply=False) + .order_by('-date_submitted')) + own_authorreplies = (Comment.objects + .filter(author=contributor,is_author_reply=True) + .order_by('-date_submitted')) lists_owned = List.objects.filter(owner=contributor) lists = List.objects.filter(teams_with_access__members__in=[contributor]) teams_led = Team.objects.filter(leader=contributor) diff --git a/submissions/models.py b/submissions/models.py index 595dfad87..549d98bc4 100644 --- a/submissions/models.py +++ b/submissions/models.py @@ -20,6 +20,7 @@ from journals.models import journals_submit_dict, journals_domains_dict, journal SUBMISSION_STATUS = ( ('unassigned', 'Unassigned'), ('assigned', 'Assigned to a specialty editor (response pending)'), + ('assignment_failed', 'Failed to assign Editor-in-charge; manuscript rejected'), ('EICassigned', 'Editor-in-charge assigned, manuscript under review'), ('review_closed', 'Review period closed, editorial recommendation pending'), ('EIC_has_recommended', 'Editor-in-charge has provided recommendation'), @@ -43,7 +44,6 @@ SUBMISSION_ACTION_REQUIRED = ( class Submission(models.Model): submitted_by = models.ForeignKey(Contributor) - assigned = models.BooleanField(default=False) editor_in_charge = models.ForeignKey(Contributor, related_name='EIC', blank=True, null=True) submitted_to_journal = models.CharField(max_length=30, choices=SCIPOST_JOURNALS_SUBMIT, verbose_name="Journal to be submitted to") discipline = models.CharField(max_length=20, choices=SCIPOST_DISCIPLINES, default='physics') @@ -51,9 +51,9 @@ class Submission(models.Model): specialization = models.CharField(max_length=1, choices=SCIPOST_JOURNALS_SPECIALIZATIONS) status = models.CharField(max_length=30, choices=SUBMISSION_STATUS) # set by Editors referees_flagged = models.TextField(blank=True, null=True) - open_for_reporting = models.BooleanField(default=True) + open_for_reporting = models.BooleanField(default=False) reporting_deadline = models.DateTimeField(default=timezone.now) - open_for_commenting = models.BooleanField(default=True) + open_for_commenting = models.BooleanField(default=False) title = models.CharField(max_length=300) author_list = models.CharField(max_length=1000, verbose_name="author list") # Authors which have been mapped to contributors: @@ -117,6 +117,29 @@ class Submission(models.Model): return template.render(context) + def header_as_li_for_Fellows (self): + # for submissions queue + header = '<li><div class="flex-container">' + header += '<div class="flex-whitebox0"><p><a href="/submission/{{ id }}" class="pubtitleli">{{ title }}</a></p>' + header += ('<p>by {{ author_list }}</p><p> (submitted {{ submission_date }} to {{ to_journal }})' + ' - latest activity: {{ latest_activity }}</p>' + '<p>Editor-in-charge: {{ EIC }}</p>') + if self.status == 'unassigned': + header += ('<p style="color: red">Status: {{ status }}.' + ' You can <a href="/submissions/volunteer_as_EIC/{{ id }}">volunteer</a> to become Editor-in-charge</p>') + else: + header += '<p>Status: {{ status }}</p>' + header += '</div></div></li>' + context = Context({'id': self.id, 'title': self.title, 'author_list': self.author_list, + 'submission_date': self.submission_date, + 'to_journal': journals_submit_dict[self.submitted_to_journal], + 'latest_activity': self.latest_activity.strftime('%Y-%m-%d %H:%M'), + 'EIC': str(self.editor_in_charge), + 'status': submission_status_dict[self.status]}) + template = Template(header) + return template.render(context) + + def simple_header_as_li (self): # for Lists header = '<li><div class="flex-container">' @@ -177,8 +200,22 @@ class EditorialAssignment(models.Model): 'to_journal': journals_submit_dict[self.submission.submitted_to_journal], 'status': submission_status_dict[self.submission.status]}) return template.render(context) - + def declination_as_li(self): + output = '<li' + if self.refusal_reason == 'NIE' or self.refusal_reason == 'DNP': + output += ' style="color: red"' + output += ('>Fellow {{ first_name }} {{ last_name }}, ' + 'assigned {{ date_created }}, declined {{ date_answered }}, ' + 'reason: {{ reason }}</li>') + template = Template(output) + context = Context({'first_name': self.to.user.first_name, + 'last_name': self.to.user.last_name, + 'date_created': self.date_created.strftime('%Y-%m-%d %H:%M'), + 'date_answered': self.date_answered.strftime('%Y-%m-%d %H:%M'), + 'reason': assignment_refusal_reasons_dict[self.refusal_reason]}) + return template.render(context) + class RefereeInvitation(models.Model): submission = models.ForeignKey(Submission) @@ -372,12 +409,12 @@ class EditorialCommunication(models.Model): """ 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) + comtype = models.CharField(max_length=4, choices=ED_CORR_CHOICES) timestamp = models.DateTimeField(default=timezone.now) text = models.TextField() def __str__ (self): - output = self.type + output = self.comtype if self.referee is not None: output += ' ' + self.referee.user.first_name + ' ' + self.referee.user.last_name output += ' for submission ' + self.submission.title[:30] + ' by ' + self.submission.author_list[:30] @@ -386,25 +423,25 @@ class EditorialCommunication(models.Model): def print_contents_as_li(self): context = Context({'timestamp': self.timestamp.strftime("%Y-%m-%d %H:%M"), 'text': self.text}) output = '<li><p>' - if self.type == 'EtoA': + if self.comtype == 'EtoA': output += 'From you to Authors' - elif self.type == 'EtoR': + elif self.comtype == 'EtoR': output += 'From you to Referee ' try: output += self.referee.user.first_name + ' ' + self.referee.user.last_name except AttributeError: pass - elif self.type == 'EtoS': + elif self.comtype == 'EtoS': output += 'From you to SciPost Ed Admin' - elif self.type == 'AtoE': + elif self.comtype == 'AtoE': output += 'From Authors to you' - elif self.type == 'RtoE': + elif self.comtype == 'RtoE': output += 'From Referee ' try: output += self.referee.user.first_name + ' ' + self.referee.user.last_name + ' to you' except AttributeError: pass - elif self.type == 'StoE': + elif self.comtype == 'StoE': output += 'From SciPost Ed Admin to you' output += ' on {{ timestamp }}</p><p>{{ text }}</p>' template = Template(output) diff --git a/submissions/templates/submissions/accept_or_decline_assignments.html b/submissions/templates/submissions/accept_or_decline_assignments.html index 03ef0b5e6..066c4376b 100644 --- a/submissions/templates/submissions/accept_or_decline_assignments.html +++ b/submissions/templates/submissions/accept_or_decline_assignments.html @@ -29,7 +29,7 @@ $(document).ready(function(){ {% else %} <div class="flex-greybox"> - <h1>SciPost Submission Assigned to you (see below to accept/decline):</h1> + <h1>SciPost Submission: can you act as Editor-in-charge? (see below to accept/decline):</h1> </div> <br> <hr> diff --git a/submissions/templates/submissions/assign_submissions.html b/submissions/templates/submissions/assign_submissions.html index 72821ee59..50fda0819 100644 --- a/submissions/templates/submissions/assign_submissions.html +++ b/submissions/templates/submissions/assign_submissions.html @@ -20,10 +20,16 @@ <p>{{ submission_to_assign.abstract }}</p> <br/> <hr/> - {{ submission_to_assign.status_info_as_table }} + + {% if submission_to_assign.referees_flagged %} + <h3>Referees flagged upon submission (treat reports with caution):</h3> + <p>{{ submission_to_assign.referees_flagged }}</p> + {% endif %} <br/> <hr> + {{ submission_to_assign.status_info_as_table }} + <hr> <h1>Required actions:</h1> <form action="{% url 'submissions:assign_submission_ack' submission_id=submission_to_assign.id %}" method="post"> {% csrf_token %} @@ -31,6 +37,16 @@ <input type="submit" value="Submit" /> </form> + {% if assignments_declined %} + <h3>If more than 5 Fellows have declined an assignment for a red-marked reason, the Submission should be rejected.</h3> + <h3>Previously-declined Assignments:</h3> + <ul> + {% for assignment in assignments_declined %} + {{ assignment.declination_as_li }} + {% endfor %} + </ul> + {% endif %} + {% endif %} </section> diff --git a/submissions/templates/submissions/communication.html b/submissions/templates/submissions/communication.html index 5e2b263ec..5719eee74 100644 --- a/submissions/templates/submissions/communication.html +++ b/submissions/templates/submissions/communication.html @@ -14,22 +14,22 @@ <div class="flex-greybox"> <h1>Send a Communication</h1> </div> - {% if type == 'EtoA' %} + {% if comtype == 'EtoA' %} <p>to the submitting Author of Submission</p> - {% elif type == 'AtoE' or type == 'RtoE' or type == 'StoE' %} + {% elif comtype == 'AtoE' or comtype == 'RtoE' or comtype == 'StoE' %} <p>to the Editor-in-charge of Submission</p> - {% elif type == 'EtoR' %} + {% elif comtype == 'EtoR' %} <p>to Referee of Submission</p> - {% elif type == 'EtoS' %} + {% elif comtype == 'EtoS' %} <p>to SciPost Editorial Administrators</p> {% endif %} <ul>{{ submission.header_as_li }}</ul> <br/> - <form action="{% url 'submissions:communication' submission_id=submission.id type=type %}" method="post"> + <form action="{% url 'submissions:communication' submission_id=submission.id comtype=comtype %}" method="post"> {% csrf_token %} {{ form }} - <input type="submit" value="Send communication"/> + <input comtype="submit" value="Send communication"/> </form> </section> diff --git a/submissions/templates/submissions/editorial_page.html b/submissions/templates/submissions/editorial_page.html index d0481bcb4..70b283077 100644 --- a/submissions/templates/submissions/editorial_page.html +++ b/submissions/templates/submissions/editorial_page.html @@ -56,18 +56,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><a href="{% url 'submissions:communication' submission_id=submission.id type='EtoA' %}">Communicate with the submitting Author</a></li> + <li><a href="{% url 'submissions:communication' submission_id=submission.id comtype='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 %}"> + <li><a href="{% url 'submissions:communication' submission_id=submission.id comtype='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><a href="{% url 'submissions:communication' submission_id=submission.id comtype='EtoS' %}">Communicate with SciPost Editorial Administration</a></li> <li><a href="{% url 'submissions:eic_recommendation' submission_id=submission.id %}">Formulate an Editorial Recommendation</a> <p>If you recommend revisions, this will be communicated directly to the Authors, who will be asked to resubmit. <br/>If you recommend acceptance or rejection, this will be put to the Editorial College for ratification.</p> diff --git a/submissions/templates/submissions/queue.html b/submissions/templates/submissions/queue.html new file mode 100644 index 000000000..9b1351620 --- /dev/null +++ b/submissions/templates/submissions/queue.html @@ -0,0 +1,23 @@ +{% extends 'scipost/base.html' %} + +{% block pagetitle %}: Submissions Queue{% endblock pagetitle %} + +{% block bodysup %} + + +<section> + <div class="flex-container"> + <div class="flex-greybox320"> + <h1>SciPost Submissions Queue</h1> + </div> + </div> + + <ul> + {% for sub in submissions_in_queue %} + {{ sub.header_as_li_for_Fellows }} + {% endfor %} + </ul> + +</section> + +{% endblock bodysup %} diff --git a/submissions/templates/submissions/submission_detail.html b/submissions/templates/submissions/submission_detail.html index 5578d6e90..08780cd95 100644 --- a/submissions/templates/submissions/submission_detail.html +++ b/submissions/templates/submissions/submission_detail.html @@ -57,7 +57,7 @@ {% else %} <li>Reporting for this Submission is now closed.</li> {% endif %} - {% if perms.scipost.can_submit_comments %} + {% if submission.open_for_commenting and perms.scipost.can_submit_comments %} <li><h3><a href="#contribute_comment">Contribute a Comment</a></h3></li> {% endif %} </ul> @@ -79,10 +79,10 @@ {% if report.flagged %} <h4 style="color: red">CAUTION: check if this referee has been flagged by the authors</h4> {% endif %} - {{ report.print_contents_for_editors }} + {{ report.print_contents_for_editors|linebreaks }} {% else %} {{ report.print_identifier }} - {{ report.print_contents }} + {{ report.print_contents|linebreaks }} {% endif %} <hr style="border-style: dotted;" /> diff --git a/submissions/templates/submissions/submit_manuscript.html b/submissions/templates/submissions/submit_manuscript.html index 8b17b4463..9ff54998f 100644 --- a/submissions/templates/submissions/submit_manuscript.html +++ b/submissions/templates/submissions/submit_manuscript.html @@ -5,17 +5,24 @@ {% block bodysup %} <section> - <h1>Submit a manuscript to SciPost</h1> - + <div class="flex-greybox"> + <h1>Submit a manuscript to SciPost</h1> + </div> + <p id="journalsannounce">OPEN FOR SUBMISSION STARTING JUNE 2016</p> - {% if False %} <!-- Temporary deactivate submissions --> + <p>Before submitting, make sure you agree with the <a href="{% url 'journals:journals_terms_and_conditions' %}">SciPost Journals Terms and Conditions</a>.</p> + <p>In particular, make sure you are familiar with the <a href="{% url 'journals:journals_terms_and_conditions' %}#license_and_copyright_agreement">license and copyright agreement</a> + and the <a href="{% url 'journals:journals_terms_and_conditions' %}#author_obligations">author obligations</a>.</p> + <p>Please prepare your manuscript according to the <a href="{% url 'submissions:author_guidelines' %}">author guidelines</a>.</p> + + {% if True %} <!-- Temporary deactivate submissions --> {% if perms.scipost.can_submit_manuscript %} <div class="flex-greybox"> <h3><em>You can prefill part of the form using the arXiv identifier:</em></h3> - <p><em>(give the identifier without prefix, as per the placeholder)</em></p> + <p><em>(give the identifier without prefix but with version number, as per the placeholder)</em></p> {% if errormessage %} <h3 style="color: red;">Error: {{ errormessage }}</h3> {% endif %} diff --git a/submissions/urls.py b/submissions/urls.py index d9a59d3c6..f15081962 100644 --- a/submissions/urls.py +++ b/submissions/urls.py @@ -14,10 +14,14 @@ urlpatterns = [ url(r'^prefill_using_identifier$', views.prefill_using_identifier, name='prefill_using_identifier'), url(r'^submit_manuscript$', views.submit_manuscript, name='submit_manuscript'), url(r'^submit_manuscript_ack$', TemplateView.as_view(template_name='submissions/submit_manuscript_ack.html'), name='submit_manuscript_ack'), + url(r'^queue$', views.queue, name='queue'), + # Assignment of Editor-in-charge url(r'^assign_submissions$', views.assign_submissions, name='assign_submissions'), url(r'^assign_submission_ack/(?P<submission_id>[0-9]+)$', views.assign_submission_ack, name='assign_submission_ack'), url(r'^accept_or_decline_assignments$', views.accept_or_decline_assignments, name='accept_or_decline_assignments'), url(r'^accept_or_decline_assignment_ack/(?P<assignment_id>[0-9]+)$', views.accept_or_decline_assignment_ack, name='accept_or_decline_assignment_ack'), + url(r'^volunteer_as_EIC/(?P<submission_id>[0-9]+)$', views.volunteer_as_EIC, name='volunteer_as_EIC'), + # Editorial workflow and refereeing url(r'^editorial_page/(?P<submission_id>[0-9]+)$', views.editorial_page, name='editorial_page'), url(r'^select_referee/(?P<submission_id>[0-9]+)$', views.select_referee, name='select_referee'), url(r'^recruit_referee/(?P<submission_id>[0-9]+)$', views.recruit_referee, name='recruit_referee'), @@ -26,8 +30,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'^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'), + url(r'^communication/(?P<submission_id>[0-9]+)/(?P<comtype>[a-zA-Z]{4,})$', views.communication, name='communication'), + url(r'^communication/(?P<submission_id>[0-9]+)/(?P<comtype>[a-zA-Z]{4,})/(?P<referee_id>[0-9]+)$', views.communication, name='communication'), url(r'^eic_recommendation/(?P<submission_id>[0-9]+)$', views.eic_recommendation, name='eic_recommendation'), # Reports url(r'^submit_report/(?P<submission_id>[0-9]+)$', views.submit_report, name='submit_report'), diff --git a/submissions/views.py b/submissions/views.py index 2941f197a..fc3bcebf5 100644 --- a/submissions/views.py +++ b/submissions/views.py @@ -8,7 +8,7 @@ from django.shortcuts import get_object_or_404, render, redirect from django.contrib.auth import authenticate, login, logout from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.models import User -from django.core.mail import send_mail +from django.core.mail import EmailMessage from django.core.urlresolvers import reverse from django.http import HttpResponse, HttpResponseRedirect from django.views.decorators.csrf import csrf_protect @@ -39,6 +39,9 @@ def prefill_using_identifier(request): errormessage = '' if not identifierpattern.match(identifierform.cleaned_data['identifier']): errormessage = 'The identifier you entered is improperly formatted (did you forget the version number?).' + elif Submission.objects.filter(arxiv_link__contains=identifierform.cleaned_data['identifier']).exists(): + errormessage = 'This preprint has already been submitted to SciPost.' + if errormessage != '': form = SubmissionForm() return render(request, 'submissions/submit_manuscript.html', {'identifierform': identifierform, 'form': form, @@ -102,6 +105,7 @@ def submit_manuscript(request): author_list = form.cleaned_data['author_list'], abstract = form.cleaned_data['abstract'], arxiv_link = form.cleaned_data['arxiv_link'], + metadata = form.cleaned_data['metadata'], submission_date = timezone.now(), referees_flagged = form.cleaned_data['referees_flagged'], ) @@ -122,9 +126,7 @@ def submissions(request): title__icontains=form.cleaned_data['title_keyword'], author_list__icontains=form.cleaned_data['author'], abstract__icontains=form.cleaned_data['abstract_keyword'], - status__gte=1, - ) - submission_search_list.order_by('-pub_date') + ).exclude(status__in=['unassigned', 'assigned', 'assignment_failed']).order_by('-submission_date') else: submission_search_list = [] @@ -133,9 +135,8 @@ def submissions(request): submission_search_list = [] submission_recent_list = Submission.objects.filter( - status__gte=1, latest_activity__gte=timezone.now() + datetime.timedelta(days=-7) - ) - submission_recent_list = Submission.objects.filter(status__gte=1) + latest_activity__gte=timezone.now() + datetime.timedelta(days=-7) + ).exclude(status__in=['unassigned', 'assigned', 'assignment_failed']).order_by('-submission_date') context = {'form': form, 'submission_search_list': submission_search_list, 'submission_recent_list': submission_recent_list } return render(request, 'submissions/submissions.html', context) @@ -149,9 +150,7 @@ def browse(request, discipline, nrweeksback): title__icontains=form.cleaned_data['title_keyword'], author_list__icontains=form.cleaned_data['author'], abstract__icontains=form.cleaned_data['abstract_keyword'], - assigned=True, - ) - submission_search_list.order_by('-submission_date') + ).exclude(status__in=['unassigned', 'assigned', 'assignment_failed']).order_by('-submission_date') else: submission_search_list = [] context = {'form': form, 'submission_search_list': submission_search_list } @@ -159,9 +158,9 @@ def browse(request, discipline, nrweeksback): else: form = SubmissionSearchForm() submission_browse_list = Submission.objects.filter( - assigned=True, discipline=discipline, + discipline=discipline, latest_activity__gte=timezone.now() + datetime.timedelta(weeks=-int(nrweeksback)) - ) + ).exclude(status__in=['unassigned', 'assigned', 'assignment_failed']).order_by('-submission_date') context = {'form': form, 'discipline': discipline, 'nrweeksback': nrweeksback, 'submission_browse_list': submission_browse_list } return render(request, 'submissions/submissions.html', context) @@ -214,15 +213,35 @@ def submission_detail(request, submission_id): # Editorial workflow # ###################### +@permission_required('scipost.can_take_charge_of_submissions', raise_exception=True) +def queue(request): + """ + The Submissions queue contains all submissions which are undergoing + the editorial process, from assignment acceptance (Editor-in-charge appointed) + to publication acceptance or rejection. + All members of the Editorial College have access. + """ + submissions_in_queue=Submission.objects.all().exclude(status__in=['decided']) + + context = {'submissions_in_queue': submissions_in_queue} + return render(request, 'submissions/queue.html', context) + + @permission_required('scipost.can_assign_submissions', raise_exception=True) def assign_submissions(request): submission_to_assign = Submission.objects.filter(status='unassigned').first() # only handle one at at time + assignments_declined = None if submission_to_assign is not None: - form = AssignSubmissionForm(discipline=submission_to_assign.discipline) # form = AssignSubmissionForm(discipline=submission_to_assign.discipline, specialization=submission_to_assign.specialization) # reactivate later on + form = AssignSubmissionForm(discipline=submission_to_assign.discipline) + assignments_declined = (EditorialAssignment.objects + .filter(submission=submission_to_assign, accepted=False) + .order_by('date_created')) else: form = AssignSubmissionForm(discipline='physics') - context = {'submission_to_assign': submission_to_assign, 'form': form } + context = {'submission_to_assign': submission_to_assign, + 'form': form, + 'assignments_declined': assignments_declined} return render(request, 'submissions/assign_submissions.html', context) @@ -237,14 +256,30 @@ def assign_submission_ack(request, submission_id): to=editor_in_charge, date_created=timezone.now()) ed_assignment.save() - submission.assigned = True submission.status = 'assigned' submission.latest_activity = timezone.now() submission.save() + # Email Fellow + email_text = ('Dear ' + title_dict[ed_assignment.to.title] + ' ' + + ed_assignment.to.user.last_name + + ', \n\nWe have received a Submission to SciPost ' + + 'for which we would like you to consider becoming Editor-in-charge:\n\n' + + submission.title + ' by ' + submission.author_list + + '\n\nPlease visit https://scipost.org/submissions/accept_or_decline_assignments \n' + + 'in order to accept or decline. Many thanks in advance for your consideration. ' + + '\n\nThe SciPost Team.') + emailmessage = EmailMessage( + 'SciPost: potential Submission assignment', email_text, + 'SciPost Editorial Admin <submissions@scipost.org>', + [ed_assignment.to.user.email, 'submissions@scipost.org'], + reply_to=['submissions@scipost.org']) + emailmessage.send(fail_silently=False) + context = {} return render(request, 'submissions/assign_submission_ack.html', context) +@login_required @permission_required('scipost.can_take_charge_of_submissions', raise_exception=True) def accept_or_decline_assignments(request): contributor = Contributor.objects.get(user=request.user) @@ -270,15 +305,56 @@ def accept_or_decline_assignment_ack(request, assignment_id): assignment.to = contributor assignment.submission.status = 'EICassigned' assignment.submission.editor_in_charge = contributor + assignment.submission.open_for_reporting = True assignment.submission.reporting_deadline = deadline - assignment.submission.save() + assignment.submission.open_for_commenting = True else: assignment.accepted = False assignment.refusal_reason = form.cleaned_data['refusal_reason'] + assignment.submission.status = 'unassigned' assignment.save() + assignment.submission.save() + + context = {'assignment': assignment} + return render(request, 'submissions/accept_or_decline_assignment_ack.html', context) + +@permission_required('scipost.can_take_charge_of_submissions', raise_exception=True) +def volunteer_as_EIC(request, submission_id): + """ + Called when a Fellow volunteers while perusing the submissions queue. + This is an adapted version of the accept_or_decline_assignment_ack method. + """ + submission = get_object_or_404(Submission, pk=submission_id) + contributor = Contributor.objects.get(user=request.user) + assignment = EditorialAssignment(submission=submission, + to=contributor, + accepted=True, + date_created=timezone.now(), + date_answered=timezone.now()) + deadline = timezone.now() + datetime.timedelta(days=28) # for papers + if submission.submitted_to_journal == 'SciPost Physics Lecture Notes': + deadline += datetime.timedelta(days=28) + submission.status = 'EICassigned' + submission.editor_in_charge = contributor + submission.open_for_reporting = True + submission.reporting_deadline = deadline + submission.open_for_commenting = True + assignment.save() + submission.save() context = {'assignment': assignment} return render(request, 'submissions/accept_or_decline_assignment_ack.html', context) + + +@permission_required('scipost.can_take_charge_of_submissions', raise_exception=True) +def assignment_failed(request, submission_id): + """ + No Editorial Fellow has accepted or volunteered to become Editor-in-charge. + The submission is rejected. + This method is called from assign_submissions.html. + """ + submission = get_object_or_404(Submission, pk=submission_id) + # TODO @permission_required('scipost.can_take_charge_of_submissions', raise_exception=True) @@ -361,12 +437,36 @@ def send_refereeing_invitation(request, submission_id, contributor_id): referee=contributor, title=contributor.title, first_name=contributor.user.first_name, last_name=contributor.user.last_name, email_address=contributor.user.email, + invitation_key='notused', # the key is only used for inviting unregistered users date_invited=timezone.now(), - invited_by = request.user.contributor) + invited_by=request.user.contributor) invitation.save() + # Email Contributor + email_text = ('Dear ' + title_dict[contributor.title] + ' ' + + contributor.user.last_name + + ', \n\nWe have received a Submission to SciPost ' + 'which, in view of your expertise, we would like to invite you to referee:\n\n' + + submission.title + ' by ' + submission.author_list + + ' (see https://scipost.org/submission/' + submission_id + ').' + '\n\nPlease visit https://scipost.org/submissions/accept_or_decline_ref_invitations ' + '(login required) in order to accept or decline this invitation.' + '\n\nIf you accept, your report can be submitted by simply clicking on the "Contribute a Report" link at ' + 'https://scipost.org/submission/' + submission_id + ' before the reporting deadline ' + '(currently set at ' + datetime.datetime.strftime(submission.reporting_deadline, "%Y-%m-%d") + + '; your report will be automatically recognized as an invited report).' + '\n\nWe would be extremely grateful for your contribution, ' + 'and thank you in advance for your consideration.' + '\n\nThe SciPost Team.') + emailmessage = EmailMessage( + 'SciPost: refereeing request', email_text, + 'SciPost Editorial Admin <submissions@scipost.org>', + [contributor.user.email, 'submissions@scipost.org'], + reply_to=['submissions@scipost.org']) + emailmessage.send(fail_silently=False) return redirect(reverse('submissions:editorial_page', kwargs={'submission_id': submission_id})) +@login_required @permission_required('scipost.can_referee', raise_exception=True) def accept_or_decline_ref_invitations(request): contributor = Contributor.objects.get(user=request.user) @@ -414,7 +514,7 @@ def close_refereeing_round(request, submission_id): @login_required -def communication(request, submission_id, type, referee_id=None): +def communication(request, submission_id, comtype, referee_id=None): """ Communication between editor-in-charge, author or referee occurring during the submission refereeing. @@ -425,20 +525,20 @@ def communication(request, submission_id, type, referee_id=None): form = EditorialCommunicationForm(request.POST) if form.is_valid(): communication = EditorialCommunication(submission=submission, - type=type, + comtype=comtype, timestamp=timezone.now(), text=form.cleaned_data['text']) if referee_id is not None: referee = get_object_or_404(Contributor, pk=referee_id) communication.referee = referee communication.save() - if type == 'EtoA' or type == 'EtoR' or type == 'EtoS': + if comtype == 'EtoA' or comtype == 'EtoR' or comtype == 'EtoS': return redirect(reverse('submissions:editorial_page', kwargs={'submission_id': submission_id})) - elif type == 'AtoE' or type == 'RtoE' or type == 'StoE': + elif comtype == 'AtoE' or comtype == 'RtoE' or comtype == 'StoE': return redirect(reverse('scipost:personal_page')) else: form = EditorialCommunicationForm() - context = {'submission': submission, 'type': type, 'form': form} + context = {'submission': submission, 'comtype': comtype, 'form': form} return render(request, 'submissions/communication.html', context) -- GitLab