diff --git a/scipost/templates/scipost/personal_page.html b/scipost/templates/scipost/personal_page.html index 9d2c6a70dd2fafcb36deb25bb6cdc38004dd22f8..201f6bc75327f1df479eeaa9a8bfd494a5ba4f6c 100644 --- a/scipost/templates/scipost/personal_page.html +++ b/scipost/templates/scipost/personal_page.html @@ -40,7 +40,7 @@ {% endif %} {% if perms.scipost.can_referee %} <li class="nav-item btn btn-secondary"> - <a class="nav-link" data-toggle="tab" href="#refereeing">Refereeing</a> + <a class="nav-link" data-toggle="tab" href="#refereeing">Refereeing {% if nr_ref_inv_to_consider %}({{ nr_ref_inv_to_consider }}){% endif %}</a> </li> {% endif %} <li class="nav-item btn btn-secondary"> diff --git a/submissions/constants.py b/submissions/constants.py index d8d508b8d29c9a65d5f0b02057a9da1cd463fd2f..7929aeec9e1aa50afc56658d5e95960487adc2ea 100644 --- a/submissions/constants.py +++ b/submissions/constants.py @@ -29,15 +29,19 @@ SUBMISSION_STATUS = ( ('withdrawn', 'Withdrawn by the Authors'), ) -SUBMISSION_STATUS_OUT_OF_POOL = [ +SUBMISSION_HTTP404_ON_EDITORIAL_PAGE = [ 'assignment_failed', - 'resubmitted', 'published', 'withdrawn', 'rejected', 'rejected_visible', ] +SUBMISSION_STATUS_OUT_OF_POOL = SUBMISSION_HTTP404_ON_EDITORIAL_PAGE + [ + 'resubmitted' +] + + # Submissions which are allowed/required to submit a EIC Recommendation SUBMISSION_EIC_RECOMMENDATION_REQUIRED = [ STATUS_EIC_ASSIGNED, diff --git a/submissions/managers.py b/submissions/managers.py index 8fdfc9d7f22e682b56ea2ace42e1515df2b570b4..0019a4024d886d4a05b7dcc54271e9a25f4a0120 100644 --- a/submissions/managers.py +++ b/submissions/managers.py @@ -3,19 +3,41 @@ from django.db.models import Q from .constants import SUBMISSION_STATUS_OUT_OF_POOL, SUBMISSION_STATUS_PUBLICLY_UNLISTED,\ SUBMISSION_STATUS_PUBLICLY_INVISIBLE, STATUS_UNVETTED, STATUS_VETTED,\ - STATUS_UNCLEAR, STATUS_INCORRECT, STATUS_NOT_USEFUL, STATUS_NOT_ACADEMIC + STATUS_UNCLEAR, STATUS_INCORRECT, STATUS_NOT_USEFUL, STATUS_NOT_ACADEMIC,\ + SUBMISSION_HTTP404_ON_EDITORIAL_PAGE class SubmissionManager(models.Manager): - def get_pool(self, user): - return self.exclude(status__in=SUBMISSION_STATUS_OUT_OF_POOL)\ - .exclude(is_current=False)\ - .exclude(authors=user.contributor)\ + def user_filter(self, user): + """ + Prevent conflic of interest by filtering submissions possible related to user. + This filter should be inherited by other filters. + """ + return (self.exclude(authors=user.contributor) .exclude(Q(author_list__icontains=user.last_name), - ~Q(authors_false_claims=user.contributor))\ - .order_by('-submission_date') + ~Q(authors_false_claims=user.contributor))) + + def get_pool(self, user): + """ + This filter will return submission currently in an active submission cycle. + """ + return (self.user_filter(user) + .exclude(is_current=False) + .exclude(status__in=SUBMISSION_STATUS_OUT_OF_POOL) + .order_by('-submission_date')) + + def filter_editorial_page(self, user): + """ + This filter returns a subgroup of the `get_pool` filter, to allow opening and editing + certain submissions that are officially out of the submission cycle i.e. due + to resubmission, but should still have the possibility to be opened by the EIC. + """ + return (self.user_filter(user) + .exclude(status__in=SUBMISSION_HTTP404_ON_EDITORIAL_PAGE) + .order_by('-submission_date')) def public(self): + '''List only all public submissions. Should be used as a default filter!''' return self.exclude(status__in=SUBMISSION_STATUS_PUBLICLY_UNLISTED) def public_overcomplete(self): diff --git a/submissions/templates/submissions/accept_or_decline_ref_invitations.html b/submissions/templates/submissions/accept_or_decline_ref_invitations.html index 12465e7f61e2510bd50d5b474c0dad6df54fe8c5..932ecaa432168d1e5e9369b07dd5d572c23e56fe 100644 --- a/submissions/templates/submissions/accept_or_decline_ref_invitations.html +++ b/submissions/templates/submissions/accept_or_decline_ref_invitations.html @@ -2,56 +2,50 @@ {% block pagetitle %}: accept or decline refereeing invitations{% endblock pagetitle %} -{% block bodysup %} +{% load bootstrap %} + +{% block content %} <script> $(document).ready(function(){ - $('#ref_reason').hide(); - - $('#id_accept').on('change', function() { - if ($('#id_accept_1').is(':checked')) { - $('#ref_reason').show(); + $('[name="accept"]').on('change', function() { + if($('[name="accept"]:checked').val() == 'False') { + $('#id_refusal_reason').parents('.form-group').show(); } else { - $('#ref_reason').hide(); + $('#id_refusal_reason').parents('.form-group').hide(); } - }); + }).trigger('change'); }); </script> - -<section> - {% if not invitation_to_consider %} - <h1>There are no Refereeing Invitations for you to consider.</h1> - - {% else %} - - <div class="flex-greybox"> - <h1>SciPost Submission which you are asked to Referee (see below to accept/decline):</h1> - </div> - <br> - <hr> - {% include 'submissions/_submission_summary.html' with submission=invitation_to_consider.submission %} - - <br/> - <hr> - <div class="flex-greybox"> - <h1>Accept or Decline this Refereeing Invitation</h1> - </div> - <h3>Please let us know if you can provide us with a Report for this Submission:</h3> - <form action="{% url 'submissions:accept_or_decline_ref_invitation_ack' invitation_id=invitation_to_consider.id %}" method="post"> - {% csrf_token %} - {{ form.accept }} - <div id="ref_reason"> - <p>Please select a reason for declining this invitation:</p> - {{ form.refusal_reason }} +{% if not invitation_to_consider %} + <div class="row"> + <div class="col-12"> + <h1>There are no Refereeing Invitations for you to consider.</h1> + </div> </div> - <input type="submit" value="Submit" /> - </form> - - {% endif %} -</section> +{% else %} + <div class="row"> + <div class="col-12"> + <h1 class="highlight">SciPost Submission which you are asked to Referee (see below to accept/decline):</h1> + {% include 'submissions/_submission_summary.html' with submission=invitation_to_consider.submission %} + </div> + </div> + <div class="row"> + <div class="col-12"> + <h2 class="highlight">Accept or Decline this Refereeing Invitation</h2> + <h3>Please let us know if you can provide us with a Report for this Submission:</h3> + <p class="text-muted">We will expect your report by <b>{{invitation_to_consider.submission.reporting_deadline}}</b></p> + <form action="{% url 'submissions:accept_or_decline_ref_invitation_ack' invitation_id=invitation_to_consider.id %}" method="post"> + {% csrf_token %} + {{ form|bootstrap }} + <input type="submit" class="btn btn-secondary" value="Submit" /> + </form> + </div> + </div> +{% endif %} -{% endblock bodysup %} +{% endblock content %} diff --git a/submissions/urls.py b/submissions/urls.py index d23178730644d792914e5c62c16567da4b44caef..042f2f393d35300d05d06f8433619dfde5aae436 100644 --- a/submissions/urls.py +++ b/submissions/urls.py @@ -50,7 +50,7 @@ urlpatterns = [ views.recruit_referee, name='recruit_referee'), url(r'^send_refereeing_invitation/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})/(?P<contributor_id>[0-9]+)$', views.send_refereeing_invitation, name='send_refereeing_invitation'), - url(r'^accept_or_decline_ref_invitations$', + 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'), diff --git a/submissions/utils.py b/submissions/utils.py index 8e680d8cd9e4a0c34f429540bc545aeee5a60dad..8672905d722ddfe055835195229a122318458d1e 100644 --- a/submissions/utils.py +++ b/submissions/utils.py @@ -852,64 +852,25 @@ class SubmissionUtils(BaseMailUtil): @classmethod def email_referee_response_to_EIC(cls): - """ Requires loading 'invitation' attribute. """ - email_text = ('Dear ' + cls.invitation.submission.editor_in_charge.get_title_display() + ' ' + - cls.invitation.submission.editor_in_charge.user.last_name + ',' - '\n\nReferee ' + cls.invitation.referee.get_title_display() + ' ' + - cls.invitation.referee.user.last_name + ' has ') - email_text_html = ( - '<p>Dear {{ EIC_title }} {{ EIC_last_name }},</p>' - '<p>Referee {{ ref_title }} {{ ref_last_name }} has ') - email_subject = 'SciPost: referee declines to review' - if cls.invitation.accepted: - email_text += 'accepted ' - email_text_html += 'accepted ' - email_subject = 'SciPost: referee accepts to review' - elif not cls.invitation.accepted: - email_text += ('declined (due to reason: ' - + cls.invitation.get_refusal_reason_display() + ') ') - email_text_html += 'declined (due to reason: {{ reason }}) ' - - email_text += ('to referee Submission\n\n' - + cls.invitation.submission.title + ' by ' - + cls.invitation.submission.author_list + '.') - email_text_html += ( - 'to referee Submission</p>' - '<p>{{ sub_title }}</p>\n<p>by {{ author_list }}.</p>') - if not cls.invitation.accepted: - email_text += ('\n\nPlease invite another referee from the Submission\'s editorial page ' - 'at https://scipost.org/submissions/editorial_page/' - + cls.invitation.submission.arxiv_identifier_w_vn_nr + '.') - email_text_html += ( - '\n<p>Please invite another referee from the Submission\'s ' - '<a href="https://scipost.org/submissions/editorial_page/' - '{{ arxiv_identifier_w_vn_nr }}">editorial page</a>.</p>') - email_text += ('\n\nMany thanks for your collaboration,' - '\n\nThe SciPost Team.') - email_text_html += ('<p>Many thanks for your collaboration,</p>' - '<p>The SciPost Team.</p>') - email_context = Context({ - 'EIC_title': cls.invitation.submission.editor_in_charge.get_title_display(), - 'EIC_last_name': cls.invitation.submission.editor_in_charge.user.last_name, - 'ref_title': cls.invitation.referee.get_title_display(), - 'ref_last_name': cls.invitation.referee.user.last_name, - 'sub_title': cls.invitation.submission.title, - 'author_list': cls.invitation.submission.author_list, - 'arxiv_identifier_w_vn_nr': cls.invitation.submission.arxiv_identifier_w_vn_nr, - }) - if cls.invitation.refusal_reason: - email_context['reason'] = cls.invitation.get_refusal_reason_display - email_text_html += '<br/>' + EMAIL_FOOTER - html_template = Template(email_text_html) - html_version = html_template.render(email_context) - emailmessage = EmailMultiAlternatives( - email_subject, email_text, - 'SciPost Editorial Admin <submissions@scipost.org>', - [cls.invitation.submission.editor_in_charge.user.email], - bcc=['submissions@scipost.org'], - reply_to=['submissions@scipost.org']) - emailmessage.attach_alternative(html_version, 'text/html') - emailmessage.send(fail_silently=False) + '''Requires loading `invitation` attribute.''' + if cls._context['invitation'].accepted: + email_subject = 'referee accepts to review' + else: + email_subject = 'referee declines to review' + cls._send_mail(cls, 'referee_response_to_EIC', + [cls._context['invitation'].submission.editor_in_charge.user.email], + email_subject) + + @classmethod + def email_referee_in_response_to_decision(cls): + '''Requires loading `invitation` attribute.''' + if cls._context['invitation'].accepted: + email_subject = 'confirmation accepted invitation' + else: + email_subject = 'confirmation declined invitation' + cls._send_mail(cls, 'referee_in_response_to_decision', + [cls._context['invitation'].referee.user.email], + email_subject) @classmethod def email_EIC_report_delivered(cls): diff --git a/submissions/views.py b/submissions/views.py index e245596456aa25d037d606ab2f456deb25be910c..8f9643fcca79426eee43f68c4ca407c7d6878725 100644 --- a/submissions/views.py +++ b/submissions/views.py @@ -614,14 +614,14 @@ def assignments(request): @permission_required_or_403('can_take_editorial_actions', (Submission, 'arxiv_identifier_w_vn_nr', 'arxiv_identifier_w_vn_nr')) def editorial_page(request, arxiv_identifier_w_vn_nr): - submission = get_object_or_404(Submission.objects.get_pool(request.user), + submission = get_object_or_404(Submission.objects.filter_editorial_page(request.user), arxiv_identifier_w_vn_nr=arxiv_identifier_w_vn_nr) other_versions = (Submission.objects .filter(arxiv_identifier_wo_vn_nr=submission.arxiv_identifier_wo_vn_nr) .exclude(pk=submission.id)) ref_invitations = RefereeInvitation.objects.filter(submission=submission) nr_reports_to_vet = (Report.objects - .filter(status=0, submission__editor_in_charge=request.user.contributor) + .filter(status=0, submission=submission) .count()) communications = (EditorialCommunication.objects .filter(submission=submission).order_by('timestamp')) @@ -814,8 +814,7 @@ def ref_invitation_reminder(request, arxiv_identifier_w_vn_nr, invitation_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) - invitation = RefereeInvitation.objects.filter(referee=contributor, accepted=None).first() + invitation = RefereeInvitation.objects.filter(referee__user=request.user, accepted=None)[0] form = ConsiderRefereeInvitationForm() context = {'invitation_to_consider': invitation, 'form': form} return render(request, 'submissions/accept_or_decline_ref_invitations.html', context) @@ -825,18 +824,18 @@ def accept_or_decline_ref_invitations(request): @permission_required('scipost.can_referee', raise_exception=True) def accept_or_decline_ref_invitation_ack(request, invitation_id): invitation = get_object_or_404(RefereeInvitation, pk=invitation_id) - if request.method == 'POST': - form = ConsiderRefereeInvitationForm(request.POST) - if form.is_valid(): - invitation.date_responded = timezone.now() - if form.cleaned_data['accept'] == 'True': - invitation.accepted = True - else: - invitation.accepted = False - invitation.refusal_reason = form.cleaned_data['refusal_reason'] - invitation.save() - SubmissionUtils.load({'invitation': invitation}) - SubmissionUtils.email_referee_response_to_EIC() + form = ConsiderRefereeInvitationForm(request.POST or None) + if form.is_valid(): + invitation.date_responded = timezone.now() + if form.cleaned_data['accept'] == 'True': + invitation.accepted = True + else: + invitation.accepted = False + invitation.refusal_reason = form.cleaned_data['refusal_reason'] + invitation.save() + SubmissionUtils.load({'invitation': invitation}) + SubmissionUtils.email_referee_response_to_EIC() + SubmissionUtils.email_referee_in_response_to_decision() context = {'invitation': invitation} return render(request, 'submissions/accept_or_decline_ref_invitation_ack.html', context) diff --git a/templates/email/referee_in_response_to_decision.html b/templates/email/referee_in_response_to_decision.html new file mode 100644 index 0000000000000000000000000000000000000000..563f0956c294831980e22356293ffa4d6432d5f2 --- /dev/null +++ b/templates/email/referee_in_response_to_decision.html @@ -0,0 +1,12 @@ +Dear {{invitation.referee.get_title_display}} {{invitation.referee.user.last_name}},\n\n + +We hereby confirm your choice to {% if invitation.accepted %}accept{% else %}decline (due to reason: {{invitation.get_refusal_reason_display}}){% endif %} to referee Submission\n\n + +{{invitation.submission.title}} by {{invitation.submission.author_list}}\n\n + +{% if invitation.accepted %} +Many thanks for your collaboration,\n +{% else %} +Nonetheless, we thank you very much for considering this refereeing invitation,\n +{% endif %} +The SciPost Team. diff --git a/templates/email/referee_in_response_to_decision_html.html b/templates/email/referee_in_response_to_decision_html.html new file mode 100644 index 0000000000000000000000000000000000000000..3df02ac1a9987ff481ee7f9b390e7fea89ee0218 --- /dev/null +++ b/templates/email/referee_in_response_to_decision_html.html @@ -0,0 +1,19 @@ +<h3>Dear {{invitation.referee.get_title_display}} {{invitation.referee.user.last_name}},</h3> + +<p> + We hereby confirm your choice to {% if invitation.accepted %}accepte{% else %}decline (due to reason: {{invitation.get_refusal_reason_display}}){% endif %} to referee Submission +</p> +<p> + "<span style="color: grey;">{{invitation.submission.title}} by {{invitation.submission.author_list}}</span>". +</p> + +<p> + {% if invitation.accepted %} + Many thanks for your collaboration, + {% else %} + Nonetheless, we thank you very much for considering this refereeing invitation, + {% endif %} + The SciPost Team. +</p> + +{% include 'email/_footer.html' %} diff --git a/templates/email/referee_response_to_EIC.html b/templates/email/referee_response_to_EIC.html new file mode 100644 index 0000000000000000000000000000000000000000..e4c185bf57304fb41a0b26488e1a991a4982e3f9 --- /dev/null +++ b/templates/email/referee_response_to_EIC.html @@ -0,0 +1,12 @@ +Dear {{invitation.submission.editor_in_charge.get_title_display}} {{invitation.submission.editor_in_charge.user.last_name}},\n\n + +Referee {{invitation.referee.get_title_display}} {{invitation.referee.user.last_name}} has {% if invitation.accepted %}accepted{% else %}declined (due to reason: {{invitation.get_refusal_reason_display}}){% endif %} to referee Submission\n\n + +{{invitation.submission.title}} by {{invitation.submission.author_list}}\n\n + +{% if not invitation.accepted %} +Please invite another referee from the Submission\'s editorial page at {% url 'submissions:editorial_page' invitation.submission.arxiv_identifier_w_vn_nr %}.\n\n +{% endif %} + +Many thanks for your collaboration,\n +The SciPost Team. diff --git a/templates/email/referee_response_to_EIC_html.html b/templates/email/referee_response_to_EIC_html.html new file mode 100644 index 0000000000000000000000000000000000000000..2c50f00c02770229387d6797769ecad7de11b1a2 --- /dev/null +++ b/templates/email/referee_response_to_EIC_html.html @@ -0,0 +1,21 @@ +<h3>Dear {{invitation.submission.editor_in_charge.get_title_display}} {{invitation.submission.editor_in_charge.user.last_name}},</h3> + +<p> + Referee {{invitation.referee.get_title_display}} {{invitation.referee.user.last_name}} has {% if invitation.accepted %}accepted{% else %}declined (due to reason: {{invitation.get_refusal_reason_display}}){% endif %} to referee Submission +</p> +<p> + "<span style="color: grey;">{{invitation.submission.title}} by {{invitation.submission.author_list}}</span>". +</p> + +{% if not invitation.accepted %} + <p> + Please invite another referee from the Submission's <a href="{% url 'submissions:editorial_page' invitation.submission.arxiv_identifier_w_vn_nr %}">editorial page</a>. + </p> +{% endif %} + +<p> + Many thanks for your collaboration, + The SciPost Team. +</p> + +{% include 'email/_footer.html' %}