diff --git a/SciPost_v1/settings/local_JSC.py b/SciPost_v1/settings/local_JSC.py index 843fea24255bb1b2521b3144f4d0f173eb79d460..14b06d9ab44773b41532eee5147490c1cc7deb22 100644 --- a/SciPost_v1/settings/local_JSC.py +++ b/SciPost_v1/settings/local_JSC.py @@ -15,3 +15,4 @@ MAILCHIMP_API_KEY = get_secret("MAILCHIMP_API_KEY") # Logging LOGGING['handlers']['scipost_file_arxiv']['filename'] = '/Users/jscaux/Sites/SciPost.org/scipost_v1/local_files/logs/arxiv.log' LOGGING['handlers']['scipost_file_doi']['filename'] = '/Users/jscaux/Sites/SciPost.org/scipost_v1/local_files/logs/doi.log' +CROSSREF_DEPOSIT_EMAIL = 'jscaux@scipost.org' diff --git a/SciPost_v1/settings/staging_release.py b/SciPost_v1/settings/staging_release.py index 5962728ad4ce750a697b5c614aef535d886b7c1a..bef469d83c25b8e9418898747d2057fa002aa0fd 100644 --- a/SciPost_v1/settings/staging_release.py +++ b/SciPost_v1/settings/staging_release.py @@ -20,9 +20,6 @@ MEDIA_ROOT = '/home/jdewit/webapps/scipost_media/' WEBPACK_LOADER['DEFAULT']['CACHE'] = True WEBPACK_LOADER['DEFAULT']['BUNDLE_DIR_NAME'] = '/home/jdewit/webapps/scipost_static/bundles/' -# Error reporting -ADMINS = MANAGERS = (('J. de Wit', 'jorrandewit@outlook.com'), ) - # Logging LOGGING['handlers']['scipost_file_arxiv']['filename'] = '/home/jdewit/webapps/scipost/logs/arxiv.log' LOGGING['handlers']['scipost_file_doi']['filename'] = '/home/jdewit/webapps/scipost/logs/doi.log' diff --git a/comments/templates/comments/new_comment.html b/comments/templates/comments/new_comment.html index f0470b100f6328b3c92f161ca597155fc7ad46f2..9ba609a99d15c54b8a6938e43f906a934dd1e3bf 100644 --- a/comments/templates/comments/new_comment.html +++ b/comments/templates/comments/new_comment.html @@ -2,7 +2,7 @@ {% if user.is_authenticated and open_for_commenting and perms.scipost.can_submit_comments %} -<hr> +<hr class="divider"> <h2 class="highlight" id="contribute_comment">Contribute a Comment: {% if user_is_referee and submission %} <small class="text-danger">you are an invited referee, please <a href="{% url 'submissions:submit_report' submission.arxiv_identifier_w_vn_nr %}">submit a Report</a> instead</small>{% endif %}</h2> diff --git a/journals/admin.py b/journals/admin.py index 2604c736d9db910ab2f5c6e3e02d7db920613359..1011ffaea461237dfb56b9a384b174c7d80d64da 100644 --- a/journals/admin.py +++ b/journals/admin.py @@ -2,11 +2,13 @@ from django.contrib import admin, messages from django import forms from journals.models import UnregisteredAuthor, Journal, Volume, Issue, Publication, \ - Deposit, DOAJDeposit, GenericDOIDeposit + Deposit, DOAJDeposit, GenericDOIDeposit, Reference from scipost.models import Contributor from submissions.models import Submission +admin.site.register(Reference) + class UnregisteredAuthorAdmin(admin.ModelAdmin): search_fields = ['last_name'] diff --git a/journals/forms.py b/journals/forms.py index 3883c6af3ad372e3489770a8ea5b00f3cfe6e822..12ec2553258242d43497e4b735ba05d325ccb7ad 100644 --- a/journals/forms.py +++ b/journals/forms.py @@ -1,10 +1,14 @@ import re +from datetime import datetime + from django import forms +from django.forms import BaseModelFormSet, modelformset_factory from django.utils import timezone -from .models import UnregisteredAuthor, Issue, Publication +from .models import UnregisteredAuthor, Issue, Publication, Reference +from scipost.services import DOICaller from submissions.models import Submission @@ -77,3 +81,96 @@ class CreateMetadataXMLForm(forms.ModelForm): 'rows': 50, 'cols': 50 }) + + +class BaseReferenceFormSet(BaseModelFormSet): + """ + BaseReferenceFormSet is used to help fill the Reference list for Publications + + It is required to add the required keyword argument `publication` to this FormSet. + """ + initial_references = [] + + def __init__(self, *args, **kwargs): + self.publication = kwargs.pop('publication') + extra = kwargs.pop('extra') + self.extra = int(extra if extra else '0') + kwargs['form_kwargs'] = {'publication': self.publication} + super().__init__(*args, **kwargs) + + def prefill(self): + citations = self.publication.metadata.get('citation_list', []) + + for cite in citations: + caller = DOICaller(cite['doi']) + + if caller.is_valid: + # Authors + author_list = [] + for author in caller._crossref_data['author'][:3]: + try: + author_list.append('{}. {}'.format(author['given'][0], author['family'])) + except KeyError: + author_list.append(author['name']) + + if len(author_list) > 3: + authors = author_list[0] + ' et al.' + elif len(author_list) == 3: + authors = '{}, {} and {}'.format( + author_list[0], author_list[1], author_list[2]) + else: + authors = ' and '.join(author_list) + + # Citation + citation = '{} <b>{}</b>, {} ({})'.format( + caller.data['journal'], + caller.data['volume'], + caller.data['pages'], + datetime.strptime(caller.data['pub_date'], '%Y-%m-%d').year) + + self.initial_references.append({ + 'reference_number': cite['key'][3:], + 'authors': authors, + 'title': caller.data['title'], + 'citation': citation, + 'vor': cite['doi'], + 'vor_url': 'https://doi.org/{}'.format(cite['doi']), + }) + else: + self.initial_references.append({ + 'reference_number': cite['key'][3:], + 'vor': cite['doi'], + 'vor_url': 'https://doi.org/{}'.format(cite['doi']), + }) + + # Add prefill information to the form + if not self.initial_extra: + self.initial_extra = self.initial_references + else: + self.initial_extra.extend(self.initial_references) + self.extra += len(self.initial_extra) + + +class ReferenceForm(forms.ModelForm): + class Meta: + model = Reference + fields = [ + 'reference_number', + 'authors', + 'title', + 'citation', + 'vor', + 'vor_url', + ] + + def __init__(self, *args, **kwargs): + self.publication = kwargs.pop('publication') + super().__init__(*args, **kwargs) + + def save(self, *args, **kwargs): + self.instance.publication = self.publication + super().save(*args, **kwargs) + + +ReferenceFormSet = modelformset_factory(Reference, formset=BaseReferenceFormSet, + form=ReferenceForm, can_delete=True) diff --git a/journals/migrations/0004_auto_20180121_1202.py b/journals/migrations/0004_auto_20180121_1202.py new file mode 100644 index 0000000000000000000000000000000000000000..141a392ff3128ccded9d2c857ccf8f8d7d1f30be --- /dev/null +++ b/journals/migrations/0004_auto_20180121_1202.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-01-21 11:02 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0003_auto_20180117_2323'), + ] + + operations = [ + migrations.CreateModel( + name='Reference', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('citation_count', models.IntegerField()), + ('authors', models.CharField(max_length=512)), + ('title', models.CharField(max_length=512)), + ('citation', models.CharField(max_length=512)), + ('vor', models.CharField(max_length=128)), + ('vor_url', models.URLField()), + ('publication', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='references', to='journals.Publication')), + ], + options={ + 'default_related_name': 'references', + 'ordering': ['-citation_count'], + }, + ), + migrations.AlterField( + model_name='doajdeposit', + name='timestamp', + field=models.CharField(max_length=40), + ), + migrations.AlterUniqueTogether( + name='reference', + unique_together=set([('citation_count', 'publication')]), + ), + ] diff --git a/journals/migrations/0005_auto_20180122_1003.py b/journals/migrations/0005_auto_20180122_1003.py new file mode 100644 index 0000000000000000000000000000000000000000..86a398beb1bd80d31ef70f0ec9671df368326732 --- /dev/null +++ b/journals/migrations/0005_auto_20180122_1003.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-01-22 09:03 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0004_auto_20180121_1202'), + ] + + operations = [ + migrations.AlterModelOptions( + name='reference', + options={'ordering': ['reference_number']}, + ), + migrations.RenameField( + model_name='reference', + old_name='citation_count', + new_name='reference_number', + ), + migrations.AlterField( + model_name='reference', + name='citation', + field=models.CharField(blank=True, max_length=512), + ), + migrations.AlterUniqueTogether( + name='reference', + unique_together=set([('reference_number', 'publication')]), + ), + ] diff --git a/journals/migrations/0006_merge_20180123_2040.py b/journals/migrations/0006_merge_20180123_2040.py new file mode 100644 index 0000000000000000000000000000000000000000..88366b30e6f7401626337b4804e1f9ec03d641bb --- /dev/null +++ b/journals/migrations/0006_merge_20180123_2040.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-01-23 19:40 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0004_auto_20180122_2109'), + ('journals', '0005_auto_20180122_1003'), + ] + + operations = [ + ] diff --git a/journals/models.py b/journals/models.py index 60d245706428e48f5974260e269da3ef485be0b1..5ca16ad8fe0dbe6dab6e636209d9ff728154e276 100644 --- a/journals/models.py +++ b/journals/models.py @@ -324,6 +324,29 @@ class Publication(models.Model): return 0 +class Reference(models.Model): + """ + A Refence is a reference used in a specific Publication. + """ + reference_number = models.IntegerField() + publication = models.ForeignKey('journals.Publication', on_delete=models.CASCADE) + + authors = models.CharField(max_length=512) + title = models.CharField(max_length=512) + citation = models.CharField(max_length=512, blank=True) + + vor = models.CharField(max_length=128) + vor_url = models.URLField() + + class Meta: + unique_together = ('reference_number', 'publication') + ordering = ['reference_number'] + default_related_name = 'references' + + def __str__(self): + return '[{}] {}'.format(self.reference_number, self.publication.doi_label) + + class Deposit(models.Model): """ Each time a Crossref deposit is made for a Publication, @@ -355,7 +378,7 @@ class DOAJDeposit(models.Model): For the Directory of Open Access Journals. """ publication = models.ForeignKey(Publication, on_delete=models.CASCADE) - timestamp = models.CharField(max_length=40, default='') + timestamp = models.CharField(max_length=40) metadata_DOAJ = JSONField() metadata_DOAJ_file = models.FileField(blank=True, null=True, max_length=512) deposition_date = models.DateTimeField(blank=True, null=True) diff --git a/journals/templates/journals/publication_detail.html b/journals/templates/journals/publication_detail.html index a02fa03f8809361ed24f0dac7de156c36278cee4..67fbaf1ab5263eba5682c59cc0dcf7b7f3dcbcf0 100644 --- a/journals/templates/journals/publication_detail.html +++ b/journals/templates/journals/publication_detail.html @@ -7,6 +7,8 @@ {% block pagetitle %}: {{ publication.citation }} - {{ publication.title }}{% endblock pagetitle %} +{% block body_class %}{{ block.super }} publication{% endblock %} + {% block breadcrumb_items %} {{block.super}} <a href="{{journal.get_absolute_url}}" class="breadcrumb-item">{{journal}}</a> @@ -58,7 +60,6 @@ </p> {% endif %} - <hr> {% if publication.citedby|length >= 1 %} <div class="row"> @@ -79,21 +80,17 @@ <div class="row"> <div class="col-12"> - <h3>View more material from these authors:</h3> - <p> - {% for author in publication.authors.all %} - <a href="{{author.get_absolute_url}}">{{ author }}</a> {% if not forloop.last %} · {% endif %} - - {% if forloop.last %} - {% if publication.authors_unregistered.exists %} - · - {% endif %} - {% endif %} - {% endfor %} - {% for author in publication.authors_unregistered.all %} - {{ author }} {% if not forloop.last %} · {% endif %} - {% endfor %} - </p> + <h3>Authors</h3> + <ul> + {% for author in publication.authors.all %} + <li><a href="{{author.get_absolute_url}}">{{ author }}</a></li> + {% endfor %} + {% for author in publication.authors_unregistered.all %} + <li>{{ author }}</li> + {% endfor %} + </ul> + + {% include 'partials/journals/references.html' with publication=publication %} {% if is_edcol_admin %} {# This function is not available for public yet! #} @@ -165,6 +162,7 @@ <li><a href="{% url 'journals:harvest_citedby_links' publication.doi_label %}">Update Crossref cited-by links</a></li> <li><a href="{% url 'journals:manage_metadata' %}">Metadata management page</a></li> <li><a href="{% url 'journals:manage_metadata' doi_label=publication.doi_label %}">This publication's metadata management page</a></li> + <li><a href="{% url 'journals:update_references' doi_label=publication.doi_label %}">Update references</a></li> </ul> </div> </div> diff --git a/journals/templates/journals/sign_existing_report.html b/journals/templates/journals/sign_existing_report.html index 99b8db35f47f12abc642bc14ddad4f0b96eb5a78..422659cd787f3887bd2c3ca8d230a118a83a33f1 100644 --- a/journals/templates/journals/sign_existing_report.html +++ b/journals/templates/journals/sign_existing_report.html @@ -4,30 +4,46 @@ {% block pagetitle %}: sign existing Report{% endblock pagetitle %} +{% block breadcrumb %} +<div class="container-outside breadcrumb-nav"> + <div class="container"> + <nav class="breadcrumb"> + <a href="{% url 'submissions:submissions' %}" class="breadcrumb-item">Submissions</a> + <a href="{{ report.submission.get_absolute_url }}" class="breadcrumb-item">{{ report.submission.arxiv_identifier_w_vn_nr }}</a> + <span class="breadcrumb-item">Sign existing Report</span> + + </nav> + </div> +</div> +{% endblock %} + {% block content %} <div class="row"> - <div class="col-12"> - <hr class="hr12"> - <div class="row"> - <div class="col-6"> - <h2>Confirmation page: do you wish to sign this Report?</h2> - <h3>(your Report is reproduced below for your convenience)</h3> - </div> - <div class="col-6"> - <form action="{% url 'journals:sign_existing_report' report_id=report.id %}" method="post"> - {% csrf_token %} - {{ form|bootstrap }} - <input class="btn btn-secondary" type="submit" value="Submit" /> - </form> - </div> + <div class="col-12"> + <h1 class="highlight">Sign existing Report</h1> </div> +</div> - <h3>Report on Submission <a href="{{report.submission.get_absolute_url}}">{{report.submission.title}}</a></h3> - - {% include 'submissions/_single_public_report_without_comments.html' with report=report user=request.user perms=perms %} +<div class="row"> + <div class="col-md-6"> + <h2>Confirmation page: do you wish to sign this Report?</h2> + <h3>(your Report is reproduced below for your convenience)</h3> + </div> + <div class="col-md-6"> + <form action="{% url 'journals:sign_existing_report' report_id=report.id %}" method="post"> + {% csrf_token %} + {{ form|bootstrap }} + <input class="btn btn-secondary" type="submit" value="Submit" /> + </form> + </div> +</div> - </div> +<div class="row"> + <div class="col-12"> + <h3>Report on Submission <a href="{{report.submission.get_absolute_url}}">{{report.submission.title}}</a></h3> + {% include 'submissions/_single_public_report_without_comments.html' with report=report user=request.user perms=perms %} + </div> </div> {% endblock %} diff --git a/journals/templates/journals/update_references.html b/journals/templates/journals/update_references.html new file mode 100644 index 0000000000000000000000000000000000000000..9d490a4cd1f65184a80d0444f2289ac784daf386 --- /dev/null +++ b/journals/templates/journals/update_references.html @@ -0,0 +1,50 @@ +{% extends 'scipost/base.html' %} + +{% load bootstrap %} + +{% block pagetitle %}: Update References{% endblock pagetitle %} + +{% block breadcrumb %} + <div class="container-outside header"> + <div class="container"> + <nav class="breadcrumb hidden-sm-down"> + <a href="{{publication.get_absolute_url}}" class="breadcrumb-item">{{publication.citation}}</a> + <span class="breadcrumb-item active">Update References</span> + </nav> + </div> + </div> +{% endblock %} + +{% block content %} + +<h1>Update References</h1> + +<form action="{% url 'journals:update_references' publication.doi_label %}" method="get" class="mb-4"> + + <div class="form-group row"> + <label class="col-form-label col-md-4 text-right" for="id_email">Number of additional empty references</label> + <div class="col-md-8"> + <input class="form-control" name="extra" type="number" value="{{ request.GET.extra|default:'0' }}"> + </div> + </div> + <div class="ml-auto col-md-8 multiple-checkbox"> + <label for="prefiller_checkbox"> + <input type="checkbox" name="prefill" value="form" id="prefiller_checkbox"> + Prefill form with Crossref data (this might take a minute) + </label> + </div> + <input type="submit" class="btn btn-primary" value="Refresh"> +</form> + +<hr> + +<form action="{% url 'journals:update_references' publication.doi_label %}" method="post" enctype="multipart/form-data"> + {% csrf_token %} + {{ formset|bootstrap }} + <input type="submit" class="btn btn-primary" value="Submit"> +</form> + + + + +{% endblock %} diff --git a/journals/templates/partials/journals/references.html b/journals/templates/partials/journals/references.html new file mode 100644 index 0000000000000000000000000000000000000000..6467b62ea35106c4f7295bb502a4ac5954e4adac --- /dev/null +++ b/journals/templates/partials/journals/references.html @@ -0,0 +1,14 @@ +{% if publication.references.all %} + <h3>References ({{ publication.references.all|length }})</h3> + <a class="mb-2 d-block" href="javascript:;" data-toggle="toggle" data-target="#reference_list">Click to expand</a> + <ul class="references" id="reference_list" style="display:none;"> + {% for reference in publication.references.all %} + <li> + <span class="counter">[{{ reference.reference_number }}]</span> + <span class="authors">{{ reference.authors }}</span>, + <span class="title">{{ reference.title }}</span>{% if reference.citation %}, <span class="citation">{{ reference.citation|safe }}</span>{% endif %} + <span class="doi">doi: <a href="{{ reference.vor_url }}" target="_blank">{{ reference.vor }}</a></span> + </li> + {% endfor %} + </ul> +{% endif %} diff --git a/journals/urls/general.py b/journals/urls/general.py index 5570b662718dd4458c5d43ddd33f02efb8df178d..fe28da6578e252e6d3521331a7673122159998ba 100644 --- a/journals/urls/general.py +++ b/journals/urls/general.py @@ -53,6 +53,8 @@ urlpatterns = [ url(r'^create_citation_list_metadata/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})$', journals_views.create_citation_list_metadata, name='create_citation_list_metadata'), + url(r'^update_references/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})$', + journals_views.update_references, name='update_references'), url(r'^create_funding_info_metadata/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})$', journals_views.create_funding_info_metadata, name='create_funding_info_metadata'), diff --git a/journals/views.py b/journals/views.py index de1aad618bf388baa280325c2d3740f682cb367e..06edc2905c7199c411b8dce3f7207e9d0adf349d 100644 --- a/journals/views.py +++ b/journals/views.py @@ -7,24 +7,25 @@ import shutil import string import xml.etree.ElementTree as ET + from django.contrib.auth.decorators import login_required from django.contrib.contenttypes.models import ContentType from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.core.urlresolvers import reverse from django.conf import settings from django.contrib import messages -from django.http import Http404 +from django.db import transaction +from django.http import Http404, HttpResponse from django.utils import timezone from django.shortcuts import get_object_or_404, render, redirect -from django.db import transaction -from django.http import HttpResponse from .exceptions import PaperNumberingError from .helpers import paper_nr_string, issue_doi_label_from_doi_label from .models import Journal, Issue, Publication, UnregisteredAuthor, Deposit, DOAJDeposit,\ GenericDOIDeposit from .forms import FundingInfoForm, InitiatePublicationForm, ValidatePublicationForm,\ - UnregisteredAuthorForm, CreateMetadataXMLForm, CitationListBibitemsForm + UnregisteredAuthorForm, CreateMetadataXMLForm, CitationListBibitemsForm,\ + ReferenceFormSet from .utils import JournalUtils from comments.models import Comment @@ -448,6 +449,32 @@ def create_citation_list_metadata(request, doi_label): return render(request, 'journals/create_citation_list_metadata.html', context) +@permission_required('scipost.can_publish_accepted_submission', return_403=True) +def update_references(request, doi_label): + """ + Update the References for a certain Publication. + """ + publication = get_object_or_404(Publication, doi_label=doi_label) + references = publication.references.all() + + formset = ReferenceFormSet(request.POST or None, queryset=references, publication=publication, + extra=request.GET.get('extra')) + + if request.GET.get('prefill'): + formset.prefill() + + if formset.is_valid(): + formset.save() + messages.success(request, 'References saved') + return redirect(publication.get_absolute_url()) + + context = { + 'publication': publication, + 'formset': formset, + } + return render(request, 'journals/update_references.html', context) + + @permission_required('scipost.can_publish_accepted_submission', return_403=True) @transaction.atomic def create_funding_info_metadata(request, doi_label): diff --git a/scipost/managers.py b/scipost/managers.py index b62ac59bb7cfdf7faa5990ed6afa2f20b55e812b..5ed6efd72c6b6dd4ee64791308fa36efafb79cfe 100644 --- a/scipost/managers.py +++ b/scipost/managers.py @@ -18,10 +18,15 @@ class FellowManager(models.Manager): ).order_by('contributor__user__last_name') -class ContributorManager(models.Manager): +class ContributorQuerySet(models.QuerySet): def active(self): return self.filter(user__is_active=True, status=CONTRIBUTOR_NORMAL) + def available(self): + return self.exclude( + unavailability_periods__start__lte=today, + unavailability_periods__end__lte=today) + def awaiting_validation(self): return self.filter(user__is_active=False, status=CONTRIBUTOR_NEWLY_REGISTERED) @@ -52,6 +57,9 @@ class UnavailabilityPeriodManager(models.Manager): def today(self): return self.filter(start__lte=today, end__gte=today) + def future(self): + return self.filter(end__gte=today) + class AuthorshipClaimQuerySet(models.QuerySet): def awaiting_vetting(self): diff --git a/scipost/models.py b/scipost/models.py index 265d2d9b63ab269a8de20aebb859a63343c5ba8d..7666fdf99faf4ec9d2d9a1b0a806cd0af4037545 100644 --- a/scipost/models.py +++ b/scipost/models.py @@ -16,7 +16,7 @@ from .constants import SCIPOST_DISCIPLINES, SCIPOST_SUBJECT_AREAS,\ INVITATION_CONTRIBUTOR, INVITATION_FORMAL,\ AUTHORSHIP_CLAIM_PENDING, AUTHORSHIP_CLAIM_STATUS from .fields import ChoiceArrayField -from .managers import FellowManager, ContributorManager, RegistrationInvitationManager,\ +from .managers import FellowManager, ContributorQuerySet, RegistrationInvitationManager,\ UnavailabilityPeriodManager, AuthorshipClaimQuerySet today = timezone.now().date() @@ -61,7 +61,7 @@ class Contributor(models.Model): default=True, verbose_name="I accept to receive SciPost emails") - objects = ContributorManager() + objects = ContributorQuerySet.as_manager() def __str__(self): return '%s, %s' % (self.user.last_name, self.user.first_name) diff --git a/scipost/services.py b/scipost/services.py index 65755b64633cc83954c05b5d8e8a4d9c6827b9a8..908f731778c2fed20ab70b253a88a3819e6d2acc 100644 --- a/scipost/services.py +++ b/scipost/services.py @@ -42,9 +42,16 @@ class DOICaller: def _format_data(self): data = self._crossref_data title = data['title'][0] - author_list = ['{} {}'.format(author['given'], author['family']) for author in data['author']] - # author_list is given as a comma separated list of names on the relevant models (Commentary, Submission) - author_list = ", ".join(author_list) + + # author_list is given as a comma separated list of names on the relevant models + author_list = [] + for author in data['author']: + try: + author_list.append('{} {}'.format(author['given'], author['family'])) + except KeyError: + author_list.append(author['name']) + author_list = ', '.join(author_list) + journal = data['container-title'][0] volume = data.get('volume', '') pages = self._get_pages(data) @@ -77,8 +84,8 @@ class DOICaller: if date_parts: date_parts = date_parts[0] year = date_parts[0] - month = date_parts[1] if len(date_parts) > 1 else '1' - day = date_parts[2] if len(date_parts) > 2 else '1' + month = date_parts[1] if len(date_parts) > 1 else 1 + day = date_parts[2] if len(date_parts) > 2 else 1 pub_date = datetime.date(year, month, day).isoformat() else: pub_date = '' diff --git a/scipost/static/scipost/assets/config/preconfig.scss b/scipost/static/scipost/assets/config/preconfig.scss index 8955cee5caa9d80c108c59ef83368d67bb58cc40..a9b5266ee232496003ad1e192a0c62ba668a0b79 100644 --- a/scipost/static/scipost/assets/config/preconfig.scss +++ b/scipost/static/scipost/assets/config/preconfig.scss @@ -17,13 +17,6 @@ $alert-padding-x: 0.75rem; // Grid // $grid-gutter-width: 20px; -// $container-max-widths: ( -// xs: , -// sm: 728px, -// md: 720px, -// lg: 960px, -// xl: 1140px -// ); // Colors // @@ -66,7 +59,6 @@ $alert-border-radius: $base-border-radius; // Cards // $card-border-radius: $base-border-radius; -// $card-bg: $gray-200; $card-border-color: $gray-200; $card-spacer-x: 0.75rem; $card-spacer-y: 0.5rem; diff --git a/scipost/static/scipost/assets/css/_journals.scss b/scipost/static/scipost/assets/css/_journals.scss index a2f1946f4558a8964fab6ceb18be41f0224f4d4a..490351d34a4027661c60611b65fa9f5d33d07893 100644 --- a/scipost/static/scipost/assets/css/_journals.scss +++ b/scipost/static/scipost/assets/css/_journals.scss @@ -74,3 +74,10 @@ ul.publicationClickables { } } } + + +.publication { + .abstract { + margin-bottom: 0; + } +} diff --git a/scipost/static/scipost/assets/css/_list_group.scss b/scipost/static/scipost/assets/css/_list_group.scss index 4755ec289813f41a3be705df5c68db980d203c26..6bf5d2d7e491a3d7bcfea4be366bfaa8f00d9884 100644 --- a/scipost/static/scipost/assets/css/_list_group.scss +++ b/scipost/static/scipost/assets/css/_list_group.scss @@ -23,3 +23,25 @@ ul.events-list { } } } + + +.fa-li { + line-height: 1.25 !important; +} + +ul.references { + list-style: none; + padding-left: 0; + + .counter { + margin-right: 0.25rem; + } + + .title { + font-style: italic; + } + .doi { + display: block; + margin: 0 0 0.2rem 1.5rem; + } +} diff --git a/scipost/static/scipost/assets/css/_nav.scss b/scipost/static/scipost/assets/css/_nav.scss index 974bcde176520b2f82900249c0c8a030a514c39e..d33cb87ddac8b2f23a73f83970814fe8d1764fca 100644 --- a/scipost/static/scipost/assets/css/_nav.scss +++ b/scipost/static/scipost/assets/css/_nav.scss @@ -71,3 +71,8 @@ nav.main-nav { .nav { flex-wrap: nowrap; } + +.tab-pane .loading { + text-align: center; + padding: 3rem; +} diff --git a/scipost/static/scipost/assets/js/dynamic_loading.js b/scipost/static/scipost/assets/js/dynamic_loading.js new file mode 100644 index 0000000000000000000000000000000000000000..5064be98fb68d318671d523bd3694ce75d0efb3c --- /dev/null +++ b/scipost/static/scipost/assets/js/dynamic_loading.js @@ -0,0 +1,54 @@ +function dynamic_load_tab( target_tab ) { + var tab = $(target_tab); + var url = tab.attr('sp-dynamic-load'); + if(tab.data('sp-loaded') == 'true') { + // window.history.replaceState('scipost', document.title, url); + return; // Only load once + } + + var target = $(tab.attr('href')); + $(target) + .show() + .html('<div class="loading"><i class="fa fa-spinner fa-pulse fa-3x fa-fw"></i></div>'); + + $.get(url).done(function(data) { + $(target).html(data).promise().done(function() { + tab.data('sp-loaded', 'true'); + }); + + // window.history.replaceState('scipost', document.title, url); + }); +} + +$(function(){ + // Change `tab` GET parameter for page-reload + $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { + dynamic_load_tab( e.target ) + }) + $('[data-toggle="tab"][sp-autoload="true"]').tab('show'); + + // Simple simple Angular-like loading! + $('a[data-toggle="dynamic"]').on('click', function(event) { + event.preventDefault(); + var self = this, + url = $(this).attr('href'), + target = $(this).attr('data-target'); + + $(target) + .show() + .html('<div class="loading"><i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i></div>'); + + $.get(url + '?json=1').done(function(data) { + $(target).html(data).promise().done(function() { + init_page(); + }); + $('[data-target="active-list"]') + .find('> li') + .removeClass('active') + $(self).parents('[data-target="active-list"] > li') + .addClass('active'); + + window.history.replaceState('scipost', document.title, url); + }); + }); +}); diff --git a/scipost/static/scipost/assets/js/scripts.js b/scipost/static/scipost/assets/js/scripts.js index 62d164fb742284d5822e5663d37e7cab1e401187..4e96974ec3df7596e5d8cdc9f2fbafa8daedad99 100644 --- a/scipost/static/scipost/assets/js/scripts.js +++ b/scipost/static/scipost/assets/js/scripts.js @@ -59,12 +59,6 @@ $(function(){ // Remove all alerts in screen automatically after 15sec. setTimeout(function() {hide_all_alerts()}, 15000); - // Change `tab` GET parameter for page-reload - $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { - var tab_name = e.target.hash.substring(1) - window.history.replaceState({}, null, '?tab=' + tab_name); - }); - init_page(); // Simple simple Angular-like loading! diff --git a/scipost/templates/partials/scipost/personal_page/account.html b/scipost/templates/partials/scipost/personal_page/account.html new file mode 100644 index 0000000000000000000000000000000000000000..bcb731d9b2bb8a84ff3b75b0403485f9c52db829 --- /dev/null +++ b/scipost/templates/partials/scipost/personal_page/account.html @@ -0,0 +1,184 @@ +{% load bootstrap %} +{% load user_groups %} + +{# Save all Permission groups into template variables #} + {% is_edcol_admin request.user as is_edcol_admin %} + {% is_scipost_admin request.user as is_scipost_admin %} + {% is_editorial_college request.user as is_editorial_college %} + {% is_advisory_board request.user as is_advisory_board %} + {% is_vetting_editor request.user as is_vetting_editor %} + {% is_ambassador request.user as is_ambassador %} + {% is_junior_ambassador request.user as is_junior_ambassador %} + {% is_registered_contributor request.user as is_registered_contributor %} + {% is_tester request.user as is_tester %} + {% is_production_officer request.user as is_production_officer %} + +<div class="row"> + <div class="col-12"> + <div class="card card-grey"> + <div class="card-body"> + <h2 class="card-title mb-0">Your Account</h2> + </div> + </div> + </div> +</div> +<div class="row"> + <div class="col-md-6"> + <h3>Your personal details:</h3> + {% include "scipost/_private_info_as_table.html" with contributor=contributor %} + + {% if contributor %} + {# Scientist fields #} + <h3 class="mt-3">Your main discipline:</h3> + <ul><li>{{ contributor.get_discipline_display }}</li></ul> + + <h3 class="mt-3">Your expertises:</h3> + {% if contributor.expertises %} + {% include "scipost/_expertises_as_ul.html" with contributor=contributor %} + {% else %} + <p>You haven't listed your expertise(s).<br/> + Do so by <a href="{% url 'scipost:update_personal_data' %}">updating your personal data</a> + </p> + {% endif %} + {# END: Scientist fields #} + {% endif %} + </div> + <div class="col-md-6"> + {% if contributor %} + {# Scientist fields #} + {% if not contributor.is_currently_available %} + <h3 class="text-warning">You are currently unavailable</h3> + <p>Check your availability underneath if this should not be the case.</p> + <hr> + {% endif %} + {# END: Scientist fields #} + {% endif %} + + {% if not contributor.petition_signatories.exists %} + <div class="border border-danger p-2"> + <h3 class="text-danger">Scientists, please help us out!</h3> + <p class="mb-1">If it is not listed on our Partners page, please encourage your institution (through a librarian, director, ...) to join by <a class="h3 text-blue" href="{% url 'petitions:petition' slug='join-SPB' %}">signing our petition</a>.</p> + </div> + <hr> + {% endif %} + + {% if is_scipost_admin %} + <h3>You are a SciPost Administrator.</h3> + {% endif %} + {% if is_edcol_admin %} + <h3>You are a SciPost Editorial Administrator.</h3> + {% endif %} + {% if is_advisory_board %} + <h3>You are a member of the Advisory Board.</h3> + {% endif %} + {% if is_editorial_college %} + <h3>You are a member of the Editorial College.</h3> + {% endif %} + {% if is_vetting_editor %} + <h3>You are a SciPost Vetting Editor.</h3> + {% endif %} + {% if is_registered_contributor %} + <h3>You are a Registered Contributor.</h3> + {% endif %} + {% if is_tester %} + <h3>You are a SciPost Tester.</h3> + {% endif %} + {% if is_ambassador %} + <h3>You are a SciPost Ambassador.</h3> + {% endif %} + {% if is_junior_ambassador %} + <h3>You are a SciPost Junior Ambassador.</h3> + {% endif %} + {% if is_production_officer %} + <h3>You are a SciPost Production Officer.</h3> + {% endif %} + + {% if contributor.fellowships.exists %} + <h3>Your Fellowships:</h3> + <ul class="mb-2"> + {% for fellowship in contributor.fellowships.all %} + <li class="pt-1"> + {{ fellowship.contributor.get_discipline_display }} + + {% if fellowship.guest %} + (Guest Fellowship) + {% else %} + (Regular Fellowship) + {% endif %} + + {% if not fellowship.is_active %} + <span class="label label-outline-warning label-sm">Inactive</span> + {% endif %} + + {% if fellowship.start_date or fellowship.until_date %} + <div class="text-muted"> + {% if fellowship.start_date %} + from {{ fellowship.start_date }} + {% endif %} + + {% if fellowship.until_date %} + until {{ fellowship.until_date }} + {% endif %} + </div> + {% endif %} + </li> + + {% endfor %} + </ul> + <a href="{% url 'submissions:pool' %}" class="h3 text-primary ml-4 px-3 d-block-inline">Go to the Submissions Pool</a> + {% endif %} + + <h3 class="mt-3">Update your personal data or password</h3> + + <ul> + <li><a href="{% url 'scipost:update_personal_data' %}">Update your personal data</a></li> + <li><a href="{% url 'scipost:change_password' %}">Change your password</a></li> + </ul> + </div> +</div> + +{% if unavailability_form %} + <hr> + <div class="row"> + <div class="col"> + <h2 class="highlight">Your Availability</h2> + </div> + </div> + <div class="row justify-content-center"> + <div class="col-md-4 mr-md-5"> + <p>To help with the editorial workflow, you can inform us of any periods during which you are unavailable. We will do our best to respect these.</p> + <h3 class="mb-3">Mark a period as unavailable:</h3> + <form action="{% url 'scipost:mark_unavailable_period' %}" method="post"> + {% csrf_token %} + {{ unavailability_form|bootstrap }} + <input class="btn btn-secondary" type="submit" value="Submit" /> + </form> + </div> + <div class="col-md-4 ml-md-5"> + {% if unavailabilities %} + <h3>Your unavailability periods in our records</h3> + <p class="text-muted">(YYYY-MM-DD)</p> + <table class="table"> + <tr> + <th>Start</th> + <th colspan="2">End</th> + </tr> + {% for unav in unavailabilities %} + <tr> + <td>{{ unav.start }}</td> + <td>{{ unav.end }}</td> + <td> + <form action="{% url 'scipost:delete_unavailable_period' unav.id %}" method="post"> + {% csrf_token %} + <input class="btn btn-danger" type="submit" value="Delete" /> + </form> + </td> + </tr> + {% endfor %} + </table> + {% else %} + <p>You don't have any upcoming unavailability periods on record.</p> + {% endif %} + </div> + </div> +{% endif %} diff --git a/scipost/templates/partials/scipost/personal_page/author_replies.html b/scipost/templates/partials/scipost/personal_page/author_replies.html new file mode 100644 index 0000000000000000000000000000000000000000..2149474b1344a93ed81533cd57b93ce41af54ce0 --- /dev/null +++ b/scipost/templates/partials/scipost/personal_page/author_replies.html @@ -0,0 +1,23 @@ +<div class="row"> + <div class="col-12"> + <div class="card card-grey"> + <div class="card-body"> + <h2 class="card-title mb-0">Your Author Replies</h2> + </div> + </div> + </div> +</div> + +<div class="row" id="myauthorreplieslist"> + <div class="col-12"> + <ul class="list-group list-group-flush"> + {% for own_reply in own_authorreplies %} + <li class="list-group-item"> + {% include 'comments/_comment_card_extended_for_author.html' with comment=own_reply %} + </li> + {% empty %} + <li class="list-group-item"><em>You do not have Author Replies yet.</em></li> + {% endfor %} + </ul> + </div> +</div> diff --git a/scipost/templates/partials/scipost/personal_page/commentaries.html b/scipost/templates/partials/scipost/personal_page/commentaries.html new file mode 100644 index 0000000000000000000000000000000000000000..2e50be5535ef30f630b23b89d014ade1d59e8e30 --- /dev/null +++ b/scipost/templates/partials/scipost/personal_page/commentaries.html @@ -0,0 +1,32 @@ +<div class="row"> + <div class="col-12"> + <div class="card card-grey"> + <div class="card-body"> + <h2 class="card-title">Commentaries</h2> + <ul class="mb-0"> + {% if nr_commentary_authorships_to_claim > 0 %} + <li><a href="{% url 'scipost:claim_authorships' %}">Potential authorships to claim (auto-detected: {{ nr_commentary_authorships_to_claim}})</a></li> + {% endif %} + <li><a href="{% url 'commentaries:request_commentary' %}">Request opening a SciPost Commentary Page</a></li> + </ul> + </div> + </div> + </div> +</div> + +<div class="row" id="mycommentarieslist"> + <div class="col-12"> + <h3>Commentaries for which you are identified as an author:</h3> + </div> + <div class="col-12"> + <ul class="list-group list-group-flush"> + {% for com in own_commentaries %} + <li class="list-group-item"> + {% include 'commentaries/_commentary_card_content.html' with commentary=com %} + </li> + {% empty %} + <li class="list-group-item"><em>No Commentaries found</em></li> + {% endfor %} + </ul> + </div> +</div> diff --git a/scipost/templates/partials/scipost/personal_page/comments.html b/scipost/templates/partials/scipost/personal_page/comments.html new file mode 100644 index 0000000000000000000000000000000000000000..f6a5f52db29c21f347f7cc59073eb7ed99940173 --- /dev/null +++ b/scipost/templates/partials/scipost/personal_page/comments.html @@ -0,0 +1,23 @@ +<div class="row"> + <div class="col-12"> + <div class="card card-grey"> + <div class="card-body"> + <h2 class="card-title mb-0">Your Comments</h2> + </div> + </div> + </div> +</div> + +<div class="row" id="mycommentslist"> + <div class="col-12"> + <ul class="list-group list-group-flush"> + {% for own_comment in own_comments %} + <li class="list-group-item"> + {% include 'comments/_comment_card_extended_for_author.html' with comment=own_comment %} + </li> + {% empty %} + <li class="list-group-item"><em>You have not commented yet.</em></li> + {% endfor %} + </ul> + </div> +</div> diff --git a/scipost/templates/partials/scipost/personal_page/editorial_actions.html b/scipost/templates/partials/scipost/personal_page/editorial_actions.html new file mode 100644 index 0000000000000000000000000000000000000000..ffd9844f50aeebf31bc1b66010cab5049542eb23 --- /dev/null +++ b/scipost/templates/partials/scipost/personal_page/editorial_actions.html @@ -0,0 +1,167 @@ + +<div class="row"> + <div class="col-12"> + <div class="card card-grey"> + <div class="card-body"> + <h2 class="card-title mb-0">Pending Editorial Actions</h2> + </div> + </div> + </div> +</div> + +<div class="row"> + {% if perms.scipost.can_vet_registration_requests or perms.scipost.can_draft_registration_invitations or perms.scipost.can_manage_registration_invitations %} + <div class="col-md-4"> + <h3>Registration actions</h3> + <ul> + {% if perms.scipost.can_vet_registration_requests %} + <li><a href="{% url 'scipost:vet_registration_requests' %}">Vet Registration requests</a> ({{ nr_reg_to_vet }})</li> + {% endif %} + {% if perms.scipost.can_resend_registration_requests %} + <li><a href="{% url 'scipost:registration_requests' %}">Awaiting validation</a> ({{ nr_reg_awaiting_validation }})</li> + {% endif %} + {% if perms.scipost.can_draft_registration_invitations %} + <li><a href="{% url 'scipost:draft_registration_invitation' %}">Draft a Registration Invitation</a></li> + {% endif %} + {% if perms.scipost.can_manage_registration_invitations %} + <li><a href="{% url 'scipost:registration_invitations' %}">Manage Registration Invitations</a></li> + {% endif %} + </ul> + + {% if perms.scipost.can_manage_registration_invitations %} + <h3>Notifications</h3> + <ul> + <li><a href="{% url 'scipost:citation_notifications' %}">Manage citation notifications</a></li> + </ul> + {% endif %} + + {% if is_scipost_admin %} + <h3>Email communications</h3> + <ul> + {% if perms.scipost.can_email_group_members %} + <li><a href="{% url 'scipost:email_group_members' %}">Email Group Members</a></li> + {% endif %} + {% if perms.scipost.can_email_particulars %} + <li><a href="{% url 'scipost:send_precooked_email' %}">Send a precooked email</a></li> + <li><a href="{% url 'scipost:email_particular' %}">Email a particular individual/address</a></li> + {% endif %} + {% if perms.scipost.can_manage_mailchimp %} + <li><a href="{% url 'mailing_lists:overview' %}">Manage mailing lists</a></li> + {% endif %} + </ul> + + <h3>SciPost Administation</h3> + <ul> + <li><a href="{% url 'affiliations:institutions' %}">Manage Institutions database</a></li> + </ul> + {% endif %} + + {% if perms.scipost.can_view_timesheets %} + <h3>Finance</h3> + <ul> + <li><a href="{% url 'finances:timesheets' %}">Production Team Timesheets</a></li> + </ul> + {% endif %} + </div> + {% endif %} + + <div class="col-md-4"> + {% if perms.scipost.can_vet_comments or perms.scipost.can_vet_submitted_reports %} + <h3>Vetting actions</h3> + <ul> + {% if perms.scipost.can_vet_commentary_requests %} + <li><a href="{% url 'commentaries:vet_commentary_requests' %}">Vet Commentary Page requests</a> ({{ nr_commentary_page_requests_to_vet }})</li> + {% endif %} + {% if perms.scipost.can_vet_comments %} + <li><a href="{% url 'comments:vet_submitted_comments_list' %}">Vet submitted Comments</a> ({{ nr_comments_to_vet }})</li> + {% endif %} + {% if perms.scipost.can_vet_thesislink_requests %} + <li><a href="{% url 'theses:unvetted_thesislinks' %}">Vet Thesis Link Requests</a> ({{ nr_thesislink_requests_to_vet }})</li> + {% endif %} + {% if perms.scipost.can_vet_authorship_claims %} + <li><a href="{% url 'scipost:vet_authorship_claims' %}">Vet Authorship Claims</a> ({{ nr_authorship_claims_to_vet }})</li> + {% endif %} + {% if perms.scipost.can_vet_submitted_reports %} + <li><a href="{% url 'submissions:vet_submitted_reports_list' %}">Vet submitted Reports</a> ({{ nr_reports_to_vet }})</li> + {% endif %} + </ul> + {% endif %} + + {% if perms.scipost.can_oversee_refereeing %} + <h3>Editorial Admin actions</h3> + <ul> + <li><a href="{% url 'submissions:reports_accepted_list' %}">Accepted Reports</a>{% if nr_reports_without_pdf %} ({{nr_reports_without_pdf}} unfinished){% endif %}</li> + <li><a href="{% url 'submissions:pool' %}">Submissions Pool</a></li> + <li><a href="{% url 'submissions:treated_submissions_list' %}">Fully treated Submissions</a>{% if nr_treated_submissions_without_pdf %} ({{nr_treated_submissions_without_pdf}} unfinished){% endif %}</li> + <li><a href="{% url 'journals:harvest_citedby_list' %}">Harvest citedby data</a></li> + <li><a href="{% url 'journals:manage_comment_metadata' %}">Manage Comment metadata</a></li> + <li><a href="{% url 'colleges:fellowships' %}">Manage Fellowships</a></li> + <li><a href="{% url 'journals:manage_metadata' %}">Manage Publication metadata</a></li> + <li><a href="{% url 'journals:manage_report_metadata' %}">Manage Report metadata</a></li> + <li><a href="{% url 'proceedings:proceedings' %}">Manage Proceedings Issues</a></li> + </ul> + {% endif %} + + {% if perms.scipost.can_attend_VGMs %} + <h3>Virtual General Meetings</h3> + <ul> + <li><a href="{% url 'virtualmeetings:VGMs' %}">List of VGMs</a></li> + </ul> + {% endif %} + </div> + + {% if perms.scipost.can_oversee_refereeing or perms.scipost.can_take_charge_of_submissions %} + <div class="col-md-4"> + <h3>Info</h3> + <ul> + <li><a href="{% url 'submissions:editorial_workflow' %}">How-to guide: summary of the editorial workflow</a></li> + </ul> + + <h3>Submissions assignments</h3> + <ul> + <li><a href="{% url 'submissions:assignments' %}">Your assignments</a></li> + <li><a href="{% url 'scipost:Fellow_activity_overview' %}">View assignments overview</a></li> + + {% if perms.scipost.can_assign_submissions %} + <li>Assign Submissions via the <a href="{% url 'submissions:pool' %}">Submissions Pool</a> ({{ nr_submissions_to_assign }})</li> + {% endif %} + {% if perms.scipost.can_take_charge_of_submissions %} + <li>Accept or decline assignments via the <a href="{% url 'submissions:pool' %}">Submissions Pool</a> ({{ nr_assignments_to_consider }})</li> + {% endif %} + </ul> + + {% if perms.scipost.can_oversee_refereeing %} + <h3>Refereeing overview</h3> + <ul> + <li>View (and act on) outstanding refereeing invitations in the <a href="{% url 'submissions:refereeing_overview' %}">refereeing overview</a></li> + <li><a href="{% url 'stats:statistics' %}">View statistics</a> for submissions, refereeing, publishing</li> + </ul> + <h3>Voting</h3> + <ul> + <li>Prepare Editorial Recommendations for voting via the <a href="{% url 'submissions:pool' %}">Submissions Pool</a> ({{ nr_recommendations_to_prepare_for_voting }})</li> + </ul> + {% endif %} + </div> + {% endif %} +</div> + +{% if active_assignments %} + <div class="row"> + <div class="col-12"> + <h3 class="highlight">Submissions for which you are Editor-in-charge</h3> + </div> + <div class="col-12"> + <ul class="list-group list-group-flush"> + {% for assignment in active_assignments %} + <li class="list-group-item"> + <div class="card-body px-0"> + {% include 'partials/submissions/submission_card_content.html' with submission=assignment.submission %} + {% include 'submissions/_submission_status_block.html' with submission=assignment.submission %} + <p class="card-text mt-2">Manage this Submission from its <a href="{% url 'submissions:editorial_page' assignment.submission.arxiv_identifier_w_vn_nr %}">Editorial Page</a>.</p> + </div> + </li> + {% endfor %} + </ul> + </div> + </div> +{% endif %} diff --git a/scipost/templates/partials/scipost/personal_page/publications.html b/scipost/templates/partials/scipost/personal_page/publications.html new file mode 100644 index 0000000000000000000000000000000000000000..302090e22d865ec6c41654a8311462e886883fb5 --- /dev/null +++ b/scipost/templates/partials/scipost/personal_page/publications.html @@ -0,0 +1,36 @@ +<div class="row"> + <div class="col-12"> + <div class="card card-grey"> + <div class="card-body"> + <h2 class="card-title">Publications</h2> + <ul class="mb-0"> + {% if nr_publication_authorships_to_claim > 0 %} + <li><a href="{% url 'scipost:claim_authorships' %}">Potential authorships to claim (auto-detected: {{ nr_publication_authorships_to_claim}})</a></li> + {% endif %} + </ul> + </div> + </div> + </div> +</div> + + +<div class="row" id="mypublicationslist"> + <div class="col-12"> + <h3 class="mb-3">Publications for which you are identified as an author:</h3> + </div> + <div class="col-12"> + <ul class="list-unstyled"> + {% for pub in own_publications %} + <li> + <div class="card card-grey card-publication" id="{{pub.doi_label}}"> + {% include 'journals/_publication_card_content.html' with publication=pub current_user=request.user %} + </div> + </li> + {% empty %} + <li> + <em>No Publications found</em> + </li> + {% endfor %} + </ul> + </div> +</div> diff --git a/scipost/templates/partials/scipost/personal_page/refereeing.html b/scipost/templates/partials/scipost/personal_page/refereeing.html new file mode 100644 index 0000000000000000000000000000000000000000..a0fa0d7134e87b9073f52e677340361a712b4055 --- /dev/null +++ b/scipost/templates/partials/scipost/personal_page/refereeing.html @@ -0,0 +1,89 @@ +<div class="row"> + <div class="col-12"> + <div class="card card-grey"> + <div class="card-body"> + <h2 class="card-title">Refereeing Tasks</h2> + <ul class="mb-0"> + <li><a href="{% url 'submissions:accept_or_decline_ref_invitations' %}">Accept/decline refereeing invitations</a> ({{ contributor.referee_invitations.open.count }})</li> + </ul> + </div> + </div> + </div> +</div> + +{% if contributor.reports.in_draft.all %} + <div class="row"> + <div class="col-12"> + <h3>Unfinished reports:</h3> + </div> + <div class="col-12"> + <ul class="list-group list-group-flush"> + {% for report in contributor.reports.in_draft.all %} + <li class="list-group-item"> + <div class="card-body px-0"> + {% include 'partials/submissions/submission_card_content.html' with submission=report.submission %} + <a class="btn btn-outline-primary my-2" href="{% url 'submissions:submit_report' report.submission.arxiv_identifier_w_vn_nr %}">Finish report</a> + </div> + </li> + {% endfor %} + </ul> + </div> + </div> +{% endif %} + + +<div class="row"> + <div class="col-12"> + <h3>Pending Refereeing Tasks:</h3> + </div> + <div class="col-12"> + <ul class="list-group list-group-flush"> + {% for task in contributor.referee_invitations.in_process.all %} + <li class="list-group-item"> + <div class="card-body px-0"> + {% include 'partials/submissions/refereeing_invitation_card_content.html' with invitation=task %} + </div> + </li> + {% empty %} + <li class="list-group-item"><em>You do not have any pending refereeing task</em></li> + {% endfor %} + </ul> + </div> +</div> + +{% if contributor.reports.non_draft.all %} + <div class="row"> + <div class="col-12"> + <h3>Finished reports:</h3> + </div> + <div class="col-12"> + <ul class="list-group list-group-flush"> + {% for report in contributor.reports.non_draft.all %} + <li class="list-group-item"> + {% comment %} + Temporary: There is already a template for a "Report summary" in a parallel (unmerged) branch. Awaiting merge to use that template. + {% endcomment %} + <div class="card-body px-0 {% block cardblock_class_block %}{% endblock %}"> + <h3>Report on Submission <a href="{{report.submission.get_absolute_url}}">{{report.submission.title}}</a></h3> + <table> + <tr> + <th style='min-width: 100px;'>Received:</th><td>{{ report.date_submitted|date:'Y-n-j' }}<td> + </tr> + <tr> + <th>Status:</th><td {% if report.status == 'vetted' %}class="text-success"{% elif report.status == 'unvetted' %}class="text-danger"{% endif %}>{{report.get_status_display}}</td> + </tr> + {% if report.doi_label %} + <tr> + <th>DOI:</th><td>{{ report.doi_string }}</td></th> +{% endif %} + <tr> + <th>Anonymous:</th><td>{{report.anonymous|yesno:'Yes,No'}}</td>{% if report.anonymous %}<td>You can <a href="{% url 'journals:sign_existing_report' report_id=report.id %}">click here to sign this Report</a> (leads to confirmation page){% endif %}</td> + </tr> + </table> + </div> + </li> + {% endfor %} + </ul> + </div> + </div> +{% endif %} diff --git a/scipost/templates/partials/scipost/personal_page/submissions.html b/scipost/templates/partials/scipost/personal_page/submissions.html new file mode 100644 index 0000000000000000000000000000000000000000..0e9c098facb4ad2ada8762c20f92b591de04c308 --- /dev/null +++ b/scipost/templates/partials/scipost/personal_page/submissions.html @@ -0,0 +1,50 @@ +<div class="row"> + <div class="col-12"> + <div class="card card-grey"> + <div class="card-body"> + <h2 class="card-title">Submissions</h2> + <ul class="mb-0"> + {% if nr_submission_authorships_to_claim > 0 %} + <li><a href="{% url 'scipost:claim_authorships' %}">Potential authorships to claim (auto-detected: {{ nr_submission_authorships_to_claim }})</a></li> + {% endif %} + <li><a href="{% url 'submissions:submit_manuscript' %}">Submit an arXiv preprint to a SciPost Journal</a></li> + </ul> + </div> + </div> + </div> +</div> + +{# {% if own_submissions %}#} +<div class="row" id="mysubmissionslist"> + <div class="col-12"> + <h3>Submissions for which you are identified as an author:</h3> + </div> + <div class="col-12"> + <ul class="list-group list-group-flush"> + {% for sub in own_submissions %}, + <li class="list-group-item"> + <div class="card-body px-0"> + {% include 'partials/submissions/submission_card_content.html' with submission=sub %} + {% include 'submissions/_submission_status_block.html' with submission=sub %} + + {% if request.user.contributor == sub.submitted_by %} + <p class="card-text mt-1"> + {% if sub.editor_in_charge %} + <a href="{% url 'submissions:communication' sub.arxiv_identifier_w_vn_nr 'AtoE' %}">Write to the Editor-in-charge</a> + {% endif %} + {% if sub.status == 'revision_requested' %} + · <a href="{% url 'submissions:prefill_using_identifier' %}?identifier={{ sub.arxiv_identifier_wo_vn_nr }}">Resubmit this manuscript</a> + {% endif %} + </p> + {% endif %} + </div> + </li> + {% empty %} + <li class="list-group-item"> + <em>No Submissions found</em> + </li> + {% endfor %} + </ul> + </div> +</div> +{# {% endif %}#} diff --git a/scipost/templates/partials/scipost/personal_page/theses.html b/scipost/templates/partials/scipost/personal_page/theses.html new file mode 100644 index 0000000000000000000000000000000000000000..de0e22998d920a70b8a3ac95e133ae82c0d9c372 --- /dev/null +++ b/scipost/templates/partials/scipost/personal_page/theses.html @@ -0,0 +1,32 @@ +<div class="row"> + <div class="col-12"> + <div class="card card-grey"> + <div class="card-body"> + <h2 class="card-title">Theses</h2> + <ul class="mb-0"> + {% if nr_thesis_authorships_to_claim > 0 %} + <li><a href="{% url 'scipost:claim_authorships' %}">Potential authorships to claim (auto-detected: {{ nr_thesis_authorships_to_claim}})</a></li> + {% endif %} + <li><a href="{% url 'theses:request_thesislink' %}">Request a SciPost ThesisLink</a></li> + </ul> + </div> + </div> + </div> +</div> + +<div class="row" id="mytheseslist"> + <div class="col-12"> + <h3>Theses for which you are identified as an author:</h3> + </div> + <div class="col-12"> + <ul class="list-group list-group-flush"> + {% for thesis in own_thesislinks %} + <li class="list-group-item"> + {% include 'theses/_thesislink_card_content.html' with thesislink=thesis %} + </li> + {% empty %} + <li class="list-group-item"><em>No Theses found</em></li> + {% endfor %} + </ul> + </div> +</div> diff --git a/scipost/templates/scipost/personal_page.html b/scipost/templates/scipost/personal_page.html index b25b0af7c6e727a2b4ad08da84527aedd230e09f..07ef2154191f647be2d7e03d1addc02b35542ea6 100644 --- a/scipost/templates/scipost/personal_page.html +++ b/scipost/templates/scipost/personal_page.html @@ -45,40 +45,40 @@ <!-- Nav tabs --> <ul class="nav btn-group personal-page-nav" role="tablist"> <li class="nav-item btn btn-secondary"> - <a href="#account" class="nav-link active" data-toggle="tab">Account</a> + <a class="nav-link" {% if tab == 'account' %}sp-autoload='true'{% endif %} href="#account" sp-dynamic-load="{% url 'scipost:personal_page_account' %}" data-toggle="tab">Account</a> </li> {% if is_scipost_admin or is_edcol_admin or is_editorial_college or is_advisory_board or is_vetting_editor or is_ambassador or is_junior_ambassador %} <li class="nav-item btn btn-secondary"> - <a href="#editorial-actions" class="nav-link" data-toggle="tab">Editorial Actions</a> + <a class="nav-link" {% if tab == 'editorial_actions' %}sp-autoload='true'{% endif %} href="#editorial-actions" sp-dynamic-load="{% url 'scipost:personal_page_editorial_actions' %}" data-toggle="tab">Editorial Actions</a> </li> {% endif %} {% if perms.scipost.can_referee %} <li class="nav-item btn btn-secondary"> - <a class="nav-link" data-toggle="tab" href="#refereeing">Refereeing {% if refereeing_tab_total_count %}({{refereeing_tab_total_count}}){% endif %}</a> + <a class="nav-link" {% if tab == 'refereeing' %}sp-autoload='true'{% endif %} data-toggle="tab" href="#refereeing" sp-dynamic-load="{% url 'scipost:personal_page_refereeing' %}">Refereeing {% if refereeing_tab_total_count %}({{refereeing_tab_total_count}}){% endif %}</a> </li> {% endif %} {% if contributor %} {# If user is contributor #} <li class="nav-item btn btn-secondary"> - <a class="nav-link" data-toggle="tab" href="#publications">Publications</a> + <a class="nav-link" {% if tab == 'publications' %}sp-autoload='true'{% endif %} data-toggle="tab" sp-dynamic-load="{% url 'scipost:personal_page_publications' %}" href="#publications">Publications</a> </li> <li class="nav-item btn btn-secondary"> - <a class="nav-link" data-toggle="tab" href="#submissions">Submissions</a> + <a class="nav-link" {% if tab == 'submissions' %}sp-autoload='true'{% endif %} data-toggle="tab" sp-dynamic-load="{% url 'scipost:personal_page_submissions' %}" href="#submissions">Submissions</a> </li> <li class="nav-item btn btn-secondary"> - <a class="nav-link" data-toggle="tab" href="#commentaries">Commentaries</a> + <a class="nav-link" {% if tab == 'commentaries' %}sp-autoload='true'{% endif %} data-toggle="tab" sp-dynamic-load="{% url 'scipost:personal_page_commentaries' %}" href="#commentaries">Commentaries</a> </li> <li class="nav-item btn btn-secondary"> - <a class="nav-link" data-toggle="tab" href="#theses">Theses</a> + <a class="nav-link" {% if tab == 'theses' %}sp-autoload='true'{% endif %} data-toggle="tab" sp-dynamic-load="{% url 'scipost:personal_page_theses' %}" href="#theses">Theses</a> </li> <li class="nav-item btn btn-secondary"> {% with contributor.comments.regular_comments.awaiting_vetting.count as count %} - <a class="nav-link" data-toggle="tab" href="#comments">Comments{% if count %} ({{count}} unvetted){% endif %}</a> + <a class="nav-link" {% if tab == 'comments' %}sp-autoload='true'{% endif %} data-toggle="tab" sp-dynamic-load="{% url 'scipost:personal_page_comments' %}" href="#comments">Comments{% if count %} ({{count}} unvetted){% endif %}</a> {% endwith %} </li> <li class="nav-item btn btn-secondary"> {% with contributor.comments.author_replies.awaiting_vetting.count as count %} - <a class="nav-link" data-toggle="tab" href="#author-replies">Author Replies{% if count %} ({{count}} unvetted){% endif %}</a> + <a class="nav-link" {% if tab == 'author_replies' %}sp-autoload='true'{% endif %} data-toggle="tab" sp-dynamic-load="{% url 'scipost:personal_page_author_replies' %}" href="#author-replies">Author Replies{% if count %} ({{count}} unvetted){% endif %}</a> {% endwith %} </li> {# END: If user is contributor #} @@ -92,672 +92,45 @@ <div class="tab-content"> <!-- Tab: Account --> <div class="tab-pane active" id="account" role="tabpanel"> - <div class="row"> - <div class="col-12"> - <div class="card card-grey"> - <div class="card-body"> - <h2 class="card-title mb-0">Your Account</h2> - </div> - </div> - </div> - </div> - <div class="row"> - <div class="col-md-6"> - <h3>Your personal details:</h3> - {% include "scipost/_private_info_as_table.html" with contributor=contributor %} - - {% if contributor %} - {# Scientist fields #} - <h3 class="mt-3">Your main discipline:</h3> - <ul><li>{{ contributor.get_discipline_display }}</li></ul> - - <h3 class="mt-3">Your expertises:</h3> - {% if contributor.expertises %} - {% include "scipost/_expertises_as_ul.html" with contributor=contributor %} - {% else %} - <p>You haven't listed your expertise(s).<br/> - Do so by <a href="{% url 'scipost:update_personal_data' %}">updating your personal data</a> - </p> - {% endif %} - {# END: Scientist fields #} - {% endif %} - </div> - <div class="col-md-6"> - {% if contributor %} - {# Scientist fields #} - {% if not contributor.is_currently_available %} - <h3 class="text-warning">You are currently unavailable</h3> - <p>Check your availability underneath if this should not be the case.</p> - <hr> - {% endif %} - {# END: Scientist fields #} - {% endif %} - - {% if not request.user.contributor.petition_signatories.exists %} - <div class="border border-danger p-2"> - <h3 class="text-danger">Scientists, please help us out!</h3> - <p class="mb-1">If it is not listed on our Partners page, please encourage your institution (through a librarian, director, ...) to join by <a class="h3 text-blue" href="{% url 'petitions:petition' slug='join-SPB' %}">signing our petition</a>.</p> - </div> - <hr> - {% endif %} - - {% if is_scipost_admin %} - <h3>You are a SciPost Administrator.</h3> - {% endif %} - {% if is_edcol_admin %} - <h3>You are a SciPost Editorial Administrator.</h3> - {% endif %} - {% if is_advisory_board %} - <h3>You are a member of the Advisory Board.</h3> - {% endif %} - {% if is_editorial_college %} - <h3>You are a member of the Editorial College.</h3> - {% endif %} - {% if is_vetting_editor %} - <h3>You are a SciPost Vetting Editor.</h3> - {% endif %} - {% if is_registered_contributor %} - <h3>You are a Registered Contributor.</h3> - {% endif %} - {% if is_tester %} - <h3>You are a SciPost Tester.</h3> - {% endif %} - {% if is_ambassador %} - <h3>You are a SciPost Ambassador.</h3> - {% endif %} - {% if is_junior_ambassador %} - <h3>You are a SciPost Junior Ambassador.</h3> - {% endif %} - {% if is_production_officer %} - <h3>You are a SciPost Production Officer.</h3> - {% endif %} - - {% if contributor.fellowships.exists %} - <h3>Your Fellowships:</h3> - <ul class="mb-2"> - {% for fellowship in contributor.fellowships.all %} - <li class="pt-1"> - {{ fellowship.contributor.get_discipline_display }} - - {% if fellowship.guest %} - (Guest Fellowship) - {% else %} - (Regular Fellowship) - {% endif %} - - {% if not fellowship.is_active %} - <span class="label label-outline-warning label-sm">Inactive</span> - {% endif %} - - {% if fellowship.start_date or fellowship.until_date %} - <div class="text-muted"> - {% if fellowship.start_date %} - from {{ fellowship.start_date }} - {% endif %} - - {% if fellowship.until_date %} - until {{ fellowship.until_date }} - {% endif %} - </div> - {% endif %} - </li> - - {% endfor %} - </ul> - <a href="{% url 'submissions:pool' %}" class="h3 text-primary ml-4 px-3 d-block-inline">Go to the Submissions Pool</a> - {% endif %} - - <h3 class="mt-3">Update your personal data or password</h3> - - <ul> - <li><a href="{% url 'scipost:update_personal_data' %}">Update your personal data</a></li> - <li><a href="{% url 'scipost:change_password' %}">Change your password</a></li> - </ul> - </div> - </div> - - {% if unavailability_form %} - <hr> - <div class="row"> - <div class="col"> - <h2 class="highlight">Your Availability</h2> - </div> - </div> - <div class="row justify-content-center"> - <div class="col-md-4 mr-md-5"> - <p>To help with the editorial workflow, you can inform us of any periods during which you are unavailable. We will do our best to respect these.</p> - <h3 class="mb-3">Mark a period as unavailable:</h3> - <form action="{% url 'scipost:mark_unavailable_period' %}" method="post"> - {% csrf_token %} - {{ unavailability_form|bootstrap }} - <input class="btn btn-secondary" type="submit" value="Submit" /> - </form> - </div> - <div class="col-md-4 ml-md-5"> - {% if unavailabilities %} - <h3>Your unavailability periods in our records</h3> - <p class="text-muted">(YYYY-MM-DD)</p> - <table class="table"> - <tr> - <th>Start</th> - <th colspan="2">End</th> - </tr> - {% for unav in unavailabilities %} - <tr> - <td>{{ unav.start }}</td> - <td>{{ unav.end }}</td> - <td> - <form action="{% url 'scipost:delete_unavailable_period' unav.id %}" method="post"> - {% csrf_token %} - <input class="btn btn-danger" type="submit" value="Delete" /> - </form> - </td> - </tr> - {% endfor %} - </table> - {% else %} - <p>You don't have any upcoming unavailability periods on record.</p> - {% endif %} - </div> - </div> - {% endif %} </div><!-- End tab --> {% if is_scipost_admin or is_edcol_admin or is_editorial_college or is_advisory_board or is_vetting_editor or is_ambassador or is_junior_ambassador %} <!-- Tab: Editorial Actions --> <div class="tab-pane" id="editorial-actions" role="tabpanel"> - <div class="row"> - <div class="col-12"> - <div class="card card-grey"> - <div class="card-body"> - <h2 class="card-title mb-0">Pending Editorial Actions</h2> - </div> - </div> - </div> - </div> - - <div class="row"> - {% if is_scipost_admin or is_advisory_board or is_ambassador or is_junior_ambassador %} - <div class="col-md-4"> - <h3>Registration actions</h3> - <ul> - {% if perms.scipost.can_vet_registration_requests %} - <li><a href="{% url 'scipost:vet_registration_requests' %}">Vet Registration requests</a> ({{ nr_reg_to_vet }})</li> - {% endif %} - {% if perms.scipost.can_resend_registration_requests %} - <li><a href="{% url 'scipost:registration_requests' %}">Awaiting validation</a> ({{ nr_reg_awaiting_validation }})</li> - {% endif %} - {% if perms.scipost.can_draft_registration_invitations %} - <li><a href="{% url 'scipost:draft_registration_invitation' %}">Draft a Registration Invitation</a></li> - {% endif %} - {% if perms.scipost.can_manage_registration_invitations %} - <li><a href="{% url 'scipost:registration_invitations' %}">Manage Registration Invitations</a></li> - {% endif %} - </ul> - - {% if perms.scipost.can_manage_registration_invitations %} - <h3>Notifications</h3> - <ul> - <li><a href="{% url 'scipost:citation_notifications' %}">Manage citation notifications</a></li> - </ul> - {% endif %} - - {% if is_scipost_admin %} - <h3>Email communications</h3> - <ul> - {% if perms.scipost.can_email_group_members %} - <li><a href="{% url 'scipost:email_group_members' %}">Email Group Members</a></li> - {% endif %} - {% if perms.scipost.can_email_particulars %} - <li><a href="{% url 'scipost:send_precooked_email' %}">Send a precooked email</a></li> - <li><a href="{% url 'scipost:email_particular' %}">Email a particular individual/address</a></li> - {% endif %} - {% if perms.scipost.can_manage_mailchimp %} - <li><a href="{% url 'mailing_lists:overview' %}">Manage mailing lists</a></li> - {% endif %} - </ul> - - <h3>SciPost Administation</h3> - <ul> - <li><a href="{% url 'affiliations:institutions' %}">Manage Institutions database</a></li> - </ul> - {% endif %} - - {% if perms.scipost.can_view_timesheets %} - <h3>Finance</h3> - <ul> - <li><a href="{% url 'finances:timesheets' %}">Production Team Timesheets</a></li> - </ul> - {% endif %} - </div> - {% endif %} - - <div class="col-md-4"> - {% if is_vetting_editor or perms.scipost.can_vet_submitted_reports %} - <h3>Vetting actions</h3> - <ul> - {% if perms.scipost.can_vet_commentary_requests %} - <li><a href="{% url 'commentaries:vet_commentary_requests' %}">Vet Commentary Page requests</a> ({{ nr_commentary_page_requests_to_vet }})</li> - {% endif %} - {% if perms.scipost.can_vet_comments %} - <li><a href="{% url 'comments:vet_submitted_comments_list' %}">Vet submitted Comments</a> ({{ nr_comments_to_vet }})</li> - {% endif %} - {% if perms.scipost.can_vet_thesislink_requests %} - <li><a href="{% url 'theses:unvetted_thesislinks' %}">Vet Thesis Link Requests</a> ({{ nr_thesislink_requests_to_vet }})</li> - {% endif %} - {% if perms.scipost.can_vet_authorship_claims %} - <li><a href="{% url 'scipost:vet_authorship_claims' %}">Vet Authorship Claims</a> ({{ nr_authorship_claims_to_vet }})</li> - {% endif %} - {% if perms.scipost.can_vet_submitted_reports %} - <li><a href="{% url 'submissions:vet_submitted_reports_list' %}">Vet submitted Reports</a> ({{ nr_reports_to_vet }})</li> - {% endif %} - </ul> - {% endif %} - - {% if is_edcol_admin %} - <h3>Editorial Admin actions</h3> - <ul> - <li><a href="{% url 'submissions:reports_accepted_list' %}">Accepted Reports</a>{% if nr_reports_without_pdf %} ({{nr_reports_without_pdf}} unfinished){% endif %}</li> - <li><a href="{% url 'submissions:pool' %}">Submissions Pool</a></li> - <li><a href="{% url 'submissions:treated_submissions_list' %}">Fully treated Submissions</a>{% if nr_treated_submissions_without_pdf %} ({{nr_treated_submissions_without_pdf}} unfinished){% endif %}</li> - <li><a href="{% url 'journals:harvest_citedby_list' %}">Harvest citedby data</a></li> - <li><a href="{% url 'journals:manage_comment_metadata' %}">Manage Comment metadata</a></li> - <li><a href="{% url 'colleges:fellowships' %}">Manage Fellowships</a></li> - <li><a href="{% url 'journals:manage_metadata' %}">Manage Publication metadata</a></li> - <li><a href="{% url 'journals:manage_report_metadata' %}">Manage Report metadata</a></li> - <li><a href="{% url 'proceedings:proceedings' %}">Manage Proceedings Issues</a></li> - </ul> - {% endif %} - - {% if perms.scipost.can_attend_VGMs %} - <h3>Virtual General Meetings</h3> - <ul> - <li><a href="{% url 'virtualmeetings:VGMs' %}">List of VGMs</a></li> - </ul> - {% endif %} - </div> - - {% if is_edcol_admin or is_editorial_college %} - <div class="col-md-4"> - <h3>Info</h3> - <ul> - <li><a href="{% url 'submissions:editorial_workflow' %}">How-to guide: summary of the editorial workflow</a></li> - </ul> - - <h3>Submissions assignments</h3> - <ul> - {% if perms.scipost.can_view_pool %} - <li><a href="{% url 'submissions:assignments' %}">Your assignments</a></li> - <li><a href="{% url 'scipost:Fellow_activity_overview' %}">View assignments overview</a></li> - {% endif %} - {% if perms.scipost.can_assign_submissions %} - <li>Assign Submissions via the <a href="{% url 'submissions:pool' %}">Submissions Pool</a> ({{ nr_submissions_to_assign }})</li> - {% endif %} - {% if perms.scipost.can_take_charge_of_submissions %} - <li>Accept or decline assignments via the <a href="{% url 'submissions:pool' %}">Submissions Pool</a> ({{ nr_assignments_to_consider }})</li> - {% endif %} - </ul> - - {% if is_edcol_admin %} - <h3>Refereeing overview</h3> - <ul> - <li>View (and act on) outstanding refereeing invitations in the <a href="{% url 'submissions:refereeing_overview' %}">refereeing overview</a></li> - </ul> - <h3>Voting</h3> - <ul> - <li>Prepare Editorial Recommendations for voting via the <a href="{% url 'submissions:pool' %}">Submissions Pool</a> ({{ nr_recommendations_to_prepare_for_voting }})</li> - </ul> - {% endif %} - {% if perms.scipost.can_view_statistics %} - <h3>Statistics</h3> - <ul> - <li><a href="{% url 'stats:statistics' %}">View statistics</a> for submissions, refereeing, publishing</li> - </ul> - {% endif %} - </div> - {% endif %} - </div> - - {% if active_assignments %} - <div class="row"> - <div class="col-12"> - <h3 class="highlight">Submissions for which you are Editor-in-charge</h3> - </div> - <div class="col-12"> - <ul class="list-group list-group-flush"> - {% for assignment in active_assignments %} - <li class="list-group-item"> - <div class="card-body px-0"> - {% include 'partials/submissions/submission_card_content.html' with submission=assignment.submission %} - {% include 'submissions/_submission_status_block.html' with submission=assignment.submission %} - <p class="card-text mt-2">Manage this Submission from its <a href="{% url 'submissions:editorial_page' assignment.submission.arxiv_identifier_w_vn_nr %}">Editorial Page</a>.</p> - </div> - </li> - {% endfor %} - </ul> - </div> - </div> - {% endif %} </div><!-- End tab --> {% endif %} {% if contributor %} - {# If user is contributor #} + {# If user is contributor #} <!-- Tab: Publications --> <div class="tab-pane" id="publications" role="tabpanel"> - <div class="row"> - <div class="col-12"> - <div class="card card-grey"> - <div class="card-body"> - <h2 class="card-title">Publications</h2> - <ul class="mb-0"> - {% if nr_publication_authorships_to_claim > 0 %} - <li><a href="{% url 'scipost:claim_authorships' %}">Potential authorships to claim (auto-detected: {{ nr_publication_authorships_to_claim}})</a></li> - {% endif %} - </ul> - </div> - </div> - </div> - </div> - - {# {% if own_publications %}#} - <div class="row" id="mypublicationslist"> - <div class="col-12"> - <h3 class="mb-3">Publications for which you are identified as an author:</h3> - </div> - <div class="col-12"> - <ul class="list-unstyled"> - {% for pub in own_publications %} - <li> - <div class="card card-grey card-publication" id="{{pub.doi_label}}"> - {% include 'journals/_publication_card_content.html' with publication=pub current_user=request.user %} - </div> - </li> - {% empty %} - <li> - <em>No Publications found</em> - </li> - {% endfor %} - </ul> - </div> - </div> - {# {% endif %}#} </div><!-- End tab --> - {% if perms.scipost.can_referee %} + {% if perms.scipost.can_referee %} <!-- Tab: Refereeing --> <div class="tab-pane" id="refereeing" role="tabpanel"> - <div class="row"> - <div class="col-12"> - <div class="card card-grey"> - <div class="card-body"> - <h2 class="card-title">Refereeing Tasks</h2> - <ul class="mb-0"> - <li><a href="{% url 'submissions:accept_or_decline_ref_invitations' %}">Accept/decline refereeing invitations</a> ({{ nr_ref_inv_to_consider }})</li> - </ul> - </div> - </div> - </div> - </div> - - <div class="row"> - <div class="col-12"> - <h3>Pending Refereeing Tasks:</h3> - </div> - <div class="col-12"> - <ul class="list-group list-group-flush"> - {% for task in pending_ref_tasks %} - <li class="list-group-item"> - <div class="card-body px-0"> - {% include 'partials/submissions/refereeing_invitation_card_content.html' with invitation=task %} - </div> - </li> - {% empty %} - <li class="list-group-item"><em>You do not have any pending refereeing task</em></li> - {% endfor %} - </ul> - </div> - </div> - - {% if contributor.reports.in_draft.exists %} - <div class="row"> - <div class="col-12"> - <h3>Unfinished reports:</h3> - </div> - <div class="col-12"> - <ul class="list-group list-group-flush"> - {% for report in contributor.reports.in_draft.all %} - <li class="list-group-item"> - <div class="card-body px-0"> - {% include 'partials/submissions/submission_card_content.html' with submission=report.submission %} - <a class="btn btn-outline-primary my-2" href="{% url 'submissions:submit_report' report.submission.arxiv_identifier_w_vn_nr %}">Finish report</a> - </div> - </li> - {% endfor %} - </ul> - </div> - </div> - {% endif %} - - {% if contributor.reports.non_draft.exists %} - <div class="row"> - <div class="col-12"> - <h3>Finished reports:</h3> - </div> - <div class="col-12"> - <ul class="list-group list-group-flush"> - {% for report in contributor.reports.non_draft.all %} - <li class="list-group-item"> - {% comment %} - Temporary: There is already a template for a "Report summary" in a parallel (unmerged) branch. Awaiting merge to use that template. - {% endcomment %} - <div class="card-body px-0 {% block cardblock_class_block %}{% endblock %}"> - <h3>Report on Submission <a href="{{report.submission.get_absolute_url}}">{{report.submission.title}}</a></h3> - <table> - <tr> - <th style='min-width: 100px;'>Received:</th><td>{{ report.date_submitted|date:'Y-n-j' }}<td> - </tr> - <tr> - <th>Status:</th><td {% if report.status == 'vetted' %}class="text-success"{% elif report.status == 'unvetted' %}class="text-danger"{% endif %}>{{report.get_status_display}}</td> - </tr> - {% if report.doi_label %} - <tr> - <th>DOI:</th><td>{{ report.doi_string }}</td></th> - {% endif %} - <tr> - <th>Anonymous:</th><td>{{report.anonymous|yesno:'Yes,No'}}</td>{% if report.anonymous %}<td>You can <a href="{% url 'journals:sign_existing_report' report_id=report.id %}">click here to sign this Report</a> (leads to confirmation page){% endif %}</td> - </tr> - </table> - </div> - </li> - {% endfor %} - </ul> - </div> - </div> - {% endif %} </div><!-- End tab --> {% endif %} <!-- Tab: Submissions --> <div class="tab-pane" id="submissions" role="tabpanel"> - <div class="row"> - <div class="col-12"> - <div class="card card-grey"> - <div class="card-body"> - <h2 class="card-title">Submissions</h2> - <ul class="mb-0"> - {% if nr_submission_authorships_to_claim > 0 %} - <li><a href="{% url 'scipost:claim_authorships' %}">Potential authorships to claim (auto-detected: {{ nr_submission_authorships_to_claim}})</a></li> - {% endif %} - <li><a href="{% url 'submissions:submit_manuscript' %}">Submit an arXiv preprint to a SciPost Journal</a></li> - </ul> - </div> - </div> - </div> - </div> - - {# {% if own_submissions %}#} - <div class="row" id="mysubmissionslist"> - <div class="col-12"> - <h3>Submissions for which you are identified as an author:</h3> - </div> - <div class="col-12"> - <ul class="list-group list-group-flush"> - {% for sub in own_submissions %} - <li class="list-group-item"> - <div class="card-body px-0"> - {% include 'partials/submissions/submission_card_content.html' with submission=sub %} - {% include 'submissions/_submission_status_block.html' with submission=sub %} - - {% if request.user.contributor == sub.submitted_by %} - <p class="card-text mt-1"> - {% if sub.editor_in_charge %} - <a href="{% url 'submissions:communication' sub.arxiv_identifier_w_vn_nr 'AtoE' %}">Write to the Editor-in-charge</a> - {% endif %} - {% if sub.status == 'revision_requested' %} - · <a href="{% url 'submissions:prefill_using_identifier' %}?identifier={{ sub.arxiv_identifier_wo_vn_nr }}">Resubmit this manuscript</a> - {% endif %} - </p> - {% endif %} - </div> - </li> - {% empty %} - <li class="list-group-item"> - <em>No Submissions found</em> - </li> - {% endfor %} - </ul> - </div> - </div> - {# {% endif %}#} </div><!-- End tab --> <!-- Tab: Commentaries --> <div class="tab-pane" id="commentaries" role="tabpanel"> - <div class="row"> - <div class="col-12"> - <div class="card card-grey"> - <div class="card-body"> - <h2 class="card-title">Commentaries</h2> - <ul class="mb-0"> - {% if nr_commentary_authorships_to_claim > 0 %} - <li><a href="{% url 'scipost:claim_authorships' %}">Potential authorships to claim (auto-detected: {{ nr_commentary_authorships_to_claim}})</a></li> - {% endif %} - <li><a href="{% url 'commentaries:request_commentary' %}">Request opening a SciPost Commentary Page</a></li> - </ul> - </div> - </div> - </div> - </div> - - <div class="row" id="mycommentarieslist"> - <div class="col-12"> - <h3>Commentaries for which you are identified as an author:</h3> - </div> - <div class="col-12"> - <ul class="list-group list-group-flush"> - {% for com in own_commentaries %} - <li class="list-group-item"> - {% include 'commentaries/_commentary_card_content.html' with commentary=com %} - </li> - {% empty %} - <li class="list-group-item"><em>No Commentaries found</em></li> - {% endfor %} - </ul> - </div> - </div> </div><!-- End tab --> <!-- Tab: Theses --> <div class="tab-pane" id="theses" role="tabpanel"> - <div class="row"> - <div class="col-12"> - <div class="card card-grey"> - <div class="card-body"> - <h2 class="card-title">Theses</h2> - <ul class="mb-0"> - {% if nr_thesis_authorships_to_claim > 0 %} - <li><a href="{% url 'scipost:claim_authorships' %}">Potential authorships to claim (auto-detected: {{ nr_thesis_authorships_to_claim}})</a></li> - {% endif %} - <li><a href="{% url 'theses:request_thesislink' %}">Request a SciPost ThesisLink</a></li> - </ul> - </div> - </div> - </div> - </div> - - <div class="row" id="mytheseslist"> - <div class="col-12"> - <h3>Theses for which you are identified as an author:</h3> - </div> - <div class="col-12"> - <ul class="list-group list-group-flush"> - {% for thesis in own_thesislinks %} - <li class="list-group-item"> - {% include 'theses/_thesislink_card_content.html' with thesislink=thesis %} - </li> - {% empty %} - <li class="list-group-item"><em>No Theses found</em></li> - {% endfor %} - </ul> - </div> - </div> - </div><!-- End tab --> <!-- Tab: Comments --> <div class="tab-pane" id="comments" role="tabpanel"> - <div class="row"> - <div class="col-12"> - <div class="card card-grey"> - <div class="card-body"> - <h2 class="card-title mb-0">Your Comments</h2> - </div> - </div> - </div> - </div> - - <div class="row" id="mycommentslist"> - <div class="col-12"> - <ul class="list-group list-group-flush"> - {% for own_comment in own_comments %} - <li class="list-group-item"> - {% include 'comments/_comment_card_extended_for_author.html' with comment=own_comment %} - </li> - {% empty %} - <li class="list-group-item"><em>You have not commented yet.</em></li> - {% endfor %} - </ul> - </div> - </div> </div><!-- End tab --> <!-- Tab: Author Replies --> <div class="tab-pane" id="author-replies" role="tabpanel"> - <div class="row"> - <div class="col-12"> - <div class="card card-grey"> - <div class="card-body"> - <h2 class="card-title mb-0">Your Author Replies</h2> - </div> - </div> - </div> - </div> - - <div class="row" id="myauthorreplieslist"> - <div class="col-12"> - <ul class="list-group list-group-flush"> - {% for own_reply in own_authorreplies %} - <li class="list-group-item"> - {% include 'comments/_comment_card_extended_for_author.html' with comment=own_reply %} - </li> - {% empty %} - <li class="list-group-item"><em>You do not have Author Replies yet.</em></li> - {% endfor %} - </ul> - </div> - </div> </div><!-- End tab --> {# END: If user is contributor #} diff --git a/scipost/urls.py b/scipost/urls.py index b57af7e27b78b3d5c8042b185177c3c2e9f0ba84..3c8fb02160d11ca9f26401a791166ca34be9bffb 100644 --- a/scipost/urls.py +++ b/scipost/urls.py @@ -124,13 +124,33 @@ urlpatterns = [ # Authentication url(r'^login/$', views.login_view, name='login'), url(r'^logout$', views.logout_view, name='logout'), - url(r'^personal_page$', views.personal_page, name='personal_page'), url(r'^change_password$', views.change_password, name='change_password'), url(r'^reset_password_confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$', views.reset_password_confirm, name='reset_password_confirm'), url(r'^reset_password/$', views.reset_password, name='reset_password'), url(r'^update_personal_data$', views.update_personal_data, name='update_personal_data'), + # Personal Page + url(r'^personal_page/$', views.personal_page, name='personal_page'), + url(r'^personal_page/account$', views.personal_page, + name='personal_page_account', kwargs={'tab': 'account'}), + url(r'^personal_page/editorial_actions$', views.personal_page, + name='personal_page_editorial_actions', kwargs={'tab': 'editorial_actions'}), + url(r'^personal_page/refereeing$', views.personal_page, + name='personal_page_refereeing', kwargs={'tab': 'refereeing'}), + url(r'^personal_page/publications$', views.personal_page, + name='personal_page_publications', kwargs={'tab': 'publications'}), + url(r'^personal_page/submissions$', views.personal_page, + name='personal_page_submissions', kwargs={'tab': 'submissions'}), + url(r'^personal_page/commentaries$', views.personal_page, + name='personal_page_commentaries', kwargs={'tab': 'commentaries'}), + url(r'^personal_page/theses$', views.personal_page, + name='personal_page_theses', kwargs={'tab': 'theses'}), + url(r'^personal_page/comments$', views.personal_page, + name='personal_page_comments', kwargs={'tab': 'comments'}), + url(r'^personal_page/author_replies$', views.personal_page, + name='personal_page_author_replies', kwargs={'tab': 'author_replies'}), + # Unavailabilities url(r'^unavailable_period$', views.mark_unavailable_period, name='mark_unavailable_period'), url(r'^unavailable_period/(?P<period_id>[0-9]+)/delete$', views.delete_unavailable_period, diff --git a/scipost/views.py b/scipost/views.py index b7f417fe91722074cabccae13bcdf51736b005b3..dd6fd4fbb2413aa6b2b07277d62d0cb60a6ae4a3 100644 --- a/scipost/views.py +++ b/scipost/views.py @@ -9,6 +9,7 @@ from django.contrib.auth.decorators import login_required, user_passes_test from django.contrib.auth.models import Group from django.contrib.auth.views import password_reset, password_reset_confirm from django.core import mail +from django.core.exceptions import PermissionDenied from django.core.mail import EmailMessage, EmailMultiAlternatives from django.core.paginator import Paginator from django.core.urlresolvers import reverse @@ -715,14 +716,216 @@ def delete_unavailable_period(request, period_id): @login_required -def personal_page(request): +@user_passes_test(has_contributor) +def _personal_page_editorial_account(request): + """ + The Personal Page tab: Account + """ + contributor = request.user.contributor + context = { + 'contributor': contributor, + 'unavailability_form': UnavailabilityPeriodForm(), + 'unavailabilities': contributor.unavailability_periods.future().order_by('start') + } + return render(request, 'partials/scipost/personal_page/account.html', context) + + +@user_passes_test(has_contributor) +def _personal_page_editorial_actions(request): + """ + The Personal Page tab: Editorial Actions + """ + permission = request.user.groups.filter(name__in=[ + 'Ambassadors', + 'Advisory Board', + 'Editorial Administrators', + 'Editorial College', + 'Vetting Editors', + 'Junior Ambassadors']).exists() + + if not permission: + raise PermissionDenied + + context = {} + contributor = request.user.contributor + + if contributor.is_SP_Admin(): + # count the number of pending registration requests + context['nr_reg_to_vet'] = Contributor.objects.awaiting_vetting().count() + context['nr_reg_awaiting_validation'] = Contributor.objects.awaiting_validation().count() + context['nr_submissions_to_assign'] = Submission.objects.prescreening().count() + context['nr_recommendations_to_prepare_for_voting'] = EICRecommendation.objects.filter( + submission__status='voting_in_preparation').count() + + if contributor.is_VE(): + context['nr_commentary_page_requests_to_vet'] = (Commentary.objects.awaiting_vetting() + .exclude(requested_by=contributor).count()) + context['nr_comments_to_vet'] = Comment.objects.awaiting_vetting().count() + context['nr_thesislink_requests_to_vet'] = ThesisLink.objects.awaiting_vetting().count() + context['nr_authorship_claims_to_vet'] = AuthorshipClaim.objects.awaiting_vetting().count() + + if contributor.is_MEC(): + context['nr_assignments_to_consider'] = contributor.editorial_assignments.open().count() + context['active_assignments'] = contributor.editorial_assignments.ongoing() + context['nr_reports_to_vet'] = Report.objects.awaiting_vetting().filter( + submission__editor_in_charge=contributor).count() + + if contributor.is_EdCol_Admin(): + context['nr_reports_without_pdf'] = Report.objects.accepted().filter(pdf_report='').count() + context['nr_treated_submissions_without_pdf'] = Submission.objects.treated().filter( + pdf_refereeing_pack='').count() + + return render(request, 'partials/scipost/personal_page/editorial_actions.html', context) + + +@permission_required('scipost.can_referee', return_403=True) +@user_passes_test(has_contributor) +def _personal_page_refereeing(request): + """ + The Personal Page tab: Refereeing + """ + context = { + 'contributor': request.user.contributor + } + return render(request, 'partials/scipost/personal_page/refereeing.html', context) + + +@login_required +@user_passes_test(has_contributor) +def _personal_page_publications(request): + """ + The Personal Page tab: Publications + """ + contributor = request.user.contributor + context = { + 'contributor': contributor, + 'own_publications': contributor.publications.order_by('-publication_date') + } + context['nr_publication_authorships_to_claim'] = Publication.objects.filter( + author_list__contains=request.user.last_name).exclude( + authors=contributor).exclude( + authors_claims=contributor).exclude( + authors_false_claims=contributor).count() + return render(request, 'partials/scipost/personal_page/publications.html', context) + + +@login_required +@user_passes_test(has_contributor) +def _personal_page_submissions(request): + """ + The Personal Page tab: Submissions + """ + contributor = request.user.contributor + context = {'contributor': contributor} + + context['nr_submission_authorships_to_claim'] = Submission.objects.filter( + author_list__contains=request.user.last_name).exclude( + authors=contributor).exclude( + authors_claims=contributor).exclude( + authors_false_claims=contributor).count() + context['own_submissions'] = contributor.submissions.filter( + is_current=True).order_by('-submission_date') + return render(request, 'partials/scipost/personal_page/submissions.html', context) + + +@login_required +@user_passes_test(has_contributor) +def _personal_page_commentaries(request): + """ + The Personal Page tab: Commentaries + """ + contributor = request.user.contributor + context = {'contributor': contributor} + + context['nr_commentary_authorships_to_claim'] = Commentary.objects.filter( + author_list__contains=request.user.last_name).exclude( + authors=contributor).exclude( + authors_claims=contributor).exclude( + authors_false_claims=contributor).count() + context['own_submissions'] = contributor.commentaries.order_by('-latest_activity') + return render(request, 'partials/scipost/personal_page/commentaries.html', context) + + +@login_required +@user_passes_test(has_contributor) +def _personal_page_theses(request): + """ + The Personal Page tab: Theses + """ + contributor = request.user.contributor + context = {'contributor': contributor} + + context['nr_thesis_authorships_to_claim'] = ThesisLink.objects.filter( + author__contains=request.user.last_name).exclude( + author_as_cont=contributor).exclude( + author_claims=contributor).exclude( + author_false_claims=contributor).count() + context['own_thesislinks'] = contributor.theses.all() + return render(request, 'partials/scipost/personal_page/theses.html', context) + + +@login_required +@user_passes_test(has_contributor) +def _personal_page_comments(request): + """ + The Personal Page tab: Comments + """ + contributor = request.user.contributor + context = { + 'contributor': contributor, + 'own_comments': contributor.comments.regular_comments().order_by('-date_submitted') + } + + return render(request, 'partials/scipost/personal_page/comments.html', context) + + +@login_required +@user_passes_test(has_contributor) +def _personal_page_author_replies(request): + """ + The Personal Page tab: Author Replies + """ + contributor = request.user.contributor + context = { + 'contributor': contributor, + 'own_authorreplies': contributor.comments.author_replies().order_by('-date_submitted'), + } + + return render(request, 'partials/scipost/personal_page/author_replies.html', context) + + +@login_required +def personal_page(request, tab='account'): """ The Personal Page is the main view for accessing user functions. """ + if request.is_ajax(): + if tab == 'account': + return _personal_page_editorial_account(request) + elif tab == 'editorial_actions': + return _personal_page_editorial_actions(request) + elif tab == 'refereeing': + return _personal_page_refereeing(request) + elif tab == 'publications': + return _personal_page_publications(request) + elif tab == 'submissions': + return _personal_page_submissions(request) + elif tab == 'commentaries': + return _personal_page_commentaries(request) + elif tab == 'theses': + return _personal_page_theses(request) + elif tab == 'comments': + return _personal_page_comments(request) + elif tab == 'author_replies': + return _personal_page_author_replies(request) + raise Http404 + context = { 'appellation': str(request.user), 'needs_validation': False, + 'tab': tab, } + try: contributor = Contributor.objects.select_related('user').get(user=request.user) context['needs_validation'] = contributor.status != CONTRIBUTOR_NORMAL @@ -730,127 +933,14 @@ def personal_page(request): contributor = None if contributor: - # Compile the unavailability periods: - now = timezone.now() - unavailabilities = contributor.unavailability_periods.exclude(end__lt=now).order_by('start') - unavailability_form = UnavailabilityPeriodForm() - - # if an editor, count the number of actions required: - nr_reg_to_vet = 0 - nr_reg_awaiting_validation = 0 - nr_submissions_to_assign = 0 - nr_recommendations_to_prepare_for_voting = 0 - if contributor.is_SP_Admin(): - # count the number of pending registration requests - nr_reg_to_vet = Contributor.objects.awaiting_vetting().count() - nr_reg_awaiting_validation = (Contributor.objects.awaiting_validation() - .count()) - nr_submissions_to_assign = Submission.objects.prescreening().count() - nr_recommendations_to_prepare_for_voting = EICRecommendation.objects.filter( - submission__status='voting_in_preparation').count() - - nr_assignments_to_consider = 0 - active_assignments = None - nr_reports_to_vet = 0 - if contributor.is_MEC(): - nr_assignments_to_consider = (contributor.editorial_assignments - .open().count()) - active_assignments = contributor.editorial_assignments.ongoing() - nr_reports_to_vet = (Report.objects.awaiting_vetting() - .filter(submission__editor_in_charge=contributor).count()) - - nr_commentary_page_requests_to_vet = 0 - nr_comments_to_vet = 0 - nr_thesislink_requests_to_vet = 0 - nr_authorship_claims_to_vet = 0 - if contributor.is_VE(): - nr_commentary_page_requests_to_vet = (Commentary.objects.awaiting_vetting() - .exclude(requested_by=contributor).count()) - nr_comments_to_vet = Comment.objects.awaiting_vetting().count() - nr_thesislink_requests_to_vet = ThesisLink.objects.awaiting_vetting().count() - nr_authorship_claims_to_vet = AuthorshipClaim.objects.awaiting_vetting().count() - # Refereeing - nr_ref_inv_to_consider = contributor.referee_invitations.open().count() - pending_ref_tasks = contributor.referee_invitations.in_process() - refereeing_tab_total_count = nr_ref_inv_to_consider + len(pending_ref_tasks) + refereeing_tab_total_count = contributor.referee_invitations.open().count() + refereeing_tab_total_count += contributor.referee_invitations.in_process().count() refereeing_tab_total_count += contributor.reports.in_draft().count() - # Verify if there exist objects authored by this contributor, - # whose authorship hasn't been claimed yet - own_publications = contributor.publications.order_by('-publication_date') - own_submissions = contributor.submissions.filter(is_current=True).order_by('-submission_date') - own_commentaries = contributor.commentaries.order_by('-latest_activity') - own_thesislinks = contributor.theses.all() - nr_publication_authorships_to_claim = (Publication.objects.filter( - author_list__contains=contributor.user.last_name) - .exclude(authors=contributor) - .exclude(authors_claims=contributor) - .exclude(authors_false_claims=contributor) - .count()) - nr_submission_authorships_to_claim = (Submission.objects.filter( - author_list__contains=contributor.user.last_name) - .exclude(authors=contributor) - .exclude(authors_claims=contributor) - .exclude(authors_false_claims=contributor) - .count()) - nr_commentary_authorships_to_claim = (Commentary.objects.filter( - author_list__contains=contributor.user.last_name) - .exclude(authors=contributor) - .exclude(authors_claims=contributor) - .exclude(authors_false_claims=contributor) - .count()) - nr_thesis_authorships_to_claim = (ThesisLink.objects.filter( - author__contains=contributor.user.last_name) - .exclude(author_as_cont=contributor) - .exclude(author_claims=contributor) - .exclude(author_false_claims=contributor) - .count()) - own_comments = (contributor.comments.regular_comments() - .select_related('author', 'submission') - .order_by('-date_submitted')) - own_authorreplies = (contributor.comments.author_replies() - .order_by('-date_submitted')) - - appellation = contributor.get_title_display() + ' ' + contributor.user.last_name - - context.update({ - 'contributor': contributor, - 'appellation': appellation, - 'unavailabilities': unavailabilities, - 'unavailability_form': unavailability_form, - 'nr_reg_to_vet': nr_reg_to_vet, - 'nr_reg_awaiting_validation': nr_reg_awaiting_validation, - 'nr_commentary_page_requests_to_vet': nr_commentary_page_requests_to_vet, - 'nr_comments_to_vet': nr_comments_to_vet, - 'nr_thesislink_requests_to_vet': nr_thesislink_requests_to_vet, - 'nr_authorship_claims_to_vet': nr_authorship_claims_to_vet, - 'nr_reports_to_vet': nr_reports_to_vet, - 'nr_submissions_to_assign': nr_submissions_to_assign, - 'nr_recommendations_to_prepare_for_voting': nr_recommendations_to_prepare_for_voting, - 'nr_assignments_to_consider': nr_assignments_to_consider, - 'active_assignments': active_assignments, - 'nr_publication_authorships_to_claim': nr_publication_authorships_to_claim, - 'nr_submission_authorships_to_claim': nr_submission_authorships_to_claim, - 'nr_commentary_authorships_to_claim': nr_commentary_authorships_to_claim, - 'nr_thesis_authorships_to_claim': nr_thesis_authorships_to_claim, - 'nr_ref_inv_to_consider': nr_ref_inv_to_consider, - 'pending_ref_tasks': pending_ref_tasks, - 'refereeing_tab_total_count': refereeing_tab_total_count, - 'own_publications': own_publications, - 'own_submissions': own_submissions, - 'own_commentaries': own_commentaries, - 'own_thesislinks': own_thesislinks, - 'own_comments': own_comments, - 'own_authorreplies': own_authorreplies, - }) - - # Only add variables if user has right permission - if request.user.has_perm('scipost.can_manage_reports'): - context['nr_reports_without_pdf'] = (Report.objects.accepted() - .filter(pdf_report='').count()) - context['nr_treated_submissions_without_pdf'] = (Submission.objects.treated() - .filter(pdf_refereeing_pack='').count()) + context['refereeing_tab_total_count'] = refereeing_tab_total_count + context['appellation'] = contributor.get_title_display() + ' ' + contributor.user.last_name + context['contributor'] = contributor return render(request, 'scipost/personal_page.html', context) diff --git a/submissions/admin.py b/submissions/admin.py index 0466fba0ced8fc05b6e32063d5ef07e9deb1ed97..41ecfd38bdc3d4c0a9432d067cd531a997618f80 100644 --- a/submissions/admin.py +++ b/submissions/admin.py @@ -183,7 +183,7 @@ class ReportAdminForm(forms.ModelForm): class ReportAdmin(admin.ModelAdmin): - search_fields = ['author__user__last_name', 'submission'] + search_fields = ['author__user__last_name', 'submission__title'] list_display = ('author', 'status', 'doi_label', submission_short_title, 'date_submitted', ) list_display_links = ('author',) date_hierarchy = 'date_submitted' @@ -233,6 +233,7 @@ class EICRecommendationAdminForm(forms.ModelForm): class EICRecommendationAdmin(admin.ModelAdmin): search_fields = ['submission__title'] + list_display = (submission_short_title, 'recommendation', 'active', 'version') form = EICRecommendationAdminForm diff --git a/submissions/constants.py b/submissions/constants.py index b360f7f6f3c74e164d86266b63dc11c7e165ce91..5d9c0a2b25974dfe02f6f05c7e5e0cca899004f4 100644 --- a/submissions/constants.py +++ b/submissions/constants.py @@ -58,11 +58,11 @@ SUBMISSION_STATUS_OUT_OF_POOL = SUBMISSION_HTTP404_ON_EDITORIAL_PAGE + [ ] SUBMISSION_EXCLUDE_FROM_REPORTING = SUBMISSION_HTTP404_ON_EDITORIAL_PAGE + [ - STATUS_AWAITING_ED_REC, - STATUS_REVIEW_CLOSED, - STATUS_ACCEPTED, - STATUS_VOTING_IN_PREPARATION, - STATUS_PUT_TO_EC_VOTING, + # STATUS_AWAITING_ED_REC, + # STATUS_REVIEW_CLOSED, + # STATUS_ACCEPTED, + # STATUS_VOTING_IN_PREPARATION, + # STATUS_PUT_TO_EC_VOTING, STATUS_WITHDRAWN, ] @@ -113,7 +113,7 @@ ED_COMM_CHOICES = ( ('EtoR', 'Editor-in-charge to Referee'), ('EtoS', 'Editor-in-charge to SciPost Editorial Administration'), ('AtoE', 'Author to Editor-in-charge'), - ('RtoE', 'Referee to Editor-in-Charge'), + ('RtoE', 'Referee to Editor-in-charge'), ('StoE', 'SciPost Editorial Administration to Editor-in-charge'), ) @@ -208,6 +208,21 @@ REPORT_STATUSES = ( (STATUS_NOT_ACADEMIC, 'Rejected (not academic in style)') ) +REPORT_NORMAL = 'report_normal' +REPORT_POST_EDREC = 'report_post_edrec' +REPORT_TYPES = ( + (REPORT_NORMAL, 'Normal Report'), + (REPORT_POST_EDREC, 'Post-Editorial Recommendation Report'), +) + +POST_PUBLICATION_STATUSES = [ + STATUS_AWAITING_ED_REC, + STATUS_REVIEW_CLOSED, + STATUS_ACCEPTED, + STATUS_VOTING_IN_PREPARATION, + STATUS_PUT_TO_EC_VOTING, +] + CYCLE_DEFAULT = 'default' CYCLE_SHORT = 'short' CYCLE_DIRECT_REC = 'direct_rec' diff --git a/submissions/forms.py b/submissions/forms.py index c13adbe6e338f45fe1a42840c05e0d26ca5ec337..a25a3b5022e656561dc64c6354b6f670f32546b3 100644 --- a/submissions/forms.py +++ b/submissions/forms.py @@ -1,3 +1,4 @@ +import datetime import re from django import forms @@ -9,10 +10,11 @@ from .constants import ASSIGNMENT_BOOL, ASSIGNMENT_REFUSAL_REASONS, STATUS_RESUB REPORT_ACTION_CHOICES, REPORT_REFUSAL_CHOICES, STATUS_REVISION_REQUESTED,\ STATUS_REJECTED, STATUS_REJECTED_VISIBLE, STATUS_RESUBMISSION_INCOMING,\ STATUS_DRAFT, STATUS_UNVETTED, REPORT_ACTION_ACCEPT, REPORT_ACTION_REFUSE,\ - STATUS_VETTED, EXPLICIT_REGEX_MANUSCRIPT_CONSTRAINTS, SUBMISSION_STATUS + STATUS_VETTED, EXPLICIT_REGEX_MANUSCRIPT_CONSTRAINTS, SUBMISSION_STATUS,\ + POST_PUBLICATION_STATUSES, REPORT_POST_EDREC, REPORT_NORMAL from . import exceptions, helpers from .models import Submission, RefereeInvitation, Report, EICRecommendation, EditorialAssignment,\ - iThenticateReport + iThenticateReport, EditorialCommunication from colleges.models import Fellowship from journals.constants import SCIPOST_JOURNAL_PHYSICS_PROC @@ -421,16 +423,23 @@ class SubmissionReportsForm(forms.ModelForm): # Editorial workflow # ###################### -class AssignSubmissionForm(forms.Form): +class EditorialAssignmentForm(forms.ModelForm): + class Meta: + model = EditorialAssignment + fields = ('to',) + labels = { + 'to': 'Fellow', + } def __init__(self, *args, **kwargs): - discipline = kwargs.pop('discipline') - super(AssignSubmissionForm, self).__init__(*args, **kwargs) - self.fields['editor_in_charge'] = forms.ModelChoiceField( - queryset=Contributor.objects.filter(user__groups__name='Editorial College', - user__contributor__discipline=discipline, - ).order_by('user__last_name'), - required=True, label='Select an Editor-in-charge') + self.submission = kwargs.pop('submission') + super().__init__(*args, **kwargs) + self.fields['to'].queryset = Contributor.objects.available().filter( + fellowships__pool=self.submission).distinct() + + def save(self, commit=True): + self.instance.submission = self.submission + return super().save(commit) class ConsiderAssignmentForm(forms.Form): @@ -518,6 +527,8 @@ class ReportPDFForm(forms.ModelForm): class ReportForm(forms.ModelForm): + report_type = REPORT_NORMAL + class Meta: model = Report fields = ['qualification', 'strengths', 'weaknesses', 'report', 'requested_changes', @@ -536,6 +547,8 @@ class ReportForm(forms.ModelForm): } }) + self.submission = kwargs.pop('submission') + super(ReportForm, self).__init__(*args, **kwargs) self.fields['strengths'].widget.attrs.update({ 'placeholder': ('Give a point-by-point ' @@ -577,14 +590,18 @@ class ReportForm(forms.ModelForm): if self.fields[field].required: self.fields[field].label += ' *' - def save(self, submission): + if self.submission.status in POST_PUBLICATION_STATUSES: + self.report_type = REPORT_POST_EDREC + + def save(self): """ Update meta data if ModelForm is submitted (non-draft). Possibly overwrite the default status if user asks for saving as draft. """ report = super().save(commit=False) + report.report_type = self.report_type - report.submission = submission + report.submission = self.submission report.date_submitted = timezone.now() # Save with right status asked by user @@ -594,15 +611,14 @@ class ReportForm(forms.ModelForm): report.status = STATUS_UNVETTED # Update invitation and report meta data if exist - invitation = submission.referee_invitations.filter(referee=report.author).first() - if invitation: - invitation.fulfilled = True - invitation.save() + updated_invitations = self.submission.referee_invitations.filter( + referee=report.author).update(fulfilled=True) + if updated_invitations > 0: report.invited = True # Check if report author if the report is being flagged on the submission - if submission.referees_flagged: - if report.author.user.last_name in submission.referees_flagged: + if self.submission.referees_flagged: + if report.author.user.last_name in self.submission.referees_flagged: report.flagged = True report.save() return report @@ -656,13 +672,16 @@ class VetReportForm(forms.Form): # Communications # ################### -class EditorialCommunicationForm(forms.Form): - text = forms.CharField(widget=forms.Textarea(), label='') - - def __init__(self, *args, **kwargs): - super(EditorialCommunicationForm, self).__init__(*args, **kwargs) - self.fields['text'].widget.attrs.update( - {'rows': 5, 'cols': 50, 'placeholder': 'Write your message in this box.'}) +class EditorialCommunicationForm(forms.ModelForm): + class Meta: + model = EditorialCommunication + fields = ('text',) + widgets = { + 'text': forms.Textarea(attrs={ + 'rows': 5, + 'placeholder': 'Write your message in this box.' + }), + } ###################### @@ -670,6 +689,10 @@ class EditorialCommunicationForm(forms.Form): ###################### class EICRecommendationForm(forms.ModelForm): + DAYS_TO_VOTE = 7 + assignment = None + earlier_recommendations = None + class Meta: model = EICRecommendation fields = [ @@ -678,17 +701,102 @@ class EICRecommendationForm(forms.ModelForm): 'requested_changes', 'remarks_for_editorial_college' ] + widgets = { + 'remarks_for_authors': forms.Textarea({ + 'placeholder': 'Your general remarks for the authors', + 'rows': 10, + }), + 'requested_changes': forms.Textarea({ + 'placeholder': ('If you request revisions, give a numbered (1-, 2-, ...)' + ' list of specifically requested changes'), + }), + 'remarks_for_editorial_college': forms.Textarea({ + 'placeholder': ('If you recommend to accept or refuse, the Editorial College ' + 'will vote; write any relevant remarks for the EC here.'), + }), + } def __init__(self, *args, **kwargs): + self.submission = kwargs.pop('submission') + self.reformulate = kwargs.pop('reformulate', False) + if self.reformulate: + self.load_earlier_recommendations() + latest_recommendation = self.earlier_recommendations.first() + if latest_recommendation: + kwargs['initial'] = { + 'recommendation': latest_recommendation.recommendation, + 'remarks_for_authors': latest_recommendation.remarks_for_authors, + 'requested_changes': latest_recommendation.requested_changes, + 'remarks_for_editorial_college': + latest_recommendation.remarks_for_editorial_college, + } + super().__init__(*args, **kwargs) - self.fields['remarks_for_authors'].widget.attrs.update( - {'placeholder': 'Your general remarks for the authors', - 'rows': 10, 'cols': 100}) - self.fields['requested_changes'].widget.attrs.update( - {'placeholder': 'If you request revisions, give a numbered (1-, 2-, ...) list of specifically requested changes', - 'cols': 100}) - self.fields['remarks_for_editorial_college'].widget.attrs.update( - {'placeholder': 'If you recommend to accept or refuse, the Editorial College will vote; write any relevant remarks for the EC here.'}) + self.load_assignment() + + def save(self, commit=True): + recommendation = super().save(commit=False) + recommendation.submission = self.submission + recommendation.voting_deadline += datetime.timedelta(days=self.DAYS_TO_VOTE) # Test this + if self.reformulate: + # Increment version number + recommendation.version = len(self.earlier_recommendations) + 1 + event_text = 'The Editorial Recommendation has been reformulated: {}.' + else: + event_text = 'An Editorial Recommendation has been formulated: {}.' + + if recommendation.recommendation in [1, 2, 3, -3]: + # Accept/Reject: Forward to the Editorial College for voting + self.submission.status = 'voting_in_preparation' + + if commit: + # Add SubmissionEvent for EIC only + self.submission.add_event_for_eic(event_text.format( + recommendation.get_recommendation_display())) + elif recommendation.recommendation in [-1, -2]: + # Minor/Major revision: return to Author; ask to resubmit + self.submission.status = 'revision_requested' + self.submission.open_for_reporting = False + + if commit: + # Add SubmissionEvents for both Author and EIC + self.submission.add_general_event(event_text.format( + recommendation.get_recommendation_display())) + + if commit: + if self.earlier_recommendations: + self.earlier_recommendations.update(active=False) + + # All reports already submitted are now formulated *after* eic rec formulation + Report.objects.filter( + submission__eicrecommendations__in=self.earlier_recommendations).update( + report_type=REPORT_NORMAL) + + recommendation.save() + self.submission.save() + + if self.assignment: + # The EIC has fulfilled this editorial assignment. + self.assignment.completed = True + self.assignment.save() + + def revision_requested(self): + return self.instance.recommendation in [-1, -2] + + def has_assignment(self): + return self.assignment is not None + + def load_assignment(self): + # Find EditorialAssignment for Submission + try: + self.assignment = self.submission.editorial_assignments.accepted().get( + to=self.submission.editor_in_charge) + return True + except EditorialAssignment.DoesNotExist: + return False + + def load_earlier_recommendations(self): + self.earlier_recommendations = self.submission.eicrecommendations.all() ############### diff --git a/submissions/managers.py b/submissions/managers.py index 021daa46a5a214d085a4a1faa3ba7890e14b3ff7..e85f7218cbfaf72c5d5479d5b316e59a8aab5c87 100644 --- a/submissions/managers.py +++ b/submissions/managers.py @@ -112,6 +112,14 @@ class SubmissionQuerySet(models.QuerySet): qs = qs.filter(editor_in_charge=user.contributor) return qs + def filter_for_author(self, user): + """ + Return the set of Submissions for which the user is a registered author. + """ + if not hasattr(user, 'contributor'): + return self.none() + return self.filter(authors=user.contributor) + def prescreening(self): """ Return submissions just coming in and going through pre-screening. @@ -294,6 +302,9 @@ class EICRecommendationQuerySet(models.QuerySet): def voting_in_preparation(self): return self.filter(submission__status=STATUS_VOTING_IN_PREPARATION) + def active(self): + return self.filter(active=True) + class ReportQuerySet(models.QuerySet): def accepted(self): @@ -320,6 +331,9 @@ class ReportQuerySet(models.QuerySet): class RefereeInvitationQuerySet(models.QuerySet): + def awaiting_response(self): + return self.pending().open() + def pending(self): return self.filter(accepted=None) @@ -348,3 +362,8 @@ class RefereeInvitationQuerySet(models.QuerySet): deadline = now qs = qs.filter(submission__reporting_deadline__lte=deadline) return qs + + +class EditorialCommunicationQueryset(models.QuerySet): + def for_referees(self): + return self.filter(comtype__in=['EtoR', 'RtoE']) diff --git a/submissions/migrations/0002_report_report_type.py b/submissions/migrations/0002_report_report_type.py new file mode 100644 index 0000000000000000000000000000000000000000..1a05cc4ad2b1472746e8d41bcf44631141dde4f1 --- /dev/null +++ b/submissions/migrations/0002_report_report_type.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-01-13 10:14 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('submissions', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='report', + name='report_type', + field=models.CharField(choices=[('report_normal', 'Normal Report'), ('report_post_pub', 'Post-publication Report')], default='report_normal', max_length=16), + ), + ] diff --git a/submissions/migrations/0003_auto_20180120_1102.py b/submissions/migrations/0003_auto_20180120_1102.py new file mode 100644 index 0000000000000000000000000000000000000000..1f921da764c7b7ba6b30595a24a7587555787853 --- /dev/null +++ b/submissions/migrations/0003_auto_20180120_1102.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-01-20 10:02 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('submissions', '0002_auto_20180118_1033'), + ] + + operations = [ + migrations.AlterField( + model_name='editorialcommunication', + name='comtype', + field=models.CharField(choices=[('EtoA', 'Editor-in-charge to Author'), ('EtoR', 'Editor-in-charge to Referee'), ('EtoS', 'Editor-in-charge to SciPost Editorial Administration'), ('AtoE', 'Author to Editor-in-charge'), ('RtoE', 'Referee to Editor-in-charge'), ('StoE', 'SciPost Editorial Administration to Editor-in-charge')], max_length=4), + ), + migrations.AlterField( + model_name='editorialcommunication', + name='referee', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='editorial_communications', to='scipost.Contributor'), + ), + ] diff --git a/submissions/migrations/0004_merge_20180123_2041.py b/submissions/migrations/0004_merge_20180123_2041.py new file mode 100644 index 0000000000000000000000000000000000000000..4c883fba8cd0358ce35cc1ba47ea3df655542504 --- /dev/null +++ b/submissions/migrations/0004_merge_20180123_2041.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-01-23 19:41 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('submissions', '0002_report_report_type'), + ('submissions', '0003_auto_20180120_1102'), + ] + + operations = [ + ] diff --git a/submissions/migrations/0005_auto_20180123_2312.py b/submissions/migrations/0005_auto_20180123_2312.py new file mode 100644 index 0000000000000000000000000000000000000000..35d948ca90f1ac4165226f5ba50b886734d7a877 --- /dev/null +++ b/submissions/migrations/0005_auto_20180123_2312.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-01-23 22:12 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('submissions', '0004_merge_20180123_2041'), + ] + + operations = [ + migrations.AlterModelOptions( + name='eicrecommendation', + options={'ordering': ['version']}, + ), + migrations.AddField( + model_name='eicrecommendation', + name='version', + field=models.SmallIntegerField(default=1), + ), + migrations.AlterUniqueTogether( + name='eicrecommendation', + unique_together=set([('submission', 'version')]), + ), + ] diff --git a/submissions/migrations/0006_eicrecommendation_active.py b/submissions/migrations/0006_eicrecommendation_active.py new file mode 100644 index 0000000000000000000000000000000000000000..94d1c1555565a43e21e7d53baa97f6762e8ffd6a --- /dev/null +++ b/submissions/migrations/0006_eicrecommendation_active.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-01-23 22:42 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('submissions', '0005_auto_20180123_2312'), + ] + + operations = [ + migrations.AddField( + model_name='eicrecommendation', + name='active', + field=models.BooleanField(default=True), + ), + ] diff --git a/submissions/migrations/0007_auto_20180124_1501.py b/submissions/migrations/0007_auto_20180124_1501.py new file mode 100644 index 0000000000000000000000000000000000000000..fd947ed4e358269561cfcebd81a6d8b856d7d474 --- /dev/null +++ b/submissions/migrations/0007_auto_20180124_1501.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-01-24 14:01 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('submissions', '0006_eicrecommendation_active'), + ] + + operations = [ + migrations.AlterField( + model_name='report', + name='report_type', + field=models.CharField(choices=[('report_normal', 'Normal Report'), ('report_post_edrec', 'Post-Editorial Recommendation Report')], default='report_normal', max_length=32), + ), + ] diff --git a/submissions/models.py b/submissions/models.py index 374a6f022c0a9d232ca5762181029e331314f80b..d322603d05fbd16a1dd2b9e39a1f2fb2282590aa 100644 --- a/submissions/models.py +++ b/submissions/models.py @@ -14,9 +14,12 @@ from .constants import ASSIGNMENT_REFUSAL_REASONS, ASSIGNMENT_NULLBOOL,\ RANKING_CHOICES, REPORT_REC, SUBMISSION_STATUS, STATUS_UNASSIGNED,\ REPORT_STATUSES, STATUS_UNVETTED, SUBMISSION_EIC_RECOMMENDATION_REQUIRED,\ SUBMISSION_CYCLES, CYCLE_DEFAULT, CYCLE_SHORT, CYCLE_DIRECT_REC,\ - EVENT_GENERAL, EVENT_TYPES, EVENT_FOR_AUTHOR, EVENT_FOR_EIC + EVENT_GENERAL, EVENT_TYPES, EVENT_FOR_AUTHOR, EVENT_FOR_EIC,\ + REPORT_TYPES, REPORT_NORMAL, STATUS_DRAFT, STATUS_VETTED,\ + STATUS_VOTING_IN_PREPARATION, STATUS_PUT_TO_EC_VOTING from .managers import SubmissionQuerySet, EditorialAssignmentQuerySet, EICRecommendationQuerySet,\ - ReportQuerySet, SubmissionEventQuerySet, RefereeInvitationQuerySet + ReportQuerySet, SubmissionEventQuerySet, RefereeInvitationQuerySet,\ + EditorialCommunicationQueryset from .utils import ShortSubmissionCycle, DirectRecommendationSubmissionCycle,\ GeneralSubmissionCycle @@ -360,6 +363,10 @@ class RefereeInvitation(SubmissionRelatedObjectMixin, models.Model): def notification_name(self): return self.submission.arxiv_identifier_w_vn_nr + @property + def related_report(self): + return self.submission.reports.filter(author=self.referee).first() + def reset_content(self): self.nr_reminders = 0 self.date_last_reminded = None @@ -386,6 +393,7 @@ class Report(SubmissionRelatedObjectMixin, models.Model): to explicitly implement the perticular differences in for example the form used. """ status = models.CharField(max_length=16, choices=REPORT_STATUSES, default=STATUS_UNVETTED) + report_type = models.CharField(max_length=32, choices=REPORT_TYPES, default=REPORT_NORMAL) submission = models.ForeignKey('submissions.Submission', related_name='reports', on_delete=models.CASCADE) report_nr = models.PositiveSmallIntegerField(default=0, @@ -452,6 +460,24 @@ class Report(SubmissionRelatedObjectMixin, models.Model): return (self.author.user.first_name + ' ' + self.author.user.last_name + ' on ' + self.submission.title[:50] + ' by ' + self.submission.author_list[:50]) + @property + def is_in_draft(self): + return self.status == STATUS_DRAFT + + @property + def is_vetted(self): + return self.status == STATUS_VETTED + + def save(self, *args, **kwargs): + # Control Report count per Submission. + if not self.report_nr: + self.report_nr = self.submission.reports.count() + 1 + return super().save(*args, **kwargs) + + def create_doi_label(self): + self.doi_label = 'SciPost.Report.' + str(self.id) + self.save() + def get_absolute_url(self): return self.submission.get_absolute_url() + '#report_' + str(self.report_nr) @@ -483,16 +509,6 @@ class Report(SubmissionRelatedObjectMixin, models.Model): submission__arxiv_identifier_wo_vn_nr=self.submission.arxiv_identifier_wo_vn_nr, submission__arxiv_vn_nr__lt=self.submission.arxiv_vn_nr).exists()) - def save(self, *args, **kwargs): - # Control Report count per Submission. - if not self.report_nr: - self.report_nr = self.submission.reports.count() + 1 - return super().save(*args, **kwargs) - - def create_doi_label(self): - self.doi_label = 'SciPost.Report.' + str(self.id) - self.save() - def latest_report_from_series(self): """ Get latest Report from the same author for the Submission series. @@ -501,7 +517,6 @@ class Report(SubmissionRelatedObjectMixin, models.Model): submission__arxiv_identifier_wo_vn_nr=self.submission.arxiv_identifier_wo_vn_nr) .order_by('submission__arxiv_identifier_wo_vn_nr').last()) - @property def associated_published_doi(self): """ @@ -560,16 +575,18 @@ class EditorialCommunication(SubmissionRelatedObjectMixin, models.Model): Each individual communication between Editor-in-charge to and from Referees and Authors becomes an instance of this class. """ - submission = models.ForeignKey('submissions.Submission', on_delete=models.CASCADE, - related_name='editorial_communications') - referee = models.ForeignKey('scipost.Contributor', related_name='referee_in_correspondence', - blank=True, null=True, on_delete=models.CASCADE) + submission = models.ForeignKey('submissions.Submission', on_delete=models.CASCADE) + referee = models.ForeignKey('scipost.Contributor', on_delete=models.CASCADE, + blank=True, null=True) comtype = models.CharField(max_length=4, choices=ED_COMM_CHOICES) timestamp = models.DateTimeField(default=timezone.now) text = models.TextField() + objects = EditorialCommunicationQueryset.as_manager() + class Meta: ordering = ['timestamp'] + default_related_name = 'editorial_communications' def __str__(self): output = self.comtype @@ -595,6 +612,8 @@ class EICRecommendation(SubmissionRelatedObjectMixin, models.Model): verbose_name='optional remarks for the' ' Editorial College') recommendation = models.SmallIntegerField(choices=REPORT_REC) + version = models.SmallIntegerField(default=1) + active = models.BooleanField(default=True) # Editorial Fellows who have assessed this recommendation: eligible_to_vote = models.ManyToManyField('scipost.Contributor', blank=True, @@ -608,9 +627,17 @@ class EICRecommendation(SubmissionRelatedObjectMixin, models.Model): objects = EICRecommendationQuerySet.as_manager() + class Meta: + unique_together = ('submission', 'version') + ordering = ['version'] + def __str__(self): - return (self.submission.title[:20] + ' by ' + self.submission.author_list[:30] + - ', ' + self.get_recommendation_display()) + return '{title} by {author}, {recommendation} version {version}'.format( + title=self.submission.title[:20], + author=self.submission.author_list[:30], + recommendation=self.get_recommendation_display(), + version=self.version, + ) def get_absolute_url(self): # TODO: Fix this weird redirect, but it's neccesary for the notifications to have one. @@ -632,6 +659,12 @@ class EICRecommendation(SubmissionRelatedObjectMixin, models.Model): def nr_abstained(self): return self.voted_abstain.count() + def may_be_reformulated(self): + if not self.active: + # Already reformulated before; please use the latest version + return False + return self.submission.status in [STATUS_VOTING_IN_PREPARATION, STATUS_PUT_TO_EC_VOTING] + class iThenticateReport(TimeStampedModel): """ diff --git a/submissions/templates/partials/submissions/pool/submission_info_table.html b/submissions/templates/partials/submissions/pool/submission_info_table.html index 49ec884f6b0d51add7cdc85e399e81cf4fc808b5..d7cbb822007a1fa89613c4872a1669e633c595a7 100644 --- a/submissions/templates/partials/submissions/pool/submission_info_table.html +++ b/submissions/templates/partials/submissions/pool/submission_info_table.html @@ -3,6 +3,12 @@ <td style="min-width: 40%;">Version</td> <td>{{submission.arxiv_vn_nr}} ({% if submission.is_current %}current version{% else %}deprecated version {{submission.arxiv_vn_nr}}{% endif %})</td> </tr> + <tr> + <td>Arxiv Link:</td> + <td> + <a href="{{ submission.arxiv_link }}" target="_blank">{{ submission.arxiv_link }}</a> + </td> + </tr> <tr> <td>Submitted</td> <td>{{submission.submission_date}} to {{submission.get_submitted_to_journal_display}}</td> diff --git a/submissions/templates/partials/submissions/refereeing_status_card.html b/submissions/templates/partials/submissions/refereeing_status_card.html new file mode 100644 index 0000000000000000000000000000000000000000..83db31f6e9f60c01854f40bb6474228e03121e15 --- /dev/null +++ b/submissions/templates/partials/submissions/refereeing_status_card.html @@ -0,0 +1,73 @@ + +<div class="card border-warning"> + <div class="card-body"> + {% if invitation.accepted is None %} + <h3 class="card-title">Referee Invitation response pending</h3> + <p class="card-text"> + In view of your expertise and on behalf of the Editor-in-charge we would like to invite you to referee this Submission. Please accept or decline this invitation. + We would be extremely grateful for your contribution, and thank you in advance for your consideration. + </p> + <a href="{% url 'submissions:accept_or_decline_ref_invitations' invitation.id %}" class="card-link">Accept or decline here</a> + {% elif invitation.accepted %} + <h3 class="card-title">Referee Invitation</h3> + <p class="card-text">Thank you for agreeing to referee this Submission. The following checklist will guide you through the steps needed to complete your refereeing task.</p> + <ul class="fa-ul"> + <li><i class="fa-li fa fa-check-square-o"></i>Accepted Invitation on {{ invitation.date_responded }}.</li> + + <li> + {% if invitation.fulfilled %} + Thank you! + <i class="fa-li fa fa-check-square-o"></i>Report submitted on {{ invitation.related_report.date_submitted }}. + <ul class="list-style-none"> + <li>Status: <span class="{% if invitation.related_report.status == 'vetted' %}text-success{% elif invitation.related_report.status == 'unvetted' %}text-danger{% endif %}">{{ invitation.related_report.get_status_display }}</span></li> + <li>Anonymous: {{ invitation.related_report.anonymous|yesno:'Yes,No' }}</li> + {% if invitation.related_report.doi_label %}<li>DOI: {{ invitation.related_report.doi_string }}</li>{% endif %} + </ul> + {% else %} + <i class="fa-li fa fa-square-o"></i> + {% if invitation.related_report.is_in_draft %} + You have a Report in draft, <a href="{% url 'submissions:submit_report' invitation.submission.arxiv_identifier_w_vn_nr %}">finish your Report</a>. + {% else %} + <a href="{% url 'submissions:submit_report' invitation.submission.arxiv_identifier_w_vn_nr %}">Submit your Report</a> due on {{ invitation.submission.reporting_deadline }}. + {% endif %} + {% endif %} + </li> + </ul> + + {% if invitation.related_report.anonymous %} + <p class="card-text">You have submitted your Report anonymously. <a href="{% url 'journals:sign_existing_report' report_id=invitation.related_report.id %}">You can click here to sign this Report</a> (leads to confirmation page).</p> + {% endif %} + + {% if invitation.submission.editor_in_charge %} + <h3 class="card-title">Communications</h3> + <a class="card-link" href="{% url 'submissions:communication' invitation.submission.arxiv_identifier_w_vn_nr 'RtoE' %}">Write to the Editor-in-charge</a> + + <ul class="pl-4 mt-2"> + {% for comm in communication %} + <li> + <span class="font-weight-bold"> + {% if comm.comtype == 'RtoE' %} + From {{ comm.referee.user.first_name }} {{ comm.referee.user.last_name }} to Editor-in-charge + {% elif comm.comtype == 'EtoR' %} + From Editor-in-charge to + {% if comm.referee %} + {{ comm.referee.user.first_name }} {{ comm.referee.user.last_name }} + {% endif %} + {% endif %} + </span> + <small class="d-inline-block text-muted text-sm">on {{ comm.timestamp }}</small> + + <p class="card-text">{{ comm.text }}</p> + </li> + {% empty %} + <li>There have been no communications for this Submission.</li> + {% endfor %} + </ul> + {% endif %} + {% else %} + <h3 class="card-title">Referee Invitation</h3> + <p class="card-text">You have declined to contribute a Report. Nonetheless, we thank you very much for considering this refereeing invitation.</p> + <p class="card-text">Reason: {{ invitation.get_refusal_reason_display }}</p> + {% endif %} + </div> +</div> diff --git a/submissions/templates/submissions/_recommendation_author_content.html b/submissions/templates/submissions/_recommendation_author_content.html index d646512ba90d33dcee14957a7d92f4c13d8a107e..39743bb936a0139a8e3325bc8477a2669f79ea6b 100644 --- a/submissions/templates/submissions/_recommendation_author_content.html +++ b/submissions/templates/submissions/_recommendation_author_content.html @@ -1,6 +1,6 @@ <div class="card bg-white"> <div class="card-body"> - <h2 class="pb-0 mb-0">Editorial Recommendation</h2> + <h2 class="card-title mb-0">Editorial Recommendation <small>(version {{ recommendation.version }}){% if not recommendation.active %} (Deprecated: This Editorial Recommendation has been reformulated){% endif %}</small></h2> {% block recommendation_header %} <h3 class="card-title text-muted">Date {{recommendation.date_submitted}}</h3> diff --git a/submissions/templates/submissions/_required_actions_block.html b/submissions/templates/submissions/_required_actions_block.html index 33279aa057e062f25f38f6465c00942b341c7e69..05066fa33f44cbe804c9f70240f1e428dd57799f 100644 --- a/submissions/templates/submissions/_required_actions_block.html +++ b/submissions/templates/submissions/_required_actions_block.html @@ -1,5 +1,5 @@ {% if submission.cycle.has_required_actions %} - <div class="card {% if submission.cycle.get_required_actions %}bg-danger text-white{% else %}border-success text-success{% endif %}"> + <div class="card {% if submission.cycle.get_required_actions %}bg-danger text-white{% else %}bg-white border-success text-success{% endif %}"> <div class="card-header py-1 {% if not submission.cycle.get_required_actions %}bg-transparent border-0 pb-0{% endif %}"> <h3>Required actions:</h3> </div> diff --git a/submissions/templates/submissions/_single_public_report_without_comments.html b/submissions/templates/submissions/_single_public_report_without_comments.html index cd14902fb992c60cbd8637ef23a32ae738648149..af833812c9e19c8bba2d595264b754c305a2b92e 100644 --- a/submissions/templates/submissions/_single_public_report_without_comments.html +++ b/submissions/templates/submissions/_single_public_report_without_comments.html @@ -10,6 +10,9 @@ <h3> {% if report.anonymous %}(chose public anonymity) {% endif %}<a href="{{report.author.get_absolute_url}}">{{ report.author.user.first_name }} {{ report.author.user.last_name }}</a> on {{ report.date_submitted|date:'Y-n-j' }} + {% if report.report_type == 'report_post_edrec' %} + <small><label class="label label-outline-primary ml-2">Post-Editorial Recommendation Report</label> <i class="fa fa-question-circle-o text-primary" data-toggle="tooltip" data-placement="auto" data-html="true" title="Post-Editorial Reports are submitted after the Editorial Recommendation has been formulated.<br>Hence, they have not been part of the College's decision to Publish the Submission." aria-hidden="true"></i></small> + {% endif %} </h3> {% if report.doi_string or report.pdf_report %} <ul class="clickables"> @@ -63,7 +66,10 @@ <div class="reportid"> <h3> {% if report.anonymous %}Anonymous Report {{report.report_nr}}{% else %}<a href="{{report.author.get_absolute_url}}">{{ report.author.user.first_name }} {{ report.author.user.last_name }}</a>{% endif %} - on {{ report.date_submitted|date:'Y-n-j' }}</h3> + on {{ report.date_submitted|date:'Y-n-j' }} + {% if report.report_type == 'report_post_edrec' %} + <small><label class="label label-outline-primary ml-2">Post-Editorial Recommendation Report</label> <i class="fa fa-question-circle-o text-primary" data-toggle="tooltip" data-placement="auto" data-html="true" title="Post-Editorial Reports are submitted after the Editorial Recommendation has been formulated.<br>Hence, they have not been part of the College's decision to Publish the Submission." aria-hidden="true"></i></small> + {% endif %} </h3> {% if report.doi_string or report.pdf_report %} <ul class="clickables"> diff --git a/submissions/templates/submissions/assign_submission.html b/submissions/templates/submissions/assign_submission.html index ce677a88fe3d4413f8cff2bc61373a0532ce9a89..54574a668dbc3eee84644aaf5253beea98d93e53 100644 --- a/submissions/templates/submissions/assign_submission.html +++ b/submissions/templates/submissions/assign_submission.html @@ -54,7 +54,7 @@ <div class="row mb-md-4"> <div class="col-12"> <h2 class="highlight">Send a new assignment request:</h2> - <form action="{% url 'submissions:assign_submission_ack' arxiv_identifier_w_vn_nr=submission_to_assign.arxiv_identifier_w_vn_nr %}" method="post"> + <form action="{% url 'submissions:assign_submission' arxiv_identifier_w_vn_nr=submission_to_assign.arxiv_identifier_w_vn_nr %}" method="post"> {% csrf_token %} {{ form|bootstrap }} <input class="btn btn-secondary" type="submit" value="Submit" /> diff --git a/submissions/templates/submissions/editorial_page.html b/submissions/templates/submissions/editorial_page.html index 66b4ad1c6369970cd97ad11a33af114e55fb43e3..bb0048c3e0cdd2752a92d6fa7162209dd270641b 100644 --- a/submissions/templates/submissions/editorial_page.html +++ b/submissions/templates/submissions/editorial_page.html @@ -76,17 +76,16 @@ </div> </div> -{% with recommendation as submission.eicrecommendations.first %} - {% if recommendation %} - <div class="row"> - <div class="col-12"> - <div class="card card-outline-secondary"> - {% include 'submissions/_recommendation_author_content.html' with recommendation=recommendation %} - </div> - </div> - </div> - {% endif %} -{% endwith %} +<div class="row"> + <div class="col-12"> + {% for recommendation in submission.eicrecommendations.all %} + {% include 'submissions/_recommendation_author_content.html' with recommendation=recommendation %} + {% if recommendation.may_be_reformulated %} + <a href="{% url 'submissions:reformulate_eic_recommendation' submission.arxiv_identifier_w_vn_nr %}">Reformulate this Editorial Recommendation</a> + {% endif %} + {% endfor %} + </div> +</div> <div class="card card-grey my-4"> <div class="card-body"> @@ -237,6 +236,11 @@ </p> </li> {% endif %} + {% if submission.eicrecommendations.active.first %} + {% if submission.eicrecommendations.active.first.may_be_reformulated %} + <li><a href="{% url 'submissions:reformulate_eic_recommendation' submission.arxiv_identifier_w_vn_nr %}">Reformulate Editorial Recommendation</a></li> + {% endif %} + {% endif %} </ul> </div> </div> diff --git a/submissions/templates/submissions/pool/pool.html b/submissions/templates/submissions/pool/pool.html index 5806e9d40bc7839f6cef44cc36e3a490039c5d37..a1112527805bd55a2242b181b9dcb4d99a96aab4 100644 --- a/submissions/templates/submissions/pool/pool.html +++ b/submissions/templates/submissions/pool/pool.html @@ -26,11 +26,11 @@ <h1>SciPost Submissions Pool</h1> {% if is_ECAdmin %} - {% if recommendations.voting_in_preparation.exists or recommendations.put_to_voting.exists or latest_submission_events %} + {% if recommendations.voting_in_preparation or recommendations.put_to_voting or latest_submission_events %} <div class="quote-border"> <h2 class="text-primary">Administrative Tasks</h2> - {% if recommendations.voting_in_preparation.exists %} + {% if recommendations.voting_in_preparation %} <h3>Recommendations to prepare for voting <i class="fa fa-exclamation-circle text-warning"></i></h3> <ul> {% for recommendation in recommendations.voting_in_preparation %} @@ -41,7 +41,7 @@ </ul> {% endif %} - {% if recommendations.put_to_voting.exists %} + {% if recommendations.put_to_voting %} <h3>Recommendations undergoing voting <i class="fa fa-exclamation-circle text-warning"></i></h3> <ul class="fa-ul"> {% for recommendation in recommendations.put_to_voting %} diff --git a/submissions/templates/submissions/pool/recommendation.html b/submissions/templates/submissions/pool/recommendation.html index e7f703eabbe8dc7d4e39f12660d7deabdcf14255..e4022613693d85d850eb27fe1f8d338c8bf6aad6 100644 --- a/submissions/templates/submissions/pool/recommendation.html +++ b/submissions/templates/submissions/pool/recommendation.html @@ -15,6 +15,9 @@ {% include 'partials/submissions/submission_title.html' with submission=recommendation.submission %} + + <a class="d-inline-block mb-3" href="{{ recommendation.submission.get_absolute_url }}" target="_blank">View Reports and Submission details</a> + {% include 'partials/submissions/pool/submission_info_table.html' with submission=recommendation.submission %} <br> diff --git a/submissions/templates/submissions/pool/recommendation_formulate_rewrite.html b/submissions/templates/submissions/pool/recommendation_formulate_rewrite.html new file mode 100644 index 0000000000000000000000000000000000000000..58b73d22d4a46f9f5313b830eb8743ed00495376 --- /dev/null +++ b/submissions/templates/submissions/pool/recommendation_formulate_rewrite.html @@ -0,0 +1,68 @@ +{% extends 'submissions/pool/base.html' %} + +{% block pagetitle %}: Editorial Recommendation for Submission{% endblock pagetitle %} + +{% load scipost_extras %} +{% load bootstrap %} + +{% block breadcrumb_items %} + {{block.super}} + <a href="{% url 'submissions:editorial_page' submission.arxiv_identifier_w_vn_nr %}" class="breadcrumb-item">Editorial Page ({{submission.arxiv_identifier_w_vn_nr}})</a> + <span class="breadcrumb-item">Reformulate Editorial Recommendation</span> +{% endblock %} + +{% block content %} + +<h1 class="highlight">Reformulate Editorial Recommendation for Submission</h1> + +<br> +{% include 'submissions/_submission_summary.html' with submission=submission %} + +<br> +<div class="card card-grey"> + <div class="card-body"> + <h2 class="card-title">Reformulate Editorial Recommendation</h2> + <p class="card-text">You recommendation will be processed by the Editorial Administration.</p> + <ul> + <li>acceptance or rejection: forwarded to the Editorial College for ratification</li> + <li>request for revision: sent directly to the authors</li> + </ul> + + <p class="card-text"> + This recommendation will be saved with a new version number and any other Editorial Recommendation will be deactived. <b>All obtained votes will be lost.</b> + </p> + </div> +</div> + +{% if submission.editor_in_charge != request.user.contributor %} + <div class="row"> + <div class="col-12"> + <div class="card border-danger"> + <div class="card-body d-flex flex-row"> + <div class="p-2"> + <i class="fa fa-2x fa-exclamation-triangle text-warning" aria-hidden="true"></i> + </div> + <div class="px-2"> + You are not assigned as Editor in charge. However, you can formulate an Editorial Recommendation because you are Editorial Administrator. <strong>This Editorial Recommendation will still be signed by the Editor-in-charge.</strong> + </div> + + </div> + </div> + </div> + </div> +{% endif %} + +<br> + +<div class="row"> + <div class="col-12"> + <form method="post"> + {% csrf_token %} + {{ form|bootstrap }} + <input class="btn btn-primary" type="submit" value="Submit Recommendation"/> + </form> + </div> +</div> + + +{% endblock content %} diff --git a/submissions/templates/submissions/submission_detail.html b/submissions/templates/submissions/submission_detail.html index 6cfb4f77b8cca9c30b2acd2bed508075ee731ad2..dfd390661a321543e2c64ad961c8f6b529e7063c 100644 --- a/submissions/templates/submissions/submission_detail.html +++ b/submissions/templates/submissions/submission_detail.html @@ -20,7 +20,7 @@ {% block content %} <div class="row"> - <div class="col-12"> + <div class="col"> <h2>SciPost Submission Page</h2> <h1 class="text-primary">{{submission.title}}</h1> <h3 class="mb-3">by {{submission.author_list}}</h3> @@ -57,11 +57,6 @@ {% endif %} </div> - </div> -</div> - -<div class="row"> - <div class="col-12"> <h3>Submission summary</h3> {% include 'submissions/_submission_summary.html' with submission=submission hide_title=1 %} @@ -81,6 +76,14 @@ <div class="blockquote">{{ submission.list_of_changes|linebreaks }}</div> {% endif %} </div> + + {% if invitations %} + <div class="col-md-4"> + {% for invitation in invitations %} + {% include 'partials/submissions/refereeing_status_card.html' with invitation=invitation %} + {% endfor %} + </div> + {% endif %} </div> {% if is_author or user|is_in_group:'Editorial College' or user|is_in_group:'Editorial Administrators' %} diff --git a/submissions/templates/submissions/submit_report.html b/submissions/templates/submissions/submit_report.html index 8a47160f086f19f9f9adcd332406c088af09e974..274ff302e0da162931869bd42e77e541529736f6 100644 --- a/submissions/templates/submissions/submit_report.html +++ b/submissions/templates/submissions/submit_report.html @@ -91,12 +91,24 @@ {% endif %} </div> </div> + {% if form.report_type == 'report_post_edrec' %} + <div class="card border-warning my-4"> + <div class="card-body">The Editorial Recommendation for this Submission has already been formulated. Therefore, your report will be labelled as <label class="label label-warning">Post-Editorial Recommendation Report</label>. + </div> + </div> + {% endif %} <form action="{% url 'submissions:submit_report' arxiv_identifier_w_vn_nr=submission.arxiv_identifier_w_vn_nr %}" method="post"> {% csrf_token %} {{ form|bootstrap:'3,9' }} <p>Any fields with an asterisk (*) are required.</p> <input class="btn btn-primary" type="submit" name="save_submit" value="Submit your report"/> <input class="btn btn-secondary ml-2" type="submit" name="save_draft" value="Save your report as draft"/> + {% if form.report_type == 'report_post_edrec' %} + <div class="card border-warning mt-4"> + <div class="card-body">The Editorial Recommendation for this Submission has already been formulated. Therefore, your report will be labelled as <label class="label label-warning">Post-Editorial Recommendation Report</label>. + </div> + </div> + {% endif %} <div class="my-4"> <em>By clicking on Submit, you state that you abide by the <a href="{% url 'journals:journals_terms_and_conditions' %}#referee_code_of_conduct">referee code of conduct</a>.</em> </div> diff --git a/submissions/urls.py b/submissions/urls.py index 3981f846e168f36a98797b95874c8b942ac59615..c840e1c0d843ce035b1e2134e1082037d70576aa 100644 --- a/submissions/urls.py +++ b/submissions/urls.py @@ -51,8 +51,6 @@ urlpatterns = [ # Assignment of Editor-in-charge url(r'^assign_submission/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), views.assign_submission, name='assign_submission'), - url(r'^assign_submission_ack/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), - views.assign_submission_ack, name='assign_submission_ack'), url(r'^pool/assignment_request/(?P<assignment_id>[0-9]+)$', views.assignment_request, name='assignment_request'), url(r'^volunteer_as_EIC/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), @@ -68,7 +66,8 @@ urlpatterns = [ views.select_referee, name='select_referee'), url(r'^recruit_referee/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), views.recruit_referee, name='recruit_referee'), - url(r'^send_refereeing_invitation/{regex}/(?P<contributor_id>[0-9]+)$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), + url(r'^send_refereeing_invitation/{regex}/(?P<contributor_id>[0-9]+)$'.format( + regex=SUBMISSIONS_COMPLETE_REGEX), views.send_refereeing_invitation, name='send_refereeing_invitation'), url(r'^accept_or_decline_ref_invitations/$', views.accept_or_decline_ref_invitations, name='accept_or_decline_ref_invitations'), @@ -76,23 +75,30 @@ urlpatterns = [ views.accept_or_decline_ref_invitations, name='accept_or_decline_ref_invitations'), url(r'^decline_ref_invitation/(?P<invitation_key>.+)$', views.decline_ref_invitation, name='decline_ref_invitation'), - url(r'^ref_invitation_reminder/{regex}/(?P<invitation_id>[0-9]+)$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), + url(r'^ref_invitation_reminder/{regex}/(?P<invitation_id>[0-9]+)$'.format( + regex=SUBMISSIONS_COMPLETE_REGEX), views.ref_invitation_reminder, name='ref_invitation_reminder'), - url(r'^cancel_ref_invitation/{regex}/(?P<invitation_id>[0-9]+)$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), + url(r'^cancel_ref_invitation/{regex}/(?P<invitation_id>[0-9]+)$'.format( + regex=SUBMISSIONS_COMPLETE_REGEX), views.cancel_ref_invitation, name='cancel_ref_invitation'), - url(r'^extend_refereeing_deadline/{regex}/(?P<days>[0-9]+)$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), + url(r'^extend_refereeing_deadline/{regex}/(?P<days>[0-9]+)$'.format( + regex=SUBMISSIONS_COMPLETE_REGEX), views.extend_refereeing_deadline, name='extend_refereeing_deadline'), url(r'^set_refereeing_deadline/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), views.set_refereeing_deadline, name='set_refereeing_deadline'), url(r'^close_refereeing_round/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), views.close_refereeing_round, name='close_refereeing_round'), url(r'^refereeing_overview$', views.refereeing_overview, name='refereeing_overview'), - url(r'^communication/{regex}/(?P<comtype>[a-zA-Z]{{4,}})$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), + url(r'^communication/{regex}/(?P<comtype>[a-zA-Z]{{4,}})$'.format( + regex=SUBMISSIONS_COMPLETE_REGEX), views.communication, name='communication'), - url(r'^communication/{regex}/(?P<comtype>[a-zA-Z]{{4,}})/(?P<referee_id>[0-9]+)$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), + url(r'^communication/{regex}/(?P<comtype>[a-zA-Z]{{4,}})/(?P<referee_id>[0-9]+)$'.format( + regex=SUBMISSIONS_COMPLETE_REGEX), views.communication, name='communication'), url(r'^eic_recommendation/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), views.eic_recommendation, name='eic_recommendation'), + url(r'^eic_recommendation/{regex}/reformulate$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), + views.reformulate_eic_recommendation, name='reformulate_eic_recommendation'), url(r'^cycle/{regex}/submit$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), views.cycle_form_submit, name='cycle_confirmation'), @@ -104,7 +110,8 @@ urlpatterns = [ name='vet_submitted_report'), # Voting - url(r'^prepare_for_voting/(?P<rec_id>[0-9]+)$', views.prepare_for_voting, name='prepare_for_voting'), + url(r'^prepare_for_voting/(?P<rec_id>[0-9]+)$', views.prepare_for_voting, + name='prepare_for_voting'), url(r'^vote_on_rec/(?P<rec_id>[0-9]+)$', views.vote_on_rec, name='vote_on_rec'), url(r'^remind_Fellows_to_vote$', views.remind_Fellows_to_vote, name='remind_Fellows_to_vote'), diff --git a/submissions/views.py b/submissions/views.py index 341ca6f10aa8826dcfabd730348687804e5b1fdc..ca61186bf2d3666eb0ef21c3a68a80624206198b 100644 --- a/submissions/views.py +++ b/submissions/views.py @@ -4,6 +4,7 @@ import feedparser from django.contrib import messages from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.models import Group +from django.core.exceptions import PermissionDenied from django.core.urlresolvers import reverse, reverse_lazy from django.db import transaction, IntegrityError from django.http import Http404, HttpResponse, HttpResponseRedirect @@ -19,13 +20,14 @@ from django.views.generic.list import ListView from guardian.shortcuts import assign_perm from .constants import STATUS_VETTED, STATUS_EIC_ASSIGNED,\ - SUBMISSION_STATUS_PUBLICLY_INVISIBLE, SUBMISSION_STATUS, ED_COMM_CHOICES,\ - STATUS_DRAFT, CYCLE_DIRECT_REC + SUBMISSION_STATUS_PUBLICLY_INVISIBLE, SUBMISSION_STATUS,\ + STATUS_DRAFT, CYCLE_DIRECT_REC, STATUS_VOTING_IN_PREPARATION,\ + STATUS_PUT_TO_EC_VOTING from .models import Submission, EICRecommendation, EditorialAssignment,\ - RefereeInvitation, Report, EditorialCommunication, SubmissionEvent + RefereeInvitation, Report, SubmissionEvent from .mixins import SubmissionAdminViewMixin from .forms import SubmissionIdentifierForm, RequestSubmissionForm, SubmissionSearchForm,\ - RecommendationVoteForm, ConsiderAssignmentForm, AssignSubmissionForm,\ + RecommendationVoteForm, ConsiderAssignmentForm, EditorialAssignmentForm,\ SetRefereeingDeadlineForm, RefereeSelectForm, RefereeRecruitmentForm,\ ConsiderRefereeInvitationForm, EditorialCommunicationForm,\ EICRecommendationForm, ReportForm, VetReportForm, VotingEligibilityForm,\ @@ -211,7 +213,16 @@ def submission_detail(request, arxiv_identifier_w_vn_nr): author_replies = (submission.comments.vetted() .filter(is_author_reply=True).order_by('-date_submitted')) - recommendations = submission.eicrecommendations.all() + # User is referee for the Submission + if request.user.is_authenticated: + invitations = submission.referee_invitations.filter(referee__user=request.user) + else: + invitations = None + if invitations: + context['communication'] = submission.editorial_communications.for_referees().filter( + referee__user=request.user) + + recommendations = submission.eicrecommendations.active() context.update({ 'submission': submission, @@ -224,6 +235,7 @@ def submission_detail(request, arxiv_identifier_w_vn_nr): 'form': form, 'is_author': is_author, 'is_author_unchecked': is_author_unchecked, + 'invitations': invitations, }) return render(request, 'submissions/submission_detail.html', context) @@ -347,8 +359,7 @@ def pool(request, arxiv_identifier_w_vn_nr=None): # Mainly as fallback for the old-pool while in test phase. submissions = Submission.objects.pool(request.user) - # submissions = Submission.objects.pool(request.user) - recommendations = EICRecommendation.objects.filter(submission__in=submissions) + recommendations = EICRecommendation.objects.active().filter(submission__in=submissions) recs_to_vote_on = recommendations.user_may_vote_on(request.user) assignments_to_consider = EditorialAssignment.objects.open().filter( to=request.user.contributor) @@ -417,48 +428,25 @@ def add_remark(request, arxiv_identifier_w_vn_nr): @login_required @permission_required('scipost.can_assign_submissions', raise_exception=True) def assign_submission(request, arxiv_identifier_w_vn_nr): - """ - Assign Editor-in-charge to Submission. - Action done by SciPost Administration or Editorial College Administration. - """ - submission_to_assign = get_object_or_404(Submission.objects.pool_editable(request.user), - arxiv_identifier_w_vn_nr=arxiv_identifier_w_vn_nr) - form = AssignSubmissionForm(discipline=submission_to_assign.discipline) - context = {'submission_to_assign': submission_to_assign, - 'form': form} - return render(request, 'submissions/assign_submission.html', context) - - -@login_required -@permission_required('scipost.can_assign_submissions', raise_exception=True) -def assign_submission_ack(request, arxiv_identifier_w_vn_nr): """ Assign Editor-in-charge to Submission. Action done by SciPost Administration or Editorial College Administration. """ submission = get_object_or_404(Submission.objects.pool_editable(request.user), arxiv_identifier_w_vn_nr=arxiv_identifier_w_vn_nr) - if request.method == 'POST': - form = AssignSubmissionForm(request.POST, discipline=submission.discipline) - if form.is_valid(): - suggested_editor_in_charge = form.cleaned_data['editor_in_charge'] - # TODO: check for possible co-authorships, disqualifying this suggested EIC - if not suggested_editor_in_charge.is_currently_available: - errormessage = ('This Fellow is marked as currently unavailable. ' - 'Please go back and select another one.') - return render(request, 'scipost/error.html', {'errormessage': errormessage}) - ed_assignment = EditorialAssignment(submission=submission, - to=suggested_editor_in_charge, - date_created=timezone.now()) - ed_assignment.save() - SubmissionUtils.load({'assignment': ed_assignment}) - SubmissionUtils.send_assignment_request_email() - - context = {'ack_header': 'Your assignment request has been sent successfully.', - 'followup_message': 'Return to the ', - 'followup_link': reverse('submissions:pool'), - 'followup_link_label': 'Submissions pool'} - return render(request, 'scipost/acknowledgement.html', context) + form = EditorialAssignmentForm(request.POST or None, submission=submission) + + if form.is_valid(): + ed_assignment = form.save() + SubmissionUtils.load({'assignment': ed_assignment}) + SubmissionUtils.send_assignment_request_email() + messages.success(request, 'Your assignment request has been sent successfully.') + return redirect('submissions:pool') + context = { + 'submission_to_assign': submission, + 'form': form + } + return render(request, 'submissions/assign_submission.html', context) @login_required @@ -890,8 +878,7 @@ def accept_or_decline_ref_invitations(request, invitation_id=None): RefereeInvitations need to be either accepted or declined by the invited user using this view. The decision will be taken one invitation at a time. """ - invitation = RefereeInvitation.objects.filter( - referee__user=request.user, accepted=None, cancelled=False) + invitation = RefereeInvitation.objects.awaiting_response().filter(referee__user=request.user) if invitation_id: try: invitation = invitation.get(id=invitation_id) @@ -919,7 +906,7 @@ def accept_or_decline_ref_invitations(request, invitation_id=None): invitation.accepted = False decision_string = 'declined' invitation.refusal_reason = form.cleaned_data['refusal_reason'] - messages.success(request, ('<h1>You have declined to contribute a Report</h1>' + messages.success(request, ('<h3>You have declined to contribute a Report</h3>' 'Nonetheless, we thank you very much for considering' ' this refereeing invitation.</p>')) invitation.save() @@ -933,7 +920,10 @@ def accept_or_decline_ref_invitations(request, invitation_id=None): invitation.submission.add_event_for_eic('Referee %s has %s the refereeing invitation.' % (invitation.referee.user.last_name, decision_string)) - return redirect('submissions:accept_or_decline_ref_invitations') + + if request.user.contributor.referee_invitations.awaiting_response().exists(): + return redirect('submissions:accept_or_decline_ref_invitations') + return redirect(invitation.submission.get_absolute_url()) form = ConsiderRefereeInvitationForm() context = { 'invitation': invitation, @@ -1096,55 +1086,60 @@ def communication(request, arxiv_identifier_w_vn_nr, comtype, referee_id=None): occurring during the submission refereeing. """ referee = None - if comtype == 'AtoE': - submissions_qs = Submission.objects.filter(authors__user=request.user) + if comtype in ['EtoA', 'EtoR', 'EtoS']: + # Editor to {Author, Referee, Editorial Administration} + submissions_qs = Submission.objects.filter_for_eic(request.user) + elif comtype == 'AtoE': + # Author to Editor + submissions_qs = Submission.objects.filter_for_author(request.user) + referee = request.user.contributor + elif comtype == 'RtoE': + # Referee to Editor (Contributor account required) + if not hasattr(request.user, 'contributor'): + # Raise PermissionDenied to let the user know something is wrong with its account. + raise PermissionDenied + + submissions_qs = Submission.objects.filter( + referee_invitations__referee__user=request.user) + referee = request.user.contributor + elif comtype == 'StoE': + # Editorial Administration to Editor + if not request.user.has_perm('scipost.can_oversee_refereeing'): + raise PermissionDenied + submissions_qs = Submission.objects.filter_for_author(request.user) + referee = request.user.contributor else: - submissions_qs = Submission.objects.pool_full(request.user) + # Invalid commtype in the url! + raise Http404 + + # Get the showpiece itself or return 404 submission = get_object_or_404(submissions_qs, arxiv_identifier_w_vn_nr=arxiv_identifier_w_vn_nr) - errormessage = None - if comtype not in dict(ED_COMM_CHOICES).keys(): - errormessage = 'Unknown type of cummunication.' - # TODO: Verify that this is requested by an authorized contributor (eic, ref, author) - elif (comtype in ['EtoA', 'EtoR', 'EtoS'] and - submission.editor_in_charge != request.user.contributor): - errormessage = 'Only the Editor-in-charge can perform this action.' - elif (comtype in ['AtoE'] and - not (request.user.contributor == submission.submitted_by)): - errormessage = 'Only the corresponding author can perform this action.' - elif (comtype in ['RtoE'] and - not (RefereeInvitation.objects - .filter(submission=submission, referee=request.user.contributor).exists())): - errormessage = 'Only invited referees for this Submission can perform this action.' - elif (comtype in ['StoE'] and - not request.user.groups.filter(name='Editorial Administrators').exists()): - errormessage = 'Only Editorial Administrators can perform this action.' - if errormessage is not None: - messages.warning(request, errormessage) - return redirect(reverse('submissions:pool')) + + if referee_id and not referee: + # Get the Contributor to communicate with if not already defined (`Eto?` communication) + # To Fix: Assuming the Editorial Administrator won't make any `referee_id` mistakes + referee = get_object_or_404(Contributor, pk=referee_id) form = EditorialCommunicationForm(request.POST or None) if form.is_valid(): - communication = EditorialCommunication(submission=submission, - comtype=comtype, - timestamp=timezone.now(), - text=form.cleaned_data['text']) - if referee_id is not None: - referee = get_object_or_404(Contributor, pk=referee_id) - communication.referee = referee - - if comtype == 'RtoE': - communication.referee = request.user.contributor + communication = form.save(commit=False) + communication.submission = submission + communication.comtype = comtype + communication.referee = referee communication.save() + SubmissionUtils.load({'communication': communication}) SubmissionUtils.send_communication_email() - if comtype == 'EtoA' or comtype == 'EtoR' or comtype == 'EtoS': + + if comtype in ['EtoA', 'EtoR', 'EtoS']: return redirect(reverse('submissions:editorial_page', kwargs={'arxiv_identifier_w_vn_nr': arxiv_identifier_w_vn_nr})) - elif comtype == 'AtoE' or comtype == 'RtoE': + elif comtype == 'AtoE': return redirect(reverse('scipost:personal_page')) elif comtype == 'StoE': return redirect(reverse('submissions:pool')) + return redirect(submission.get_absolute_url()) context = { 'submission': submission, @@ -1174,11 +1169,9 @@ def eic_recommendation(request, arxiv_identifier_w_vn_nr): return redirect(reverse('submissions:editorial_page', args=[submission.arxiv_identifier_w_vn_nr])) + form = EICRecommendationForm(request.POST or None, submission=submission) # Find EditorialAssignment for user - try: - assignment = submission.editorial_assignments.accepted().get( - to=submission.editor_in_charge) - except EditorialAssignment.DoesNotExist: + if not form.has_assignment(): messages.warning(request, ('You cannot formulate an Editorial Recommendation,' ' because the Editorial Assignment has not been set properly.' ' Please ' @@ -1186,53 +1179,68 @@ def eic_recommendation(request, arxiv_identifier_w_vn_nr): return redirect(reverse('submissions:editorial_page', args=[submission.arxiv_identifier_w_vn_nr])) - form = EICRecommendationForm(request.POST or None) if form.is_valid(): - # Create new EICRecommendation - recommendation = form.save(commit=False) - recommendation.submission = submission - recommendation.voting_deadline = timezone.now() + datetime.timedelta(days=7) - recommendation.save() - - # If recommendation is to accept or reject, - # it is forwarded to the Editorial College for voting - # If it is to carry out minor or major revisions, - # it is returned to the Author who is asked to resubmit - if (recommendation.recommendation == 1 or - recommendation.recommendation == 2 or - recommendation.recommendation == 3 or - recommendation.recommendation == -3): - submission.status = 'voting_in_preparation' - - # Add SubmissionEvent for EIC - submission.add_event_for_eic('An Editorial Recommendation has been formulated: %s.' - % recommendation.get_recommendation_display()) - - elif (recommendation.recommendation == -1 or - recommendation.recommendation == -2): - submission.status = 'revision_requested' - SubmissionUtils.load({'submission': submission, - 'recommendation': recommendation}) + recommendation = form.save() + if form.revision_requested(): + # Send mail to authors to notify about the request for revision. + SubmissionUtils.load({ + 'submission': form.submission, + 'recommendation': recommendation, + }) SubmissionUtils.send_author_revision_requested_email() - # Add SubmissionEvents - submission.add_general_event('An Editorial Recommendation has been formulated: %s.' - % recommendation.get_recommendation_display()) - submission.open_for_reporting = False - submission.save() - - # The EIC has fulfilled this editorial assignment. - assignment.completed = True - assignment.save() - messages.success(request, 'Your Editorial Recommendation has been succesfully submitted') + messages.success(request, 'Editorial Recommendation succesfully submitted') return redirect(reverse('submissions:editorial_page', kwargs={'arxiv_identifier_w_vn_nr': arxiv_identifier_w_vn_nr})) - context = {'submission': submission, - 'form': form} + context = { + 'submission': submission, + 'form': form + } return render(request, 'submissions/pool/recommendation_formulate.html', context) +@login_required +@fellowship_or_admin_required() +@transaction.atomic +def reformulate_eic_recommendation(request, arxiv_identifier_w_vn_nr): + """ + Reformulate EIC Recommendation. + + Accessible for: Editor-in-charge and Editorial Administration + """ + submission = get_object_or_404(Submission.objects.filter_for_eic(request.user), + arxiv_identifier_w_vn_nr=arxiv_identifier_w_vn_nr) + + if submission.status not in [STATUS_VOTING_IN_PREPARATION, STATUS_PUT_TO_EC_VOTING]: + messages.warning(request, ('With the current status of the Submission you are not ' + 'allowed to reformulate the Editorial Recommendation')) + return redirect(reverse('submissions:editorial_page', + args=[submission.arxiv_identifier_w_vn_nr])) + + form = EICRecommendationForm(request.POST or None, submission=submission, reformulate=True) + + if form.is_valid(): + recommendation = form.save() + if form.revision_requested(): + # Send mail to authors to notify about the request for revision. + SubmissionUtils.load({ + 'submission': form.submission, + 'recommendation': recommendation, + }) + SubmissionUtils.send_author_revision_requested_email() + + messages.success(request, 'Editorial Recommendation succesfully reformulated') + return redirect(reverse('submissions:editorial_page', + kwargs={'arxiv_identifier_w_vn_nr': arxiv_identifier_w_vn_nr})) + + context = { + 'submission': submission, + 'form': form + } + return render(request, 'submissions/pool/recommendation_formulate_rewrite.html', context) + + ########### # Reports ########### @@ -1288,11 +1296,11 @@ def submit_report(request, arxiv_identifier_w_vn_nr): report_in_draft = submission.reports.in_draft().get(author=current_contributor) except Report.DoesNotExist: report_in_draft = Report(author=current_contributor, submission=submission) - form = ReportForm(request.POST or None, instance=report_in_draft) + form = ReportForm(request.POST or None, instance=report_in_draft, submission=submission) # Check if data sent is valid if form.is_valid(): - newreport = form.save(submission) + newreport = form.save() if newreport.status == STATUS_DRAFT: messages.success(request, ('Your Report has been saved. ' 'You may carry on working on it,' @@ -1310,7 +1318,7 @@ def submit_report(request, arxiv_identifier_w_vn_nr): % request.user.last_name) messages.success(request, 'Thank you for your Report') - return redirect(reverse('scipost:personal_page')) + return redirect(submission.get_absolute_url()) context = {'submission': submission, 'form': form} return render(request, 'submissions/submit_report.html', context) @@ -1383,7 +1391,7 @@ def vet_submitted_report(request, report_id): def prepare_for_voting(request, rec_id): submissions = Submission.objects.pool_editable(request.user) recommendation = get_object_or_404( - EICRecommendation.objects.filter(submission__in=submissions), id=rec_id) + EICRecommendation.objects.active().filter(submission__in=submissions), id=rec_id) fellows_with_expertise = recommendation.submission.fellows.filter( contributor__expertises__contains=[recommendation.submission.subject_area]) @@ -1435,7 +1443,7 @@ def prepare_for_voting(request, rec_id): def vote_on_rec(request, rec_id): submissions = Submission.objects.pool_editable(request.user) recommendation = get_object_or_404( - EICRecommendation.objects.filter(submission__in=submissions), id=rec_id) + EICRecommendation.objects.active().filter(submission__in=submissions), id=rec_id) form = RecommendationVoteForm(request.POST or None) if form.is_valid(): @@ -1489,7 +1497,8 @@ def remind_Fellows_to_vote(request): TODO: This reminder function doesn't filter per submission?! """ submissions = Submission.objects.pool_editable(request.user) - recommendations = EICRecommendation.objects.filter(submission__in=submissions).put_to_voting() + recommendations = EICRecommendation.objects.active().filter( + submission__in=submissions).put_to_voting() Fellow_emails = [] Fellow_names = [] diff --git a/webpack.config.js b/webpack.config.js index b27be106e76544fa453dadb207f296cebe238ad2..6a9275d4de51bfd576ea29122e1eb64da64ec829 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -10,6 +10,7 @@ module.exports = { entry: { main: [ "bootstrap-loader", + "./scipost/static/scipost/assets/js/dynamic_loading.js", "./scipost/static/scipost/assets/js/scripts.js", ], homepage: [