From e8dadc8a12850d7a39f2960118a0df2f9e20a519 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-S=C3=A9bastien=20Caux?= <git@jscaux.org>
Date: Mon, 5 Dec 2022 19:51:51 +0100
Subject: [PATCH] Add admissibility step to incoming phase

---
 scipost_django/edadmin/forms/__init__.py      | 10 ++++
 .../edadmin/forms/submission_admissibility.py | 55 +++++++++++++++++++
 .../_hx_submission_admissibility_form.html    |  9 +++
 .../_hx_submission_details_contents.html      |  1 +
 .../edadmin/_hx_submission_incoming.html      | 54 +++++++++++++-----
 .../edadmin/_hx_submissions_list.html         |  7 ++-
 scipost_django/edadmin/urls/incoming.py       |  5 ++
 scipost_django/edadmin/views/incoming.py      | 39 +++++++++++--
 scipost_django/submissions/forms.py           |  4 ++
 .../submissions/managers/submission.py        |  6 +-
 .../0129_alter_submission_status.py           | 18 ++++++
 .../submissions/models/submission.py          |  5 +-
 12 files changed, 189 insertions(+), 24 deletions(-)
 create mode 100644 scipost_django/edadmin/forms/__init__.py
 create mode 100644 scipost_django/edadmin/forms/submission_admissibility.py
 create mode 100644 scipost_django/edadmin/templates/edadmin/_hx_submission_admissibility_form.html
 create mode 100644 scipost_django/submissions/migrations/0129_alter_submission_status.py

diff --git a/scipost_django/edadmin/forms/__init__.py b/scipost_django/edadmin/forms/__init__.py
new file mode 100644
index 000000000..eb4736b71
--- /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 000000000..d4dd6d336
--- /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 000000000..0cca6a4f4
--- /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 8bbbb962c..a28e97f08 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 8f271f21a..c4a1e5835 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 608e654a6..f93ad210b 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>:&nbsp;{{ 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 545c11ad2..3724d492c 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 f8fcc0288..d6241eb59 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 77716af80..251c4331f 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 1eb8f2057..dcc66ff84 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 000000000..2a584bbb8
--- /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 fecc0f626..a293a3f4c 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,)
-- 
GitLab