Skip to content

Commit

Permalink
Merge branch 'master' into 3478-submission_summary
Browse files Browse the repository at this point in the history
Signed-off-by: Andy Byers <[email protected]>
  • Loading branch information
ajrbyers authored Aug 13, 2024
2 parents 189caeb + 10973fc commit 39923ce
Show file tree
Hide file tree
Showing 31 changed files with 575 additions and 181 deletions.
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ requests==2.31.0
six==1.14.0
sqlparse==0.4.4
swapper==1.3.0
tqdm==4.65.0
tqdm==4.66.3
ua-parser==0.16.1
# See #3736
urllib3<2
Expand Down
6 changes: 5 additions & 1 deletion src/copyediting/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ class CopyeditAssignment(models.Model):
date_decided = models.DateTimeField(null=True, blank=True)

files_for_copyediting = models.ManyToManyField('core.File', related_name='files_for_copyediting')
copyeditor_files = models.ManyToManyField('core.File', blank=True)
copyeditor_files = models.ManyToManyField(
'core.File',
blank=True,
related_name='copyeditor_files',
)
copyeditor_completed = models.DateTimeField(blank=True, null=True)

copyedit_reopened = models.DateTimeField(blank=True, null=True)
Expand Down
36 changes: 36 additions & 0 deletions src/core/migrations/0096_update_review_ack_email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Generated by Django 4.2 on 2024-07-17 09:52

from django.db import migrations
from utils import migration_utils

OLD_VALUE_ONE = 'Dear {{ review_assignment.editor.first_name }}, <br><br>This is a notification that the reviewer status on "{{ article.safe_title }}" in {{ article.journal.name }} has been updated. You can view more information on the journal site: {{ review_in_review_url }} <br><br>All best wishes, <br>{{ request.user.signature }}'
OLD_VALUE_TWO = 'Dear {{ review_assignment.editor.full_name }}, <br/><br/>This is a notification that the reviewer status on "{{ article.safe_title }}" in {{ article.journal.name }} has been updated. You can view more information on the journal site: {{ review_in_review_url }} <br/><br/>Regards,'
OLD_VALUE_THREE = 'Dear {{ review_assignment.editor.full_name }}, <br/><br/>This is a notification that the reviewer status on "{{ article.safe_title }}" in {{ article.journal.name }} has been updated. You can view more information on the journal site: {{ review_in_review_url }} <br/><br/>Regards, <br/>{{ request.user.signature }}'
OLD_VALUE_FOUR = 'Dear {{ review_assignment.editor.full_name }}, <br><br>This is a notification that the reviewer status on "{{ article.safe_title }}" in {{ article.journal.name }} has been updated. You can view more information on the journal website at: {{ review_in_review_url }} <br><br>Best wishes,<br> <br>{{ request.user.signature }}'
OLD_VALUE_FIVE = 'Dear {{ review_assignment.editor.full_name }}, <br><br>This is a notification that the reviewer status on "{{ article.safe_title }}" in {{ article.journal.name }} has been updated. You can view more information on the journal site: {{ review_in_review_url }} <br><br>Regards, <br>{{ request.user.signature }}'
OLD_VALUE_SIX = 'Dear {{ review_assignment.editor.full_name }}, <br><br>This is a notification that the reviewer status on "{{ article.safe_title }}" in {{ article.journal.name }} has been updated. You can view more information on the journal site: {{ review_in_review_url }} <br><br>Regards,'
NEW_VALUE = "<p>Dear {{ review_assignment.editor.full_name }},</p><p>{{ review_assignment.reviewer.full_name }} has completed their review of \"{{ review_assignment.article.title }}\"{% if review_assignment.decision %} with this recommendation: {{ review_assignment.get_decision_display }}{% endif %}.</p><p>You can view more information on the journal site: {% journal_url 'review_view_review' review_assignment.article.pk review_assignment.pk %}</p><p>Regards,</p>"


def replace_review_completed_ack(apps, schema_editor):
migration_utils.update_default_setting_values(
apps,
setting_name='review_complete_acknowledgement',
group_name='email',
values_to_replace=[OLD_VALUE_ONE, OLD_VALUE_TWO, OLD_VALUE_THREE, OLD_VALUE_FOUR, OLD_VALUE_FIVE, OLD_VALUE_SIX],
replacement_value=NEW_VALUE,
)


class Migration(migrations.Migration):

dependencies = [
('core', '0095_auto_20240605_1024'),
]

operations = [
migrations.RunPython(
replace_review_completed_ack,
reverse_code=migrations.RunPython.noop,
),
]
12 changes: 11 additions & 1 deletion src/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import statistics
import json
from datetime import timedelta
from django.utils.html import format_html
import pytz
from hijack.signals import hijack_started, hijack_ended
import warnings
Expand All @@ -32,7 +33,7 @@
from django.dispatch import receiver
from django.urls import reverse
from django.utils.functional import cached_property
from django.template.defaultfilters import linebreaksbr
from django.template.defaultfilters import date
import swapper

from core import files, validators
Expand Down Expand Up @@ -1225,6 +1226,15 @@ def unlink_files(self):
def __str__(self):
return "{0} ({1})".format(self.id, self.label)

def detail(self):
return format_html(
'{} galley linked to <a href="#file_{}">file {}: {}</a>',
self.label,
self.file.pk,
self.file.pk,
self.file.original_filename,
)

def render(self, recover=False):
return files.render_xml(
self.file, self.article,
Expand Down
30 changes: 30 additions & 0 deletions src/core/templatetags/dates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from django import template
from django.utils import timezone


register = template.Library()

@register.simple_tag
def offset_date(days=0, input_type="date"):
"""
Get a string representing today's date or the now datetime,
with an offset of X days.
:param days: number of days from now that the date should be set
:param date: date or datetime-local
Usage:
<input
id="due"
name="due"
type="{{ input_type }}"
value="{% offset_date days=3 input_type=input_type %}">
See admin.core.widgets.soon_date_buttons for a use case.
"""
due = timezone.now() + timezone.timedelta(days=days)
if input_type == "date":
return due.strftime("%Y-%m-%d")
elif input_type == "datetime-local":
return due.strftime("%Y-%m-%dT%H:%M")
8 changes: 8 additions & 0 deletions src/core/templatetags/uuid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django import template
from uuid import uuid4

register = template.Library()

@register.simple_tag
def get_uuid4():
return f'u{str(uuid4())}'
2 changes: 1 addition & 1 deletion src/journal/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2646,7 +2646,7 @@ def serve_article_pdf(request, identifier_type, identifier):
identifier,
)

if not article_object and not article_object.is_published:
if not article_object or not article_object.is_published:
raise Http404

pdf = article_object.pdfs.first()
Expand Down
31 changes: 31 additions & 0 deletions src/repository/migrations/0044_alter_repositoryfield_help_text.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Generated by Django 4.2 on 2024-06-26 10:12

from django.db import migrations, models
from django.template.defaultfilters import striptags


def strip_html_tags_from_repo_field_help_text(apps, schema_editor):
RepositoryField = apps.get_model('repository', 'RepositoryField')

for field in RepositoryField.objects.all():
field.help_text = striptags(field.help_text)
field.save()


class Migration(migrations.Migration):

dependencies = [
('repository', '0043_auto_20240402_1256'),
]

operations = [
migrations.AlterField(
model_name='repositoryfield',
name='help_text',
field=models.TextField(blank=True, null=True),
),
migrations.RunPython(
strip_html_tags_from_repo_field_help_text,
reverse_code=migrations.RunPython.noop,
)
]
5 changes: 4 additions & 1 deletion src/repository/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,10 @@ class RepositoryField(models.Model):
)
required = models.BooleanField(default=True)
order = models.IntegerField()
help_text = models.TextField(blank=True, null=True)
help_text = models.TextField(
blank=True,
null=True,
)
display = models.BooleanField(
default=False,
help_text='Whether or not display this field in the article page',
Expand Down
44 changes: 44 additions & 0 deletions src/review/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,50 @@ def test_csv_doesnt_create_duplicate_assignments(self):
self.review_assignment_complete,
)

def test_incomplete_reviews(self):
args = {'owner': self.regular_user,
'title': 'Test Article',
'stage': submission_models.STAGE_UNDER_REVIEW,}
article1 = helpers.create_article(self.journal_one, **args)

article1.correspondence_author = self.regular_user
article1.save()

round = review_models.ReviewRound.objects.create(article=article1,
round_number=1,)

assignment = helpers.create_review_assignment(
journal=self.journal_one,
article=article1,
is_complete=False,
review_round=round,
reviewer=self.regular_user,
)
assignment.decision = None
assignment.save()

self.client.force_login(self.editor)
decline_url = reverse('review_decision',
kwargs={'article_id': article1.pk,
'decision': 'decline'})
response = self.client.get(decline_url,
SERVER_NAME=self.journal_one.domain,)
msg = "The following incomplete reviews will be marked as withdrawn"
self.assertContains(response, msg)

data = {'cc': [''],
'bcc': [''],
'subject': ['Article Declined'],
'body': ['Article Declined'],
'attachments': [''],
'skip': ['skip']}
response = self.client.post(decline_url,
data,
SERVER_NAME=self.journal_one.domain,)
review_set = article1.reviewassignment_set.all()
self.assertEqual(review_set.filter(is_complete=True).count(), 1)
self.assertEqual(review_set.filter(is_complete=False).count(), 0)

def test_completed_reviews_shared_setting(self):
# setup data
article_with_completed_reviews = helpers.create_article(
Expand Down
4 changes: 3 additions & 1 deletion src/static/admin/js/check_all.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Deprecated. Use select_all.html instead.

$("#checkall").click(function () {
$('input:checkbox').not(this).prop('checked', this.checked);
});
});
29 changes: 29 additions & 0 deletions src/static/common/css/common.css
Original file line number Diff line number Diff line change
Expand Up @@ -298,3 +298,32 @@ article .list-romanupper {
padding: 15px;
margin-top: 15px;
}

/* Styling for skip to main content link */
.skip-container {
position: relative;
display: flex;
justify-content: center;
align-items: center;
height: 0;
overflow: hidden;
}

.skip-to-content,
.skip-to-nav {
position: absolute;
left: -999px;
}

.skip-to-content:focus,
.skip-to-nav:focus {
position: relative;
left: 0;
margin-block-start: 10px;
margin-block-end: 10px;
}

.skip-container:focus-within {
height: auto;
overflow: visible;
}
9 changes: 9 additions & 0 deletions src/submission/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1514,6 +1514,10 @@ def decline_article(self):
self.date_declined = timezone.now()
self.date_accepted = None
self.stage = STAGE_REJECTED

self.incomplete_reviews().update(decision=RD.DECISION_WITHDRAWN.value,
date_complete=timezone.now(),
is_complete=True,)
self.save()

def undo_review_decision(self):
Expand Down Expand Up @@ -1831,6 +1835,11 @@ def hidden_completed_reviews(self):
decision='withdrawn',
)

def incomplete_reviews(self):
return self.reviewassignment_set.filter(is_complete=False,
date_declined__isnull=True,
decision__isnull=True)

def ms_and_figure_files(self):
return chain(self.manuscript_files.all(), self.data_figure_files.all())

Expand Down
44 changes: 44 additions & 0 deletions src/templates/admin/core/widgets/select_all.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{% comment %}
Easily select or deselect all the checkboxes for a given field.

Make sure to wrap this widget and the checkboxes in fieldset.

Usage:

<fieldset>
<legend>Which colours?</legend>
{% include "admin/core/widgets/select_all.html" %}
<input id="red" name="red" type="checkbox"><label for="red">Red</label>
<input id="blue" name="blue" type="checkbox"><label for="blue">Blue</label>
</fieldset>

{% endcomment %}

{% load uuid %}

{% get_uuid4 as pid %}

<div id="{{ pid }}" style="button-group">
<button class="button selectall" type="button">
<span class="fa fa-check"></span>
Select all
</button>
<button class="button deselectall" type="button">
<span class="fa fa-close"></span>
Deselect all
</button>
</div>
<script defer type="module">
function toggleInputsInFieldset(event) {
const checked = event.currentTarget.classList.contains('selectall');
const fieldset = event.currentTarget.closest('fieldset');
const checkboxes = fieldset.querySelectorAll('input[type="checkbox"]');
Array.from(checkboxes).forEach(checkbox => {
checkbox.checked = checked;
});
}
const selectAll = document.querySelector('#{{ pid }} .selectall');
selectAll.addEventListener('click', toggleInputsInFieldset);
const deselectAll = document.querySelector('#{{ pid }} .deselectall');
deselectAll.addEventListener('click', toggleInputsInFieldset);
</script>
Loading

0 comments on commit 39923ce

Please sign in to comment.