From c27587eb0684251bfe99372f3a8a86fe3c7f4fb4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-S=C3=A9bastien=20Caux?= <git@jscaux.org>
Date: Fri, 20 Jan 2023 07:49:00 +0100
Subject: [PATCH] Work on ethics app (clearance, competing interests etc)

---
 scipost_django/SciPost_v1/urls.py             |   1 +
 scipost_django/ethics/admin.py                |   1 +
 scipost_django/ethics/forms.py                |  73 ++++++++++++
 .../migrations/0002_submissionclearance.py    |  29 +++++
 .../migrations/0003_auto_20230119_1520.py     |  21 ++++
 scipost_django/ethics/models.py               |  44 +++++++
 .../ethics/_hx_submission_clearance.html      |  33 ++++++
 ...hx_submission_competing_interest_form.html |  20 ++++
 .../ethics/_hx_submission_ethics.html         |  40 +++++++
 ...ubmission_competing_interests_details.html |  37 ++++++
 scipost_django/ethics/urls.py                 |  43 +++++++
 scipost_django/ethics/views.py                | 109 +++++++++++++++++-
 scipost_django/submissions/admin.py           |  13 +++
 ...137_alter_qualification_expertise_level.py |  18 +++
 .../submissions/models/qualification.py       |   1 +
 .../submissions/pool/_hx_appraisal.html       |  29 +++--
 .../pool/_hx_qualification_form.html          |   7 ++
 .../submissions/pool/_hx_submission_tab.html  |  39 ++-----
 .../_submission_details_summary_contents.html |  10 --
 .../_submission_qualifications_details.html   |  27 +++++
 20 files changed, 544 insertions(+), 51 deletions(-)
 create mode 100644 scipost_django/ethics/forms.py
 create mode 100644 scipost_django/ethics/migrations/0002_submissionclearance.py
 create mode 100644 scipost_django/ethics/migrations/0003_auto_20230119_1520.py
 create mode 100644 scipost_django/ethics/templates/ethics/_hx_submission_clearance.html
 create mode 100644 scipost_django/ethics/templates/ethics/_hx_submission_competing_interest_form.html
 create mode 100644 scipost_django/ethics/templates/ethics/_hx_submission_ethics.html
 create mode 100644 scipost_django/ethics/templates/ethics/_submission_competing_interests_details.html
 create mode 100644 scipost_django/ethics/urls.py
 create mode 100644 scipost_django/submissions/migrations/0137_alter_qualification_expertise_level.py
 create mode 100644 scipost_django/submissions/templates/submissions/pool/_submission_qualifications_details.html

diff --git a/scipost_django/SciPost_v1/urls.py b/scipost_django/SciPost_v1/urls.py
index 709938678..e70cda882 100644
--- a/scipost_django/SciPost_v1/urls.py
+++ b/scipost_django/SciPost_v1/urls.py
@@ -93,6 +93,7 @@ urlpatterns = [
     path("commentary/", include("commentaries.urls", namespace="_commentaries")),
     path("comments/", include("comments.urls", namespace="comments")),
     path("edadmin/", include("edadmin.urls", namespace="edadmin")),
+    path("ethics/", include("ethics.urls", namespace="ethics")),
     path("forums/", include("forums.urls", namespace="forums")),
     path("funders/", include("funders.urls", namespace="funders")),
     path("finances/", include("finances.urls", namespace="finances")),
diff --git a/scipost_django/ethics/admin.py b/scipost_django/ethics/admin.py
index ab83842bb..152796f4c 100644
--- a/scipost_django/ethics/admin.py
+++ b/scipost_django/ethics/admin.py
@@ -27,6 +27,7 @@ class CompetingInterestAdmin(admin.ModelAdmin):
     autocomplete_fields = (
         "profile",
         "related_profile",
+        "declared_by",
         "affected_submissions",
         "affected_publications",
     )
diff --git a/scipost_django/ethics/forms.py b/scipost_django/ethics/forms.py
new file mode 100644
index 000000000..3d97580ae
--- /dev/null
+++ b/scipost_django/ethics/forms.py
@@ -0,0 +1,73 @@
+__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, HTML, ButtonHolder, Submit
+from crispy_bootstrap5.bootstrap5 import FloatingField
+
+from .models import CompetingInterest
+
+from profiles.models import Profile
+
+
+class SubmissionCompetingInterestForm(forms.ModelForm):
+
+    class Meta:
+        model = CompetingInterest
+        fields = [
+            "profile",
+            "related_profile",
+            "nature",
+            "date_from",
+            "date_until",
+            "declared_by",
+            # "comments",
+        ]
+        widgets = {
+            "profile": forms.HiddenInput(),
+            "declared_by": forms.HiddenInput(),
+        }
+
+    def __init__(self, *args, **kwargs):
+        self.submission = kwargs.pop("submission")
+        super().__init__(*args, **kwargs)
+        self.helper = FormHelper()
+        self.fields["related_profile"].label = "With which author?"
+        self.fields["related_profile"].queryset = Profile.objects.filter(
+            id__in=[ap.profile.id for ap in self.submission.author_profiles.all()]
+        )
+        self.fields["nature"].label = "Nature? You are/have..."
+        self.helper.layout = Layout(
+            Div(
+                Div(Field("related_profile"), css_class="col-lg-6"),
+                Div(Field("nature"), css_class="col-lg-6"),
+                css_class="row mb-0",
+            ),
+            Div(
+                Div(Field("date_from"), css_class="col-lg-6"),
+                Div(Field("date_until"), css_class="col-lg-6"),
+                css_class="row mb-0",
+            ),
+            Div(
+                Div(
+                    Field("profile"),
+                    Field("declared_by"),
+                    HTML("<em class='text-danger'>This Submission will not be visible to you anymore</em>"),
+                    css_class="col-lg-6",
+                ),
+                Div(
+                    ButtonHolder(
+                        Submit(
+                            "submit",
+                            "Submit",
+                            css_class="btn btn-danger mt-auto",
+                        )
+                    ),
+                    css_class="col-lg-6",
+                ),
+                css_class="row",
+            )
+        )
diff --git a/scipost_django/ethics/migrations/0002_submissionclearance.py b/scipost_django/ethics/migrations/0002_submissionclearance.py
new file mode 100644
index 000000000..9a4b6163e
--- /dev/null
+++ b/scipost_django/ethics/migrations/0002_submissionclearance.py
@@ -0,0 +1,29 @@
+# Generated by Django 3.2.16 on 2023-01-19 14:06
+
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('scipost', '0040_auto_20210310_2026'),
+        ('profiles', '0035_alter_profile_title'),
+        ('submissions', '0137_alter_qualification_expertise_level'),
+        ('ethics', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='SubmissionClearance',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('asserted_on', models.DateTimeField(default=django.utils.timezone.now)),
+                ('comments', models.TextField(blank=True)),
+                ('asserted_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asserted_submission_clearances', to='scipost.contributor')),
+                ('profile', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='submission_clearances', to='profiles.profile')),
+                ('submission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='clearances', to='submissions.submission')),
+            ],
+        ),
+    ]
diff --git a/scipost_django/ethics/migrations/0003_auto_20230119_1520.py b/scipost_django/ethics/migrations/0003_auto_20230119_1520.py
new file mode 100644
index 000000000..1b33a5f50
--- /dev/null
+++ b/scipost_django/ethics/migrations/0003_auto_20230119_1520.py
@@ -0,0 +1,21 @@
+# Generated by Django 3.2.16 on 2023-01-19 14:20
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ethics', '0002_submissionclearance'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='submissionclearance',
+            options={'ordering': ['submission', 'profile']},
+        ),
+        migrations.AddConstraint(
+            model_name='submissionclearance',
+            constraint=models.UniqueConstraint(fields=('profile', 'submission'), name='unique_together_profile_submission'),
+        ),
+    ]
diff --git a/scipost_django/ethics/models.py b/scipost_django/ethics/models.py
index 9be3530cc..466196fb8 100644
--- a/scipost_django/ethics/models.py
+++ b/scipost_django/ethics/models.py
@@ -8,7 +8,51 @@ from django.utils import timezone
 from ethics.managers import CompetingInterestQuerySet
 
 
+class SubmissionClearance(models.Model):
+    """
+    Assertion that a Profile has no competing interests with regards to a Submission.
+    """
+
+    profile = models.ForeignKey(
+        "profiles.Profile",
+        on_delete=models.CASCADE,
+        related_name="submission_clearances",
+    )
+
+    submission = models.ForeignKey(
+        "submissions.Submission",
+        on_delete=models.CASCADE,
+        related_name="clearances",
+    )
+
+    asserted_by = models.ForeignKey(
+        "scipost.Contributor",
+        on_delete=models.CASCADE,
+        related_name="asserted_submission_clearances",
+    )
+    asserted_on = models.DateTimeField(default=timezone.now)
+
+    comments = models.TextField(blank=True)
+
+    class Meta:
+        constraints = [
+            models.UniqueConstraint(
+                fields=["profile", "submission"],
+                name="unique_together_profile_submission",
+            ),
+        ]
+        ordering =["submission", "profile"]
+
+
+    def __str__(self):
+        return f"{self.profile} clearance for {self.submission}"
+
+
 class CompetingInterest(models.Model):
+    """
+    Competing interest relating two Profiles, affecting Submissions and Publications.
+    """
+
     COAUTHOR = "coauthor"
     COLLEAGUE = "colleague"
     PhD_SUPERVISOR = "PhD_supervisor"
diff --git a/scipost_django/ethics/templates/ethics/_hx_submission_clearance.html b/scipost_django/ethics/templates/ethics/_hx_submission_clearance.html
new file mode 100644
index 000000000..4e522d5ea
--- /dev/null
+++ b/scipost_django/ethics/templates/ethics/_hx_submission_clearance.html
@@ -0,0 +1,33 @@
+<div class="row">
+  <div class="col">
+    {% if clearance %}
+      <div class="bg-white d-inline-flex p-2">
+	<span class="text-success">
+	  {% include "bi/check-square-fill.html" %}
+	  &nbsp;You are free of competing interests
+	</span>
+	<a class="text-danger ms-4"
+	   hx-get="{% url 'ethics:_hx_submission_clearance_revoke' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}"
+	   hx-target="#submission-{{ submission.pk }}-clearance"
+	>{% include "bi/trash-fill.html" %}&nbsp;<em>Revoke this declaration</em></a>
+      </div>
+    {% elif %}
+    {% else %}
+      <span class="text-danger">Please specify!</span>
+      <div class="row border border-danger p-2">
+	<div class="col">
+	  <a class="btn btn-sm btn-success text-white my-auto"
+	     hx-get="{% url 'ethics:_hx_submission_clearance_assert' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}"
+	     hx-target="#submission-{{ submission.pk }}-clearance"
+	     hx-confirm="Confirm that you have no competing interest with regards to this Submission?"
+	  >I have no competing interest<br>with regards to this Submission</a>
+	</div>
+	<div class="col">
+	  <a class="btn btn-sm btn-danger text-white my-auto" href="">
+	    I have a competing interest<br>(<em>aka conflict of interest</em>)
+	  </a>
+	</div>
+      </div>
+    {% endif %}
+  </div>
+</div>
diff --git a/scipost_django/ethics/templates/ethics/_hx_submission_competing_interest_form.html b/scipost_django/ethics/templates/ethics/_hx_submission_competing_interest_form.html
new file mode 100644
index 000000000..f6d396403
--- /dev/null
+++ b/scipost_django/ethics/templates/ethics/_hx_submission_competing_interest_form.html
@@ -0,0 +1,20 @@
+{% load crispy_forms_tags %}
+
+<div class="border border-danger p-2">
+  <h3>
+    Declare a competing interest
+    <a class="ms-4 px-1 py-0 btn btn-sm btn-warning text-white"
+       hx-get="{% url 'ethics:_hx_submission_ethics' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}"
+       hx-target="#submission-{{ submission.pk }}-ethics"
+    >
+      Cancel
+    </a>
+  </h3>
+  <form class="mt-2"
+	hx-post="{% url 'ethics:_hx_submission_competing_interest_form' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}"
+	hx-target="#submission-{{ submission.id }}-competing-interest-form"
+	hx-confirm="Declare this competing interest?<br><strong>(this Submission will not be visible to you anymore)</strong>"
+  >
+    {% crispy form %}
+  </form>
+</div>
diff --git a/scipost_django/ethics/templates/ethics/_hx_submission_ethics.html b/scipost_django/ethics/templates/ethics/_hx_submission_ethics.html
new file mode 100644
index 000000000..d8dead478
--- /dev/null
+++ b/scipost_django/ethics/templates/ethics/_hx_submission_ethics.html
@@ -0,0 +1,40 @@
+<div class="row">
+  <div class="col">
+    {% if clearance %}
+      <div class="bg-white d-inline-flex p-2">
+	<span class="text-success">
+	  {% include "bi/check-square-fill.html" %}
+	  &nbsp;You are free of competing interests
+	</span>
+	<a class="text-danger ms-4"
+	   hx-get="{% url 'ethics:_hx_submission_clearance_revoke' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}"
+	   hx-target="#submission-{{ submission.pk }}-ethics"
+	>{% include "bi/trash-fill.html" %}&nbsp;<em>Revoke this declaration</em></a>
+      </div>
+    {% else %}
+      <span class="text-danger">Please specify!</span>
+      <div class="row border border-danger p-2">
+	<div class="col">
+	  <a class="btn btn-sm btn-success text-white my-auto"
+	     hx-get="{% url 'ethics:_hx_submission_clearance_assert' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}"
+	     hx-target="#submission-{{ submission.pk }}-ethics"
+	     hx-confirm="Confirm that you have no competing interest with regards to this Submission?"
+	  >I have no competing interest<br>with regards to this Submission</a>
+	</div>
+	<div class="col">
+	  <a class="btn btn-sm btn-danger text-white my-auto"
+	     hx-get="{% url 'ethics:_hx_submission_competing_interest_form' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}"
+	     hx-target="#submission-{{ submission.pk }}-ethics"
+	  >
+	    I have a competing interest<br>(<em>aka conflict of interest</em>)
+	  </a>
+	</div>
+      </div>
+    {% endif %}
+  </div>
+</div>
+
+<div id="submission-{{ submission.pk }}-competing-interest-form"
+     class="col-lg-12"
+>
+</div>
diff --git a/scipost_django/ethics/templates/ethics/_submission_competing_interests_details.html b/scipost_django/ethics/templates/ethics/_submission_competing_interests_details.html
new file mode 100644
index 000000000..8552ae322
--- /dev/null
+++ b/scipost_django/ethics/templates/ethics/_submission_competing_interests_details.html
@@ -0,0 +1,37 @@
+<details class="border border-2 mx-3 my-2">
+  <summary class="bg-primary bg-opacity-10 p-2">
+    <h2>
+      Competing Interests
+    </h2>
+  </summary>
+  <table class="table table-bordered">
+    <thead>
+      <tr>
+	<th>Fellow</th>
+	<th>Nature</th>
+	<th>Related author Profile</th>
+	{% if request.user.contributor.is_ed_admin %}
+	  <th>EdAdmin actions</th>
+	{% endif %}
+      </tr>
+    </thead>
+    <tbody>
+      {% for ci in submission.competing_interests.all %}
+	<tr>
+	  <td>{{ ci.profile }}</td>
+	  <td>{{ ci.get_nature_display }}</td>
+	  <td>{{ ci.related_profile }}
+	    {% if request.user.contributor.is_ed_admin %}
+	      <td>
+		Delete
+	      </td>
+	    {% endif %}
+	</tr>
+      {% empty %}
+	<tr>
+	  <td colspan="2">No Fellow has declared competing interests</td>
+	</tr>
+      {% endfor %}
+    </tbody>
+  </table>
+</details>
diff --git a/scipost_django/ethics/urls.py b/scipost_django/ethics/urls.py
new file mode 100644
index 000000000..4f9ec5109
--- /dev/null
+++ b/scipost_django/ethics/urls.py
@@ -0,0 +1,43 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from django.urls import include, path
+
+from . import views
+
+app_name = "ethics"
+
+
+urlpatterns = [
+    path(
+        "<identifier:identifier_w_vn_nr>/_hx_submission_ethics/",
+        include([
+            path(
+                "",
+                views._hx_submission_ethics,
+                name="_hx_submission_ethics",
+            ),
+            path(
+                "clearance/",
+                include([
+                    path(
+                        "assert",
+                        views._hx_submission_clearance_assert,
+                        name="_hx_submission_clearance_assert",
+                    ),
+                    path(
+                        "revoke",
+                        views._hx_submission_clearance_revoke,
+                        name="_hx_submission_clearance_revoke",
+                    ),
+                ]),
+            ),
+            path(
+                "_hx_submission_competing_interest_form",
+                views._hx_submission_competing_interest_form,
+                name="_hx_submission_competing_interest_form",
+            ),
+        ]),
+    ),
+]
diff --git a/scipost_django/ethics/views.py b/scipost_django/ethics/views.py
index bd26fc01a..d9a144c6d 100644
--- a/scipost_django/ethics/views.py
+++ b/scipost_django/ethics/views.py
@@ -2,6 +2,111 @@ __copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
 __license__ = "AGPL v3"
 
 
-from django.shortcuts import render
+from django.contrib.auth.decorators import login_required
+from django.http import HttpResponse
+from django.shortcuts import get_object_or_404, render, redirect
+from django.urls import reverse
 
-# Create your views here.
+from ethics.models import SubmissionClearance, CompetingInterest
+from ethics.forms import SubmissionCompetingInterestForm
+
+from submissions.models import Submission
+
+
+@login_required
+def _hx_submission_ethics(request, identifier_w_vn_nr):
+    submission = get_object_or_404(
+        Submission.objects.in_pool(request.user),
+        preprint__identifier_w_vn_nr=identifier_w_vn_nr,
+    )
+    clearance = SubmissionClearance.objects.filter(
+            profile=request.user.contributor.profile,
+            submission=submission,
+    ).first()
+    competing_interest = CompetingInterest.objects.filter(
+        profile=request.user.contributor.profile,
+        affected_submissions=submission,
+    ).first()
+    context = {
+        "submission": submission,
+        "clearance": clearance,
+        "competing_interest": competing_interest,
+    }
+    return render(request, "ethics/_hx_submission_ethics.html", context)
+
+
+@login_required
+def _hx_submission_clearance_assert(request, identifier_w_vn_nr):
+    submission = get_object_or_404(
+        Submission.objects.in_pool(request.user),
+        preprint__identifier_w_vn_nr=identifier_w_vn_nr,
+    )
+    clearance, created = SubmissionClearance.objects.get_or_create(
+        profile=request.user.contributor.profile,
+        submission=submission,
+        asserted_by=request.user.contributor,
+    )
+    return redirect(
+        reverse(
+            "ethics:_hx_submission_ethics",
+            kwargs={"identifier_w_vn_nr": identifier_w_vn_nr,},
+        )
+    )
+
+
+@login_required
+def _hx_submission_clearance_revoke(request, identifier_w_vn_nr):
+    submission = get_object_or_404(
+        Submission.objects.in_pool(request.user),
+        preprint__identifier_w_vn_nr=identifier_w_vn_nr,
+    )
+    SubmissionClearance.objects.filter(
+        profile=request.user.contributor.profile,
+        submission=submission,
+        asserted_by=request.user.contributor, # can only revoke own clearances
+    ).delete()
+    return redirect(
+        reverse(
+            "ethics:_hx_submission_ethics",
+            kwargs={"identifier_w_vn_nr": identifier_w_vn_nr,},
+        )
+    )
+
+
+#######################
+# Competing interests #
+#######################
+
+@login_required
+def _hx_submission_competing_interest_form(request, identifier_w_vn_nr):
+    submission = get_object_or_404(
+        Submission.objects.in_pool(request.user),
+        preprint__identifier_w_vn_nr=identifier_w_vn_nr,
+    )
+    form = SubmissionCompetingInterestForm(
+        request.POST or None,
+        submission=submission,
+        initial={
+            "profile": request.user.contributor.profile,
+            "declared_by": request.user.contributor,
+        }
+    )
+    if form.is_valid():
+        instance = form.save()
+        instance.affected_submissions.add(submission)
+        response = render(
+            request,
+            "submissions/pool/_hx_appraisal.html",
+            context={"submission": submission},
+        )
+        response["HX-Retarget"] = f"#submission-{submission.id}-appraisal"
+        return response
+    context = {
+        "submission": submission,
+        "form": form,
+    }
+    return render(
+        request,
+        "ethics/_hx_submission_competing_interest_form.html",
+        context,
+    )
diff --git a/scipost_django/submissions/admin.py b/scipost_django/submissions/admin.py
index dd910f0d7..ba3cba905 100644
--- a/scipost_django/submissions/admin.py
+++ b/scipost_django/submissions/admin.py
@@ -28,6 +28,7 @@ from submissions.models import (
 )
 from scipost.models import Contributor
 from colleges.models import Fellowship
+from ethics.models import SubmissionClearance
 
 
 def submission_short_title(obj):
@@ -74,6 +75,17 @@ class QualificationInline(admin.StackedInline):
     ]
 
 
+class SubmissionClearanceInline(admin.StackedInline):
+    model = SubmissionClearance
+    extra = 0
+    min_num = 0
+    autocomplete_fields = [
+        "profile",
+        "submission",
+        "asserted_by",
+    ]
+
+
 class SubmissionAuthorProfileInline(admin.TabularInline):
     model = SubmissionAuthorProfile
     extra = 0
@@ -135,6 +147,7 @@ class SubmissionAdmin(GuardedModelAdmin):
         iThenticatePlagiarismAssessmentInline,
         SubmissionAuthorProfileInline,
         QualificationInline,
+        SubmissionClearanceInline,
         SubmissionTieringInline,
     ]
 
diff --git a/scipost_django/submissions/migrations/0137_alter_qualification_expertise_level.py b/scipost_django/submissions/migrations/0137_alter_qualification_expertise_level.py
new file mode 100644
index 000000000..c53c1b452
--- /dev/null
+++ b/scipost_django/submissions/migrations/0137_alter_qualification_expertise_level.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.16 on 2023-01-19 12:35
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('submissions', '0136_alter_submissionauthorprofile_profile'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='qualification',
+            name='expertise_level',
+            field=models.CharField(blank=True, choices=[('expert', 'Expert in this subject'), ('very_knowledgeable', 'Very knowledgeable in this subject'), ('knowledgeable', 'Knowledgeable in this subject'), ('marginally_qualified', 'Marginally qualified'), ('not_really_qualified', 'Not really qualified'), ('not_at_all_qualified', 'Not at all qualified')], max_length=32),
+        ),
+    ]
diff --git a/scipost_django/submissions/models/qualification.py b/scipost_django/submissions/models/qualification.py
index 8a7ef753e..dcf49a043 100644
--- a/scipost_django/submissions/models/qualification.py
+++ b/scipost_django/submissions/models/qualification.py
@@ -41,6 +41,7 @@ class Qualification(models.Model):
     expertise_level = models.CharField(
         max_length=32,
         choices=EXPERTISE_LEVEL_CHOICES,
+        blank=True,
     )
 
     comments = models.TextField(blank=True)
diff --git a/scipost_django/submissions/templates/submissions/pool/_hx_appraisal.html b/scipost_django/submissions/templates/submissions/pool/_hx_appraisal.html
index 58902f996..ddf45aada 100644
--- a/scipost_django/submissions/templates/submissions/pool/_hx_appraisal.html
+++ b/scipost_django/submissions/templates/submissions/pool/_hx_appraisal.html
@@ -9,20 +9,27 @@
        hx-trigger="revealed"
   >
   </div>
+
   {% if qualification and qualification.is_qualified %}
     <div class="col-lg-6">
-      <div>
-	<a class="btn btn-sm btn-success text-white" href="{% url 'submissions:pool:editorial_assignment' submission.preprint.identifier_w_vn_nr %}">I will take charge of this Submission</a>
-      </div>
-      <div>
-	<a class="btn btn-sm btn-danger text-white" href="">
-	  I have a conflict of interest
-	</a>
-      </div>
-      <div>
-	readiness form
+      <div id="submission-{{ submission.pk }}-ethics"
+	   hx-get="{% url 'ethics:_hx_submission_ethics' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}"
+	   hx-trigger="revealed"
+      >
       </div>
     </div>
-
   {% endif %}
 </div>
+
+
+{% if qualification and qualification.is_qualified %}
+  <div class="row">
+    <div class="col">
+      <a class="btn btn-sm btn-success text-white" href="{% url 'submissions:pool:editorial_assignment' submission.preprint.identifier_w_vn_nr %}">I will take charge of this Submission</a>
+    </div>
+    <div class=""col">
+      Not ready to take charge? Please specify:
+      readiness form
+    </div>
+  </div>
+{% endif %}
diff --git a/scipost_django/submissions/templates/submissions/pool/_hx_qualification_form.html b/scipost_django/submissions/templates/submissions/pool/_hx_qualification_form.html
index dd3dc7d57..8de3b4c41 100644
--- a/scipost_django/submissions/templates/submissions/pool/_hx_qualification_form.html
+++ b/scipost_django/submissions/templates/submissions/pool/_hx_qualification_form.html
@@ -1,5 +1,9 @@
 {% load crispy_forms_tags %}
 
+{% if not form.initial.expertise_level %}
+  <div class="border border-danger px-2">
+    <span class="text-danger">Please specify!</span>
+{% endif %}
 <form
     hx-post="{% url 'submissions:pool:_hx_qualification_form' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}"
     hx-target="#submission-{{ submission.id }}-qualification-form"
@@ -7,3 +11,6 @@
 >
   {% crispy form %}
 </form>
+{% if not form.initial.expertise_level %}
+  </div>
+{% endif %}
diff --git a/scipost_django/submissions/templates/submissions/pool/_hx_submission_tab.html b/scipost_django/submissions/templates/submissions/pool/_hx_submission_tab.html
index 5ecc3e285..1b03d5f40 100644
--- a/scipost_django/submissions/templates/submissions/pool/_hx_submission_tab.html
+++ b/scipost_django/submissions/templates/submissions/pool/_hx_submission_tab.html
@@ -8,7 +8,7 @@
     {% include "submissions/pool/_submission_tab_link.html" with submission=submission tab=tab target="info" text="Submission information" %}
   </li>
   <li class="nav-item">
-    {% include "submissions/pool/_submission_tab_link.html" with submission=submission tab=tab target="qualifications" text="Fellow qualifications" %}
+    {% include "submissions/pool/_submission_tab_link.html" with submission=submission tab=tab target="appraisals" text="Fellow appraisals" %}
   </li>
   <li class="nav-item">
     {% include "submissions/pool/_submission_tab_link.html" with submission=submission tab=tab target="refereeing" text="Refereeing history" %}
@@ -39,37 +39,20 @@
 <hr>
 <div class="tab-content">
   {% if tab == "info" %}
-    {% include 'submissions/_submission_summary.html' with submission=submission hide_title=1 show_abstract=1 %}
+    {% include "submissions/_submission_summary.html" with submission=submission hide_title=1 show_abstract=1 %}
 
-  {% elif tab == "qualifications" %}
-    <table class="table table-bordered">
-      <thead>
-	<tr>
-	  <th>Fellow</th><th>Qualification</th>
-	</tr>
-      </thead>
-      <tbody>
-	{% for qualification in submission.qualification_set.all %}
-	  <tr>
-	    <td>{{ qualification.fellow }}</td>
-	    <td>{{ qualification.get_expertise_level_display }}</td>
-	  </tr>
-	{% empty %}
-	  <tr>
-	    <td colspan="2">No Fellow has specified their qualification</td>
-	  </tr>
-	{% endfor %}
-      </tbody>
-    </table>
+  {% elif tab == "appraisals" %}
+    {% include "submissions/pool/_submission_qualifications_details.html" with submission=submission %}
+    {% include "ethics/_submission_competing_interests_details.html" with submission=submission %}
 
   {% elif tab == "remarks" %}
     {% if remark_form %}
-      {% include 'submissions/pool/_remark_form.html' with submission=submission form=remark_form auto_show=1 %}
+      {% include "submissions/pool/_remark_form.html" with submission=submission form=remark_form auto_show=1 %}
     {% endif %}
     <p class="mb-1">Current remarks:</p>
     <ul>
       {% for rem in submission.remarks.all %}
-        {% include 'scipost/_remark_li.html' with remark=rem %}
+        {% include "scipost/_remark_li.html" with remark=rem %}
       {% empty %}
         <li>No Remarks found.</li>
       {% endfor %}
@@ -81,15 +64,15 @@
   {% elif tab == "events" %}
     <div id="eventslist">
       {% if request.user.contributor.is_ed_admin %}
-	{% include 'submissions/_submission_events.html' with events=submission.events.for_edadmin %}
+	{% include "submissions/_submission_events.html" with events=submission.events.for_edadmin %}
       {% elif request.user.contributor == submission.editor_in_charge %}
-	{% include 'submissions/_submission_events.html' with events=submission.events.for_eic %}
+	{% include "submissions/_submission_events.html" with events=submission.events.for_eic %}
       {% else %}
-	{% include 'submissions/_submission_events.html' with events=submission.events.for_author %}
+	{% include "submissions/_submission_events.html" with events=submission.events.for_author %}
       {% endif %}
     </div>
 
-  {% elif tab == 'edadmin' and request.user.contributor.is_ed_admin %}
+  {% elif tab == "edadmin" and request.user.contributor.is_ed_admin %}
     <div id="submission-{{ submission.pk }}-tab-contents-edadmin"
 	 hx-get="{% url 'edadmin:_hx_submission_tab_contents_edadmin' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}"
 	 hx-trigger="load, submission-{{ submission.pk }}-tab-edadmin-updated"
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 d2a51079b..85443065d 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
@@ -75,16 +75,6 @@
 	      <strong>{{ submission.editor_in_charge }}</strong>
 	    {% endif %}
 	  </div>
-	{% else %}
-	  {% if request.user.contributor.is_ed_admin or request.user.contributor.is_active_senior_fellow %}
-	    <div class="col-md-4">
-	      <a class="btn btn-primary" href="{% url 'submissions:editor_invitations' submission.preprint.identifier_w_vn_nr %}"><small>View/update invitations</small></a>
-	    </div>
-	  {% endif %}
-	  <div class="col-md-4">
-	    <a class="btn btn-success text-white" href="{% url 'submissions:pool:editorial_assignment' submission.preprint.identifier_w_vn_nr %}"><small>Volunteer as EiC</small></a>
-	  </div>
-
 	{% endif %}
       </div>
     {% endif %}
diff --git a/scipost_django/submissions/templates/submissions/pool/_submission_qualifications_details.html b/scipost_django/submissions/templates/submissions/pool/_submission_qualifications_details.html
new file mode 100644
index 000000000..1dcf3fa47
--- /dev/null
+++ b/scipost_django/submissions/templates/submissions/pool/_submission_qualifications_details.html
@@ -0,0 +1,27 @@
+<details class="border border-2 mx-3 my-2">
+  <summary class="bg-primary bg-opacity-10 p-2">
+    <h2>
+      Qualifications
+    </h2>
+  </summary>
+  <table class="table table-bordered">
+    <thead>
+      <tr>
+	<th>Fellow</th>
+	<th>Qualification</th>
+      </tr>
+    </thead>
+    <tbody>
+      {% for qualification in submission.qualification_set.all %}
+	<tr>
+	  <td>{{ qualification.fellow }}</td>
+	  <td>{{ qualification.get_expertise_level_display }}</td>
+	</tr>
+      {% empty %}
+	<tr>
+	  <td colspan="2">No Fellow has specified their qualification</td>
+	</tr>
+      {% endfor %}
+    </tbody>
+  </table>
+</details>
-- 
GitLab