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