From 87d92bddb4226d475914029b62b6fe200a289d88 Mon Sep 17 00:00:00 2001 From: George Katsikas <giorgakis.katsikas@gmail.com> Date: Fri, 27 Sep 2024 15:30:24 +0200 Subject: [PATCH] add `autorejection_date` to Submissions add auto-rejection date extension button for edadmin/authors fixes #335 --- .../_submission_maintenance_card_general.html | 36 ++++++++---- scipost_django/edadmin/views/preassignment.py | 20 ++++--- .../0138_journal_autoreject_period.py | 18 ++++++ scipost_django/journals/models/journal.py | 1 + .../personal_page/_hx_submissions.html | 3 + scipost_django/submissions/forms/__init__.py | 4 ++ .../0163_submission_autorejection_date.py | 37 ++++++++++++ .../submissions/models/submission.py | 13 +++++ .../_submission_details_summary_contents.html | 8 +++ scipost_django/submissions/urls/__init__.py | 10 ++++ scipost_django/submissions/views/__init__.py | 56 ++++++++++++++++++- 11 files changed, 182 insertions(+), 24 deletions(-) create mode 100644 scipost_django/journals/migrations/0138_journal_autoreject_period.py create mode 100644 scipost_django/submissions/migrations/0163_submission_autorejection_date.py diff --git a/scipost_django/edadmin/templates/edadmin/_submission_maintenance_card_general.html b/scipost_django/edadmin/templates/edadmin/_submission_maintenance_card_general.html index d4cf1c43b..fd7ceb96f 100644 --- a/scipost_django/edadmin/templates/edadmin/_submission_maintenance_card_general.html +++ b/scipost_django/edadmin/templates/edadmin/_submission_maintenance_card_general.html @@ -20,25 +20,37 @@ hx-prompt="What is the reason for putting the submission on hold? (will be added as a submission internal note)" {% endif %} hx-target="#submission-{{ submission.id }}-details"> - {% if submission.on_hold %} - Take off hold - {% else %} - Put on hold - {% endif %} + {% if submission.on_hold %} + Take off hold + {% else %} + Put on hold + {% endif %} </button> </li> + {% if submission.autorejection_date %} + <li> + <div> + Extend autorejection date by: + </div> + <ul role="menu" class="d-flex gap-3 list-unstyled"> + <li role="menuitem"><a href="{% url 'submissions:extend_autorejection_date' submission.preprint.identifier_w_vn_nr 7 %}">7 days</a></li> + <li role="menuitem"><a href="{% url 'submissions:extend_autorejection_date' submission.preprint.identifier_w_vn_nr 14 %}">14 days</a></li> + <li role="menuitem"><a href="{% url 'submissions:extend_autorejection_date' submission.preprint.identifier_w_vn_nr 28 %}">28 days</a></li> + </ul> + </li> + {% endif %} {% if 'Proceedings' in submission.submitted_to.name %} - <li id="submission-{{ submission.id }}-update-target-proceedings"> - {% include "submissions/admin/_submission_update_target_proceedings.html" with submission=submission %} - </li> + <li id="submission-{{ submission.id }}-update-target-proceedings"> + {% include "submissions/admin/_submission_update_target_proceedings.html" with submission=submission %} + </li> {% endif %} {% if submission.preprint.has_file %} - <li id="submission-{{ submission.id }}-update-preprint-file"> - {% include "submissions/admin/_submission_update_preprint_file.html" with submission=submission %} - </li> + <li id="submission-{{ submission.id }}-update-preprint-file"> + {% include "submissions/admin/_submission_update_preprint_file.html" with submission=submission %} + </li> {% endif %} {% if submission.editor_in_charge and perms.scipost.can_reassign_submissions %} - <li><a href="{% url 'submissions:reassign_submission' submission.preprint.identifier_w_vn_nr %}">Reassign Editor-in-charge</a></li> + <li><a href="{% url 'submissions:reassign_submission' submission.preprint.identifier_w_vn_nr %}">Reassign Editor-in-charge</a></li> {% endif %} {% if submission.editor_in_charge %} <li class="pb-2"><a href="{% url 'submissions:communication' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr comtype='StoE' %}">Send a communication to the Editor-in-charge</a></li> diff --git a/scipost_django/edadmin/views/preassignment.py b/scipost_django/edadmin/views/preassignment.py index 20fb718e4..086d3759c 100644 --- a/scipost_django/edadmin/views/preassignment.py +++ b/scipost_django/edadmin/views/preassignment.py @@ -6,6 +6,7 @@ from django.contrib.auth.decorators import login_required, user_passes_test from django.http import HttpResponse from django.shortcuts import get_object_or_404, render, redirect from django.urls import reverse +from django.utils import timezone from colleges.permissions import is_edadmin from mails.utils import DirectMailUtil @@ -103,9 +104,9 @@ def _hx_author_profile_row(request, identifier_w_vn_nr, order: int): "edadmin/preassignment/_hx_author_profile_row.html", context, ) - response[ - "HX-Trigger-After-Settle" - ] = f"submission-{submission.pk}-author-profiles-details-updated" + response["HX-Trigger-After-Settle"] = ( + f"submission-{submission.pk}-author-profiles-details-updated" + ) return response @@ -159,19 +160,21 @@ def _hx_submission_preassignment_decision(request, identifier_w_vn_nr): form = SubmissionPreassignmentDecisionForm(request.POST or None) if form.is_valid(): if form.cleaned_data["choice"] == "pass": - Submission.objects.filter(pk=submission.id).update( - status=Submission.SEEKING_ASSIGNMENT + submission.status = Submission.SEEKING_ASSIGNMENT + submission.autorejection_date = ( + timezone.now() + submission.submitted_to.autoreject_period ) + submission.save() # send authors admission passed email mail_util = DirectMailUtil( "authors/preassignment_completed", submission=submission, comments_for_authors=form.cleaned_data["comments_for_authors"], ) + else: # inadmissible, inform authors and set status to PREASSIGNMENT_FAILED - Submission.objects.filter(pk=submission.id).update( - status=Submission.PREASSIGNMENT_FAILED - ) + submission.status = Submission.PREASSIGNMENT_FAILED + submission.save() # send authors admission failed email mail_util = DirectMailUtil( "authors/preassignment_failed", @@ -179,7 +182,6 @@ def _hx_submission_preassignment_decision(request, identifier_w_vn_nr): comments_for_authors=form.cleaned_data["comments_for_authors"], ) mail_util.send_mail() - submission.refresh_from_db() response = HttpResponse() # trigger refresh of pool listing response["HX-Trigger-After-Settle"] = "search-conditions-updated" diff --git a/scipost_django/journals/migrations/0138_journal_autoreject_period.py b/scipost_django/journals/migrations/0138_journal_autoreject_period.py new file mode 100644 index 000000000..7d4b5aa12 --- /dev/null +++ b/scipost_django/journals/migrations/0138_journal_autoreject_period.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.15 on 2024-09-27 09:21 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("journals", "0137_alter_journal_alternative_journals"), + ] + + operations = [ + migrations.AddField( + model_name="journal", + name="autoreject_period", + field=models.DurationField(default=datetime.timedelta(days=28)), + ), + ] diff --git a/scipost_django/journals/models/journal.py b/scipost_django/journals/models/journal.py index 092f29930..97138a20e 100644 --- a/scipost_django/journals/models/journal.py +++ b/scipost_django/journals/models/journal.py @@ -84,6 +84,7 @@ class Journal(models.Model): ) refereeing_period = models.DurationField(default=datetime.timedelta(days=28)) + autoreject_period = models.DurationField(default=datetime.timedelta(days=28)) style = models.TextField( blank=True, diff --git a/scipost_django/scipost/templates/scipost/personal_page/_hx_submissions.html b/scipost_django/scipost/templates/scipost/personal_page/_hx_submissions.html index 40351b2ff..693b7ce73 100644 --- a/scipost_django/scipost/templates/scipost/personal_page/_hx_submissions.html +++ b/scipost_django/scipost/templates/scipost/personal_page/_hx_submissions.html @@ -45,6 +45,9 @@ <li><a class="btn btn-primary my-1 px-1 py-0" href="{% url 'submissions:accept_puboffer' sub.preprint.identifier_w_vn_nr %}">Accept offer for publication in {{ sub.editorial_decision.for_journal }} (one-click action)</a></li> {% endif %} {% endif %} + {% if sub.editor_in_charge is None and sub.autorejection_date is not None %} + <li><a href="{% url 'submissions:extend_autorejection_date' sub.preprint.identifier_w_vn_nr %}">Extend auto-rejection date (by {{ sub.submitted_to.autoreject_period.days }} days)</a></li> + {% endif %} <li><a href="{% url 'submissions:withdraw_manuscript' sub.preprint.identifier_w_vn_nr %}"><span class="text-danger">Withdraw</span> (leads to confirmation page)</a></li> {% endif %} </ul> diff --git a/scipost_django/submissions/forms/__init__.py b/scipost_django/submissions/forms/__init__.py index 29f4a329d..23b6c53a8 100644 --- a/scipost_django/submissions/forms/__init__.py +++ b/scipost_django/submissions/forms/__init__.py @@ -226,6 +226,7 @@ class SubmissionPoolSearchForm(forms.Form): orderby = forms.ChoiceField( label="Order by", choices=( + ("autorejection_date", "Auto-rejection in"), ("submission_date", "Submission date"), ("latest_activity", "Latest activity"), ), @@ -2323,6 +2324,7 @@ class WithdrawSubmissionForm(forms.Form): open_for_commenting=False, open_for_reporting=False, status=Submission.WITHDRAWN, + autorejection_date=None, latest_activity=timezone.now(), ) self.submission.get_other_versions().update(visible_public=False) @@ -2422,6 +2424,7 @@ class EditorialAssignmentForm(forms.ModelForm): status=Submission.IN_REFEREEING, editor_in_charge=self.request.user.contributor, reporting_deadline=None, + autorejection_date=None, open_for_reporting=True, open_for_commenting=True, visible_public=True, @@ -2441,6 +2444,7 @@ class EditorialAssignmentForm(forms.ModelForm): status=Submission.REFEREEING_CLOSED, editor_in_charge=self.request.user.contributor, reporting_deadline=timezone.now(), + autorejection_date=None, open_for_reporting=False, open_for_commenting=True, visible_public=visible_public, diff --git a/scipost_django/submissions/migrations/0163_submission_autorejection_date.py b/scipost_django/submissions/migrations/0163_submission_autorejection_date.py new file mode 100644 index 000000000..7634ec1b1 --- /dev/null +++ b/scipost_django/submissions/migrations/0163_submission_autorejection_date.py @@ -0,0 +1,37 @@ +# Generated by Django 4.2.15 on 2024-09-27 09:21 + +from django.db import migrations, models +from django.utils import timezone + + +def add_autorejection_date(apps, schema_editor): + Submission = apps.get_model("submissions", "Submission") + for submission in Submission.objects.filter( + status="seeking_assignment", + autorejection_date__isnull=True, + ): + submission.autorejection_date = ( + timezone.now() + submission.submitted_to.autoreject_period + if submission.submitted_to + else None + ) + submission.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("submissions", "0162_alter_refereeinvitation_referee"), + ] + + operations = [ + migrations.AddField( + model_name="submission", + name="autorejection_date", + field=models.DateField( + blank=True, null=True, verbose_name="autorejection date" + ), + ), + migrations.RunPython( + add_autorejection_date, reverse_code=migrations.RunPython.noop + ), + ] diff --git a/scipost_django/submissions/models/submission.py b/scipost_django/submissions/models/submission.py index dc7c6dbae..56a462701 100644 --- a/scipost_django/submissions/models/submission.py +++ b/scipost_django/submissions/models/submission.py @@ -430,6 +430,9 @@ class Submission(models.Model): completion_date = models.DateField( verbose_name="completion date", null=True, blank=True ) + autorejection_date = models.DateField( + verbose_name="autorejection date", null=True, blank=True + ) latest_activity = models.DateTimeField(auto_now=True) update_search_index = models.BooleanField(default=True) @@ -740,6 +743,16 @@ class Submission(models.Model): """Check if Submission has fixed EICRecommendation asking for revision.""" return self.status == self.AWAITING_RESUBMISSION + @property + def has_extended_autorejection_date(self): + """ + Check if Submission has had its autorejection date extended, by looking for the corresponding event. + """ + return self.events.filter( + text__icontains="autorejection date", + text__contains="extended", + ).exists() + @property def reporting_deadline_has_passed(self): """Check if Submission has passed its reporting deadline.""" diff --git a/scipost_django/submissions/templates/submissions/pool/_submission_details_summary_contents.html b/scipost_django/submissions/templates/submissions/pool/_submission_details_summary_contents.html index 440612606..3faf5e5fb 100644 --- a/scipost_django/submissions/templates/submissions/pool/_submission_details_summary_contents.html +++ b/scipost_django/submissions/templates/submissions/pool/_submission_details_summary_contents.html @@ -60,6 +60,14 @@ <br /> {{ submission.submission_date|date:'Y-m-d' }} </div> + + {% if submission.autorejection_date is not None %} + <div class="col"> + <small class="text-muted">Auto-rejection</small> + <br /> + in {{ submission.autorejection_date|timeuntil }} + </div> + {% endif %} {% endif %} <div class="col"> diff --git a/scipost_django/submissions/urls/__init__.py b/scipost_django/submissions/urls/__init__.py index 45a0fec75..6a6e6a4a3 100644 --- a/scipost_django/submissions/urls/__init__.py +++ b/scipost_django/submissions/urls/__init__.py @@ -358,6 +358,16 @@ urlpatterns = [ views.withdraw_manuscript, name="withdraw_manuscript", ), + path( + "<identifier:identifier_w_vn_nr>/extend_autorejection_date/<int:days>", + views.extend_autorejection_date, + name="extend_autorejection_date", + ), + path( + "<identifier:identifier_w_vn_nr>/extend_autorejection_date", + views.extend_autorejection_date, + name="extend_autorejection_date", + ), path( "update_authors_assignment/<identifier:identifier_w_vn_nr>/<int:nrweeks>", views.update_authors_assignment, diff --git a/scipost_django/submissions/views/__init__.py b/scipost_django/submissions/views/__init__.py index 66e116ac0..411d33033 100644 --- a/scipost_django/submissions/views/__init__.py +++ b/scipost_django/submissions/views/__init__.py @@ -610,6 +610,58 @@ def withdraw_manuscript(request, identifier_w_vn_nr): return render(request, "submissions/withdraw_manuscript.html", context) +@login_required +def extend_autorejection_date( + request, identifier_w_vn_nr: str, days: int | None = None +): + """ + Extend the auto-rejection date of a Submission. + + Accessible to EdAdmin (always) or authors of the Submission (for first extension). + """ + submission = get_object_or_404( + Submission, preprint__identifier_w_vn_nr=identifier_w_vn_nr + ) + + if submission.autorejection_date is None: + raise PermissionDenied("This Submission has no autorejection date.") + + is_submission_author = request.user.contributor in submission.authors.all() + if not (is_edadmin(request.user) or is_submission_author): + raise PermissionDenied("You are not allowed to extend the autorejection date.") + + # Compute the new auto-rejection date if days is given, otherwise use the default + # For authors, ignore the days even if given + extension_date = ( + submission.autorejection_date + timedelta(days=days) + if days is not None and is_edadmin(request.user) + else submission.autorejection_date + submission.submitted_to.autoreject_period + ) + + if extension_date < submission.autorejection_date: + raise PermissionDenied("You cannot set an earlier date.") + + if is_submission_author and submission.has_extended_autorejection_date: + raise PermissionDenied( + "You are not allowed to extend the autorejection date again." + ) + + submission.autorejection_date = extension_date + submission.save() + + submission.add_general_event( + f"The autorejection date has been extended to {extension_date}." + ) + messages.success(request, "The autorejection date has been extended.") + + return redirect( + reverse( + "submissions:submission", + kwargs={"identifier_w_vn_nr": identifier_w_vn_nr}, + ) + ) + + # Marked for deprecation class SubmissionListView(PaginationMixin, ListView): """List all publicly available Submissions.""" @@ -882,9 +934,7 @@ def report_attachment(request, identifier_w_vn_nr, report_nr): if not report.is_vetted: # Only Admins and EICs are allowed to see non-vetted Report attachments. if ( - not Submission.objects.in_pool_filter_for_eic( - request.user, historical=True, latest=False - ) + not Submission.objects.in_pool_filter_for_eic(request.user) .filter(preprint__identifier_w_vn_nr=identifier_w_vn_nr) .exists() ): -- GitLab