From abe43299875279adb7da20e5509903539c7d30bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-S=C3=A9bastien=20Caux?= <git@jscaux.org>
Date: Sun, 4 Dec 2022 16:10:53 +0100
Subject: [PATCH] Work on plagiarism assessments

---
 scipost_django/edadmin/forms/plagiarism.py    |  34 ++++++
 .../edadmin/_hx_plagiarism_iThenticate.html   | 111 +++++++++++-------
 .../edadmin/_hx_submission_incoming.html      |  20 ++++
 .../edadmin/_hx_submissions_list.html         |  23 +---
 scipost_django/edadmin/views/incoming.py      |  16 ++-
 scipost_django/submissions/admin.py           |  17 ++-
 ...204_1327.py => 0127_auto_20221204_1439.py} |  26 ++--
 .../submissions/models/iThenticate_report.py  |   6 +-
 .../models/plagiarism_assessment.py           |  23 +++-
 .../submissions/models/submission.py          |  12 --
 10 files changed, 190 insertions(+), 98 deletions(-)
 create mode 100644 scipost_django/edadmin/forms/plagiarism.py
 create mode 100644 scipost_django/edadmin/templates/edadmin/_hx_submission_incoming.html
 rename scipost_django/submissions/migrations/{0127_auto_20221204_1327.py => 0127_auto_20221204_1439.py} (77%)

diff --git a/scipost_django/edadmin/forms/plagiarism.py b/scipost_django/edadmin/forms/plagiarism.py
new file mode 100644
index 000000000..bd23e2045
--- /dev/null
+++ b/scipost_django/edadmin/forms/plagiarism.py
@@ -0,0 +1,34 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from django import forms
+
+from submissions.models import (
+    InternalPlagiarismAssessment,
+    iThenticatePlagiarismAssessment,
+)
+
+
+class InternalPlagiarismAssessmentForm(forms.ModelForm):
+    class Meta:
+        model = InternalPlagiarismAssessment
+        fields = [
+            "submission",
+            "status",
+            "passed_on",
+            "comments_for_edadmin",
+            "comments_for_authors",
+        ]
+
+
+class iThenticatePlagiarismAssessmentForm(forms.ModelForm):
+    class Meta:
+        model = iThenticatePlagiarismAssessment
+        fields = [
+            "submission",
+            "status",
+            "passed_on",
+            "comments_for_edadmin",
+            "comments_for_authors",
+        ]
diff --git a/scipost_django/edadmin/templates/edadmin/_hx_plagiarism_iThenticate.html b/scipost_django/edadmin/templates/edadmin/_hx_plagiarism_iThenticate.html
index 5076870e7..1ed0f83bf 100644
--- a/scipost_django/edadmin/templates/edadmin/_hx_plagiarism_iThenticate.html
+++ b/scipost_django/edadmin/templates/edadmin/_hx_plagiarism_iThenticate.html
@@ -1,49 +1,70 @@
 {% load bootstrap %}
 
-{% if submission.iThenticate_plagiarism_report %}
-  <table>
-    <tr>
-      <td style="min-width: 150px;">iThenticate document</td>
-      <td>{{ submission.iThenticate_plagiarism_report.doc_id }}</td>
-    </tr>
-    <tr>
-      <td>Percent match</td>
-      <td>{{ submission.iThenticate_plagiarism_report.percent_match }}%</td>
-    </tr>
-    <tr>
-      <td>Processed</td>
-      <td>{{ submission.iThenticate_plagiarism_report.processed_time }}</td>
-    </tr>
-    <tr>
-      <td>Uploaded</td>
-      <td>{{ submission.iThenticate_plagiarism_report.uploaded_time }}</td>
-    </tr>
-    <tr>
-      <td>Latest update</td>
-      <td>{{ submission.iThenticate_plagiarism_report.latest_activity }}</td>
-    </tr>
-  </table>
-{% else %}
-  No Plagiarism Report found.
-{% endif %}
+<div class="row">
+  <div class="col">
+    <h3>iThenticate report</h3>
+    {% if submission.iThenticate_plagiarism_report %}
+      <table>
+	<tr>
+	  <td>Latest update</td>
+	  <td>{{ submission.iThenticate_plagiarism_report.latest_activity }}</td>
+	</tr>
+	<tr>
+	  <td style="min-width: 150px;">iThenticate document</td>
+	  <td>{{ submission.iThenticate_plagiarism_report.doc_id }}</td>
+	</tr>
+	<tr>
+	  <td>Uploaded</td>
+	  <td>{{ submission.iThenticate_plagiarism_report.uploaded_time }}</td>
+	</tr>
+	{% if submission.iThenticate_plagiarism_report.processed %}
+	  <tr>
+	    <td>Processed</td>
+	    <td>{{ submission.iThenticate_plagiarism_report.processed_time }}</td>
+	  </tr>
+	  <tr>
+	    <td>Percent match</td>
+	    <td>{{ submission.iThenticate_plagiarism_report.percent_match }}%</td>
+	  </tr>
+	  <tr>
+	    <td><a href="{% url 'submissions:iThenticate_plagiarism_report' submission.preprint.identifier_w_vn_nr %}" class="btn btn-success text-white" target="_blank">Download report pdf</a></td>
+	  </tr>
+	{% else %}
+	  <tr>
+	    <td>Report not yet received</td>
+	  </tr>
+	{% endif %}
+      </table>
+    {% else %}
+      <p>No report yet</p>
+    {% endif %}
+    {% if not submission.iThenticate_plagiarism_report.processed %}
+      <form
+	  class="mt-3" enctype="multipart/form-data"
+	  hx-post="{% url 'edadmin:_hx_plagiarism_iThenticate' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}"
+	hx-target="#submission-{{ submission.pk }}-iThenticate"
+	hx-indicator="#indicator-{{ submission.pk }}-iThenticate"
+	>
+	{% csrf_token %}
+	{{ form|bootstrap }}
+	<input type="submit" class="btn btn-primary" value="{% if submission.iThenticate_plagiarism_report %}Update report status{% else %}Submit document for plagiarism check{% endif %}">
+      </form>
+      <div id="indicator-{{ submission.pk }}-iThenticate" class="htmx-indicator">
+	<button class="btn btn-sm btn-warning" type="button" disabled>
+	  <strong>Loading...</strong>
+	  <div class="spinner-grow spinner-grow-sm ms-2" role="status" aria-hidden="true"></div>
+	</button>
+      </div>
+    {% endif %}
+  </div>
 
-<form
-    class="mt-3" enctype="multipart/form-data"
-    hx-post="{% url 'edadmin:_hx_plagiarism_iThenticate' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}"
-    hx-target="#submission-{{ submission.pk }}-iThenticate"
-    hx-indicator="#indicator-{{ submission.pk }}-iThenticate"
->
-  {% csrf_token %}
-  {{ form|bootstrap }}
-  <input type="submit" class="btn btn-primary" value="{% if submission.iThenticate_plagiarism_report %}Update report status{% else %}Submit submission for plagiarism check{% endif %}">
-</form>
-<div id="indicator-{{ submission.pk }}-iThenticate" class="htmx-indicator">
-  <button class="btn btn-sm btn-warning" type="button" disabled>
-    <strong>Loading...</strong>
-    <div class="spinner-grow spinner-grow-sm ms-2" role="status" aria-hidden="true"></div>
-  </button>
-</div>
+  <div class="col">
+    <h3>Assessment</h3>
+    {% if submission.iThenticate_plagiarism_assessment %}
+      {{ submission.iThenticate_plagiarism_assessment }}
+    {% else %}
+      TODO: assessment form
+    {% endif %}
+  </div>
 
-{% if submission.iThenticate_plagiarism_report %}
-  <a href="{% url 'submissions:iThenticate_plagiarism_report' submission.preprint.identifier_w_vn_nr %}" class="ms-2 btn btn-default">Download report pdf</a>
-{% endif %}
+</div>
diff --git a/scipost_django/edadmin/templates/edadmin/_hx_submission_incoming.html b/scipost_django/edadmin/templates/edadmin/_hx_submission_incoming.html
new file mode 100644
index 000000000..4f3ef8e7d
--- /dev/null
+++ b/scipost_django/edadmin/templates/edadmin/_hx_submission_incoming.html
@@ -0,0 +1,20 @@
+<h1>Plagiarism</h1>
+<div class="p-2">
+  <h2>Internal plagiarism checks</h2>
+  <button class="m-2 btn btn-primary"
+		 id="submission-{{ submission.preprint.identifier_w_vn_nr }}"
+		 hx-get="{% url 'edadmin:_hx_plagiarism_internal' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}"
+		 hx-swap="outerHTML"
+		 hx-trigger="revealed"
+  >load internal plagiarism info</button>
+</div>
+<div class="p-2">
+  <h2>iThenticate checks {% if submission.iThenticate_plagiarism_assessment %}{% if submission.iThenticate_plagiarism_assessment.passed %}[passed]{% elif submission.iThenticate_plagiarism_assessment.ongoing %}[ongoing]{% elif submission.iThenticate_plagiarism_assessment.failed %}[failed]{% else %}[STATUS ERROR] {{ submission.iThenticate_plagiarism_assessment.get_status_display }}{% endif %}{% else %}[not assessed yet]{% endif %}</h2>
+  <div id="submission-{{ submission.pk }}-iThenticate">
+    <button class="m-2 btn btn-primary"
+	    hx-get="{% url 'edadmin:_hx_plagiarism_iThenticate' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}"
+	    hx-swap="outerHTML"
+	    hx-trigger="revealed"
+    >load iThenticate plagiarism info</button>
+  </div>
+</div>
diff --git a/scipost_django/edadmin/templates/edadmin/_hx_submissions_list.html b/scipost_django/edadmin/templates/edadmin/_hx_submissions_list.html
index cf0ca8b1d..757f87afa 100644
--- a/scipost_django/edadmin/templates/edadmin/_hx_submissions_list.html
+++ b/scipost_django/edadmin/templates/edadmin/_hx_submissions_list.html
@@ -20,26 +20,9 @@
       <h1>Summary</h1>
       {% include 'submissions/_submission_summary.html' with submission=submission hide_title=1 show_abstract=1 %}
 
-      <h1>Plagiarism</h1>
-      <div class="p-2">
-	<h2>Internal plagiarism checks</h2>
-	<button class="m-2 btn btn-primary"
-		id="submission-{{ submission.preprint.identifier_w_vn_nr }}"
-		hx-get="{% url 'edadmin:_hx_plagiarism_internal' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}"
-		hx-swap="outerHTML"
-		hx-trigger="revealed"
-	>load internal plagiarism info</button>
-      </div>
-      <div class="p-2">
-	<h2>iThenticate checks</h2>
-	<div id="submission-{{ submission.pk }}-iThenticate">
-	  <button class="m-2 btn btn-primary"
-		  hx-get="{% url 'edadmin:_hx_plagiarism_iThenticate' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}"
-		  hx-swap="outerHTML"
-		  hx-trigger="revealed"
-	  >load iThenticate plagiarism info</button>
-	</div>
-      </div>
+      {% if phase == "incoming" %}
+	{% include "edadmin/_hx_submission_incoming.html" with submission=submission %}
+      {% endif %}
 
       <h1>Workflow diagram</h1>
       <button class="m-2 btn btn-primary workflowDiagram"
diff --git a/scipost_django/edadmin/views/incoming.py b/scipost_django/edadmin/views/incoming.py
index f78658efa..b9dd0f279 100644
--- a/scipost_django/edadmin/views/incoming.py
+++ b/scipost_django/edadmin/views/incoming.py
@@ -19,7 +19,10 @@ def _hx_incoming_list(request):
     EdAdmin page for incoming Submissions.
     """
     submissions = get_objects_for_user(request.user, "submissions.take_edadmin_actions")
-    context = { "submissions": submissions.incoming() }
+    context = {
+        "phase": "incoming",
+        "submissions": submissions.incoming(),
+    }
     return render(request, "edadmin/_hx_submissions_list.html", context)
 
 
@@ -55,6 +58,17 @@ def _hx_plagiarism_internal(request, identifier_w_vn_nr):
     return render(request, "edadmin/_hx_plagiarism_internal.html", context)
 
 
+@login_required
+@user_passes_test(is_edadmin)
+def _hx_plagiarism_internal_assess(request, identifier_w_vn_nr):
+    submission = get_object_or_404(
+        Submission, preprint__identifier_w_vn_nr=identifier_w_vn_nr
+    )
+    form = InternalPlagiarismAssessmentForm(request.POST or None)
+    if form.is_valid():
+        form.save()
+
+
 @login_required
 @user_passes_test(is_edadmin)
 def _hx_plagiarism_iThenticate(request, identifier_w_vn_nr):
diff --git a/scipost_django/submissions/admin.py b/scipost_django/submissions/admin.py
index 78fc925a8..c7b83302b 100644
--- a/scipost_django/submissions/admin.py
+++ b/scipost_django/submissions/admin.py
@@ -20,6 +20,8 @@ from submissions.models import (
     EditorialDecision,
     SubmissionEvent,
     iThenticateReport,
+    InternalPlagiarismAssessment,
+    iThenticatePlagiarismAssessment,
     PreprintServer,
 )
 from scipost.models import Contributor
@@ -52,6 +54,14 @@ class iThenticateReportAdmin(admin.ModelAdmin):
 admin.site.register(iThenticateReport, iThenticateReportAdmin)
 
 
+class InternalPlagiarismAssessmentInline(admin.StackedInline):
+    model = InternalPlagiarismAssessment
+
+
+class iThenticatePlagiarismAssessmentInline(admin.StackedInline):
+    model = iThenticatePlagiarismAssessment
+
+
 class SubmissionTieringInline(admin.StackedInline):
     model = SubmissionTiering
     extra = 0
@@ -100,6 +110,8 @@ class SubmissionAdmin(GuardedModelAdmin):
         "topics",
     ]
     inlines = [
+        InternalPlagiarismAssessmentInline,
+        iThenticatePlagiarismAssessmentInline,
         SubmissionTieringInline,
     ]
 
@@ -166,7 +178,10 @@ class SubmissionAdmin(GuardedModelAdmin):
             "Plagiarism",
             {
                 "classes": ("collapse",),
-                "fields": ("internal_plagiarism_matches", "plagiarism_report"),
+                "fields": (
+                    "internal_plagiarism_matches",
+                    "iThenticate_plagiarism_report",
+                ),
             }
         ),
         (
diff --git a/scipost_django/submissions/migrations/0127_auto_20221204_1327.py b/scipost_django/submissions/migrations/0127_auto_20221204_1439.py
similarity index 77%
rename from scipost_django/submissions/migrations/0127_auto_20221204_1327.py
rename to scipost_django/submissions/migrations/0127_auto_20221204_1439.py
index c7cba2353..8ebacbdbd 100644
--- a/scipost_django/submissions/migrations/0127_auto_20221204_1327.py
+++ b/scipost_django/submissions/migrations/0127_auto_20221204_1439.py
@@ -1,4 +1,4 @@
-# Generated by Django 3.2.16 on 2022-12-04 12:27
+# Generated by Django 3.2.16 on 2022-12-04 13:39
 
 from django.db import migrations, models
 import django.db.models.deletion
@@ -26,17 +26,11 @@ class Migration(migrations.Migration):
             old_name='plagiarism_report',
             new_name='iThenticate_plagiarism_report',
         ),
-        migrations.CreateModel(
-            name='InternalPlagiarismAssessment',
-            fields=[
-                ('plagiarismassessment_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='submissions.plagiarismassessment')),
-            ],
-            bases=('submissions.plagiarismassessment',),
-        ),
         migrations.CreateModel(
             name='iThenticatePlagiarismAssessment',
             fields=[
                 ('plagiarismassessment_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='submissions.plagiarismassessment')),
+                ('submission', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='iThenticate_plagiarism_assessment', to='submissions.submission')),
             ],
             options={
                 'verbose_name': 'iThenticate plagiarism assessment',
@@ -44,14 +38,12 @@ class Migration(migrations.Migration):
             },
             bases=('submissions.plagiarismassessment',),
         ),
-        migrations.AddField(
-            model_name='submission',
-            name='iThenticate_plagiarism_assessment',
-            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='submissions.ithenticateplagiarismassessment'),
-        ),
-        migrations.AddField(
-            model_name='submission',
-            name='internal_plagiarism_assessment',
-            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='submissions.internalplagiarismassessment'),
+        migrations.CreateModel(
+            name='InternalPlagiarismAssessment',
+            fields=[
+                ('plagiarismassessment_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='submissions.plagiarismassessment')),
+                ('submission', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='internal_plagiarism_assessment', to='submissions.submission')),
+            ],
+            bases=('submissions.plagiarismassessment',),
         ),
     ]
diff --git a/scipost_django/submissions/models/iThenticate_report.py b/scipost_django/submissions/models/iThenticate_report.py
index 9f83da854..010a0510b 100644
--- a/scipost_django/submissions/models/iThenticate_report.py
+++ b/scipost_django/submissions/models/iThenticate_report.py
@@ -5,7 +5,7 @@ __license__ = "AGPL v3"
 from django.db import models
 from django.urls import reverse
 
-from ..constants import PLAGIARISM_STATUSES, STATUS_WAITING
+from ..constants import PLAGIARISM_STATUSES, STATUS_WAITING, STATUS_RECEIVED
 
 from scipost.behaviors import TimeStampedModel
 
@@ -66,6 +66,10 @@ class iThenticateReport(TimeStampedModel):
         plagiarism = iThenticate()
         return plagiarism.get_url(self.part_id)
 
+    @property
+    def processed(self):
+        return self.processed_time is not None
+
     @property
     def score(self):
         """Return the iThenticate score returned by their API as saved in the database."""
diff --git a/scipost_django/submissions/models/plagiarism_assessment.py b/scipost_django/submissions/models/plagiarism_assessment.py
index 6f9b52629..c4b3b89cb 100644
--- a/scipost_django/submissions/models/plagiarism_assessment.py
+++ b/scipost_django/submissions/models/plagiarism_assessment.py
@@ -25,16 +25,37 @@ class PlagiarismAssessment(models.Model):
     comments_for_edadmin = models.TextField()
     comments_for_authors = models.TextField()
 
+    @property
+    def ongoing(self):
+        return self.status == self.STATUS_ONGOING
+
     @property
     def passed(self):
         return self.status == self.STATUS_PASSED
 
+    @property
+    def failed(self):
+        return self.status in [
+            self.STATUS_FAILED_TEMPORARY,
+            self.STATUS_FAILED_PERMANENT,
+        ]
+
 
 class InternalPlagiarismAssessment(PlagiarismAssessment):
-    pass
+    submission = models.OneToOneField(
+        "submissions.Submission",
+        on_delete=models.CASCADE,
+        related_name="internal_plagiarism_assessment",
+    )
 
 
 class iThenticatePlagiarismAssessment(PlagiarismAssessment):
+    submission = models.OneToOneField(
+        "submissions.Submission",
+        on_delete=models.CASCADE,
+        related_name="iThenticate_plagiarism_assessment",
+    )
+
     class Meta:
         verbose_name = "iThenticate plagiarism assessment"
         verbose_name_plural = "iThenticate plagiarism assessments"
diff --git a/scipost_django/submissions/models/submission.py b/scipost_django/submissions/models/submission.py
index 8fa980d88..c4933ce75 100644
--- a/scipost_django/submissions/models/submission.py
+++ b/scipost_django/submissions/models/submission.py
@@ -259,12 +259,6 @@ class Submission(models.Model):
         blank=True,
         null=True,
     )
-    internal_plagiarism_assessment = models.OneToOneField(
-        "submissions.InternalPlagiarismAssessment",
-        on_delete=models.CASCADE,
-        blank=True,
-        null=True,
-    )
     iThenticate_plagiarism_report = models.OneToOneField(
         "submissions.iThenticateReport",
         on_delete=models.SET_NULL,
@@ -272,12 +266,6 @@ class Submission(models.Model):
         null=True,
         related_name="to_submission",
     )
-    iThenticate_plagiarism_assessment = models.OneToOneField(
-        "submissions.iThenticatePlagiarismAssessment",
-        on_delete=models.CASCADE,
-        blank=True,
-        null=True,
-    )
 
     # Refereeing pack
     pdf_refereeing_pack = models.FileField(
-- 
GitLab