SciPost Code Repository

Skip to content
Snippets Groups Projects
Commit 6dd1511a authored by Jean-Sébastien Caux's avatar Jean-Sébastien Caux
Browse files

Merge branch 'dev_GK_20230908_submission_fellowship_update' into 'master'

[Submissions] Add periodic updates on submission fellowships

See merge request !60
parents 2396c367 6cef6411
No related branches found
No related tags found
1 merge request!60[Submissions] Add periodic updates on submission fellowships
......@@ -14,3 +14,7 @@ python manage.py organization_update_cf_expenditure_for_publication --settings=S
python manage.py organization_update_cf_balance_info --settings=SciPost_v1.settings.production_do1
python manage.py affiliatejournal_update_publications_from_Crossref --settings=SciPost_v1.settings.production_do1
# Update the fellowship of submissions for new fellows
python manage.py update_submission_fellowships --settings=SciPost_v1.settings.production_do1 >
\ No newline at end of file
......@@ -506,6 +506,12 @@ LOGGING = {
"filename": "/path/to/logs/oauth.log",
"formatter": "verbose",
},
"submission_fellowship_updates": {
"level": "INFO",
"class": "logging.FileHandler",
"filename": "/path/to/logs/submission_fellowship_updates.log",
"formatter": "verbose",
},
},
"loggers": {
"scipost.services.arxiv": {
......@@ -538,6 +544,12 @@ LOGGING = {
"propagate": False,
"formatter": "verbose",
},
"submissions.fellowships.update": {
"handlers": ["submission_fellowship_updates"],
"level": "INFO",
"propagate": False,
"formatter": "verbose",
},
},
}
......
......@@ -74,3 +74,34 @@ class SubmissionCompetingInterestForm(forms.ModelForm):
css_class="row",
),
)
class SubmissionCompetingInterestTableRowForm(SubmissionCompetingInterestForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["profile"].widget = forms.Select(
choices=[(self.initial["profile"].id, str(self.initial["profile"]))],
)
self.fields["declared_by"].widget = forms.Select(
choices=(
list(self.fields["related_profile"].choices)
+ [(self.initial["profile"].id, str(self.initial["profile"]))]
+ [(self.initial["declared_by"].id, str(self.initial["declared_by"]))]
),
)
self.helper.layout = Layout(
Div(
FloatingField("profile", wrapper_class="mb-0"),
FloatingField("related_profile", wrapper_class="mb-0"),
FloatingField("nature", wrapper_class="mb-0"),
FloatingField("date_from", wrapper_class="mb-0"),
FloatingField("date_until", wrapper_class="mb-0"),
FloatingField("declared_by", wrapper_class="mb-0"),
ButtonHolder(Submit("submit", "Declare")),
css_class="d-flex justify-content-between align-items-center",
)
)
self.fields["related_profile"].label = "Submission author"
self.fields["nature"].label = "Nature"
{% load crispy_forms_tags %}
<td colspan="7">
<form hx-post="{% url "ethics:_hx_submission_competing_interest_create" identifier_w_vn_nr fellowship_id %}"
hx-target="closest tr"
hx-swap="outerHTML">
{% crispy form %}
</form>
</td>
......@@ -41,6 +41,11 @@ urlpatterns = [
views._hx_submission_competing_interest_form,
name="_hx_submission_competing_interest_form",
),
path(
"_hx_submission_competing_interest_create/<int:fellowship_id>",
views._hx_submission_competing_interest_create,
name="_hx_submission_competing_interest_create",
),
path(
"_hx_submission_competing_interest_delete/<int:pk>",
views._hx_submission_competing_interest_delete,
......
......@@ -8,9 +8,13 @@ from django.shortcuts import get_object_or_404, render, redirect
from django.urls import reverse
from ethics.models import SubmissionClearance, CompetingInterest
from ethics.forms import SubmissionCompetingInterestForm
from ethics.forms import (
SubmissionCompetingInterestForm,
SubmissionCompetingInterestTableRowForm,
)
from colleges.permissions import is_edadmin
from colleges.models.fellowship import Fellowship
from submissions.models import Submission
......@@ -134,3 +138,46 @@ def _hx_submission_competing_interest_delete(request, identifier_w_vn_nr, pk):
"submissions/pool/_submission_fellows.html",
context,
)
@login_required
@user_passes_test(is_edadmin)
def _hx_submission_competing_interest_create(
request, identifier_w_vn_nr, fellowship_id
):
submission = get_object_or_404(
Submission.objects.in_pool(request.user),
preprint__identifier_w_vn_nr=identifier_w_vn_nr,
)
fellowship = get_object_or_404(Fellowship, id=fellowship_id)
initial = {
"profile": fellowship.contributor.profile,
"declared_by": request.user.contributor,
}
if submission.author_profiles.count() == 1:
initial["related_profile"] = submission.author_profiles.first().profile
form = SubmissionCompetingInterestTableRowForm(
request.POST or None,
submission=submission,
initial=initial,
)
if form.is_valid():
instance = form.save()
instance.affected_submissions.add(submission)
return render(
request,
"submissions/pool/_hx_submission_fellow_row.html",
context={"submission": submission, "fellowship": fellowship},
)
context = {
"submission": submission,
"form": form,
"identifier_w_vn_nr": identifier_w_vn_nr,
"fellowship_id": fellowship_id,
}
return render(
request,
"ethics/_hx_submission_competing_interest_create.html",
context,
)
__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
__license__ = "AGPL v3"
from django.core.management.base import BaseCommand
from ...models import Submission
import logging
class Command(BaseCommand):
help = "Update the fellowships of submissions without EiC assignment."
def handle(self, *args, **kwargs):
fellowship_updates_logger = logging.getLogger("submissions.fellowships.update")
status_no_eic = [
Submission.INCOMING,
Submission.ADMISSIBLE,
Submission.PREASSIGNMENT,
Submission.SEEKING_ASSIGNMENT,
]
for submission in Submission.objects.filter(status__in=status_no_eic):
old_fellowship = set(submission.fellows.all())
new_fellowship = set(submission.get_fellowship())
to_add_fellows = new_fellowship - old_fellowship
if len(to_add_fellows) == 0:
continue
# Only add fellowships and not remove them
submission.fellows.add(*to_add_fellows)
update_msg = (
"Updated the fellowships of {submission} with: {added_fellows}".format(
submission=submission.preprint.identifier_w_vn_nr,
added_fellows=", ".join(
map(
lambda f: f.contributor.user.get_full_name(),
to_add_fellows,
)
),
)
)
fellowship_updates_logger.info(update_msg)
self.stdout.write(self.style.SUCCESS(update_msg))
......@@ -3,6 +3,7 @@ __license__ = "AGPL v3"
import datetime
from typing import List
import feedparser
import uuid
......@@ -22,6 +23,7 @@ from scipost.fields import ChoiceArrayField
from scipost.models import Contributor
from comments.models import Comment
from colleges.models.fellowship import Fellowship
from ..behaviors import SubmissionRelatedObjectMixin
from ..constants import (
......@@ -862,6 +864,33 @@ class Submission(models.Model):
).values_list("id", flat=True)
return self.editor_in_charge.id not in contributors_ids
def get_fellowship(self) -> List["Fellowship"]:
"""
Return the default *list* of Fellows for this Submission.
- For a regular Submission, this is the subset of Fellowships of the Editorial College
which have at least one Specialty in common with the Submission.
- For a Proceedings Submission, this is the guest Editor of the Proceedings.
"""
fellowships = Fellowship.objects.active()
if self.proceedings:
# Add only Proceedings-related Fellowships
fellows = fellowships.filter(
proceedings=self.proceedings
).return_active_for_submission(self)
else:
# Add only Fellowships from the same College and with matching specialties
fellows = (
fellowships.regular_or_senior()
.filter(
college=self.submitted_to.college,
contributor__profile__specialties__in=self.specialties.all(),
)
.return_active_for_submission(self)
)
return fellows
@property
def editorial_decision(self):
"""Returns the latest EditorialDecision (if it exists)."""
......
{% load submissions_pool %}
{% load ethics_extras %}
<tr>
<td>{{ fellowship.contributor }}</td>
<td>{{ fellowship.get_status_display }}</td>
<td>
{% if fellowship.contributor.is_currently_available %}
<span class="text-success">{% include "bi/check-square-fill.html" %}</span>
{% else %}
<span class="text-danger">{% include "bi/x-square-fill.html" %}</span>
{% endif %}
</td>
<td>{% get_fellow_qualification_expertise_level_display submission fellowship %}</td>
{% if "edadmin" in user_roles or "active_senior_fellow" in user_roles %}
<td>
{% get_profile_clearance submission.clearances fellowship.contributor.profile as clearance %}
{% if clearance %}
<span class="text-success">all clear</span>
&nbsp;<em>(asserted by {{ clearance.asserted_by }})</em>
{% else %}
{% get_profile_competing_interests submission.competing_interests fellowship.contributor.profile as ci_qs %}
{% if ci_qs %}
<table class="table table-bordered bg-danger bg-opacity-10 mb-0">
<thead>
<tr>
<th>
Related Profile / <em>(nature)</em>
</th>
{% if "edadmin" in user_roles %}<th>Actions</th>{% endif %}
</tr>
</thead>
<tbody>
{% for ci in ci_qs %}
<tr>
<td>
{{ ci.related_profile }}
<br />
<em>({{ ci.get_nature_display }})</em>
</td>
<td>
{% if "edadmin" in user_roles %}
<a class="btn btn-sm btn-danger px-1 py-0"
hx-get="{% url 'ethics:_hx_submission_competing_interest_delete' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr pk=ci.pk %}"
hx-confirm="Delete this competing interest?"
hx-target="#submission-{{ submission.pk }}-fellows-details">
{% include "bi/trash-fill.html" %}
</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<em class="text-danger">unknown</em>
{% endif %}
{% endif %}
</td>
{% if "edadmin" in user_roles %}
<td>
<a class="btn btn-sm btn-danger px-1 py-0"
title="Declare competing interest"
hx-get="{% url 'ethics:_hx_submission_competing_interest_create' submission.preprint.identifier_w_vn_nr fellowship.id %}"
hx-target="closest tr">{% include "bi/plus-square-fill.html" %}</a>
<a class="btn btn-sm btn-danger px-1 py-0"
hx-get="{% url 'colleges:_hx_submission_remove_fellowship' submission.preprint.identifier_w_vn_nr fellowship.id %}"
hx-confirm="Remove this Fellow from this Submission's Fellowship?"
hx-target="#submission-{{ submission.pk }}-fellows-details">{% include "bi/trash-fill.html" %}</a>
</td>
{% endif %}
{% endif %}
<td>{% get_fellow_readiness_status_display submission fellowship %}</td>
</tr>
{% load submissions_pool %}
{% load ethics_extras %}
<h2>
This Submission's Fellowship
</h2>
<h2>This Submission's Fellowship</h2>
{% if "edadmin" in user_roles or "active_senior_fellow" in user_roles %}
<div id="submission-{{ submission.pk }}-add-fellow"
>
<div id="submission-{{ submission.pk }}-add-fellow">
<a class="btn btn-sm btn-primary m-2"
hx-get="{% url 'colleges:_hx_submission_add_fellowship' submission.preprint.identifier_w_vn_nr %}"
hx-target="#submission-{{ submission.pk }}-add-fellow"
>
Add a Fellow to this Submission's fellowship
</a>
hx-target="#submission-{{ submission.pk }}-add-fellow">Add a Fellow to this Submission's fellowship</a>
</div>
{% endif %}
......@@ -22,82 +13,31 @@
<tr>
<th>Fellow</th>
<th>Status</th>
<th>Currently<br>available</th>
<th>
Currently
<br />
available
</th>
<th>Qualification</th>
{% if "edadmin" in user_roles or "active_senior_fellow" in user_roles %}
<th>Competing<br>interests</th>
{% if "edadmin" in user_roles %}
<th>Actions</th>
{% endif %}
<th>
Competing
<br />
interests
</th>
{% if "edadmin" in user_roles %}<th>Actions</th>{% endif %}
{% endif %}
<th>Readiness to take charge</th>
</tr>
</thead>
<tbody>
{% for fellowship in submission.fellows.select_related_contributor__user_and_profile %}
<tr>
<td>{{ fellowship.contributor }}</td>
<td>{{ fellowship.get_status_display }}</td>
<td>{% if fellowship.contributor.is_currently_available %}<span class="text-success">{% include "bi/check-square-fill.html" %}</span>{% else %}<span class="text-danger">{% include "bi/x-square-fill.html" %}</span>{% endif %}</td>
<td>{% get_fellow_qualification_expertise_level_display submission fellowship %}</td>
{% if "edadmin" in user_roles or "active_senior_fellow" in user_roles %}
<td>
{% get_profile_clearance submission.clearances fellowship.contributor.profile as clearance %}
{% if clearance %}
<span class="text-success">all clear</span>
&nbsp;<em>(asserted by {{ clearance.asserted_by }})</em>
{% else %}
{% get_profile_competing_interests submission.competing_interests fellowship.contributor.profile as ci_qs %}
{% if ci_qs %}
<table class="table table-bordered bg-danger bg-opacity-10 mb-0">
<thead>
<tr>
<th>Related Profile / <em>(nature)</em></th>
{% if "edadmin" in user_roles %}
<th>Actions</th>
{% endif %}
</tr>
</thead>
<tbody>
{% for ci in ci_qs %}
<tr>
<td>
{{ ci.related_profile }}
<br>
<em>({{ ci.get_nature_display }})</em>
</td>
<td>
{% if "edadmin" in user_roles %}
<a class="btn btn-sm btn-danger px-1 py-0"
hx-get="{% url 'ethics:_hx_submission_competing_interest_delete' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr pk=ci.pk %}"
hx-confirm="Delete this competing interest?"
hx-target="#submission-{{ submission.pk }}-fellows-details"
>
{% include "bi/trash-fill.html" %}
</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<em class="text-danger">unknown</em>
{% endif %}
{% endif %}
</td>
{% if "edadmin" in user_roles %}
<td>
<a class="btn btn-sm btn-danger px-1 py-0"
hx-get="{% url 'colleges:_hx_submission_remove_fellowship' submission.preprint.identifier_w_vn_nr fellowship.id %}"
hx-contirm="Remove this Fellow from this Submission's Fellowship?"
hx-target="#submission-{{ submission.pk }}-fellows-details"
>{% include "bi/trash-fill.html" %}</a>
</td>
{% endif %}
{% endif %}
<td>{% get_fellow_readiness_status_display submission fellowship %}</td>
</tr>
{% include "submissions/pool/_hx_submission_fellow_row.html" with fellowship=fellowship submission=submission %}
{% endfor %}
</tbody>
</table>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment