diff --git a/submissions/admin.py b/submissions/admin.py index 1559b0b3ccb149c02bface831a595dc9ca1990bd..a4161b29c442cb8d772526afe5a0143eb5f5f3a5 100644 --- a/submissions/admin.py +++ b/submissions/admin.py @@ -10,7 +10,7 @@ from guardian.admin import GuardedModelAdmin from submissions.models import ( Submission, EditorialAssignment, RefereeInvitation, Report, EditorialCommunication, - EICRecommendation, SubmissionTiering, AlternativeRecommendation, + EICRecommendation, SubmissionTiering, AlternativeRecommendation, EditorialDecision, SubmissionEvent, iThenticateReport) from scipost.models import Contributor @@ -268,4 +268,18 @@ class EICRecommendationAdmin(admin.ModelAdmin): admin.site.register(EICRecommendation, EICRecommendationAdmin) + +class EditorialDecisionAdmin(admin.ModelAdmin): + search_fields = [ + 'submission__title', + 'submission__author_list', + 'submission__preprint__identifier_w_vn_nr' + ] + list_filter = ['for_journal', 'decision', 'status',] + list_display = [submission_short_title, 'for_journal', 'decision', + 'taken_on', 'status'] + +admin.site.register(EditorialDecision, EditorialDecisionAdmin) + + admin.site.register(SubmissionEvent) diff --git a/submissions/constants.py b/submissions/constants.py index ed406a32e6a37efa5ed36bcec4f689e35403c0cb..82ba22efd6134393f7deae2b374fd1af33ae402e 100644 --- a/submissions/constants.py +++ b/submissions/constants.py @@ -244,6 +244,15 @@ EIC_REC_STATUSES = ( (DEPRECATED, 'Editorial Recommendation deprecated'), ) + +# Editorial decision +# (see other constants inside class; these are here because they use EIC_REC values) +EDITORIAL_DECISION_CHOICES = ( + (EIC_REC_PUBLISH, 'Publish'), + (EIC_REC_REJECT, 'Reject'), +) + + # Plagiarism Report statuses STATUS_WAITING = 'waiting' STATUS_SENT, STATUS_RECEIVED = 'sent', 'received' diff --git a/submissions/forms.py b/submissions/forms.py index 61b1e53656ee8e925f115ec90fadc0fa60b297d7..59db3182668ba354b1063ce22b22e4b17fcfa050 100644 --- a/submissions/forms.py +++ b/submissions/forms.py @@ -31,6 +31,7 @@ from . import exceptions, helpers from .helpers import to_ascii_only from .models import ( Submission, RefereeInvitation, Report, EICRecommendation, EditorialAssignment, + EditorialDecision, iThenticateReport, EditorialCommunication) from .signals import notify_manuscript_accepted @@ -1383,6 +1384,34 @@ class RecommendationVoteForm(forms.Form): '(by filling both the for journal and recommendation fields).') +class EditorialDecisionForm(forms.ModelForm): + """For EdAdmin to fix the outcome on a Submission, after voting is completed.""" + + class Meta: + model = EditorialDecision + fields = [ + 'submission', + 'for_journal', + 'decision', + 'taken_on', + 'remarks_for_authors', + 'remarks_for_editorial_college', + 'status', + ] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if self.instance.id: + self.fields['submission'].queryset = Submission.objects.filter( + pk=self.instance.submission.id) + else: + self.fields['submission'].queryset = Submission.objects.actively_refereeing() + self.fields['remarks_for_authors'].widget.attrs.update({ + 'placeholder': '[will be seen by authors and Fellows]'}) + self.fields['remarks_for_editorial_college'].widget.attrs.update({ + 'placeholder': '[will only be seen by Fellows]'}) + + class SubmissionCycleChoiceForm(forms.ModelForm): """Make a decision on the Submission's cycle and make publicly available.""" @@ -1530,94 +1559,94 @@ class iThenticateReportForm(forms.ModelForm): return data -class FixCollegeDecisionForm(forms.ModelForm): - """Fix EICRecommendation decision.""" - - FIX, DEPRECATE = 'fix', 'deprecate' - action = forms.ChoiceField(choices=((FIX, FIX), (DEPRECATE, DEPRECATE))) - - class Meta: - model = EICRecommendation - fields = () - - def __init__(self, *args, **kwargs): - """Accept request as argument.""" - self.submission = kwargs.pop('submission', None) - self.request = kwargs.pop('request', None) - return super().__init__(*args, **kwargs) - - def clean(self): - """Check if EICRecommendation has the right decision.""" - data = super().clean() - if self.instance.status == DECISION_FIXED: - self.add_error(None, 'This EICRecommendation is already fixed.') - elif self.instance.status == DEPRECATED: - self.add_error(None, 'This EICRecommendation is deprecated.') - return data - - def is_fixed(self): - """Check if decision is fixed.""" - return self.cleaned_data['action'] == self.FIX - - def fix_decision(self, recommendation): - """Fix decision of EICRecommendation.""" - EICRecommendation.objects.filter(id=recommendation.id).update( - status=DECISION_FIXED) - submission = recommendation.submission - if recommendation.recommendation in [REPORT_PUBLISH_1, REPORT_PUBLISH_2, REPORT_PUBLISH_3]: - # Publish as Tier I, II or III - Submission.objects.filter(id=submission.id).update( - visible_public=True, status=STATUS_ACCEPTED, acceptance_date=datetime.date.today(), - latest_activity=timezone.now()) - - # Start a new ProductionStream - get_or_create_production_stream(submission) - - if self.request: - # Add SubmissionEvent for authors - notify_manuscript_accepted(self.request.user, submission, False) - elif recommendation.recommendation == REPORT_REJECT: - # Decision: Rejection. Auto hide from public and Pool. - Submission.objects.filter(id=submission.id).update( - visible_public=False, visible_pool=False, - status=STATUS_REJECTED, latest_activity=timezone.now()) - submission.get_other_versions().update(visible_public=False) - - # Force-close the refereeing round for new referees. - Submission.objects.filter(id=submission.id).update( - open_for_reporting=False, - open_for_commenting=False) - - # Update Editorial Assignment statuses. - EditorialAssignment.objects.filter( - submission=submission, to=submission.editor_in_charge).update(status=STATUS_COMPLETED) - - # Add SubmissionEvent for authors - submission.add_event_for_author( - 'The Editorial Recommendation has been formulated: {0}.'.format( - recommendation.get_recommendation_display())) - submission.add_event_for_eic( - 'The Editorial Recommendation has been fixed: {0}.'.format( - recommendation.get_recommendation_display())) - return recommendation - - def deprecate_decision(self, recommendation): - """Deprecate decision of EICRecommendation.""" - EICRecommendation.objects.filter(id=recommendation.id).update( - status=DEPRECATED, active=False) - recommendation.submission.add_event_for_eic( - 'The Editorial Recommendation (version {version}) has been deprecated: {decision}.'.format( - version=recommendation.version, - decision=recommendation.get_recommendation_display())) - - return recommendation - - def save(self): - """Update EICRecommendation and related Submission.""" - if self.is_fixed(): - return self.fix_decision(self.instance) - elif self.cleaned_data['action'] == self.DEPRECATE: - return self.deprecate_decision(self.instance) - else: - raise ValueError('The decision given is invalid') - return self.instance +# class FixCollegeDecisionForm(forms.ModelForm): +# """Fix EICRecommendation decision.""" + +# FIX, DEPRECATE = 'fix', 'deprecate' +# action = forms.ChoiceField(choices=((FIX, FIX), (DEPRECATE, DEPRECATE))) + +# class Meta: +# model = EICRecommendation +# fields = () + +# def __init__(self, *args, **kwargs): +# """Accept request as argument.""" +# self.submission = kwargs.pop('submission', None) +# self.request = kwargs.pop('request', None) +# return super().__init__(*args, **kwargs) + +# def clean(self): +# """Check if EICRecommendation has the right decision.""" +# data = super().clean() +# if self.instance.status == DECISION_FIXED: +# self.add_error(None, 'This EICRecommendation is already fixed.') +# elif self.instance.status == DEPRECATED: +# self.add_error(None, 'This EICRecommendation is deprecated.') +# return data + +# def is_fixed(self): +# """Check if decision is fixed.""" +# return self.cleaned_data['action'] == self.FIX + +# def fix_decision(self, recommendation): +# """Fix decision of EICRecommendation.""" +# EICRecommendation.objects.filter(id=recommendation.id).update( +# status=DECISION_FIXED) +# submission = recommendation.submission +# if recommendation.recommendation in [REPORT_PUBLISH_1, REPORT_PUBLISH_2, REPORT_PUBLISH_3]: +# # Publish as Tier I, II or III +# Submission.objects.filter(id=submission.id).update( +# visible_public=True, status=STATUS_ACCEPTED, acceptance_date=datetime.date.today(), +# latest_activity=timezone.now()) + +# # Start a new ProductionStream +# get_or_create_production_stream(submission) + +# if self.request: +# # Add SubmissionEvent for authors +# notify_manuscript_accepted(self.request.user, submission, False) +# elif recommendation.recommendation == REPORT_REJECT: +# # Decision: Rejection. Auto hide from public and Pool. +# Submission.objects.filter(id=submission.id).update( +# visible_public=False, visible_pool=False, +# status=STATUS_REJECTED, latest_activity=timezone.now()) +# submission.get_other_versions().update(visible_public=False) + +# # Force-close the refereeing round for new referees. +# Submission.objects.filter(id=submission.id).update( +# open_for_reporting=False, +# open_for_commenting=False) + +# # Update Editorial Assignment statuses. +# EditorialAssignment.objects.filter( +# submission=submission, to=submission.editor_in_charge).update(status=STATUS_COMPLETED) + +# # Add SubmissionEvent for authors +# submission.add_event_for_author( +# 'The Editorial Recommendation has been formulated: {0}.'.format( +# recommendation.get_recommendation_display())) +# submission.add_event_for_eic( +# 'The Editorial Recommendation has been fixed: {0}.'.format( +# recommendation.get_recommendation_display())) +# return recommendation + +# def deprecate_decision(self, recommendation): +# """Deprecate decision of EICRecommendation.""" +# EICRecommendation.objects.filter(id=recommendation.id).update( +# status=DEPRECATED, active=False) +# recommendation.submission.add_event_for_eic( +# 'The Editorial Recommendation (version {version}) has been deprecated: {decision}.'.format( +# version=recommendation.version, +# decision=recommendation.get_recommendation_display())) + +# return recommendation + +# def save(self): +# """Update EICRecommendation and related Submission.""" +# if self.is_fixed(): +# return self.fix_decision(self.instance) +# elif self.cleaned_data['action'] == self.DEPRECATE: +# return self.deprecate_decision(self.instance) +# else: +# raise ValueError('The decision given is invalid') +# return self.instance diff --git a/submissions/migrations/0076_editorialdecision.py b/submissions/migrations/0076_editorialdecision.py new file mode 100644 index 0000000000000000000000000000000000000000..77d7c21871d437a1d3c272e3eee55b4aeb28ddf3 --- /dev/null +++ b/submissions/migrations/0076_editorialdecision.py @@ -0,0 +1,33 @@ +# Generated by Django 2.1.8 on 2019-10-20 09:05 + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0085_auto_20191017_0949'), + ('submissions', '0075_auto_20191017_1942'), + ] + + operations = [ + migrations.CreateModel( + name='EditorialDecision', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('decision', models.SmallIntegerField(choices=[(1, 'Publish'), (-3, 'Reject')])), + ('taken_on', models.DateTimeField(default=django.utils.timezone.now)), + ('remarks_for_authors', models.TextField(blank=True, verbose_name='optional remarks for the authors')), + ('remarks_for_editorial_college', models.TextField(blank=True, verbose_name='optional remarks for the Editorial College')), + ('status', models.SmallIntegerField(choices=[(1, 'Editorial decision fixed and (if required) accepted by authors'), (2, 'Awaiting author acceptance of publication offer'), (3, 'Publication offer refused by authors; manuscript will not be produced'), (4, 'Editorial decision appealed by authors'), (5, 'Editorial decision under review by ombudsperson'), (0, 'Deprecated')])), + ('for_journal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journals.Journal')), + ('submission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submissions.Submission')), + ], + options={ + 'verbose_name_plural': 'Editorial Decisions', + 'verbose_name': 'Editorial Decision', + }, + ), + ] diff --git a/submissions/migrations/0077_populate_editorialdecision.py b/submissions/migrations/0077_populate_editorialdecision.py new file mode 100644 index 0000000000000000000000000000000000000000..6863ed24db3bbb11444432d5c7967b22a1dadeb9 --- /dev/null +++ b/submissions/migrations/0077_populate_editorialdecision.py @@ -0,0 +1,35 @@ +# Generated by Django 2.1.8 on 2019-10-20 08:51 + +from django.db import migrations + +from submissions.constants import ( + EIC_REC_PUBLISH, EIC_REC_REJECT, DECISION_FIXED +) + + +def populate_editorialdecision(apps, schema_editor): + EICRecommendation = apps.get_model('submissions', 'EICRecommendation') + EditorialDecision = apps.get_model('submissions', 'EditorialDecision') + + for eicrec in EICRecommendation.objects.filter( + recommendation__in=[EIC_REC_PUBLISH, EIC_REC_REJECT], + status=DECISION_FIXED): + decision = EditorialDecision( + submission=eicrec.submission, + for_journal=eicrec.for_journal, + decision=eicrec.recommendation, + taken_on=eicrec.date_submitted, + status=1) # EditorialDecision.FIXED_AND_ACCEPTED <- leads to AttributeError + decision.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('submissions', '0076_editorialdecision'), + ] + + operations = [ + migrations.RunPython(populate_editorialdecision, + reverse_code=migrations.RunPython.noop), + ] diff --git a/submissions/migrations/0078_auto_20191023_1508.py b/submissions/migrations/0078_auto_20191023_1508.py new file mode 100644 index 0000000000000000000000000000000000000000..7cfc1a42e947ed9ce002ebc273b7175601015345 --- /dev/null +++ b/submissions/migrations/0078_auto_20191023_1508.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.8 on 2019-10-23 13:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('submissions', '0077_populate_editorialdecision'), + ] + + operations = [ + migrations.AlterField( + model_name='editorialdecision', + name='status', + field=models.SmallIntegerField(choices=[(0, 'Editorial decision drafted (yet to be communicated to authors)'), (1, 'Editorial decision fixed and (if required) accepted by authors'), (2, 'Awaiting author acceptance of publication offer'), (3, 'Publication offer refused by authors; manuscript will not be produced'), (4, 'Editorial decision appealed by authors'), (5, 'Editorial decision under review by ombudsperson'), (-1, 'Deprecated')]), + ), + ] diff --git a/submissions/models.py b/submissions/models.py index ae117d25d2446efa72c27c4058d57fc9fea16741..44fec59ae070eda284c8520e9aa744cab490487e 100644 --- a/submissions/models.py +++ b/submissions/models.py @@ -23,7 +23,8 @@ from .constants import ( SUBMISSION_CYCLES, CYCLE_DEFAULT, CYCLE_SHORT, DECISION_FIXED, ASSIGNMENT_STATUSES, CYCLE_DIRECT_REC, EVENT_GENERAL, EVENT_TYPES, EVENT_FOR_AUTHOR, EVENT_FOR_EIC, REPORT_TYPES, REPORT_NORMAL, - EIC_REC_CHOICES, ALT_REC_CHOICES, SUBMISSION_TIERS, + EIC_REC_PUBLISH, EIC_REC_CHOICES, + ALT_REC_CHOICES, SUBMISSION_TIERS, EDITORIAL_DECISION_CHOICES, STATUS_DRAFT, STATUS_VETTED, EIC_REC_STATUSES, VOTING_IN_PREP, STATUS_UNASSIGNED, STATUS_INCORRECT, STATUS_UNCLEAR, STATUS_NOT_USEFUL, STATUS_NOT_ACADEMIC, DEPRECATED, STATUS_FAILED_PRESCREENING, STATUS_RESUBMITTED, STATUS_REJECTED, STATUS_WITHDRAWN, REPORT_REC, @@ -967,7 +968,7 @@ class EICRecommendation(SubmissionRelatedObjectMixin, models.Model): ordering = ['version'] def __str__(self): - """Summerize the EICRecommendation's meta information.""" + """Summarize the EICRecommendation's meta information.""" return '{title} by {author}, {recommendation} version {version}'.format( title=self.submission.title[:20], author=self.submission.author_list[:30], @@ -1045,6 +1046,71 @@ class AlternativeRecommendation(models.Model): recommendation = models.SmallIntegerField(choices=ALT_REC_CHOICES) +class EditorialDecision(models.Model): + """Editorial decision, created by EdAdmin based on voting results. + + If the decision is to publish in the journal the authors submitted to, + or in a higher one (e.g. Selections instead of flagship), authors are + presumed to accept the outcome. + + If the decision is to publish in a Journal which is subsidiary to the one + the authors submitted to, the authors are sent a publication offer which + they have to accept before production is initiated. + """ + + DRAFTED = 0 + FIXED_AND_ACCEPTED = 1 + AWAITING_PUBOFFER_ACCEPTANCE = 2 + PUBOFFER_REFUSED_BY_AUTHORS = 3 + APPEALED_BY_AUTHORS = 4 + UNDER_REVIEW_BY_OMBUDSPERSON = 5 + DEPRECATED = -1 + EDITORIAL_DECISION_STATUSES = ( + (DRAFTED, 'Editorial decision drafted (yet to be communicated to authors)'), + (FIXED_AND_ACCEPTED, 'Editorial decision fixed and (if required) accepted by authors'), + (AWAITING_PUBOFFER_ACCEPTANCE, + 'Awaiting author acceptance of publication offer'), + (PUBOFFER_REFUSED_BY_AUTHORS, + 'Publication offer refused by authors; manuscript will not be produced'), + (APPEALED_BY_AUTHORS, 'Editorial decision appealed by authors'), + (UNDER_REVIEW_BY_OMBUDSPERSON, 'Editorial decision under review by ombudsperson'), + (DEPRECATED, 'Deprecated'), + ) + + submission = models.ForeignKey('submissions.Submission', on_delete=models.CASCADE) + for_journal = models.ForeignKey('journals.Journal', on_delete=models.CASCADE) + decision = models.SmallIntegerField(choices=EDITORIAL_DECISION_CHOICES) + taken_on = models.DateTimeField(default=timezone.now) + remarks_for_authors = models.TextField( + blank=True, verbose_name='optional remarks for the authors') + remarks_for_editorial_college = models.TextField( + blank=True, verbose_name='optional remarks for the Editorial College') + status = models.SmallIntegerField(choices=EDITORIAL_DECISION_STATUSES) + + class Meta: + verbose_name = 'Editorial Decision' + verbose_name_plural = 'Editorial Decisions' + + def __str__(self): + return '%s: %s for journal %s' % (self.submission.preprint.identifier_w_vn_nr, + self.get_decision_display(), + self.for_journal) + + def get_absolute_url(self): + return reverse('submissions:editorial_decision_detail', + kwargs={'identifier_w_vn_nr': self.submission.preprint.identifier_w_vn_nr, + 'pk': self.id}) + + @property + def publish(self): + """Whether the decision is to publish (True) or reject (False).""" + return self.decision == EIC_REC_PUBLISH + + @property + def production_can_proceed(self): + return self.status == self.FIXED_AND_ACCEPTED + + class iThenticateReport(TimeStampedModel): """iThenticate report registration. diff --git a/submissions/templates/submissions/admin/_editorial_decision_as_table.html b/submissions/templates/submissions/admin/_editorial_decision_as_table.html new file mode 100644 index 0000000000000000000000000000000000000000..9a0cd39a4c898a8748d8cf613e5918000fcf41d3 --- /dev/null +++ b/submissions/templates/submissions/admin/_editorial_decision_as_table.html @@ -0,0 +1,29 @@ +{% load bootstrap %} +{% load automarkup %} + +<table class="table"> + <tbody> + <tr> + <td>For submission</td><td>{{ decision.submission }}</td> + </tr> + <tr> + <td>For Journal</td><td>{{ decision.for_journal }}</td> + </tr> + <tr> + <td>Decision</td><td>{{ decision.get_decision_display }}</td> + </tr> + <tr> + <td>Taken on</td><td>{{ decision.taken_on }}</td> + </tr> + <tr> + <td>Status</td><td>{{ decision.get_status_display }}</td> + </tr> + <tr> + <td>Remarks for authors</td><td>{{ decision.remarks_for_authors|automarkup }}</td> + </tr> + <tr> + <td>Remarks for the Editorial College</td> + <td>{{ decision.remarks_for_editorial_college|automarkup }}</td> + </tr> + </tbody> +</table> diff --git a/submissions/templates/submissions/admin/editorial_decision_confirm_create.html b/submissions/templates/submissions/admin/editorial_decision_confirm_create.html new file mode 100644 index 0000000000000000000000000000000000000000..219a8a561575c2206371476a5c4d2f20e25719b9 --- /dev/null +++ b/submissions/templates/submissions/admin/editorial_decision_confirm_create.html @@ -0,0 +1,54 @@ +{% extends 'submissions/admin/base.html' %} + +{% load bootstrap %} +{% load scipost_extras %} + +{% block breadcrumb_items %} + {{ block.super }} + <span class="breadcrumb-item">Editorial Decision</span> +{% endblock %} + +{% block pagetitle %}: Editorial Decision{% endblock pagetitle %} + +{% block content %} + + <div class="row"> + <div class="col-12"> + + <h1 class="highlight">Editorial Decision</h1> + + <h2>Concerning Submission:</h2> + {% include 'partials/submissions/submission_li.html' with submission=submission %} + + <a class="d-inline-block mb-3" href="{{ submission.get_absolute_url }}" target="_blank">View Reports and Submission details</a> + + {% include 'partials/submissions/pool/submission_info_table.html' with submission=submission %} + + <h2 class="highlight">Set the Editorial Decision</h2> + + {% if form.errors %} + {% for field in form %} + {% for error in field.errors %} + <div class="alert alert-danger"> + <strong>{{ field.name }} - {{ error|escape }}</strong> + </div> + {% endfor %} + {% endfor %} + {% for error in form.non_field_errors %} + <div class="alert alert-danger"> + <strong>{{ error|escape }}</strong> + </div> + {% endfor %} + {% endif %} + + <form action="" method="post"> + {% csrf_token %} + {{ form|bootstrap }} + <input type="submit" value="Confirm the decision (final)" class="btn-btn-danger"> + <span class="text-danger"> <strong>CAREFUL: this finalizes the evaluation process for the Submission.</strong><br>If you want to change the data, go back and re-fill the form.</span> + </form> + + </div> + </div> + +{% endblock %} diff --git a/submissions/templates/submissions/admin/editorial_decision_detail.html b/submissions/templates/submissions/admin/editorial_decision_detail.html new file mode 100644 index 0000000000000000000000000000000000000000..599e27a1f4e74949043ac3bf3b8c47ca17d8fe75 --- /dev/null +++ b/submissions/templates/submissions/admin/editorial_decision_detail.html @@ -0,0 +1,43 @@ +{% extends 'submissions/admin/base.html' %} + +{% load bootstrap %} +{% load scipost_extras %} + +{% block breadcrumb_items %} + {{ block.super }} + <span class="breadcrumb-item">Editorial Decision</span> +{% endblock %} + +{% block pagetitle %}: Editorial Decision{% endblock pagetitle %} + +{% block content %} + + <div class="row"> + <div class="col-12"> + + <h1 class="highlight">Editorial Decision</h1> + + {% include 'submissions/admin/_editorial_decision_as_table.html' with decision=decision %} + + <ul class="list-unstyled"> + <li class="list-item my-2"> + <a class="btn btn-warning" href="{% url 'submissions:editorial_decision_update' identifier_w_vn_nr=decision.submission.preprint.identifier_w_vn_nr pk=decision.id %}">Update this decision</a> + </li> + <li class="list-item my-2"> + <a class="btn btn-danger" href="{% url 'submissions:fix_editorial_decision' identifier_w_vn_nr=decision.submission.preprint.identifier_w_vn_nr pk=decision.id %}">Fix this decision</a> + <span class="text-danger"> + <strong>CAREFUL: MAKE SURE YOU KNOW EXACTLY WHAT YOU ARE DOING.</strong> + <br> + Clicking on this button: + <ul> + <li>finalizes the evaluation process for the Submission</li> + <li>emails authors with the result.</li> + </ul> + </span> + </li> + </ul> + + </div> + </div> + +{% endblock content %} diff --git a/submissions/templates/submissions/admin/editorial_decision_form.html b/submissions/templates/submissions/admin/editorial_decision_form.html new file mode 100644 index 0000000000000000000000000000000000000000..3e887bf0bc2a45da05d8ba2f51794b03ec0efbfa --- /dev/null +++ b/submissions/templates/submissions/admin/editorial_decision_form.html @@ -0,0 +1,61 @@ +{% extends 'submissions/admin/base.html' %} + +{% load bootstrap %} +{% load scipost_extras %} + +{% block breadcrumb_items %} + {{ block.super }} + <span class="breadcrumb-item">Editorial Decision</span> +{% endblock %} + +{% block pagetitle %}: Editorial Decision{% endblock pagetitle %} + +{% block content %} + + <div class="row"> + <div class="col-12"> + + <h1 class="highlight">Editorial Decision</h1> + + <h2>Concerning Submission:</h2> + {% include 'partials/submissions/submission_li.html' with submission=submission %} + + <h3>Editorial process details:</h3> + <ul> + <li> + <a class="d-inline-block mb-3" href="{{ submission.get_absolute_url }}" target="_blank">View Reports and Submission details</a> + </li> + <li> + <a href="{% url 'submissions:eic_recommendation_detail' submission.preprint.identifier_w_vn_nr submission.eicrecommendations.last.id %}" target="_blank">View the Recommendations and Voting results</a> + </li> + </ul> + + + <h2 class="highlight">Set the Editorial Decision</h2> + + {% if form.errors %} + {% for field in form %} + {% for error in field.errors %} + <div class="alert alert-danger"> + <strong>{{ field.name }} - {{ error|escape }}</strong> + </div> + {% endfor %} + {% endfor %} + {% for error in form.non_field_errors %} + <div class="alert alert-danger"> + <strong>{{ error|escape }}</strong> + </div> + {% endfor %} + {% endif %} + + <form action="" method="post"> + {% csrf_token %} + {{ form|bootstrap }} + <input type="submit" value="{% if form.instance.id %}Update{% else %}Draft{% endif %} the decision" class="btn btn-danger"> + <br> + </form> + + </div> + </div> + +{% endblock %} diff --git a/submissions/templates/submissions/pool/recommendation.html b/submissions/templates/submissions/pool/recommendation.html index 10b57af2fbd161c5f02e84c54e6bfb73bbb7f402..f46e45af2939c6d64cb7f76574eba6caec59ccb2 100644 --- a/submissions/templates/submissions/pool/recommendation.html +++ b/submissions/templates/submissions/pool/recommendation.html @@ -31,7 +31,7 @@ {% endif %} - <h1 class="highlight">Editorial Recommendation to vote on</h1> + <h1 class="highlight">Editorial Recommendation</h1> <h2>Concerning Submission:</h2> {% include 'partials/submissions/submission_li.html' with submission=recommendation.submission %} @@ -107,7 +107,7 @@ </div> {% endif %} - <h2 class="highlight">Editorial Recommendation to vote on</h2> + <h2 class="highlight">Editorial Recommendation (latest)</h2> {% include 'partials/submissions/recommendation_fellow_content.html' with recommendation=recommendation %} <div class="card"> @@ -199,19 +199,21 @@ {% if perms.scipost.can_fix_College_decision %} <div class="card-footer bg-light py-3"> - <h3 class="card-title">Administrative actions on recommendations undergoing voting:</h3> - <ul class="mb-0"> - <li>To send an email reminder to each Fellow with at least one voting duty: <a href="{% url 'submissions:remind_Fellows_to_vote' %}">click here</a>.</li> - <li> - The current Editorial Recommendation: {{ recommendation.get_recommendation_display }}. - <br> - <form method="post" action="{% url 'submissions:eic_recommendation_detail' recommendation.submission.preprint.identifier_w_vn_nr recommendation.id %}" class="d-inline-block my-2"> - {% csrf_token %} - <button type="submit" name="action" value="fix" class="btn btn-primary btn-sm mr-2">Fix College decision</button> - <button type="submit" name="action" value="deprecate" class="btn btn-danger btn-sm text-white">Deprecate College decision</button> - </form> + <h3 class="card-title mb-3">Administrative actions on recommendations undergoing voting:</h3> + <ul class="mb-1"> + <li class="list-item my-2"><a class="btn btn-secondary" href="{% url 'submissions:remind_Fellows_to_vote' %}" role="button">Send an email reminder to each Fellow with at least one voting duty</a><br>(this is a one-click action, for all Submissions)</li> + <li class="list-item my-2"><a class="btn btn-warning" href="{% url 'submissions:communication' identifier_w_vn_nr=recommendation.submission.preprint.identifier_w_vn_nr comtype='StoE' %}" role="button">Send a communication to the Editor-in-charge</a><br>(for example to request a reformulation of the recommendation)</li> + <li class="list-item my-2"> + <!-- The current Editorial Recommendation is for Journal {{ recommendation.for_journal }}: {{ recommendation.get_recommendation_display }}. + <br> --> + <a class="btn btn-primary" href="{% url 'submissions:editorial_decision_create' recommendation.submission.preprint.identifier_w_vn_nr %}" role="button">Initiate the process to fix the editorial decision</a> + <!-- <br> + <form method="post" action="{% url 'submissions:eic_recommendation_detail' recommendation.submission.preprint.identifier_w_vn_nr recommendation.id %}" class="d-inline-block my-2"> + {% csrf_token %} + <button type="submit" name="action" value="fix" class="btn btn-primary btn-sm mr-2">Fix College decision</button> + <button type="submit" name="action" value="deprecate" class="btn btn-danger btn-sm text-white">Deprecate College decision</button> + </form> --> </li> - <li>To request a modification of the Recommendation to request for revision: click here</li> </ul> </div> {% endif %} diff --git a/submissions/urls.py b/submissions/urls.py index 358f530c005472194605a6f19d04bfeadea130f5..ca11983958337a53176ec444cc90db10004676cd 100644 --- a/submissions/urls.py +++ b/submissions/urls.py @@ -63,9 +63,36 @@ urlpatterns = [ views.PlagiarismView.as_view(), name='plagiarism'), url(r'^admin/{regex}/plagiarism/report$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), views.PlagiarismReportPDFView.as_view(), name='plagiarism_report'), + # url(r'^admin/{regex}/recommendations/(?P<rec_id>[0-9]+)$'.format( + # regex=SUBMISSIONS_COMPLETE_REGEX), views.EICRecommendationView.as_view(), + # name='eic_recommendation_detail'), url(r'^admin/{regex}/recommendations/(?P<rec_id>[0-9]+)$'.format( - regex=SUBMISSIONS_COMPLETE_REGEX), views.EICRecommendationView.as_view(), + regex=SUBMISSIONS_COMPLETE_REGEX), views.EICRecommendationDetailView.as_view(), name='eic_recommendation_detail'), + url( + r'^admin/{regex}/editorial_decision/create/$'.format( + regex=SUBMISSIONS_COMPLETE_REGEX), + views.EditorialDecisionCreateView.as_view(), + name='editorial_decision_create' + ), + url( + r'^admin/{regex}/editorial_decision/(?P<pk>[0-9]+)/$'.format( + regex=SUBMISSIONS_COMPLETE_REGEX), + views.EditorialDecisionDetailView.as_view(), + name='editorial_decision_detail' + ), + url( + r'^admin/{regex}/editorial_decision/(?P<pk>[0-9]+)/update/$'.format( + regex=SUBMISSIONS_COMPLETE_REGEX), + views.EditorialDecisionUpdateView.as_view(), + name='editorial_decision_update' + ), + url( + r'^admin/{regex}/editorial_decision/(?P<pk>[0-9]+)/fix/$'.format( + regex=SUBMISSIONS_COMPLETE_REGEX), + views.fix_editorial_decision, + name='fix_editorial_decision' + ), url(r'^admin/reports$', views.reports_accepted_list, name='reports_accepted_list'), url(r'^admin/reports/(?P<report_id>[0-9]+)/compile$', views.report_pdf_compile, name='report_pdf_compile'), diff --git a/submissions/utils.py b/submissions/utils.py index ff7cd50541b310735972c3e4878ec3aa167b2702..e097a806bcc57c4799e7af11d6de71a93a7450e6 100644 --- a/submissions/utils.py +++ b/submissions/utils.py @@ -756,7 +756,7 @@ class SubmissionUtils(BaseMailUtil): or cls.recommendation.recommendation == 3): email_text += ('We are pleased to inform you that your Submission ' 'has been accepted for publication in ' - + str(cls.submission.submitted_to)) + + str(cls.recommendation.for_journal)) #submission.submitted_to) email_text_html += ( '<p>We are pleased to inform you that your Submission ' 'has been accepted for publication in <strong>{{ journal }}</strong>') @@ -813,7 +813,7 @@ class SubmissionUtils(BaseMailUtil): 'sub_title': cls.submission.title, 'author_list': cls.submission.author_list, 'identifier_w_vn_nr': cls.submission.preprint.identifier_w_vn_nr, - 'journal': str(cls.submission.submitted_to), + 'journal': str(cls.recommendation.for_journal), } email_text_html += '<br/>' + EMAIL_FOOTER html_template = Template(email_text_html) diff --git a/submissions/views.py b/submissions/views.py index 8171f7f95f4e5345f63f428715c4876bfd973540..4bab987eaa956625a0aae1ca306594604d31c5af 100644 --- a/submissions/views.py +++ b/submissions/views.py @@ -8,29 +8,30 @@ import strings from django.contrib import messages from django.contrib.auth.decorators import login_required, permission_required -from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin +from django.contrib.auth.mixins import ( + LoginRequiredMixin, PermissionRequiredMixin, UserPassesTestMixin) from django.contrib.messages.views import SuccessMessageMixin from django.core.exceptions import PermissionDenied -from django.urls import reverse, reverse_lazy from django.db import transaction, IntegrityError from django.db.models import Q from django.http import Http404, HttpResponse, HttpResponseRedirect from django.shortcuts import get_object_or_404, get_list_or_404, render, redirect from django.template import Template, Context +from django.urls import reverse, reverse_lazy from django.utils import timezone -from django.utils.safestring import mark_safe from django.views.generic.base import RedirectView from django.views.generic.detail import SingleObjectMixin, DetailView from django.views.generic.edit import CreateView, UpdateView from django.views.generic.list import ListView from .constants import ( - STATUS_VETTED, STATUS_EIC_ASSIGNED, SUBMISSION_STATUS, STATUS_ASSIGNMENT_FAILED, - STATUS_WITHDRAWN, STATUS_DRAFT, CYCLE_DIRECT_REC, STATUS_COMPLETED, STATUS_DEPRECATED, - DEPRECATED, EIC_REC_PUBLISH) + STATUS_ACCEPTED, STATUS_REJECTED, STATUS_VETTED, SUBMISSION_STATUS, STATUS_ASSIGNMENT_FAILED, + STATUS_DRAFT, CYCLE_DIRECT_REC, STATUS_COMPLETED, STATUS_DEPRECATED, + EIC_REC_PUBLISH, EIC_REC_REJECT, DECISION_FIXED) from .helpers import check_verified_author, check_unverified_author from .models import ( Submission, EICRecommendation, SubmissionTiering, AlternativeRecommendation, + EditorialDecision, EditorialAssignment, RefereeInvitation, Report, SubmissionEvent) from .mixins import SubmissionAdminViewMixin from .forms import ( @@ -40,7 +41,10 @@ from .forms import ( iThenticateReportForm, VotingEligibilityForm, WithdrawSubmissionForm, ConsiderRefereeInvitationForm, EditorialCommunicationForm, ReportForm, SubmissionCycleChoiceForm, ReportPDFForm, SubmissionReportsForm, EICRecommendationForm, - SubmissionPoolFilterForm, FixCollegeDecisionForm, SubmissionPrescreeningForm, + SubmissionPoolFilterForm, + # FixCollegeDecisionForm, + EditorialDecisionForm, + SubmissionPrescreeningForm, PreassignEditorsFormSet, SubmissionReassignmentForm) from .signals import ( notify_editor_assigned, notify_eic_new_report, notify_invitation_cancelled, @@ -60,12 +64,13 @@ from mails.views import MailEditorSubview from ontology.models import Topic from ontology.forms import SelectTopicForm from production.forms import ProofsDecisionForm +from production.utils import get_or_create_production_stream from profiles.models import Profile from profiles.forms import SimpleProfileForm, ProfileEmailForm from scipost.constants import TITLE_DR, INVITATION_REFEREEING from scipost.decorators import is_contributor_user from scipost.forms import RemarkForm -from scipost.mixins import PaginationMixin +from scipost.mixins import PaginationMixin, PermissionsMixin from scipost.models import Contributor, Remark @@ -1716,7 +1721,7 @@ def vote_on_rec(request, rec_id): else: form = RecommendationVoteForm(initial=initial) if form.is_valid(): - # Delete previous tiernings and alternative recs, irrespective of the vote + # Delete previous tierings and alternative recs, irrespective of the vote SubmissionTiering.objects.filter( submission=recommendation.submission, fellow=request.user.contributor).delete() @@ -1904,50 +1909,304 @@ class SubmissionConflictsView(SubmissionAdminViewMixin, DetailView): success_url = reverse_lazy('submissions:pool') -class EICRecommendationView(SubmissionAdminViewMixin, UpdateView): - """EICRecommendation detail view.""" +# TO BE DEPRECATED, replaced by EICRecommendationDetailView +# class EICRecommendationView(SubmissionAdminViewMixin, UpdateView): +# """EICRecommendation detail view.""" + +# permission_required = 'scipost.can_fix_College_decision' +# template_name = 'submissions/pool/recommendation.html' +# editorial_page = True +# form_class = FixCollegeDecisionForm +# success_url = reverse_lazy('submissions:pool') + +# def get_object(self): +# """Get EICRecommendation.""" +# submission = super().get_object() +# return get_object_or_404( +# submission.eicrecommendations.put_to_voting(), id=self.kwargs['rec_id']) + +# def get_form_kwargs(self): +# """Form accepts request as argument.""" +# kwargs = super().get_form_kwargs() +# kwargs['request'] = self.request +# return kwargs + +# def get_context_data(self, *args, **kwargs): +# """Get the EICRecommendation as a submission-related instance.""" +# ctx = super().get_context_data(*args, **kwargs) +# ctx['recommendation'] = ctx['object'] +# return ctx + +# @transaction.atomic +# def form_valid(self, form): +# """Redirect and send out mails if decision is fixed.""" +# recommendation = form.save() +# if form.is_fixed(): +# submission = recommendation.submission + +# # Temporary: Update submission instance for utils email func. +# # Won't be needed in new mail construct. +# submission = Submission.objects.get(id=recommendation.submission.id) +# SubmissionUtils.load({'submission': submission, 'recommendation': recommendation}) +# SubmissionUtils.send_author_College_decision_email() +# messages.success(self.request, 'The Editorial College\'s decision has been fixed.') +# else: +# messages.success( +# self.request, 'The Editorial College\'s decision has been deprecated.') +# return HttpResponseRedirect(self.success_url) + + +class EICRecommendationDetailView(UserPassesTestMixin, DetailView): + """EICRecommendation detail page.""" + + model = EICRecommendation + pk_url_kwarg = 'rec_id' + template_name = 'submissions/pool/recommendation.html' + context_object_name = 'recommendation' + + def test_func(self): + """Grants access to EdAdmin, voting Fellows and authors.""" + if self.request.user.has_perm('scipost.can_fix_College_decision'): + return True + eicrec = get_object_or_404(EICRecommendation, pk=self.kwargs.get('rec_id')) + if eicrec.eligible_to_vote.filter(user=self.request.user).exists(): + return True + if eicrec.submission.authors.filter(user=self.request.user).exists(): + return True + return False + + +class EditorialDecisionCreateView(PermissionsMixin, CreateView): + """For EdAdmin to create the editorial decision on a Submission, after voting is completed.""" permission_required = 'scipost.can_fix_College_decision' - template_name = 'submissions/pool/recommendation.html' - editorial_page = True - form_class = FixCollegeDecisionForm - success_url = reverse_lazy('submissions:pool') + model = EditorialDecision + form_class = EditorialDecisionForm + template_name = 'submissions/admin/editorial_decision_form.html' - def get_object(self): - """Get EICRecommendation.""" - submission = super().get_object() - return get_object_or_404( - submission.eicrecommendations.put_to_voting(), id=self.kwargs['rec_id']) + def get(self, request, *args, **kwargs): + self.submission = get_object_or_404( + Submission, preprint__identifier_w_vn_nr=kwargs.get('identifier_w_vn_nr')) + return super().get(request, *args, **kwargs) - def get_form_kwargs(self): - """Form accepts request as argument.""" - kwargs = super().get_form_kwargs() - kwargs['request'] = self.request - return kwargs + def post(self, request, *args, **kwargs): + self.submission = get_object_or_404( + Submission, preprint__identifier_w_vn_nr=kwargs.get('identifier_w_vn_nr')) + return super().post(request, *args, **kwargs) def get_context_data(self, *args, **kwargs): - """Get the EICRecommendation as a submission-related instance.""" - ctx = super().get_context_data(*args, **kwargs) - ctx['recommendation'] = ctx['object'] - return ctx + context = super().get_context_data(*args, **kwargs) + context['submission'] = self.submission + return context - @transaction.atomic - def form_valid(self, form): - """Redirect and send out mails if decision is fixed.""" - recommendation = form.save() - if form.is_fixed(): - submission = recommendation.submission - - # Temporary: Update submission instance for utils email func. - # Won't be needed in new mail construct. - submission = Submission.objects.get(id=recommendation.submission.id) - SubmissionUtils.load({'submission': submission, 'recommendation': recommendation}) - SubmissionUtils.send_author_College_decision_email() - messages.success(self.request, 'The Editorial College\'s decision has been fixed.') - else: - messages.success( - self.request, 'The Editorial College\'s decision has been deprecated.') - return HttpResponseRedirect(self.success_url) + def get_initial(self, *args, **kwargs): + initial = super().get_initial(*args, **kwargs) + eicrec = self.submission.eicrecommendations.last() + for_journal = eicrec.for_journal + decision = eicrec.recommendation if eicrec.recommendation in [ + EIC_REC_PUBLISH, EIC_REC_REJECT] else EIC_REC_PUBLISH + status = EditorialDecision.DRAFTED + initial.update({ + 'submission': self.submission, + 'for_journal': for_journal, + 'decision': decision, + 'status': status, + }) + return initial + + +class EditorialDecisionDetailView(PermissionsMixin, DetailView): + + permission_required = 'scipost.can_fix_College_decision' + model = EditorialDecision + context_object_name = 'decision' + template_name = 'submissions/admin/editorial_decision_detail.html' + + +class EditorialDecisionUpdateView(PermissionsMixin, UpdateView): + + permission_required = 'scipost.can_fix_College_decision' + model = EditorialDecision + context_object_name = 'decision' + form_class = EditorialDecisionForm + template_name = 'submissions/admin/editorial_decision_form.html' + + def get(self, request, *args, **kwargs): + self.submission = get_object_or_404( + Submission, preprint__identifier_w_vn_nr=kwargs.get('identifier_w_vn_nr')) + return super().get(request, *args, **kwargs) + + def post(self, request, *args, **kwargs): + self.submission = get_object_or_404( + Submission, preprint__identifier_w_vn_nr=kwargs.get('identifier_w_vn_nr')) + return super().post(request, *args, **kwargs) + + def get_context_data(self, *args, **kwargs): + context = super().get_context_data(*args, **kwargs) + context['submission'] = self.submission + return context + + +@login_required +@permission_required('scipost.can_fix_College_decision') +def fix_editorial_decision(request, identifier_w_vn_nr, pk): + """Fix the editorial decision, which is then final. Email authors.""" + + submission = get_object_or_404(Submission, preprint__identifier_w_vn_nr=identifier_w_vn_nr) + decision = get_object_or_404(EditorialDecision, pk=pk) + if submission != decision.submission: + errormessage = ('The Submission from the url and that of the Editorial Decision' + ' do not correspond to each other. Please update the Decision or' + ' contect techsupport.') + return render(request, 'scipost/error.html', {'errormessage', errormessage}) + # Set latest EICRecommedation to DECISION_FIXED + eicrec = submission.eicrecommendations.last() + eicrec.status = DECISION_FIXED + eicrec.save() + + if decision.decision == EIC_REC_PUBLISH: + Submission.objects.filter(id=submission.id).update( + visible_public=True, status=STATUS_ACCEPTED, acceptance_date=datetime.date.today(), + latest_activity=timezone.now()) + + # Start a new ProductionStream + get_or_create_production_stream(submission) + + elif decision.decision == EIC_REC_REJECT: + # Decision: Rejection. Auto hide from public and Pool. + Submission.objects.filter(id=submission.id).update( + visible_public=False, visible_pool=False, + status=STATUS_REJECTED, latest_activity=timezone.now()) + submission.get_other_versions().update(visible_public=False) + + # Force-close the refereeing round for new referees. + Submission.objects.filter(id=submission.id).update( + open_for_reporting=False, + open_for_commenting=False) + + # Update Editorial Assignment statuses. + EditorialAssignment.objects.filter( + submission=submission, to=submission.editor_in_charge).update(status=STATUS_COMPLETED) + + # Add SubmissionEvent for authors + submission.add_event_for_author( + 'The Editorial Decision has been fixed: {0}.'.format( + decision.get_decision_display())) + submission.add_event_for_eic( + 'The Editorial Decision has been fixed: {0}.'.format( + decision.get_decision_display())) + + mail_request = MailEditorSubview( + request, mail_code='authors/inform_authors_editorial_decision', decision=decision) + + if mail_request.is_valid(): + messages.success(request, 'Authors have been emailed about the decision') + mail_request.send_mail() + return redirect('submissions:pool') + else: + return mail_request.interrupt() + + +# ATTEMPT: to drop +# class EditorialDecisionFixView(PermissionsMixin, UpdateView): +# """This fixes the decision and emails the authors.""" + +# model = EditorialDecision +# permission_required = 'scipost.can_fix_College_decision' +# model = EditorialDecision +# context_object_name = 'decision' +# form_class = EditorialDecisionForm +# template_name = 'submissions/admin/editorial_decision_form.html' + + +# class EditorialDecisionBaseCreateView(PermissionsMixin, CreateView): +# """For EdAdmin to fix the editorial decision on a Submission, after voting is completed.""" + +# permission_required = 'scipost.can_fix_College_decision' +# model = EditorialDecision +# form_class = EditorialDecisionForm +# success_url = reverse_lazy('submissions:pool') + +# def get(self, request, *args, **kwargs): +# self.submission = get_object_or_404( +# Submission, preprint__identifier_w_vn_nr=kwargs.get('identifier_w_vn_nr')) +# return super().get(request, *args, **kwargs) + +# def post(self, request, *args, **kwargs): +# self.submission = get_object_or_404( +# Submission, preprint__identifier_w_vn_nr=kwargs.get('identifier_w_vn_nr')) +# return super().post(request, *args, **kwargs) + +# def get_context_data(self, *args, **kwargs): +# context = super().get_context_data(*args, **kwargs) +# context['submission'] = self.submission +# return context + + +# class EditorialDecisionCreateView(EditorialDecisionBaseCreateView): +# """For EdAdmin to fix the editorial decision on a Submission, after voting is completed.""" + +# template_name = 'submissions/admin/editorial_decision_form.html' + +# def get_initial(self, *args, **kwargs): +# initial = super().get_initial(*args, **kwargs) +# eicrec = self.submission.eicrecommendations.last() +# for_journal = eicrec.for_journal +# decision = eicrec.recommendation if eicrec.recommendation in [ +# EIC_REC_PUBLISH, EIC_REC_REJECT] else EIC_REC_PUBLISH +# status = EditorialDecision.FIXED_AND_ACCEPTED +# initial.update({ +# 'submission': self.submission, +# 'for_journal': for_journal, +# 'decision': decision, +# 'status': status, +# }) +# return initial + +# def form_valid(self, form): +# """Save the form data to session variables and redirect to confirmation view.""" +# self.request.session['for_journal_id'] = form.cleaned_data['for_journal'].id +# self.request.session['decision'] = form.cleaned_data['decision'] +# self.request.session['remarks_for_authors'] = form.cleaned_data['remarks_for_authors'] +# self.request.session['remarks_for_editorial_college'] = form.cleaned_data['remarks_for_editorial_college'] +# self.request.session['status'] = form.cleaned_data['status'] +# return redirect(reverse( +# 'submissions:editorial_decision_confirm_create', +# kwargs={'identifier_w_vn_nr': self.kwargs.get('identifier_w_vn_nr')})) + + +# class EditorialDecisionConfirmCreateView(EditorialDecisionBaseCreateView): +# """For EdAdmin to fix the editorial decision on a Submission, after voting is completed. + +# This is a confirmation view.""" + +# template_name = 'submissions/admin/editorial_decision_confirm_create.html' + +# def get_initial(self, *args, **kwargs): +# initial = super().get_initial(*args, **kwargs) +# for_journal = get_object_or_404( +# Journal, pk=self.request.session['for_journal_id']) +# initial.update({ +# 'submission': self.submission, +# 'for_journal': for_journal, +# 'decision': self.request.session['decision'], +# 'remarks_for_authors': self.request.session['remarks_for_authors'], +# 'remarks_for_editorial_college': self.request.session['remarks_for_editorial_college'], +# 'status': self.request.session['status'], +# }) +# return initial + +# def form_valid(self, form): +# # Delete temp session variables +# del self.request.session['for_journal_id'] +# del self.request.session['decision'] +# del self.request.session['remarks_for_authors'] +# del self.request.session['remarks_for_editorial_college'] +# del self.request.session['status'] +# # Fix the latest EICRecommendation + +# self.object = form.save() +# return redirect(self.get_success_url()) class PlagiarismView(SubmissionAdminViewMixin, UpdateView): diff --git a/templates/email/authors/inform_authors_editorial_decision.html b/templates/email/authors/inform_authors_editorial_decision.html new file mode 100644 index 0000000000000000000000000000000000000000..ea9d7fe31ae38cc2fec3c035d9e96fbd382e5a35 --- /dev/null +++ b/templates/email/authors/inform_authors_editorial_decision.html @@ -0,0 +1,77 @@ +{% load automarkup %} +<p> + Dear {{ decision.submission.submitted_by.get_title_display }} {{ decision.submission.submitted_by.user.last_name }}, +</p> +<p> + The Editorial College of SciPost has come to a decision regarding your Submission +</p> +<p>{{ decision.submission.title }}</p> +<p>by {{ decision.submission.author_list }}.</p> +{% if decision.for_journal.name == 'SciPost Selections' %} + <p> + We are pleased to let you know that your manuscript has been accepted for publication + in {{ decision.submission.submitted_to }}, with inclusion of an extended abstract + in SciPost Selections. + </p> + <p> + We warmly congratulate you on this achievement, which represents the highest mark of + recognition given by the Editorial College. + </p> + <p> + Your manuscript will now be taken charge of by our production team, + which will soon send you proofs to check before final publication. + </p> +{% else %} + {% if decision.publish %} + {% if decision.status == decision.FIXED_AND_ACCEPTED %} + <p> + We are pleased to let you know that your manuscript has been accepted for publication + in {{ decision.submission.submitted_to }}. + </p> + <p> + Your manuscript will now be taken charge of by our production team, + which will soon send you proofs to check before final publication. + </p> + {% elif decision.status == decision.AWAITING_PUBOFFER_ACCEPTANCE %} + <p> + We would like to offer you publication of your manuscript in + {{ decision.for_journal }} (you had originally submitted + to {{ decision.submission.submitted_to }}). + </p> + <p> + You can inform us of choice by replying to this email. + <strong>We will await your confirmation of acceptance of this publication offer + before initiating production of the proofs of your paper.</strong> + </p> + <p> + If you decide to turn this offer down, your manuscript will not be considered + for further processing and you will be free to submit elsewhere. + </p> + {% endif %} + {% else %} + <p> + We are sorry to inform you that your Submission has not been accepted for publication. + </p> + <p> + You can view more details at the Submission's page at + <a href="{% url 'submissions:submission' identifier_w_vn_nr={{ identifier_w_vn_nr }}">{% url 'submissions:submission' identifier_w_vn_nr={{ identifier_w_vn_nr }}</a>. + Note that these details are viewable only by the registered authors of the submission. + </p> + <p> + This Submission Page has now been removed from general public view; if you wish, + you can email us and request to make it publicly visible again. + </p> + {% endif %} +{% endif %} +{% if decision.remarks_for_authors %} + <p><strong>Further remarks from Editorial Administration:</strong></p> + <div class="mx-2 mb-4"> + {{ decision.remarks_for_authors|automarkup }} + </div> +{% endif %} +<p>We thank you very much for your contribution.</p> +<p>Sincerely,</p> +<p>The SciPost Team.</p> +{% include 'email/_footer.html' %} + +{% include 'email/_submission_thread_uuid.html' with submission=decision.submission %} diff --git a/templates/email/authors/inform_authors_editorial_decision.json b/templates/email/authors/inform_authors_editorial_decision.json new file mode 100644 index 0000000000000000000000000000000000000000..11b5270d2f26ce3f40dfeafbf9bc680103950d88 --- /dev/null +++ b/templates/email/authors/inform_authors_editorial_decision.json @@ -0,0 +1,12 @@ +{ + "subject": "SciPost: editorial decision", + "recipient_list": [ + "submission.submitted_by.user.email" + ], + "bcc": [ + "edadmin@scipost.org", + "submission.editor_in_charge.user.email" + ], + "from_name": "SciPost Editorial Administration", + "from_email": "edadmin@scipost.org" +}