diff --git a/scipost_django/edadmin/forms/__init__.py b/scipost_django/edadmin/forms/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..eb4736b710aa490a79b53c668edc1c428839e86f --- /dev/null +++ b/scipost_django/edadmin/forms/__init__.py @@ -0,0 +1,10 @@ +__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)" +__license__ = "AGPL v3" + + +from .plagiarism import ( + InternalPlagiarismAssessmentForm, + iThenticatePlagiarismAssessmentForm, +) + +from .submission_admissibility import SubmissionAdmissibilityForm diff --git a/scipost_django/edadmin/forms/submission_admissibility.py b/scipost_django/edadmin/forms/submission_admissibility.py new file mode 100644 index 0000000000000000000000000000000000000000..d4dd6d336098cae57289be6aa2f159725a7de5e8 --- /dev/null +++ b/scipost_django/edadmin/forms/submission_admissibility.py @@ -0,0 +1,55 @@ +__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)" +__license__ = "AGPL v3" + + +from django import forms + +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Layout, Div, Field, ButtonHolder, Submit + + +class SubmissionAdmissibilityForm(forms.Form): + admissibility = forms.ChoiceField( + label="Can we proceed with consideration of this Submission?", + choices=( + ("pass", "Pass, carry on with plagiarism"), + ("fail", "Fail: desk reject and email authors"), + ), + widget=forms.RadioSelect, + required=True, + ) + comments_for_authors = forms.CharField( + widget=forms.Textarea(attrs={ + "placeholder": "For fail: message to be included in email for authors", + "rows": 5, + "cols": 80, + }), + required=False, + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.layout = Layout( + Div( + Div( + Field("admissibility"), + ButtonHolder(Submit("submit", "Submit", css_class="btn btn-primary")), + css_class="col col-lg-4", + ), + Div( + Field("comments_for_authors"), + css_class="col col-lg-8", + ), + css_class="row", + ) + ) + + def clean(self): + data = super().clean() + if (self.cleaned_data["admissibility"] == "fail" and + self.cleaned_data["comments_for_authors"] is None): + self.add_error( + None, + "Comments for authors must not be empty if marked as inadmissible" + ) diff --git a/scipost_django/edadmin/templates/edadmin/_hx_submission_admissibility_form.html b/scipost_django/edadmin/templates/edadmin/_hx_submission_admissibility_form.html new file mode 100644 index 0000000000000000000000000000000000000000..0cca6a4f40843f79cc23363debad834e88d0a97b --- /dev/null +++ b/scipost_django/edadmin/templates/edadmin/_hx_submission_admissibility_form.html @@ -0,0 +1,9 @@ +{% load crispy_forms_tags %} +<h3>Assess admissibility of this Submission</h3> +<form + class="mt-3" + hx-post="{% url 'edadmin:_hx_submission_admissibility' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}" + hx-target="#submission-{{ submission.pk }}-admissibility-form" +> + {% crispy form %} +</form> diff --git a/scipost_django/edadmin/templates/edadmin/_hx_submission_details_contents.html b/scipost_django/edadmin/templates/edadmin/_hx_submission_details_contents.html index 8bbbb962c80dc16f42abcd7747d55c5e3a96d407..a28e97f083b67e5764ececc136e4d6cee5b4d020 100644 --- a/scipost_django/edadmin/templates/edadmin/_hx_submission_details_contents.html +++ b/scipost_django/edadmin/templates/edadmin/_hx_submission_details_contents.html @@ -6,6 +6,7 @@ {% include "edadmin/_hx_submission_incoming.html" with submission=submission %} {% endif %} + <hr class="my-2"> <h1>Workflow diagram</h1> <button class="m-2 btn btn-primary workflowDiagram" id="submission-{{ submission.pk }}-workflow-diagram" diff --git a/scipost_django/edadmin/templates/edadmin/_hx_submission_incoming.html b/scipost_django/edadmin/templates/edadmin/_hx_submission_incoming.html index 8f271f21a30e18d94535370dd1a6d0b649fe8bbc..c4a1e5835d334e7653e3a2ef6c68aa7b9ed6f927 100644 --- a/scipost_django/edadmin/templates/edadmin/_hx_submission_incoming.html +++ b/scipost_django/edadmin/templates/edadmin/_hx_submission_incoming.html @@ -1,17 +1,41 @@ -<h1>Plagiarism</h1> -<div class="p-2"> - <div id="submission-{{ submission.pk }}-plagiarism-internal" - class="m-2" - hx-get="{% url 'edadmin:_hx_plagiarism_internal' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}" - hx-trigger="load, {{ submission.pk }}-plagiarism-internal-updated" - > - </div> +<h1>Admissibility</h1> +<div class="p-2 mb-4" id="submission-{{ submission.pk }}-admissibility"> + {% if submission.status == submission.INCOMING %} + <div id="submission-{{ submission.pk }}-admissibility-form" + class="m-2" + hx-get="{% url 'edadmin:_hx_submission_admissibility' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}" + hx-trigger="load" + > + </div> + {% elif submission.status == submission.ADMISSIBLE %} + <span class="px-2 py-1 bg-success text-white"> + Marked as admissible + </span> + {% elif submission.status == submission.ADMISSION_FAILED %} + <span class="px-2 py-1 bg-danger text-white"> + Admission failed (authors informed) + </span> + {% else %} + Inconsistent status: {{ submission.status }} + {% endif %} </div> -<div class="p-2"> - <div id="submission-{{ submission.pk }}-plagiarism-iThenticate" - class="m-2" - hx-get="{% url 'edadmin:_hx_plagiarism_iThenticate' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}" - hx-trigger="load, {{ submission.pk }}-plagiarism-iThenticate-updated" - > + +{% if submission.status == submission.ADMISSIBLE %} + <h1>Plagiarism</h1> + <div class="p-2 mb-4"> + <div id="submission-{{ submission.pk }}-plagiarism-internal" + class="m-2" + hx-get="{% url 'edadmin:_hx_plagiarism_internal' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}" + hx-trigger="load, {{ submission.pk }}-plagiarism-internal-updated" + > + </div> </div> -</div> + <div class="p-2"> + <div id="submission-{{ submission.pk }}-plagiarism-iThenticate" + class="m-2" + hx-get="{% url 'edadmin:_hx_plagiarism_iThenticate' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}" + hx-trigger="load, {{ submission.pk }}-plagiarism-iThenticate-updated" + > + </div> + </div> +{% endif %} diff --git a/scipost_django/edadmin/templates/edadmin/_hx_submissions_list.html b/scipost_django/edadmin/templates/edadmin/_hx_submissions_list.html index 608e654a6b76c74632f24749622f8aa7924d4015..f93ad210b0be60d7aa68e91269d65ac1a814c6b0 100644 --- a/scipost_django/edadmin/templates/edadmin/_hx_submissions_list.html +++ b/scipost_django/edadmin/templates/edadmin/_hx_submissions_list.html @@ -22,15 +22,16 @@ </li> <li class="list-inline-item float-end"> <ul class="list list-unstyled"> - <li class="mb-4"><a href="{% url 'submissions:submission' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}">{% include "bi/arrow-right-square-fill.html" %}submission page</a></li> - <li class="mb-4"><a href="{% url 'submissions:editorial_page' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}" class="text-danger">{% include "bi/arrow-right-square-fill.html" %}editorial page</a></li> + <li class="mb-2"><strong>Status</strong>: {{ submission.get_status_display }}</li> + <li class="mb-2"><a href="{% url 'submissions:submission' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}">{% include "bi/arrow-right-square-fill.html" %}submission page</a></li> + <li class="mb-2"><a href="{% url 'submissions:editorial_page' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}" class="text-danger">{% include "bi/arrow-right-square-fill.html" %}editorial page</a></li> </ul> </li> </ul> </summary> <div id="submission-{{ submission.pk }}-details-contents" hx-get="{% url 'edadmin:_hx_submission_details_contents' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}" - hx-trigger="toggle once from:#submission-{{ submission.pk }}-details" + hx-trigger="toggle once from:#submission-{{ submission.pk }}-details, submission-{{ submission.pk }}-details-updated" > </div> </details> diff --git a/scipost_django/edadmin/urls/incoming.py b/scipost_django/edadmin/urls/incoming.py index 545c11ad24ea88b59a749bfe2b70370df45104bc..3724d492c3281fbff4a964a03bfdb4239091a9d0 100644 --- a/scipost_django/edadmin/urls/incoming.py +++ b/scipost_django/edadmin/urls/incoming.py @@ -16,6 +16,11 @@ urlpatterns = [ path( "<identifier:identifier_w_vn_nr>/", include([ + path( + "_hx_submission_admissibility", + incoming._hx_submission_admissibility, + name="_hx_submission_admissibility", + ), path( "_hx_submission_details_contents", incoming._hx_submission_details_contents, diff --git a/scipost_django/edadmin/views/incoming.py b/scipost_django/edadmin/views/incoming.py index f8fcc02884ab3631d40e3a1666fbd4a501a136b8..d6241eb59cd88b3f0343245cfb537cfab4cd3a8a 100644 --- a/scipost_django/edadmin/views/incoming.py +++ b/scipost_django/edadmin/views/incoming.py @@ -4,7 +4,7 @@ __license__ = "AGPL v3" from django.contrib.auth.decorators import login_required, user_passes_test from django.http import HttpResponse -from django.shortcuts import get_object_or_404, redirect, render +from django.shortcuts import get_object_or_404, render from django.urls import reverse from guardian.shortcuts import get_objects_for_user @@ -17,9 +17,10 @@ from submissions.models import ( ) from submissions.forms import iThenticateReportForm -from edadmin.forms.plagiarism import ( +from edadmin.forms import ( InternalPlagiarismAssessmentForm, iThenticatePlagiarismAssessmentForm, + SubmissionAdmissibilityForm, ) @@ -31,9 +32,9 @@ def _hx_incoming_list(request): """ submissions = get_objects_for_user(request.user, "submissions.take_edadmin_actions") context = { - "phase": "incoming", - "submissions": submissions.incoming(), + "submissions": submissions.in_stage_incoming(), } + print(f"{len(submissions.in_stage_incoming()) = }") return render(request, "edadmin/_hx_submissions_list.html", context) @@ -47,6 +48,36 @@ def _hx_submission_details_contents(request, identifier_w_vn_nr): return render(request, "edadmin/_hx_submission_details_contents.html", context) +################# +# Admissibility # +################# +@login_required +@user_passes_test(is_edadmin) +def _hx_submission_admissibility(request, identifier_w_vn_nr): + submission = get_object_or_404( + Submission, preprint__identifier_w_vn_nr=identifier_w_vn_nr + ) + form = SubmissionAdmissibilityForm(request.POST or None) + if form.is_valid(): + if form.cleaned_data["admissibility"] == "pass": + Submission.objects.filter(pk=submission.id).update( + status=Submission.ADMISSIBLE + ) + else: # inadmissible, inform authors and set status to ADMISSION_FAILED + Submission.objects.filter(pk=submission.id).update( + status=Submission.ADMISSION_FAILED + ) + # send authors admission failed email + submission.refresh_from_db() + # trigger re-rendering of the details-contents div + response = HttpResponse() + response["HX-Trigger"] = f"submission-{submission.pk}-details-updated" + return response + context = {"submission": submission, "form": form,} + return render(request, "edadmin/_hx_submission_admissibility_form.html", context) + + + ######################## # Plagiarism: internal # ######################## diff --git a/scipost_django/submissions/forms.py b/scipost_django/submissions/forms.py index 77716af803c3b4b419edfb48a5d59d176cfe4c9b..251c4331f81fdfe893c205ce6eacd1e5290b7dc4 100644 --- a/scipost_django/submissions/forms.py +++ b/scipost_django/submissions/forms.py @@ -195,6 +195,10 @@ class SubmissionPoolSearchForm(forms.Form): "Incoming", ( (Submission.INCOMING, "Incoming: awaiting EdAdmin checks"), + ( + Submission.ADMISSIBLE, + "Admissible; undergoing plagiarism checks", + ), ), ), ( diff --git a/scipost_django/submissions/managers/submission.py b/scipost_django/submissions/managers/submission.py index 1eb8f2057509dd635a84f716d2d1474c32b087d8..dcc66ff842e120bd4d0f0e49e0b40d14c4de12dd 100644 --- a/scipost_django/submissions/managers/submission.py +++ b/scipost_django/submissions/managers/submission.py @@ -18,6 +18,9 @@ class SubmissionQuerySet(models.QuerySet): def incoming(self): return self.filter(status=self.model.INCOMING) + def admissible(self): + return self.filter(status=self.model.ADMISSIBLE) + def admission_failed(self): return self.filter(status=self.model.ADMISSION_FAILED) @@ -78,8 +81,9 @@ class SubmissionQuerySet(models.QuerySet): return self.filter(status=self.model.PUBLISHED) ### Managers for stages #### + def in_stage_incoming(self): - return self.filter(status=self.model.STAGE_INCOMING) + return self.filter(status__in=self.model.STAGE_INCOMING) def stage_incoming_completed(self): return self.filter(status__in=self.model.stage_incoming_completed_statuses) diff --git a/scipost_django/submissions/migrations/0129_alter_submission_status.py b/scipost_django/submissions/migrations/0129_alter_submission_status.py new file mode 100644 index 0000000000000000000000000000000000000000..2a584bbb8e70fc878fb4c060342144b393c0bd42 --- /dev/null +++ b/scipost_django/submissions/migrations/0129_alter_submission_status.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.16 on 2022-12-05 18:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('submissions', '0128_auto_20221205_0500'), + ] + + operations = [ + migrations.AlterField( + model_name='submission', + name='status', + field=models.CharField(choices=[('incoming', 'Submission incoming, awaiting EdAdmin'), ('admissible', 'Admissible, undergoing further admission checks'), ('admission_failed', 'Admission failed'), ('preassignment', 'In preassignment'), ('preassignment_failed', 'Preassignment failed'), ('seeking_assignment', 'Seeking assignment'), ('assignment_failed', 'Failed to assign Editor-in-charge; manuscript rejected'), ('refereeing_in_preparation', 'Refereeing in preparation'), ('in_refereeing', 'In refereeing'), ('refereeing_closed', 'Refereeing closed (awaiting author replies and EdRec)'), ('awaiting_resubmission', 'Awaiting resubmission'), ('resubmitted', 'Has been resubmitted'), ('voting_in_preparation', 'Voting in preparation'), ('in_voting', 'In voting'), ('awaiting_decision', 'Awaiting decision'), ('accepted_in_target', 'Accepted in target Journal'), ('accepted_alt_puboffer_waiting', 'Accepted in alternative Journal; awaiting puboffer acceptance'), ('accepted_alt', 'Accepted in alternative Journal'), ('rejected', 'Publication decision taken: reject'), ('withdrawn', 'Withdrawn by the Authors'), ('published', 'Published')], default='incoming', max_length=30), + ), + ] diff --git a/scipost_django/submissions/models/submission.py b/scipost_django/submissions/models/submission.py index fecc0f626fe75b0582b8021b76a105e29ce4bb07..a293a3f4c9adb0b7c493abb04a6869784ad377ff 100644 --- a/scipost_django/submissions/models/submission.py +++ b/scipost_django/submissions/models/submission.py @@ -49,6 +49,7 @@ class Submission(models.Model): # Possible statuses INCOMING = "incoming" + ADMISSIBLE = "admissible" ADMISSION_FAILED = "admission_failed" PREASSIGNMENT = "preassignment" PREASSIGNMENT_FAILED = "preassignment_failed" @@ -71,6 +72,7 @@ class Submission(models.Model): SUBMISSION_STATUSES = ( (INCOMING, "Submission incoming, awaiting EdAdmin"), + (ADMISSIBLE, "Admissible, undergoing further admission checks"), (ADMISSION_FAILED, "Admission failed"), (PREASSIGNMENT, "In preassignment"), (PREASSIGNMENT_FAILED, "Preassignment failed"), @@ -101,6 +103,7 @@ class Submission(models.Model): # Submissions which are currently under consideration UNDER_CONSIDERATION = ( INCOMING, + ADMISSIBLE, PREASSIGNMENT, SEEKING_ASSIGNMENT, REFEREEING_IN_PREPARATION, @@ -115,7 +118,7 @@ class Submission(models.Model): ) # Further handy sets - STAGE_INCOMING = (INCOMING, ADMISSION_FAILED) + STAGE_INCOMING = (INCOMING, ADMISSIBLE, ADMISSION_FAILED) STAGE_PREASSIGNMENT = (PREASSIGNMENT, PREASSIGNMENT_FAILED) STAGE_ASSIGNMENT = (SEEKING_ASSIGNMENT, ASSIGNMENT_FAILED) STAGE_REFEREEING_IN_PREPARATION = (REFEREEING_IN_PREPARATION,)