diff --git a/journals/templates/journals/sign_existing_report.html b/journals/templates/journals/sign_existing_report.html index 99b8db35f47f12abc642bc14ddad4f0b96eb5a78..422659cd787f3887bd2c3ca8d230a118a83a33f1 100644 --- a/journals/templates/journals/sign_existing_report.html +++ b/journals/templates/journals/sign_existing_report.html @@ -4,30 +4,46 @@ {% block pagetitle %}: sign existing Report{% endblock pagetitle %} +{% block breadcrumb %} +<div class="container-outside breadcrumb-nav"> + <div class="container"> + <nav class="breadcrumb"> + <a href="{% url 'submissions:submissions' %}" class="breadcrumb-item">Submissions</a> + <a href="{{ report.submission.get_absolute_url }}" class="breadcrumb-item">{{ report.submission.arxiv_identifier_w_vn_nr }}</a> + <span class="breadcrumb-item">Sign existing Report</span> + + </nav> + </div> +</div> +{% endblock %} + {% block content %} <div class="row"> - <div class="col-12"> - <hr class="hr12"> - <div class="row"> - <div class="col-6"> - <h2>Confirmation page: do you wish to sign this Report?</h2> - <h3>(your Report is reproduced below for your convenience)</h3> - </div> - <div class="col-6"> - <form action="{% url 'journals:sign_existing_report' report_id=report.id %}" method="post"> - {% csrf_token %} - {{ form|bootstrap }} - <input class="btn btn-secondary" type="submit" value="Submit" /> - </form> - </div> + <div class="col-12"> + <h1 class="highlight">Sign existing Report</h1> </div> +</div> - <h3>Report on Submission <a href="{{report.submission.get_absolute_url}}">{{report.submission.title}}</a></h3> - - {% include 'submissions/_single_public_report_without_comments.html' with report=report user=request.user perms=perms %} +<div class="row"> + <div class="col-md-6"> + <h2>Confirmation page: do you wish to sign this Report?</h2> + <h3>(your Report is reproduced below for your convenience)</h3> + </div> + <div class="col-md-6"> + <form action="{% url 'journals:sign_existing_report' report_id=report.id %}" method="post"> + {% csrf_token %} + {{ form|bootstrap }} + <input class="btn btn-secondary" type="submit" value="Submit" /> + </form> + </div> +</div> - </div> +<div class="row"> + <div class="col-12"> + <h3>Report on Submission <a href="{{report.submission.get_absolute_url}}">{{report.submission.title}}</a></h3> + {% include 'submissions/_single_public_report_without_comments.html' with report=report user=request.user perms=perms %} + </div> </div> {% endblock %} diff --git a/scipost/static/scipost/assets/config/preconfig.scss b/scipost/static/scipost/assets/config/preconfig.scss index 8955cee5caa9d80c108c59ef83368d67bb58cc40..a9b5266ee232496003ad1e192a0c62ba668a0b79 100644 --- a/scipost/static/scipost/assets/config/preconfig.scss +++ b/scipost/static/scipost/assets/config/preconfig.scss @@ -17,13 +17,6 @@ $alert-padding-x: 0.75rem; // Grid // $grid-gutter-width: 20px; -// $container-max-widths: ( -// xs: , -// sm: 728px, -// md: 720px, -// lg: 960px, -// xl: 1140px -// ); // Colors // @@ -66,7 +59,6 @@ $alert-border-radius: $base-border-radius; // Cards // $card-border-radius: $base-border-radius; -// $card-bg: $gray-200; $card-border-color: $gray-200; $card-spacer-x: 0.75rem; $card-spacer-y: 0.5rem; diff --git a/scipost/static/scipost/assets/css/_list_group.scss b/scipost/static/scipost/assets/css/_list_group.scss index 4755ec289813f41a3be705df5c68db980d203c26..fda5fa2d7c7d4f0cbe84677eff66e8cf0a505092 100644 --- a/scipost/static/scipost/assets/css/_list_group.scss +++ b/scipost/static/scipost/assets/css/_list_group.scss @@ -23,3 +23,7 @@ ul.events-list { } } } + +.fa-li { + line-height: 1.25 !important; +} diff --git a/scipost/templates/scipost/personal_page.html b/scipost/templates/scipost/personal_page.html index b25b0af7c6e727a2b4ad08da84527aedd230e09f..a52677412895330839337f2beeec6e73d2549ad3 100644 --- a/scipost/templates/scipost/personal_page.html +++ b/scipost/templates/scipost/personal_page.html @@ -561,10 +561,14 @@ <tr> <th>Status:</th><td {% if report.status == 'vetted' %}class="text-success"{% elif report.status == 'unvetted' %}class="text-danger"{% endif %}>{{report.get_status_display}}</td> </tr> - {% if report.doi_label %} - <tr> - <th>DOI:</th><td>{{ report.doi_string }}</td></th> - {% endif %} + <tr> + <th>Type:</th><td>{{ report.invited|yesno:'Invited,Contributed' }} Report</td> + </tr> + {% if report.doi_label %} + <tr> + <th>DOI:</th><td>{{ report.doi_string }}</td> + </tr> + {% endif %} <tr> <th>Anonymous:</th><td>{{report.anonymous|yesno:'Yes,No'}}</td>{% if report.anonymous %}<td>You can <a href="{% url 'journals:sign_existing_report' report_id=report.id %}">click here to sign this Report</a> (leads to confirmation page){% endif %}</td> </tr> diff --git a/submissions/constants.py b/submissions/constants.py index b360f7f6f3c74e164d86266b63dc11c7e165ce91..dd5759b79595f5e1a916540b727ee9bb99ab9f18 100644 --- a/submissions/constants.py +++ b/submissions/constants.py @@ -113,7 +113,7 @@ ED_COMM_CHOICES = ( ('EtoR', 'Editor-in-charge to Referee'), ('EtoS', 'Editor-in-charge to SciPost Editorial Administration'), ('AtoE', 'Author to Editor-in-charge'), - ('RtoE', 'Referee to Editor-in-Charge'), + ('RtoE', 'Referee to Editor-in-charge'), ('StoE', 'SciPost Editorial Administration to Editor-in-charge'), ) diff --git a/submissions/forms.py b/submissions/forms.py index c13adbe6e338f45fe1a42840c05e0d26ca5ec337..0e3f6cd0c0803ec5243259bfb67a729543ca755b 100644 --- a/submissions/forms.py +++ b/submissions/forms.py @@ -594,10 +594,9 @@ class ReportForm(forms.ModelForm): report.status = STATUS_UNVETTED # Update invitation and report meta data if exist - invitation = submission.referee_invitations.filter(referee=report.author).first() - if invitation: - invitation.fulfilled = True - invitation.save() + updated_invitations = submission.referee_invitations.filter( + referee=report.author).update(fulfilled=True) + if updated_invitations > 0: report.invited = True # Check if report author if the report is being flagged on the submission diff --git a/submissions/managers.py b/submissions/managers.py index 021daa46a5a214d085a4a1faa3ba7890e14b3ff7..20b455d39e4a976872434f2fe05a23abdd23c572 100644 --- a/submissions/managers.py +++ b/submissions/managers.py @@ -320,6 +320,9 @@ class ReportQuerySet(models.QuerySet): class RefereeInvitationQuerySet(models.QuerySet): + def awaiting_response(self): + return self.pending().open() + def pending(self): return self.filter(accepted=None) @@ -348,3 +351,8 @@ class RefereeInvitationQuerySet(models.QuerySet): deadline = now qs = qs.filter(submission__reporting_deadline__lte=deadline) return qs + + +class EditorialCommunicationQueryset(models.QuerySet): + def for_referees(self): + return self.filter(comtype__in=['EtoR', 'RtoE']) diff --git a/submissions/migrations/0003_auto_20180120_1102.py b/submissions/migrations/0003_auto_20180120_1102.py new file mode 100644 index 0000000000000000000000000000000000000000..1f921da764c7b7ba6b30595a24a7587555787853 --- /dev/null +++ b/submissions/migrations/0003_auto_20180120_1102.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-01-20 10:02 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('submissions', '0002_auto_20180118_1033'), + ] + + operations = [ + migrations.AlterField( + model_name='editorialcommunication', + name='comtype', + field=models.CharField(choices=[('EtoA', 'Editor-in-charge to Author'), ('EtoR', 'Editor-in-charge to Referee'), ('EtoS', 'Editor-in-charge to SciPost Editorial Administration'), ('AtoE', 'Author to Editor-in-charge'), ('RtoE', 'Referee to Editor-in-charge'), ('StoE', 'SciPost Editorial Administration to Editor-in-charge')], max_length=4), + ), + migrations.AlterField( + model_name='editorialcommunication', + name='referee', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='editorial_communications', to='scipost.Contributor'), + ), + ] diff --git a/submissions/models.py b/submissions/models.py index 5a526bf214336075cb7e142fe344827efa6a0f59..953b6306933a01e8847b77892e2e86535636c9ed 100644 --- a/submissions/models.py +++ b/submissions/models.py @@ -14,9 +14,11 @@ from .constants import ASSIGNMENT_REFUSAL_REASONS, ASSIGNMENT_NULLBOOL,\ RANKING_CHOICES, REPORT_REC, SUBMISSION_STATUS, STATUS_UNASSIGNED,\ REPORT_STATUSES, STATUS_UNVETTED, SUBMISSION_EIC_RECOMMENDATION_REQUIRED,\ SUBMISSION_CYCLES, CYCLE_DEFAULT, CYCLE_SHORT, CYCLE_DIRECT_REC,\ - EVENT_GENERAL, EVENT_TYPES, EVENT_FOR_AUTHOR, EVENT_FOR_EIC + EVENT_GENERAL, EVENT_TYPES, EVENT_FOR_AUTHOR, EVENT_FOR_EIC,\ + STATUS_DRAFT, STATUS_VETTED from .managers import SubmissionQuerySet, EditorialAssignmentQuerySet, EICRecommendationQuerySet,\ - ReportQuerySet, SubmissionEventQuerySet, RefereeInvitationQuerySet + ReportQuerySet, SubmissionEventQuerySet, RefereeInvitationQuerySet,\ + EditorialCommunicationQueryset from .utils import ShortSubmissionCycle, DirectRecommendationSubmissionCycle,\ GeneralSubmissionCycle @@ -360,6 +362,10 @@ class RefereeInvitation(SubmissionRelatedObjectMixin, models.Model): def notification_name(self): return self.submission.arxiv_identifier_w_vn_nr + @property + def related_report(self): + return self.submission.reports.filter(author=self.referee).first() + def reset_content(self): self.nr_reminders = 0 self.date_last_reminded = None @@ -452,6 +458,14 @@ class Report(SubmissionRelatedObjectMixin, models.Model): return (self.author.user.first_name + ' ' + self.author.user.last_name + ' on ' + self.submission.title[:50] + ' by ' + self.submission.author_list[:50]) + @property + def is_in_draft(self): + return self.status == STATUS_DRAFT + + @property + def is_vetted(self): + return self.status == STATUS_VETTED + def save(self, *args, **kwargs): # Control Report count per Submission. if not self.report_nr: @@ -512,16 +526,18 @@ class EditorialCommunication(SubmissionRelatedObjectMixin, models.Model): Each individual communication between Editor-in-charge to and from Referees and Authors becomes an instance of this class. """ - submission = models.ForeignKey('submissions.Submission', on_delete=models.CASCADE, - related_name='editorial_communications') - referee = models.ForeignKey('scipost.Contributor', related_name='referee_in_correspondence', - blank=True, null=True, on_delete=models.CASCADE) + submission = models.ForeignKey('submissions.Submission', on_delete=models.CASCADE) + referee = models.ForeignKey('scipost.Contributor', on_delete=models.CASCADE, + blank=True, null=True) comtype = models.CharField(max_length=4, choices=ED_COMM_CHOICES) timestamp = models.DateTimeField(default=timezone.now) text = models.TextField() + objects = EditorialCommunicationQueryset.as_manager() + class Meta: ordering = ['timestamp'] + default_related_name = 'editorial_communications' def __str__(self): output = self.comtype diff --git a/submissions/templates/partials/submissions/refereeing_status_card.html b/submissions/templates/partials/submissions/refereeing_status_card.html new file mode 100644 index 0000000000000000000000000000000000000000..1ab7c4136cf2b24e3c486d3f632873af9050f0cd --- /dev/null +++ b/submissions/templates/partials/submissions/refereeing_status_card.html @@ -0,0 +1,79 @@ + +<div class="card border-warning"> + <div class="card-body"> + {% if invitation.accepted is None %} + <h3 class="card-title">Referee Invitation response pending</h3> + <p class="card-text"> + In view of your expertise and on behalf of the Editor-in-charge we would like to invite you to referee this Submission. Please accept or decline this invitation. + We would be extremely grateful for your contribution, and thank you in advance for your consideration. + </p> + <a href="{% url 'submissions:accept_or_decline_ref_invitations' invitation.id %}" class="card-link">Accept or decline here</a> + {% elif invitation.accepted %} + <h3 class="card-title">Referee Invitation</h3> + <p class="card-text">Thank you for agreeing to referee this Submission. The following checklist will guide you through the steps needed to complete your refereeing task.</p> + <ul class="fa-ul"> + <li><i class="fa-li fa fa-check-square-o"></i>Accepted Invitation.</li> + + <li> + {% if invitation.fulfilled %} + <i class="fa-li fa fa-check-square-o"></i>Submitted Report on {{ invitation.related_report.date_submitted }}. + <ul class="list-style-none"> + <li>Status: <span class="{% if invitation.related_report.status == 'vetted' %}text-success{% elif invitation.related_report.status == 'unvetted' %}text-danger{% endif %}">{{ invitation.related_report.get_status_display }}</span></li> + <li>Anonymous: {{ invitation.related_report.anonymous|yesno:'Yes,No' }}</li> + {% if invitation.related_report.doi_label %}<li>DOI: {{ invitation.related_report.doi_string }}</li>{% endif %} + </ul> + {% else %} + <i class="fa-li fa fa-square-o"></i> + {% if invitation.related_report.is_in_draft %} + You have a Report in draft, <a href="{% url 'submissions:submit_report' invitation.submission.arxiv_identifier_w_vn_nr %}">finish your Report</a>. + {% else %} + <a href="{% url 'submissions:submit_report' invitation.submission.arxiv_identifier_w_vn_nr %}">Submit your Report</a>. + {% endif %} + {% endif %} + </li> + <li> + {% if invitation.related_report.is_vetted %} + <i class="fa-li fa fa-check-square-o"></i>Report vetted by Editor-in-charge. + {% else %} + <i class="fa-li fa fa-square-o"></i>Report vetted by Editor-in-charge. + {% endif %} + </li> + </ul> + + {% if invitation.related_report.anonymous %} + <p class="card-text">You have anonymously submitted your Report. <a href="{% url 'journals:sign_existing_report' report_id=invitation.related_report.id %}">You can click here to sign this Report</a> (leads to confirmation page).</p> + {% endif %} + + {% if invitation.submission.editor_in_charge %} + <h3 class="card-title">Communication</h3> + <a class="card-link" href="{% url 'submissions:communication' invitation.submission.arxiv_identifier_w_vn_nr 'RtoE' %}">Write to the Editor-in-charge</a> + + <ul class="pl-4 mt-2"> + {% for comm in communication %} + <li> + <span class="font-weight-bold"> + {% if comm.comtype == 'RtoE' %} + From {{ comm.referee.user.first_name }} {{ comm.referee.user.last_name }} to Editor-in-charge + {% elif comm.comtype == 'EtoR' %} + From Editor-in-charge to + {% if comm.referee %} + {{ comm.referee.user.first_name }} {{ comm.referee.user.last_name }} + {% endif %} + {% endif %} + </span> + <small class="d-inline-block text-muted text-sm">on {{ comm.timestamp }}</small> + + <p class="card-text">{{ comm.text }}</p> + </li> + {% empty %} + <li>There have been no communications for this Submission.</li> + {% endfor %} + </ul> + {% endif %} + {% else %} + <h3 class="card-title">Referee Invitation</h3> + <p class="card-text">You have declined to contribute a Report. Nonetheless, we thank you very much for considering this refereeing invitation.</p> + <p class="card-text">Reason: {{ invitation.get_refusal_reason_display }}</p> + {% endif %} + </div> +</div> diff --git a/submissions/templates/submissions/submission_detail.html b/submissions/templates/submissions/submission_detail.html index a98239cdfab58ee3005ba7820c2c6c8ee2eb8b6d..634cb01892954d79d85ac96e76ac0f58f5886dbd 100644 --- a/submissions/templates/submissions/submission_detail.html +++ b/submissions/templates/submissions/submission_detail.html @@ -22,7 +22,7 @@ {% block content %} <div class="row"> - <div class="col-12"> + <div class="col"> <h2>SciPost Submission Page</h2> <h1 class="text-primary">{{submission.title}}</h1> <h3 class="mb-3">by {{submission.author_list}}</h3> @@ -59,11 +59,6 @@ {% endif %} </div> - </div> -</div> - -<div class="row"> - <div class="col-12"> <h3>Submission summary</h3> {% include 'submissions/_submission_summary.html' with submission=submission hide_title=1 %} @@ -83,6 +78,14 @@ <div class="blockquote">{{ submission.list_of_changes|linebreaks }}</div> {% endif %} </div> + + {% if invitations %} + <div class="col-md-4"> + {% for invitation in invitations %} + {% include 'partials/submissions/refereeing_status_card.html' with invitation=invitation %} + {% endfor %} + </div> + {% endif %} </div> {% if is_author or user|is_in_group:'Editorial College' or user|is_in_group:'Editorial Administrators' %} diff --git a/submissions/views.py b/submissions/views.py index 341ca6f10aa8826dcfabd730348687804e5b1fdc..c485c2ccc7bc58f15b76b7336a529ef33640ec8f 100644 --- a/submissions/views.py +++ b/submissions/views.py @@ -211,6 +211,12 @@ def submission_detail(request, arxiv_identifier_w_vn_nr): author_replies = (submission.comments.vetted() .filter(is_author_reply=True).order_by('-date_submitted')) + # User is referee for the Submission + invitations = submission.referee_invitations.filter(referee__user=request.user) + if invitations: + context['communication'] = submission.editorial_communications.for_referees().filter( + referee__user=request.user) + recommendations = submission.eicrecommendations.all() context.update({ @@ -224,6 +230,7 @@ def submission_detail(request, arxiv_identifier_w_vn_nr): 'form': form, 'is_author': is_author, 'is_author_unchecked': is_author_unchecked, + 'invitations': invitations, }) return render(request, 'submissions/submission_detail.html', context) @@ -890,8 +897,7 @@ def accept_or_decline_ref_invitations(request, invitation_id=None): RefereeInvitations need to be either accepted or declined by the invited user using this view. The decision will be taken one invitation at a time. """ - invitation = RefereeInvitation.objects.filter( - referee__user=request.user, accepted=None, cancelled=False) + invitation = RefereeInvitation.objects.awaiting_response().filter(referee__user=request.user) if invitation_id: try: invitation = invitation.get(id=invitation_id) @@ -919,7 +925,7 @@ def accept_or_decline_ref_invitations(request, invitation_id=None): invitation.accepted = False decision_string = 'declined' invitation.refusal_reason = form.cleaned_data['refusal_reason'] - messages.success(request, ('<h1>You have declined to contribute a Report</h1>' + messages.success(request, ('<h3>You have declined to contribute a Report</h3>' 'Nonetheless, we thank you very much for considering' ' this refereeing invitation.</p>')) invitation.save() @@ -933,7 +939,10 @@ def accept_or_decline_ref_invitations(request, invitation_id=None): invitation.submission.add_event_for_eic('Referee %s has %s the refereeing invitation.' % (invitation.referee.user.last_name, decision_string)) - return redirect('submissions:accept_or_decline_ref_invitations') + + if request.user.contributor.referee_invitations.awaiting_response().exists(): + return redirect('submissions:accept_or_decline_ref_invitations') + return redirect(invitation.submission.get_absolute_url()) form = ConsiderRefereeInvitationForm() context = { 'invitation': invitation, @@ -1141,8 +1150,10 @@ def communication(request, arxiv_identifier_w_vn_nr, comtype, referee_id=None): if comtype == 'EtoA' or comtype == 'EtoR' or comtype == 'EtoS': return redirect(reverse('submissions:editorial_page', kwargs={'arxiv_identifier_w_vn_nr': arxiv_identifier_w_vn_nr})) - elif comtype == 'AtoE' or comtype == 'RtoE': + elif comtype == 'AtoE': return redirect(reverse('scipost:personal_page')) + elif comtype == 'RtoE': + return redirect(submission.get_absolute_url()) elif comtype == 'StoE': return redirect(reverse('submissions:pool')) @@ -1310,7 +1321,7 @@ def submit_report(request, arxiv_identifier_w_vn_nr): % request.user.last_name) messages.success(request, 'Thank you for your Report') - return redirect(reverse('scipost:personal_page')) + return redirect(submission.get_absolute_url()) context = {'submission': submission, 'form': form} return render(request, 'submissions/submit_report.html', context)