diff --git a/SciPost_v1/settings/production.py b/SciPost_v1/settings/production.py index 219865aef4e97e613d84b8d9330dcb93381d00ec..be0dcf8def1ff20ca7dc29d4c5338b86fd8e52e1 100644 --- a/SciPost_v1/settings/production.py +++ b/SciPost_v1/settings/production.py @@ -64,7 +64,7 @@ LOGGING['handlers']['oauth_file']['filename'] = '/home/scipost/webapps/scipost_p # API -REST_FRAMEWORK['DEFAULT_RENDERER_CLASSES'] = ('rest_framework.renderers.JSONRenderer',) +# REST_FRAMEWORK['DEFAULT_RENDERER_CLASSES'] = ('rest_framework.renderers.JSONRenderer',) # Sentry diff --git a/api/urls.py b/api/urls.py index 9bf2d58c36ce92e4354742cae1ec09beb3e9e308..dc61d316c58808accb61e669b7390c3e774f01c1 100644 --- a/api/urls.py +++ b/api/urls.py @@ -29,6 +29,7 @@ urlpatterns += [ views.OmniAuthUserInfoView.as_view(), name='omniauth_userinfo' ), + path('finances/', include('finances.api.urls')), path('journals/', include('journals.api.urls')), path('organizations/', include('organizations.api.urls')), diff --git a/colleges/models/college.py b/colleges/models/college.py index 426e35e6dbda6aacbee619cbe5d9f5850fcf9f29..7884dcc3817b6eea87096f63f8fe918e1f0759a8 100644 --- a/colleges/models/college.py +++ b/colleges/models/college.py @@ -3,6 +3,7 @@ __license__ = "AGPL v3" from django.db import models +from django.urls import reverse from ontology.models import Specialty @@ -58,6 +59,9 @@ class College(models.Model): def __str__(self): return "Editorial College (%s)" % self.name + def get_absolute_url(self): + return reverse('colleges:college_detail', kwargs={'college': self.slug}) + @property def specialties(self): return Specialty.objects.filter(journals__college__pk=self.id).distinct() diff --git a/colleges/templates/colleges/college_detail.html b/colleges/templates/colleges/college_detail.html index e59a1b492a406952e91b6384c45cf416b01fd798..45f5581a5bd373d1fd4725892d99ed84d8088f1d 100644 --- a/colleges/templates/colleges/college_detail.html +++ b/colleges/templates/colleges/college_detail.html @@ -22,6 +22,7 @@ <ul> <li><a href="{% url 'colleges:potential_fellowships' %}">Potential Fellowships</a></li> <li><a href="{% url 'colleges:fellowships' %}">Fellowships</a></li> + <li><a href="{% url 'colleges:email_College_Fellows' college=college.slug %}">Email Fellows of this College</a></li> </ul> </div> {% endif %} diff --git a/colleges/templates/colleges/email_College_Fellows.html b/colleges/templates/colleges/email_College_Fellows.html new file mode 100644 index 0000000000000000000000000000000000000000..ae5ec78373318e55303c66ec0589b702c6b23edc --- /dev/null +++ b/colleges/templates/colleges/email_College_Fellows.html @@ -0,0 +1,33 @@ +{% extends 'colleges/base.html' %} + +{% block breadcrumb_items %} + {{ block.super }} + <a href="{% url 'colleges:colleges' %}" class="breadcrumb-item">Colleges</a> + <a href="{% url 'colleges:college_detail' college=college.slug %}" class="breadcrumb-item">{{ college }}</a> + <span class="breadcrumb-item">Email College Fellows</span> +{% endblock %} + +{% block pagetitle %}: email College Fellows{% endblock pagetitle %} + +{% load bootstrap %} + +{% block content %} + + <div class="row"> + <div class="col-12"> + <h1 class="highlight">Email Fellows of {{ college }}</h1> + + <form action="{% url 'colleges:email_College_Fellows' college=college.slug %}" method="post"> + {% csrf_token %} + {{ form|bootstrap }} + <input type="submit" class="btn btn-outline-secondary" value="Send email"/> + </form> + + </div> + </div> + +{% endblock %} + +{% block footer_script %} + {{ form.media }} +{% endblock %} diff --git a/colleges/templates/colleges/submission_pool.html b/colleges/templates/colleges/submission_fellowships.html similarity index 72% rename from colleges/templates/colleges/submission_pool.html rename to colleges/templates/colleges/submission_fellowships.html index 4ef47afbc3d542eae55da4dab45a02c216f70b86..21aaebc1d5f51085534631c10b39c576fff73d4f 100644 --- a/colleges/templates/colleges/submission_pool.html +++ b/colleges/templates/colleges/submission_fellowships.html @@ -4,19 +4,19 @@ {% block breadcrumb_items %} {{ block.super }} - <span class="breadcrumb-item">Submission Pool Composition</span> + <span class="breadcrumb-item">Submission Fellowships</span> {% endblock %} -{% block pagetitle %}: Submission Pool Composition{% endblock pagetitle %} +{% block pagetitle %}: Submission Felloswhips{% endblock pagetitle %} {% block content %} - <h1>Submission Pool Composition</h1> - <h2 class="text-primary">{{submission.title}}</h2> - <h3 class="mb-3">by {{submission.author_list}}</h3> + <h1>Submission Fellowships</h1> + <h2 class="text-primary">{{ submission.title }}</h2> + <h3 class="mb-3">by {{ submission.author_list }}</h3> {% include 'partials/submissions/submission_summary.html' with submission=submission hide_title=1 show_abstract=1 %} <br> - <h3>Pool Composition</h3> + <h3>Fellowship Composition</h3> {% include 'partials/colleges/conflicts_of_interests.html' with submission=submission %} <table class="table table-hover"> <thead> @@ -34,12 +34,12 @@ <td>{{ fellowship.contributor }}</td> <td>{{ fellowship.guest|yesno:"Guest fellowship,Regular fellowship"|safe }}</td> <td> - <a class="text-danger" href="{% url 'colleges:fellowship_remove_submission' fellowship.id submission.preprint.identifier_w_vn_nr %}">Remove this Fellowship from Submission's pool</a> + <a class="text-danger" href="{% url 'colleges:fellowship_remove_submission' fellowship.id submission.preprint.identifier_w_vn_nr %}">Remove this Fellow from Submission's fellowhship</a> </td> </tr> {% endfor %} <tr> - <td colspan="4" class="py-3 text-center"><a href="{% url 'colleges:submission_add_fellowship' submission.preprint.identifier_w_vn_nr %}">Add Fellowship to this Submission's pool</a></td> + <td colspan="4" class="py-3 text-center"><a href="{% url 'colleges:submission_add_fellowship' submission.preprint.identifier_w_vn_nr %}">Add Fellow to this Submission's fellowship</a></td> </tr> </tbody> </table> diff --git a/colleges/urls.py b/colleges/urls.py index 3f162be74868248cc94b043b6e2c3ad7e6976162..0e8553d1669390115c17660f674932c99f1ebda0 100644 --- a/colleges/urls.py +++ b/colleges/urls.py @@ -29,6 +29,12 @@ urlpatterns = [ views.CollegeDetailView.as_view(), name='college_detail' ), + path( + '<college:college>/email_Fellows', + views.email_College_Fellows, + name='email_College_Fellows' + ), + # Fellowships url( r'^fellowships/(?P<contributor_id>[0-9]+)/add/$', @@ -66,7 +72,7 @@ urlpatterns = [ ), url(r'^fellowships/submissions/{regex}/$'.format( - regex=SUBMISSIONS_COMPLETE_REGEX), views.submission_pool, + regex=SUBMISSIONS_COMPLETE_REGEX), views.submission_fellowships, name='submission'), url(r'^fellowships/submissions/{regex}/voting$'.format( regex=SUBMISSIONS_COMPLETE_REGEX), views.submission_voting_fellows, diff --git a/colleges/views.py b/colleges/views.py index 1bfa87b3962083e1d51624fa04ea583db4d5729c..a0f5fa71cca6b23ea9732b0b8cee064da110e970 100644 --- a/colleges/views.py +++ b/colleges/views.py @@ -29,6 +29,7 @@ from .forms import FellowshipForm, FellowshipRemoveSubmissionForm,\ PotentialFellowshipForm, PotentialFellowshipStatusForm, PotentialFellowshipEventForm from .models import College, Fellowship, PotentialFellowship, PotentialFellowshipEvent +from scipost.forms import EmailUsersForm from scipost.mixins import PermissionsMixin, PaginationMixin, RequestViewMixin from scipost.models import Contributor @@ -157,7 +158,24 @@ class FellowshipStartEmailView(PermissionsMixin, MailView): @login_required @permission_required('scipost.can_manage_college_composition', raise_exception=True) -def submission_pool(request, identifier_w_vn_nr): +def email_College_Fellows(request, college): + """ + Send an email to all Fellows within a College. + """ + user_ids = [f.contributor.user.id for f in college.fellowships.regular().active()] + form = EmailUsersForm(request.POST or None, initial={'users': user_ids}) + if form.is_valid(): + form.save() + messages.success(request, 'Email sent') + return redirect(college.get_absolute_url()) + return render(request, + 'colleges/email_College_Fellows.html', + {'form': form, 'college': college}) + + +@login_required +@permission_required('scipost.can_manage_college_composition', raise_exception=True) +def submission_fellowships(request, identifier_w_vn_nr): """ List all Fellowships related to Submission. """ @@ -166,7 +184,7 @@ def submission_pool(request, identifier_w_vn_nr): context = { 'submission': submission } - return render(request, 'colleges/submission_pool.html', context) + return render(request, 'colleges/submission_fellowships.html', context) @login_required diff --git a/finances/api/serializers.py b/finances/api/serializers.py new file mode 100644 index 0000000000000000000000000000000000000000..f22b49441dfcb28746c39080c301a2358afcc6c0 --- /dev/null +++ b/finances/api/serializers.py @@ -0,0 +1,18 @@ +__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)" +__license__ = "AGPL v3" + + +from rest_framework import serializers + +from organizations.api.serializers import OrganizationSerializer + +from ..models import Subsidy + + +class SubsidySerializer(serializers.ModelSerializer): + organization = serializers.CharField()#OrganizationSerializer() + subsidy_type = serializers.CharField(source='get_subsidy_type_display', read_only=True) + + class Meta: + model = Subsidy + fields = '__all__' diff --git a/finances/api/urls.py b/finances/api/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..da5f5c962d3c4c794773476172fc0da8cb824073 --- /dev/null +++ b/finances/api/urls.py @@ -0,0 +1,18 @@ +__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)" +__license__ = "AGPL v3" + + +from django.urls import path + +from finances.api import views as api_views + + +urlpatterns = [ + + path ( # /api/finances/subsidies + 'subsidies', + api_views.SubsidyListAPIView.as_view(), + name='subsidies' + ), + +] diff --git a/finances/api/views.py b/finances/api/views.py new file mode 100644 index 0000000000000000000000000000000000000000..cfad959c6e6c74afc98d1061bce12121ef43e780 --- /dev/null +++ b/finances/api/views.py @@ -0,0 +1,24 @@ +__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)" +__license__ = "AGPL v3" + + +from rest_framework.generics import ListAPIView, RetrieveAPIView +from rest_framework.permissions import BasePermission +from rest_framework.settings import api_settings +from rest_framework_csv import renderers as r + +from ..models import Subsidy +from .serializers import SubsidySerializer + + +class CanManageSubsidies(BasePermission): + def has_permission(self, request, view): + return request.user.has_perm('scipost.can_manage_subsidies') + + +class SubsidyListAPIView(ListAPIView): + pagination_class = None + permission_classes = [CanManageSubsidies] + queryset = Subsidy.objects.all() + renderer_classes = tuple(api_settings.DEFAULT_RENDERER_CLASSES) + (r.CSVRenderer, ) + serializer_class = SubsidySerializer diff --git a/mails/management/commands/send_mails.py b/mails/management/commands/send_mails.py index 7c4aeb32afaa670c7307854b7b97c2f01b54bc23..572dc3d44e9ccbbe2d6182419b2ef8b5c0dcc7dc 100644 --- a/mails/management/commands/send_mails.py +++ b/mails/management/commands/send_mails.py @@ -65,6 +65,6 @@ class Command(BaseCommand): if options.get('id'): mails = MailLog.objects.filter(id=options['id']) else: - mails = MailLog.objects.not_sent() + mails = MailLog.objects.not_sent().order_by('created')[:2] nr_mails = self.send_mails(mails) self.stdout.write('Sent {} mails.'.format(nr_mails)) diff --git a/requirements.txt b/requirements.txt index ce5f28aa53ba346526a2d3ded5cd651d860eb645..4bf8cb1006f2891453263f31762f45e55c96b3d1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -25,6 +25,7 @@ django-oauth-toolkit==1.3.2 # 2020-09-03 django-silk==2.0.0 django-webpack-loader==0.5 django-maintenancemode-2==1.1.11 +djangorestframework-csv # 2020-10-13 plotly==4.6.0 # 2020-05-05 diff --git a/scipost/forms.py b/scipost/forms.py index 3711931724643eca659dcc2d627ec81ef9c552f5..58279b0c78b551d5e9b74e6ec4c7dbbcf148d925 100644 --- a/scipost/forms.py +++ b/scipost/forms.py @@ -751,6 +751,57 @@ class EmailParticularForm(forms.Form): {'rows': 15, 'cols': 50, 'placeholder': 'Write your message in this box.'}) +class EmailUsersForm(forms.Form): + users = forms.ModelMultipleChoiceField( + queryset=User.objects.all(), + widget=autocomplete.ModelSelect2Multiple( + url='/user-autocomplete', + attrs={'data-html': True} + ), + label='Recipients', + required=True + ) + email_subject = forms.CharField(widget=forms.Textarea(), label='') + personalize = forms.BooleanField( + required=False, initial=False, + label='Personalize (Dear Prof. AAA)?') + email_text = forms.CharField(widget=forms.Textarea(), label='') + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['email_subject'].widget.attrs.update( + {'rows': 1, 'cols': 50, 'placeholder': 'Email subject'}) + self.fields['email_text'].widget.attrs.update( + {'rows': 15, 'cols': 50, 'placeholder': 'Write your message in this box.'}) + + def save(self): + from django.core import mail + from django.template import Context, Template + with mail.get_connection() as connection: + for user in self.cleaned_data['users']: + email_text = '' + email_text_html = '' + if self.cleaned_data['personalize']: + email_text = ('Dear ' + user.contributor.profile.get_title_display() + + ' ' + user.last_name + ', \n\n') + email_text_html = 'Dear {{ title }} {{ last_name }},<br/>' + email_text += self.cleaned_data['email_text'] + email_text_html += '{{ email_text|linebreaks }}' + email_context = { + 'title': user.contributor.profile.get_title_display(), + 'last_name': user.last_name, + 'email_text': self.cleaned_data['email_text'], + } + html_template = Template(email_text_html) + html_version = html_template.render(Context(email_context)) + message = mail.EmailMultiAlternatives( + self.cleaned_data['email_subject'], + email_text, 'SciPost Admin <admin@scipost.org>', + [user.email], connection=connection) + message.attach_alternative(html_version, 'text/html') + message.send() + + class SendPrecookedEmailForm(forms.Form): email_address = forms.EmailField() email_option = forms.ModelChoiceField( diff --git a/scipost/urls.py b/scipost/urls.py index 2d1103b471087ec7943a7eacec1bd29a8d5468b3..13320cea697661f218e44baa91e28e4272c178b5 100644 --- a/scipost/urls.py +++ b/scipost/urls.py @@ -46,6 +46,11 @@ urlpatterns = [ views.GroupAutocompleteView.as_view(), name='group-autocomplete' ), + path( + 'user-autocomplete', + views.UserAutocompleteView.as_view(), + name='user-autocomplete' + ), # Search url(r'^search', views.SearchView.as_view(), name='search'), diff --git a/scipost/views.py b/scipost/views.py index 9950a4a3a49dd529543be54a93e07d011b3d937c..f6ecc8cc57ec8a503df3ae93138c109d1a1be1e4 100644 --- a/scipost/views.py +++ b/scipost/views.py @@ -12,7 +12,7 @@ from django.contrib.admin.views.decorators import staff_member_required from django.contrib.auth import login, update_session_auth_hash from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin -from django.contrib.auth.models import Group +from django.contrib.auth.models import User, Group from django.contrib.auth.views import ( LoginView, LogoutView, PasswordChangeView, PasswordResetView, PasswordResetConfirmView) @@ -23,6 +23,7 @@ from django.core.mail import EmailMessage, EmailMultiAlternatives from django.core.paginator import Paginator from django.urls import reverse, reverse_lazy from django.db import transaction +from django.db.models import Q from django.http import Http404, HttpResponse, JsonResponse from django.shortcuts import redirect from django.template import Context, Template @@ -96,6 +97,7 @@ def sitemap_xml(request): } return render(request, 'scipost/sitemap.xml', context) + ################ # Autocomplete # ################ @@ -113,6 +115,25 @@ class GroupAutocompleteView(autocomplete.Select2QuerySetView): return qs +class UserAutocompleteView(autocomplete.Select2QuerySetView): + """ + View to feed the Select2 widget. + """ + def get_queryset(self): + if not self.request.user.has_perm('auth.view_user'): + return None + qs = User.objects.all() + if self.q: + qs = qs.filter(Q(username__icontains=self.q) | + Q(email__icontains=self.q) | + Q(first_name__icontains=self.q) | + Q(last_name__icontains=self.q)) + return qs + + def get_result_label(self, item): + return f"{item.last_name}, {item.first_name}" + + ############## # Utilitites # ############## diff --git a/submissions/templates/partials/submissions/admin/prescreening_failed.html b/submissions/templates/partials/submissions/admin/prescreening_failed.html new file mode 100644 index 0000000000000000000000000000000000000000..958faa5c69b0b4a2ebd1d5ed908c1040e6f6f181 --- /dev/null +++ b/submissions/templates/partials/submissions/admin/prescreening_failed.html @@ -0,0 +1,13 @@ +<h1 class="highlight">Pre-screening has failed for Submission</h1> +<h3>Submission details</h3> +{% include 'partials/submissions/submission_summary.html' with submission=object %} + +<br> +<h3>Current EIC assignment requests:</h3> +<ul> + {% for assignment in object.editorial_assignments.all %} + {% include 'partials/submissions/pool/assignment_info.html' with assignment=assignment %} + {% empty %} + <li>No assignment requests have been sent</li> + {% endfor %} +</ul> diff --git a/submissions/templates/partials/submissions/pool/submission_details.html b/submissions/templates/partials/submissions/pool/submission_details.html index c581cafe3d208a04afacba6549207526dadbe0ba..bc8675779628e231d87cab7e785bd163b24ee753 100644 --- a/submissions/templates/partials/submissions/pool/submission_details.html +++ b/submissions/templates/partials/submissions/pool/submission_details.html @@ -55,7 +55,7 @@ <h3>Editorial Administration</h3> <ul class="pl-4 mb-3"> <li><a href="{% url 'submissions:conflicts' submission.preprint.identifier_w_vn_nr %}">See conflicts of interests</a></li> - <li><a href="{% url 'colleges:submission' submission.preprint.identifier_w_vn_nr %}">Manage Pool composition</a></li> + <li><a href="{% url 'colleges:submission' submission.preprint.identifier_w_vn_nr %}">Manage this Submission's Fellowship</a></li> <li><a href="{% url 'colleges:submission_voting_fellows' submission.preprint.identifier_w_vn_nr %}">Manage Voting Fellows</a></li> {% if not submission.editor_in_charge %} <li><a href="{% url 'submissions:editor_invitations' submission.preprint.identifier_w_vn_nr %}">Manage editor invitations</a></li> @@ -74,7 +74,10 @@ {% endfor %} {% if not submission.editor_in_charge %} - <li><a href="{% url 'submissions:assignment_failed' submission.preprint.identifier_w_vn_nr %}">Close pre-screening: failure to find EIC</a></li> + <li><a href="{% url 'submissions:prescreening_failed' submission.preprint.identifier_w_vn_nr %}">Close: pre-screening failed</a></li> + <li><a href="{% url 'submissions:update_authors_screening' submission.preprint.identifier_w_vn_nr 1 %}">Update authors by email (1 week into screening)</a></li> + <li><a href="{% url 'submissions:update_authors_screening' submission.preprint.identifier_w_vn_nr 2 %}">Update authors by email (2 weeks into screening)</a></li> + <li><a href="{% url 'submissions:assignment_failed' submission.preprint.identifier_w_vn_nr %}">Close: screening failed (failure to find EIC)</a></li> {% endif %} </ul> {% endif %} diff --git a/submissions/templates/partials/submissions/pool/submission_info_table.html b/submissions/templates/partials/submissions/pool/submission_info_table.html index 1e0600e45d81b7b7df2dac1e6eeea46a78b6c634..f73d8329ef10d8c31b42309ae73b06ecf023ee1f 100644 --- a/submissions/templates/partials/submissions/pool/submission_info_table.html +++ b/submissions/templates/partials/submissions/pool/submission_info_table.html @@ -98,7 +98,7 @@ {% if perms.scipost.can_manage_college_composition %} <tr> - <td>Pool composition</td> + <td>This Submission's Fellowship</td> <td><a href="{% url 'colleges:submission' submission.preprint.identifier_w_vn_nr %}">{{ submission.fellows.count }} Fellowship{{ submission.fellows.count|pluralize }}</a></td> </tr> <tr> diff --git a/submissions/templates/partials/submissions/submission_author_information.html b/submissions/templates/partials/submissions/submission_author_information.html index eee91ec2289f52ddadf886300e8cfb8ade209e27..ef22fc69e1c0701ea2f0fce4f9309b1129243e5e 100644 --- a/submissions/templates/partials/submissions/submission_author_information.html +++ b/submissions/templates/partials/submissions/submission_author_information.html @@ -1,4 +1,5 @@ {% load bootstrap %} +{% load submissions_extras %} <h3 class="highlight"> Author information @@ -80,7 +81,7 @@ </table> </div> - {% if submission.eicrecommendations and submission.eicrecommendations.last.is_viewable_by_authors %} + {% if submission.eicrecommendations and submission.eicrecommendations.last|is_viewable_by_authors %} <div class="mb-4"> <h4 class="mb-2">Editorial Recommendation:</h4> {% include 'partials/submissions/recommendation_author_content.html' with recommendation=submission.eicrecommendations.last %} diff --git a/submissions/templates/submissions/admin/submission_presassign_editors.html b/submissions/templates/submissions/admin/submission_preassign_editors.html similarity index 89% rename from submissions/templates/submissions/admin/submission_presassign_editors.html rename to submissions/templates/submissions/admin/submission_preassign_editors.html index 408aa4b7377e61b72bce597241e944eb6913264c..f52bb87a7accd7aeebfb86d8d0cf33657768b0d7 100644 --- a/submissions/templates/submissions/admin/submission_presassign_editors.html +++ b/submissions/templates/submissions/admin/submission_preassign_editors.html @@ -7,7 +7,7 @@ {% block pagetitle %}: Submission Editors{% endblock pagetitle %} {% block breadcrumb_items %} - {{block.super}} + {{ block.super }} {% if submission.status == 'incoming' %} <a href="{% url 'submissions:do_prescreening' submission.preprint.identifier_w_vn_nr %}" class="breadcrumb-item">Pre-screening {{ submission.preprint.identifier_w_vn_nr }}</a> {% else %} @@ -20,8 +20,8 @@ <h1 class="highlight">Submission editor invitations</h1> - <h3><a href="{{ submission.get_absolute_url }}">{{submission.title}}</a></h3> - <h4>by {{submission.author_list}}</h4> + <h3><a href="{{ submission.get_absolute_url }}">{{ submission.title }}</a></h3> + <h4>by {{ submission.author_list }}</h4> <br> <h3>Submission summary</h3> @@ -34,7 +34,10 @@ {% if submission.status == 'incoming' %} <li><a href="{% url 'submissions:do_prescreening' submission.preprint.identifier_w_vn_nr %}">Go to pre-screening page</a></li> {% else %} - <li><a href="{% url 'submissions:assignment_failed' submission.preprint.identifier_w_vn_nr %}">Close pre-screening: failure to find EIC</a></li> + <li><a href="{% url 'submissions:prescreening_failed' submission.preprint.identifier_w_vn_nr %}">Close: pre-screening failed</a></li> + <li><a href="{% url 'submissions:update_authors_screening' submission.preprint.identifier_w_vn_nr nrweeks=1 %}">Update authors by email (1 week into screening)</a></li> + <li><a href="{% url 'submissions:update_authors_screening' submission.preprint.identifier_w_vn_nr nrweeks=2 %}">Update authors by email (2 weeks into screening)</a></li> + <li><a href="{% url 'submissions:assignment_failed' submission.preprint.identifier_w_vn_nr %}">Close: screening failed (failure to find EIC)</a></li> {% endif %} </ul> @@ -71,7 +74,7 @@ <td> {% if submission.editorial_assignments.declined_red.count >= 3 %} <span class="text-danger">{% include 'bi/exclamation-circle-fill.html' %} {{ submission.editorial_assignments.declined_red.count }}</span> - · <a href="{% url 'submissions:assignment_failed' submission.preprint.identifier_w_vn_nr %}">Close pre-screening: failure to find EIC</a> + · <a href="{% url 'submissions:assignment_failed' submission.preprint.identifier_w_vn_nr %}">Close screening: failure to find EIC</a> {% else %} {{ submission.editorial_assignments.declined_red.count }} {% endif %} @@ -189,7 +192,7 @@ </tbody> </table> <button class="btn btn-primary" type="submit">Save pre-assignments</button> - <a class="btn btn-default" href="{% url 'colleges:submission' submission.preprint.identifier_w_vn_nr %}">Manage Pool composition</a> + <a class="btn btn-default" href="{% url 'colleges:submission' submission.preprint.identifier_w_vn_nr %}">Manage this Submission's Fellowship</a> </form> {% endif %} diff --git a/submissions/templates/submissions/admin/submission_prescreening.html b/submissions/templates/submissions/admin/submission_prescreening.html index dee27a2adb68d88116279c06046e45c7d3caab3f..18aa47a8d1bfe70b2fe617f23538606a08c9d193 100644 --- a/submissions/templates/submissions/admin/submission_prescreening.html +++ b/submissions/templates/submissions/admin/submission_prescreening.html @@ -8,26 +8,26 @@ {% block pagetitle %}: pre-screening ({{ submission.preprint.identifier_w_vn_nr }}){% endblock pagetitle %} {% block breadcrumb_items %} - {{block.super}} + {{ block.super }} <span class="breadcrumb-item">Pre-screening {{ submission.preprint.identifier_w_vn_nr }}</span> {% endblock %} {% block content %} <h1 class="highlight">Pre-screening of Submission</h1> - <h3><a href="{{ submission.get_absolute_url }}">{{submission.title}}</a></h3> - <h4>by {{submission.author_list}}</h4> + <h3><a href="{{ submission.get_absolute_url }}">{{ submission.title }}</a></h3> + <h4>by {{ submission.author_list }}</h4> <h3 class="mt-4">Submission summary</h3> {% include 'partials/submissions/pool/submission_info_table_extended.html' with submission=submission %} <h3 class="mt-4">Abstract</h3> - <p>{{submission.abstract}}</p> + <p>{{ submission.abstract }}</p> <h3 class="mt-4">Pre-screening steps</h3> <ul> <li> <span class="text-success">{% include 'bi/check-square-fill.html' %}</span> - <a href="{% url 'colleges:submission' submission.preprint.identifier_w_vn_nr %}">Manage Pool composition ({{ submission.fellows.count }} fellows)</a><br> + <a href="{% url 'colleges:submission' submission.preprint.identifier_w_vn_nr %}">Manage this submission's Fellowship ({{ submission.fellows.count }} fellows)</a><br> </li> <li> {% if submission.plagiarism_report %} @@ -36,7 +36,7 @@ <table> <tr> <td style="min-width: 150px;">iThenticate document</td> - <td>{{submission.plagiarism_report.doc_id}}</td> + <td>{{ submission.plagiarism_report.doc_id }}</td> </tr> <tr> <td>Percent match</td> @@ -44,15 +44,15 @@ </tr> <tr> <td>Processed</td> - <td>{{submission.plagiarism_report.processed_time}}</td> + <td>{{ submission.plagiarism_report.processed_time }}</td> </tr> <tr> <td>Uploaded</td> - <td>{{submission.plagiarism_report.uploaded_time}}</td> + <td>{{ submission.plagiarism_report.uploaded_time }}</td> </tr> <tr> <td>Latest update</td> - <td>{{submission.plagiarism_report.latest_activity}}</td> + <td>{{ submission.plagiarism_report.latest_activity }}</td> </tr> </table> {% else %} diff --git a/submissions/urls.py b/submissions/urls.py index a472fbcae4e4c46f4e73a0495a1f753f2fdca184..c17716d576772d961db1befe5cf3f9094e130bd5 100644 --- a/submissions/urls.py +++ b/submissions/urls.py @@ -177,8 +177,14 @@ urlpatterns = [ url(r'^pool/{regex}/editorial_assignment/(?P<assignment_id>[0-9]+)/$'.format( regex=SUBMISSIONS_COMPLETE_REGEX), views.editorial_assignment, name='editorial_assignment'), + url(r'^prescreening_failed/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), + views.prescreening_failed, name='prescreening_failed'), + url(r'^update_authors_screening/{regex}/(?P<nrweeks>[1-2])$'.format( + regex=SUBMISSIONS_COMPLETE_REGEX), + views.update_authors_screening, name='update_authors_screening'), url(r'^assignment_failed/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), views.assignment_failed, name='assignment_failed'), + # Editorial workflow and refereeing url(r'^editorial_workflow$', views.editorial_workflow, name='editorial_workflow'), url(r'^assignments$', views.assignments, name='assignments'), diff --git a/submissions/views.py b/submissions/views.py index 29bf3589d5caa2bce1cc2249424ec3ceedf3e7fe..172d4789578b7e1d84ec3eb3699bebb0e5cbc7e3 100644 --- a/submissions/views.py +++ b/submissions/views.py @@ -29,7 +29,7 @@ from dal import autocomplete from .constants import ( STATUS_ACCEPTED_AWAITING_PUBOFFER_ACCEPTANCE, STATUS_ACCEPTED, STATUS_REJECTED, - STATUS_VETTED, SUBMISSION_STATUS, STATUS_ASSIGNMENT_FAILED, + STATUS_VETTED, SUBMISSION_STATUS, STATUS_FAILED_PRESCREENING, STATUS_ASSIGNMENT_FAILED, STATUS_DRAFT, CYCLE_DIRECT_REC, STATUS_COMPLETED, STATUS_DEPRECATED, EIC_REC_PUBLISH, EIC_REC_REJECT, DECISION_FIXED) from .helpers import check_verified_author, check_unverified_author @@ -300,6 +300,8 @@ class RequestSubmissionUsingArXivView(RequestSubmissionView): return kwargs + + @login_required def withdraw_manuscript(request, identifier_w_vn_nr): """ @@ -862,11 +864,68 @@ def assignment_request(request, assignment_id): })) +@login_required +@permission_required('scipost.can_assign_submissions', raise_exception=True) +@transaction.atomic +def prescreening_failed(request, identifier_w_vn_nr): + """ + Reject a Submission because pre-screening has failed. + """ + submission = get_object_or_404(Submission.objects.pool(request.user).unassigned(), + preprint__identifier_w_vn_nr=identifier_w_vn_nr) + + mail_editor_view = MailEditorSubview( + request, mail_code='prescreening_failed', instance=submission, + header_template='partials/submissions/admin/prescreening_failed.html') + if mail_editor_view.is_valid(): + # Deprecate old Editorial Assignments + EditorialAssignment.objects.filter(submission=submission).invited().update( + status=STATUS_DEPRECATED) + + # Update status of Submission + submission.touch() + Submission.objects.filter(id=submission.id).update( + status=STATUS_FAILED_PRESCREENING, visible_pool=False, visible_public=False) + + messages.success( + request, 'Submission {identifier} has failed pre-screening and been rejected.'.format( + identifier=submission.preprint.identifier_w_vn_nr)) + messages.success(request, 'Authors have been informed by email.') + mail_editor_view.send_mail() + return redirect(reverse('submissions:pool')) + return mail_editor_view.interrupt() + + +@login_required +@permission_required('scipost.can_assign_submissions', raise_exception=True) +def update_authors_screening(request, identifier_w_vn_nr, nrweeks): + """ + Send an email to the authors, informing them that screening is still ongoing after one week. + """ + submission = get_object_or_404(Submission.objects.pool(request.user).unassigned(), + preprint__identifier_w_vn_nr=identifier_w_vn_nr) + mail_code = 'authors/update_authors_screening_1week' + if nrweeks == '2': + mail_code = 'authors/update_authors_screening_2weeks' + + mail_editor_view = MailEditorSubview( + request, mail_code=mail_code, instance=submission) + if mail_editor_view.is_valid(): + messages.success( + request, + 'Authors of Submission {identifier} updated by email (screening week {nrweeks}).'.format( + identifier=submission.preprint.identifier_w_vn_nr, nrweeks=nrweeks)) + messages.success(request, 'Authors have been updated by email.') + mail_editor_view.send_mail() + return redirect(reverse('submissions:pool')) + return mail_editor_view.interrupt() + + @login_required @permission_required('scipost.can_assign_submissions', raise_exception=True) @transaction.atomic def assignment_failed(request, identifier_w_vn_nr): - """Reject a Submission in pre-screening. + """Reject a Submission in screening. No Editorial Fellow has accepted or volunteered to become Editor-in-charge., hence the Submission is rejected. An Editorial Administrator can access this view from the Pool. @@ -875,7 +934,7 @@ def assignment_failed(request, identifier_w_vn_nr): preprint__identifier_w_vn_nr=identifier_w_vn_nr) mail_editor_view = MailEditorSubview( - request, mail_code='submissions_assignment_failed', instance=submission, + request, mail_code='authors/submissions_assignment_failed', instance=submission, header_template='partials/submissions/admin/editorial_assignment_failed.html') if mail_editor_view.is_valid(): # Deprecate old Editorial Assignments @@ -888,8 +947,8 @@ def assignment_failed(request, identifier_w_vn_nr): status=STATUS_ASSIGNMENT_FAILED, visible_pool=False, visible_public=False) messages.success( - request, 'Submission {arxiv} has failed pre-screening and been rejected.'.format( - arxiv=submission.preprint.identifier_w_vn_nr)) + request, 'Submission {identifier} has failed screening and been rejected.'.format( + identifier=submission.preprint.identifier_w_vn_nr)) messages.success(request, 'Authors have been informed by email.') mail_editor_view.send_mail() return redirect(reverse('submissions:pool')) @@ -1912,7 +1971,7 @@ def editor_invitations(request, identifier_w_vn_nr): elif request.method == 'POST': messages.warning(request, 'Invalid form. Please try again.') context['formset'] = formset - return render(request, 'submissions/admin/submission_presassign_editors.html', context) + return render(request, 'submissions/admin/submission_preassign_editors.html', context) @permission_required('scipost.can_assign_submissions', raise_exception=True) diff --git a/templates/email/authors/submissions_assignment_failed.html b/templates/email/authors/submissions_assignment_failed.html new file mode 100644 index 0000000000000000000000000000000000000000..f1cdd31443425fa24bba06cb418da473a42abba7 --- /dev/null +++ b/templates/email/authors/submissions_assignment_failed.html @@ -0,0 +1,13 @@ +<p>Dear {{ object.submitted_by.profile.get_title_display }} {{ object.submitted_by.user.last_name }},</p> +<p>We regret to inform you that we have been unable to find an Editor-in-charge for your recent Submission to {{ object.submitted_to }},</p> +<p>{{ object.title }}</p> +<p>by {{ object.author_list }}</p> +<p>As per our standard procedures, your Submission was forwarded to specialist Fellows of our {{ object.submitted_to.college }} for consideration. Unfortunately, none of these Fellows made themselves available to take charge and run the refereeing process.</p> +<p>In view of this, we are unable to proceed further with the handling of your manuscript. We hope you can easily find an alternative publishing venue.</p> +<p>Despite this unsuccessful outcome, we thank you very much for your contribution, and hope to be able to be of service to you in the future.</p> +<p>Sincerely,</p> +<p>The SciPost Team.</p> + +{% include 'email/_footer.html' %} + +{% include 'email/_submission_thread_uuid.html' with submission=object %} diff --git a/templates/email/authors/submissions_assignment_failed.json b/templates/email/authors/submissions_assignment_failed.json new file mode 100644 index 0000000000000000000000000000000000000000..dc64745300097cb6fafd0c1e2134439d78482161 --- /dev/null +++ b/templates/email/authors/submissions_assignment_failed.json @@ -0,0 +1,11 @@ +{ + "subject": "SciPost: assignment failed", + "recipient_list": [ + "submitted_by.user.email" + ], + "bcc": [ + "submissions@scipost.org" + ], + "from_name": "SciPost Editorial Admin", + "from_email": "submissions@scipost.org" +} diff --git a/templates/email/authors/update_authors_screening_1week.html b/templates/email/authors/update_authors_screening_1week.html new file mode 100644 index 0000000000000000000000000000000000000000..98f7af7662b38f3a94191ee2b897396e8e93d716 --- /dev/null +++ b/templates/email/authors/update_authors_screening_1week.html @@ -0,0 +1,22 @@ +<p> + Dear {{ submission.submitted_by.profile.get_title_display }} {{ submission.submitted_by.user.last_name }}, +</p> +<p> + We would hereby like to give you an update on your recent SciPost submission, +</p> +<p> + {{ submission.title }} + <br/> + by {{ submission.author_list }}. +</p> +<p> + Your Submission is still in the screening stage (during which an Editor-in-charge is sought). We have contacted {{ submission.editorial_assignments.all|length }} potential Fellows up to now but are still looking for one to take charge. +</p> +<p> + Over the next week, we will expand our search for an Editor-in-charge; if this is successful, you will receive a notification from us. Should we fail to find one, we will contact you again to inform you of the situation. +</p> +<p>Sincerely,</p> +<p>The SciPost Team</p> +{% include 'email/_footer.html' %} + +{% include 'email/_submission_thread_uuid.html' with submission=submission %} diff --git a/templates/email/authors/update_authors_screening_1week.json b/templates/email/authors/update_authors_screening_1week.json new file mode 100644 index 0000000000000000000000000000000000000000..c71830e607d592200a3cf0c9e0572986eefc4798 --- /dev/null +++ b/templates/email/authors/update_authors_screening_1week.json @@ -0,0 +1,11 @@ +{ + "subject": "SciPost: update on your Submission", + "recipient_list": [ + "submitted_by.user.email" + ], + "bcc": [ + "edadmin@scipost.org" + ], + "from_name": "SciPost Submissions", + "from_email": "submissions@scipost.org" +} diff --git a/templates/email/authors/update_authors_screening_2weeks.html b/templates/email/authors/update_authors_screening_2weeks.html new file mode 100644 index 0000000000000000000000000000000000000000..911314481dcb0eb40709b6d5720f998060811d68 --- /dev/null +++ b/templates/email/authors/update_authors_screening_2weeks.html @@ -0,0 +1,28 @@ +<p> + Dear {{ submission.submitted_by.profile.get_title_display }} {{ submission.submitted_by.user.last_name }}, +</p> +<p> + We would hereby like to give you another update on your recent SciPost submission, +</p> +<p> + {{ submission.title }} + <br/> + by {{ submission.author_list }}. +</p> +<p> + Your Submission is still in the screening stage (during which an Editor-in-charge is sought). We have contacted {{ submission.editorial_assignments.all|length }} potential Fellows up to now but are still looking for one willing to take charge. +</p> +<p> + The target duration of the screening phase is one week; since two weeks have now passed, we would like to inform you of the possible further courses of action: + <ul> + <li>We will by default keep trying to find an Editor-in-charge for one more week;</li> + <li>If you do not accept this longer wait, you can at any time withdraw your Submission (from your personal page, under the Submissions tab).</li> + </ul> +</p> +<p>If we still have not found an Editor-in-charge in the coming week, you will get another update from us.</p> +<p>In the meantime, feel free to get in touch with our editorial administration if you have any questions.</p> +<p>Sincerely,</p> +<p>The SciPost Team</p> +{% include 'email/_footer.html' %} + +{% include 'email/_submission_thread_uuid.html' with submission=submission %} diff --git a/templates/email/authors/update_authors_screening_2weeks.json b/templates/email/authors/update_authors_screening_2weeks.json new file mode 100644 index 0000000000000000000000000000000000000000..c71830e607d592200a3cf0c9e0572986eefc4798 --- /dev/null +++ b/templates/email/authors/update_authors_screening_2weeks.json @@ -0,0 +1,11 @@ +{ + "subject": "SciPost: update on your Submission", + "recipient_list": [ + "submitted_by.user.email" + ], + "bcc": [ + "edadmin@scipost.org" + ], + "from_name": "SciPost Submissions", + "from_email": "submissions@scipost.org" +} diff --git a/templates/email/prescreening_failed.html b/templates/email/prescreening_failed.html new file mode 100644 index 0000000000000000000000000000000000000000..2115f4b69850e0b274ee8c22ca3ff44a3eac774d --- /dev/null +++ b/templates/email/prescreening_failed.html @@ -0,0 +1,13 @@ +<p>Dear {{ object.submitted_by.profile.get_title_display }} {{ object.submitted_by.user.last_name }},</p> +<p>Your recent Submission to SciPost,</p> +<p>{{ object.title }}</p> +<p>by {{ object.author_list }}</p> +<p>has unfortunately not passed the pre-screening stage.</p> +<p>In view of this, we are unable to proceed further with the handling of your manuscript. We hope you can easily find an alternative publishing venue.</p> +<p>Despite this unsuccessful outcome, we thank you very much for your contribution, and hope to be able to be of service to you in the future.</p> +<p>Sincerely,</p> +<p>The SciPost Team.</p> + +{% include 'email/_footer.html' %} + +{% include 'email/_submission_thread_uuid.html' with submission=object %} diff --git a/templates/email/submissions_assignment_failed.json b/templates/email/prescreening_failed.json similarity index 100% rename from templates/email/submissions_assignment_failed.json rename to templates/email/prescreening_failed.json diff --git a/templates/email/submissions_assignment_failed.html b/templates/email/submissions_assignment_failed.html deleted file mode 100644 index a1e2e83f761650742e4a696831e8528409c6fb82..0000000000000000000000000000000000000000 --- a/templates/email/submissions_assignment_failed.html +++ /dev/null @@ -1,19 +0,0 @@ -<p>Dear {{ object.submitted_by.profile.get_title_display }} {{ object.submitted_by.user.last_name }},</p> -<p>Your recent Submission to SciPost,</p> -<p>{{ object.title }}</p> -<p>by {{ object.author_list }}</p> -<p> - has unfortunately not passed the pre-screening stage. - We therefore regret to inform you that we will not - process your paper further towards publication, and that you - are now free to send your manuscript to an alternative journal. -</p> - - -<p>We nonetheless thank you very much for your contribution.</p> -<p>Sincerely,</p> -<p>The SciPost Team.</p> - -{% include 'email/_footer.html' %} - -{% include 'email/_submission_thread_uuid.html' with submission=object %}