diff --git a/journals/forms.py b/journals/forms.py index 934458f622c963b621ebd9f5a4cad1a7ba35988b..3a7d92663422da50318673838c8657b0e775ab4d 100644 --- a/journals/forms.py +++ b/journals/forms.py @@ -392,7 +392,7 @@ class DraftPublicationForm(forms.ModelForm): if self.submission: try: self.to_journal = Journal.objects.has_individual_publications().get( - name=self.submission.submitted_to_journal) + name=self.submission.submitted_to.name) except Journal.DoesNotExist: self.to_journal = None @@ -416,7 +416,7 @@ class DraftPublicationForm(forms.ModelForm): def get_possible_issues(self): issues = Issue.objects.filter(until_date__gte=timezone.now()) if self.submission: - issues = issues.for_journal(self.submission.submitted_to_journal) + issues = issues.for_journal(self.submission.submitted_to.name) return issues def delete_secondary_fields(self): diff --git a/journals/migrations/0052_journal_refereeing_period.py b/journals/migrations/0052_journal_refereeing_period.py new file mode 100644 index 0000000000000000000000000000000000000000..42ebb34af66e443290370c8ef0c83f9d91559dbd --- /dev/null +++ b/journals/migrations/0052_journal_refereeing_period.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-11-10 05:41 +from __future__ import unicode_literals + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0051_auto_20181102_1332'), + ] + + operations = [ + migrations.AddField( + model_name='journal', + name='refereeing_period', + field=models.DurationField(default=datetime.timedelta(28)), + ), + ] diff --git a/journals/models.py b/journals/models.py index c58b34aed71ef83ac21245a589d06d12d682cb03..38fcf8fe1e3a59a869fc754a0849bfa52b312bfa 100644 --- a/journals/models.py +++ b/journals/models.py @@ -2,6 +2,7 @@ __copyright__ = "Copyright 2016-2018, Stichting SciPost (SciPost Foundation)" __license__ = "AGPL v3" +import datetime from decimal import Decimal from django.contrib.contenttypes.fields import GenericForeignKey @@ -115,6 +116,7 @@ class Journal(models.Model): active = models.BooleanField(default=True) structure = models.CharField(max_length=2, choices=JOURNAL_STRUCTURE, default=ISSUES_AND_VOLUMES) + refereeing_period = models.DurationField(default=datetime.timedelta(days=28)) objects = JournalQuerySet.as_manager() diff --git a/journals/templates/journals/SciPostPhysCodeb_about.html b/journals/templates/journals/SciPostPhysCodeb_about.html index 5c991fb1e729381f1bc6f7d739070dcfb174dee5..d1d85a90d73726fb1e5954f0ae74adf3e6b180ed 100644 --- a/journals/templates/journals/SciPostPhysCodeb_about.html +++ b/journals/templates/journals/SciPostPhysCodeb_about.html @@ -28,18 +28,18 @@ <div class="col-md-6"> <h2>Aims</h2> <p>SciPost Physics Codebases is a new-generation publication venue for computer codes and algorithms of relevance to research in Physics.</p> - <p>It aims to offer a high-profile venue in which top-level numerical algorithms, protocols and software packages can be disseminated as fully-featured publication objects benefitting from all things expected of top-quality papers.</p> - <p>Contributors to research-level software development can thus obtain proper recognition and find their rightful place within modern scientific literature.</p> - <p>As per other SciPost Journals, SciPost Physics Codebases is two-way open access, peer-witnessed refereed. Publications in SciPost Physics Codebases benefit from the same professional treatment given to our other Journals, including metadata handling, citable DOI, citations listing, funding information handling, and permanent archiving.</p> + <p>It aims to offer a high-profile venue in which top-level numerical algorithms, protocols and software packages can be disseminated as fully-featured publication objects, with all the benefits associated to top-quality papers.</p> + <p>Contributors to research-level software development can thus obtain proper recognition and find their rightful place within the modern scientific literature.</p> + <p>As per other SciPost Journals, SciPost Physics Codebases is two-way open access, peer-witnessed refereed. Publications in SciPost Physics Codebases benefit from the same professional treatment given to our other Journals, including metadata handling, citable DOI, citations listing, funding information handling, permanent archiving and many others.</p> </div> <div class="col-md-6"> <h2>Scope</h2> <p>SciPost Physics Codebases publishes outstanding-quality Codebases relevant to all specializations in Computational, Experimental and Theoretical Physics.</p> <p>Examples of publishable Codebases include:</p> <ul> - <li>Novel algorithms</li> - <li>Significant and original reimplementations of well-known algorithms</li> - <li>Ports of existing codebases to new languages and platforms</li> + <li>Novel algorithms;</li> + <li>Significant and original reimplementations of well-known algorithms;</li> + <li>Ports of existing codebases to new languages and platforms.</li> </ul> </div> </div> diff --git a/journals/templates/journals/journals.html b/journals/templates/journals/journals.html index 323e57a860ff490d4592b2c8d88358f0853fc570..6595cc88dbece6fb4932f23025b24648f84a663f 100644 --- a/journals/templates/journals/journals.html +++ b/journals/templates/journals/journals.html @@ -102,7 +102,7 @@ </div> <div class="card-body"> <p>SciPost Physics Lecture Notes publishes didactic material in all domains and subject areas of Physics.</p> - <p>SciPost Physics Lecture Notes is the ideal venue for research-level didactic material, (post-)graduate course notes, or for lecture notes from international-level summer/winter schools.</p> + <p>SciPost Physics Lecture Notes is the ideal venue for research-level didactic material, (post-)graduate course notes, or for lecture notes from international-level schools.</p> <p>All submissions undergo an extended peer-witnessed refereeing process, and publications benefit from all the advantages of our Genuine Open Access standards.</p> </div> </div> diff --git a/journals/views.py b/journals/views.py index b5cf9d620ef1e310685dfec5c4799839bd534ebc..8fe74e5972b623afe6dd833521441438b757bb2c 100644 --- a/journals/views.py +++ b/journals/views.py @@ -163,7 +163,7 @@ def landing_page(request, doi_label): 'most_cited': Publication.objects.for_journal(journal.name).published().most_cited(5), 'latest_publications': Publication.objects.for_journal(journal.name).published()[:5], 'accepted_submissions': Submission.objects.accepted().filter( - submitted_to_journal=journal.name).order_by('-latest_activity'), + submitted_to=journal).order_by('-latest_activity'), } return render(request, 'journals/journal_landing_page.html', context) diff --git a/ontology/templates/ontology/_topic_card.html b/ontology/templates/ontology/_topic_card.html index e47541657ba176a63e057f00a5f01a6223a66e75..08c644fb3c40ac6a1b57a529b0e14938d6e7c5de 100644 --- a/ontology/templates/ontology/_topic_card.html +++ b/ontology/templates/ontology/_topic_card.html @@ -109,7 +109,7 @@ <li> <a href="{{ sub.get_absolute_url }}">{{ sub.title }}</a> <br>by {{ sub.author_list }} - <br>(submitted {{ sub.submission_date|date:"Y-m-d" }} to {{ sub.get_submitted_to_journal_display }}) + <br>(submitted {{ sub.submission_date|date:"Y-m-d" }} to {{ sub.submitted_to }}) </li> {% empty %} <li>No Submission found</li> diff --git a/production/templates/production/production.html b/production/templates/production/production.html index 16fafac80435f300cc4a825bb162b112060525e4..57f40db1fa14160965e598088669d7216f752d6b 100644 --- a/production/templates/production/production.html +++ b/production/templates/production/production.html @@ -73,7 +73,7 @@ <br> by {{ stream.submission.author_list }} </td> - <td>{{ stream.submission.get_submitted_to_journal_display }}{% for rec in stream.submission.eicrecommendations.all %}<br/>({{ rec|Tier }}){% endfor %}</td> + <td>{{ stream.submission.submitted_to }}{% for rec in stream.submission.eicrecommendations.all %}<br/>({{ rec|Tier }}){% endfor %}</td> <td> <div class="label label-{% if stream.status == 'initiated' %}outline-danger{% else %}secondary{% endif %} label-sm">{{ stream.get_status_display }}</div> </td> diff --git a/profiles/forms.py b/profiles/forms.py index 63a46e618701572ad757cce0d9e64053b9c3453e..92862facaf33f881125b393d8c35e6e0dd2a1da1 100644 --- a/profiles/forms.py +++ b/profiles/forms.py @@ -36,7 +36,8 @@ class ProfileForm(forms.ModelForm): def clean_email(self): """Check that the email isn't yet associated to an existing Profile.""" cleaned_email = self.cleaned_data['email'] - if ProfileEmail.objects.filter(email=cleaned_email).exclude(profile__id=self.instance.id).exists(): + if ProfileEmail.objects.filter( + email=cleaned_email).exclude(profile__id=self.instance.id).exists(): raise forms.ValidationError('A Profile with this email already exists.') return cleaned_email @@ -59,24 +60,30 @@ class ProfileForm(forms.ModelForm): instance_pk = self.cleaned_data['instance_pk'] if instance_pk: if self.cleaned_data['instance_from_type'] == 'contributor': - contributor = get_object_or_404(Contributor, pk=instance_pk) - contributor.profile = profile - contributor.save() + Contributor.objects.filter(pk=instance_pk).update(profile=profile) elif self.cleaned_data['instance_from_type'] == 'unregisteredauthor': - unreg_auth = get_object_or_404(UnregisteredAuthor, pk=instance_pk) - unreg_auth.profile = profile - unreg_auth.save() + UnregisteredAuthor.objects.filter(pk=instance_pk).update(profile=profile) elif self.cleaned_data['instance_from_type'] == 'refereeinvitation': - ref_inv = get_object_or_404(RefereeInvitation, pk=instance_pk) - ref_inv.profile = profile - ref_inv.save() + RefereeInvitation.objects.filter(pk=instance_pk).update(profile=profile) elif self.cleaned_data['instance_from_type'] == 'registrationinvitation': - reg_inv = get_object_or_404(RegistrationInvitation, pk=instance_pk) - reg_inv.profile = profile - reg_inv.save() + RegistrationInvitation.objects.filter(pk=instance_pk).update(profile=profile) return profile +class SimpleProfileForm(ProfileForm): + """ + Simple version of ProfileForm, displaying only required fields. + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['expertises'].widget = forms.HiddenInput() + self.fields['orcid_id'].widget = forms.HiddenInput() + self.fields['webpage'].widget = forms.HiddenInput() + self.fields['accepts_SciPost_emails'].widget = forms.HiddenInput() + self.fields['accepts_refereeing_requests'].widget = forms.HiddenInput() + + class ModelChoiceFieldwithid(forms.ModelChoiceField): def label_from_instance(self, obj): return '%s (id = %i)' % (super().label_from_instance(obj), obj.id) diff --git a/profiles/migrations/0014_auto_20181110_0637.py b/profiles/migrations/0014_auto_20181110_0637.py new file mode 100644 index 0000000000000000000000000000000000000000..7d3d07cb2498f21f09d149149bf31be4dd23de47 --- /dev/null +++ b/profiles/migrations/0014_auto_20181110_0637.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-11-10 05:37 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('profiles', '0013_profilenonduplicates'), + ] + + operations = [ + migrations.AlterModelOptions( + name='profilenonduplicates', + options={'verbose_name': 'Profile non-duplicates', 'verbose_name_plural': 'Profile non-duplicates'}, + ), + ] diff --git a/scipost/management/commands/add_groups_and_permissions.py b/scipost/management/commands/add_groups_and_permissions.py index a6be4d4a022428bfb91019056a2879531f8e74ad..1d13fcd2a8d05b9209234c34155c754de9421a79 100644 --- a/scipost/management/commands/add_groups_and_permissions.py +++ b/scipost/management/commands/add_groups_and_permissions.py @@ -402,6 +402,7 @@ class Command(BaseCommand): EditorialCollege.permissions.set([ can_view_pool, can_take_charge_of_submissions, + can_create_profiles, can_attend_VGMs, can_view_statistics, can_manage_ontology, diff --git a/stats/views.py b/stats/views.py index 54d597d57f93407b6a8a294b5c01636fc4dcbcbf..f9ccf9ceead3cfba8c012f0e5080756e62fe7f72 100644 --- a/stats/views.py +++ b/stats/views.py @@ -24,7 +24,7 @@ def statistics(request, journal_doi_label=None, volume_nr=None, issue_nr=None, y context['year'] = year context['citedby_impact_factor'] = journal.citedby_impact_factor(year) submissions = Submission.objects.filter( - submitted_to_journal=journal_doi_label).originally_submitted( + submitted_to__doi_label=journal_doi_label).originally_submitted( datetime.date(int(year), 1, 1), datetime.date(int(year), 12, 31)) context['submissions'] = submissions nr_ref_inv = 0 diff --git a/submissions/admin.py b/submissions/admin.py index e3d9fe3c3b550f24c28bb6911c71a72f63c8fad7..dde74c300a5d2a86f0ca058fbc7b0602a993c470 100644 --- a/submissions/admin.py +++ b/submissions/admin.py @@ -44,8 +44,9 @@ class SubmissionAdminForm(forms.ModelForm): class SubmissionAdmin(GuardedModelAdmin): date_hierarchy = 'submission_date' form = SubmissionAdminForm - list_display = ('title', 'author_list', 'status', 'submission_date', 'publication') - list_filter = ('status', 'discipline', 'submission_type', 'submitted_to_journal') + list_display = ('title', 'author_list', 'submitted_to', + 'status', 'submission_date', 'publication') + list_filter = ('status', 'discipline', 'submission_type', 'submitted_to') search_fields = ['submitted_by__user__last_name', 'title', 'author_list', 'abstract'] raw_id_fields = ('editor_in_charge', 'submitted_by') readonly_fields = ('publication',) @@ -53,7 +54,7 @@ class SubmissionAdmin(GuardedModelAdmin): # Admin fields should be added in the fieldsets radio_fields = { "discipline": admin.VERTICAL, - "submitted_to_journal": admin.VERTICAL, + "submitted_to": admin.VERTICAL, "refereeing_cycle": admin.HORIZONTAL, "submission_type": admin.VERTICAL } @@ -104,7 +105,7 @@ class SubmissionAdmin(GuardedModelAdmin): 'referees_flagged', 'referees_suggested', 'remarks_for_editors', - 'submitted_to_journal', + 'submitted_to', 'proceedings', 'pdf_refereeing_pack', 'plagiarism_report', diff --git a/submissions/factories.py b/submissions/factories.py index 647bfd22ecfe89d1fdf09bba1d77c6288dc1623f..64afff7b448b0890b0d840f4765bd93851bc8fc2 100644 --- a/submissions/factories.py +++ b/submissions/factories.py @@ -26,7 +26,7 @@ class SubmissionFactory(factory.django.DjangoModelFactory): author_list = factory.Faker('name') submitted_by = factory.Iterator(Contributor.objects.all()) submission_type = factory.Iterator(SUBMISSION_TYPE, getter=lambda c: c[0]) - submitted_to_journal = factory.Sequence(lambda n: random_scipost_journal()) + submitted_to = factory.Sequence(lambda n: Journal.objects.get(doi_label=random_scipost_journal())) title = factory.Faker('sentence') abstract = factory.Faker('paragraph', nb_sentences=10) identifier_wo_vn_nr = factory.Sequence( @@ -153,7 +153,7 @@ class ResubmittedSubmissionFactory(EICassignedSubmissionFactory): self.submitted_by = submission.submitted_by self.editor_in_charge = submission.editor_in_charge self.submission_type = submission.submission_type - self.submitted_to_journal = submission.submitted_to_journal + self.submitted_to = submission.submitted_to self.title = submission.title self.subject_area = submission.subject_area self.domain = submission.domain @@ -207,7 +207,7 @@ class ResubmissionFactory(EICassignedSubmissionFactory): self.submitted_by = submission.submitted_by self.editor_in_charge = submission.editor_in_charge self.submission_type = submission.submission_type - self.submitted_to_journal = submission.submitted_to_journal + self.submitted_to = submission.submitted_to self.title = submission.title self.subject_area = submission.subject_area self.domain = submission.domain @@ -232,7 +232,7 @@ class PublishedSubmissionFactory(EICassignedSubmissionFactory): if create and extracted is not False: from journals.factories import PublicationFactory PublicationFactory( - journal=self.submitted_to_journal, + journal=self.submitted_to.doi_label, accepted_submission=self, title=self.title, author_list=self.author_list) @factory.post_generation diff --git a/submissions/forms.py b/submissions/forms.py index 802893e428a0b91b0a8ee2244e20a31cb9c9980a..34c9ac176f4536769b28746883cb3670222640cf 100644 --- a/submissions/forms.py +++ b/submissions/forms.py @@ -30,6 +30,7 @@ from .signals import notify_manuscript_accepted from common.helpers import get_new_secrets_key from colleges.models import Fellowship from invitations.models import RegistrationInvitation +from journals.models import Journal from journals.constants import SCIPOST_JOURNAL_PHYSICS_PROC, SCIPOST_JOURNAL_PHYSICS from mails.utils import DirectMailUtil from preprints.helpers import generate_new_scipost_identifier, format_scipost_identifier @@ -192,7 +193,7 @@ class SubmissionChecks: error_message = ('The journal you want to submit to does not allow for this' ' arXiv identifier. Please contact SciPost if you have' ' any further questions.') - raise forms.ValidationError(error_message, code='submitted_to_journal') + raise forms.ValidationError(error_message, code='submitted_to') def submission_is_resubmission(self): """Check if the Submission is a resubmission.""" @@ -243,7 +244,7 @@ class SubmissionIdentifierForm(SubmissionChecks, forms.Form): 'referees_suggested': self.last_submission.referees_suggested, 'secondary_areas': self.last_submission.secondary_areas, 'subject_area': self.last_submission.subject_area, - 'submitted_to_journal': self.last_submission.submitted_to_journal, + 'submitted_to': self.last_submission.submitted_to, 'submission_type': self.last_submission.submission_type, } return data or {} @@ -272,7 +273,7 @@ class RequestSubmissionForm(SubmissionChecks, forms.ModelForm): fields = [ 'is_resubmission', 'discipline', - 'submitted_to_journal', + 'submitted_to', 'proceedings', 'submission_type', 'domain', @@ -321,17 +322,17 @@ class RequestSubmissionForm(SubmissionChecks, forms.ModelForm): del self.fields['arxiv_link'] del self.fields['identifier_w_vn_nr'] + self.fields['submitted_to'].queryset = Journal.objects.filter(active=True) + self.fields['submitted_to'].label = 'Journal: submit to' + # Proceedings submission fields qs = self.fields['proceedings'].queryset.open_for_submission() self.fields['proceedings'].queryset = qs self.fields['proceedings'].empty_label = None if not qs.exists(): - # Open the proceedings Journal for submission - def filter_proceedings(item): - return item[0] != SCIPOST_JOURNAL_PHYSICS_PROC - - self.fields['submitted_to_journal'].choices = filter( - filter_proceedings, self.fields['submitted_to_journal'].choices) + # No proceedings issue to submit to, so adapt the form fields + self.fields['submitted_to'].queryset = self.fields['submitted_to'].exclude( + doi_label=SCIPOST_JOURNAL_PHYSICS_PROC) del self.fields['proceedings'] # Submission type is optional @@ -347,9 +348,9 @@ class RequestSubmissionForm(SubmissionChecks, forms.ModelForm): self.do_pre_checks(cleaned_data['identifier_w_vn_nr']) self.identifier_matches_regex( - cleaned_data['identifier_w_vn_nr'], cleaned_data['submitted_to_journal']) + cleaned_data['identifier_w_vn_nr'], cleaned_data['submitted_to'].doi_label) - if self.cleaned_data['submitted_to_journal'] != SCIPOST_JOURNAL_PHYSICS_PROC: + if self.cleaned_data['submitted_to'].doi_label != SCIPOST_JOURNAL_PHYSICS_PROC: try: del self.cleaned_data['proceedings'] except KeyError: @@ -382,8 +383,8 @@ class RequestSubmissionForm(SubmissionChecks, forms.ModelForm): The SciPost Physics journal requires a Submission type to be specified. """ submission_type = self.cleaned_data['submission_type'] - journal = self.cleaned_data['submitted_to_journal'] - if journal == SCIPOST_JOURNAL_PHYSICS and not submission_type: + journal_doi_label = self.cleaned_data['submitted_to'].doi_label + if journal_doi_label == SCIPOST_JOURNAL_PHYSICS and not submission_type: self.add_error('submission_type', 'Please specify the submission type.') return submission_type @@ -760,9 +761,7 @@ class EditorialAssignmentForm(forms.ModelForm): # Update related Submission. if self.is_normal_cycle(): # Default Refereeing process - deadline = timezone.now() + datetime.timedelta(days=28) - if assignment.submission.submitted_to_journal == 'SciPostPhysLectNotes': - deadline += datetime.timedelta(days=28) + deadline = timezone.now() + self.instance.submission.submitted_to.refereeing_period # Update related Submission. Submission.objects.filter(id=self.submission.id).update( @@ -809,80 +808,9 @@ class ConsiderAssignmentForm(forms.Form): refusal_reason = forms.ChoiceField(choices=ASSIGNMENT_REFUSAL_REASONS, required=False) -class RefereeSelectForm(forms.Form): - """Pre-fill form to get the last name of the requested referee.""" - +class RefereeSearchForm(forms.Form): last_name = forms.CharField(widget=forms.TextInput({ - 'placeholder': 'Search in contributors database'})) - - -class RefereeRecruitmentForm(forms.ModelForm): - """Invite non-registered scientist to register and referee a Submission.""" - - class Meta: - model = RefereeInvitation - fields = [ - 'profile', - 'title', - 'first_name', - 'last_name', - 'email_address', - 'auto_reminders_allowed', - 'invitation_key'] - widgets = { - 'profile': forms.HiddenInput(), - 'invitation_key': forms.HiddenInput() - } - - def __init__(self, *args, **kwargs): - self.request = kwargs.pop('request', None) - self.submission = kwargs.pop('submission', None) - - initial = kwargs.pop('initial', {}) - initial['invitation_key'] = get_new_secrets_key() - kwargs['initial'] = initial - super().__init__(*args, **kwargs) - - def clean_email_address(self): - email = self.cleaned_data['email_address'] - if Contributor.objects.filter(user__email=email).exists(): - contr = Contributor.objects.get(user__email=email) - msg = ( - 'This email address is already registered. ' - 'Invite {title} {last_name} using the link above.') - self.add_error('email_address', msg.format( - title=contr.get_title_display(), last_name=contr.user.last_name)) - return email - - def save(self, commit=True): - if not self.request or not self.submission: - raise forms.ValidationError('No request or Submission given.') - - # Try to associate an existing Profile to ref/reg invitations: - profile = Profile.objects.get_unique_from_email_or_None( - email=self.cleaned_data['email_address']) - self.instance.profile = profile - - self.instance.submission = self.submission - self.instance.invited_by = self.request.user.contributor - referee_invitation = super().save(commit=False) - - registration_invitation = RegistrationInvitation( - profile=profile, - title=referee_invitation.title, - first_name=referee_invitation.first_name, - last_name=referee_invitation.last_name, - email=referee_invitation.email_address, - invitation_type=INVITATION_REFEREEING, - created_by=self.request.user, - invited_by=self.request.user, - invitation_key=referee_invitation.invitation_key, - key_expires=timezone.now() + datetime.timedelta(days=365)) - - if commit: - referee_invitation.save() - registration_invitation.save() - return (referee_invitation, registration_invitation) + 'placeholder': 'Search for a referee in the SciPost Profiles database'})) class ConsiderRefereeInvitationForm(forms.Form): diff --git a/submissions/migrations/0041_submission_submitted_to.py b/submissions/migrations/0041_submission_submitted_to.py new file mode 100644 index 0000000000000000000000000000000000000000..c60bc2f7f8aa93b207a5a72f7dffb2e682b96421 --- /dev/null +++ b/submissions/migrations/0041_submission_submitted_to.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-11-10 07:32 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0052_journal_refereeing_period'), + ('submissions', '0040_auto_20181102_1332'), + ] + + operations = [ + migrations.AddField( + model_name='submission', + name='submitted_to', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='journals.Journal'), + ), + ] diff --git a/submissions/migrations/0042_populate_submitted_to.py b/submissions/migrations/0042_populate_submitted_to.py new file mode 100644 index 0000000000000000000000000000000000000000..a4889cbd9a1f75a702ef12a33d4521540597a698 --- /dev/null +++ b/submissions/migrations/0042_populate_submitted_to.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-11-10 07:33 +from __future__ import unicode_literals + +from django.db import migrations + + +def populate_submitted_to_from_submitted_to_journal(apps, schema_editor): + Journal = apps.get_model('journals', 'Journal') + Submission = apps.get_model('submissions', 'Submission') + + for journal in Journal.objects.all(): + Submission.objects.filter(submitted_to_journal=journal.name).update( + submitted_to=journal) + + + +class Migration(migrations.Migration): + + dependencies = [ + ('submissions', '0041_submission_submitted_to'), + ] + + operations = [ + migrations.RunPython(populate_submitted_to_from_submitted_to_journal, + reverse_code=migrations.RunPython.noop), + ] diff --git a/submissions/migrations/0043_remove_submission_submitted_to_journal.py b/submissions/migrations/0043_remove_submission_submitted_to_journal.py new file mode 100644 index 0000000000000000000000000000000000000000..c96d3314ce9ffe7f8713187f5d74541836c466ba --- /dev/null +++ b/submissions/migrations/0043_remove_submission_submitted_to_journal.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-11-10 12:51 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('submissions', '0042_populate_submitted_to'), + ] + + operations = [ + migrations.RemoveField( + model_name='submission', + name='submitted_to_journal', + ), + ] diff --git a/submissions/models.py b/submissions/models.py index 39a0772a346016de08ce8f6d07da269ba3ad7063..8d54ceed41cf4133a290839c073220ea6af258da 100644 --- a/submissions/models.py +++ b/submissions/models.py @@ -39,7 +39,7 @@ from scipost.constants import TITLE_CHOICES from scipost.constants import SCIPOST_DISCIPLINES, SCIPOST_SUBJECT_AREAS from scipost.fields import ChoiceArrayField from scipost.storage import SecureFileStorage -from journals.constants import SCIPOST_JOURNALS_SUBMIT, SCIPOST_JOURNALS_DOMAINS +from journals.constants import SCIPOST_JOURNALS_DOMAINS from journals.models import Publication from mails.utils import DirectMailUtil @@ -93,9 +93,8 @@ class Submission(models.Model): voting_fellows = models.ManyToManyField('colleges.Fellowship', blank=True, related_name='voting_pool') - # Replace this by foreignkey? - submitted_to_journal = models.CharField(max_length=30, choices=SCIPOST_JOURNALS_SUBMIT, - verbose_name="Journal to be submitted to") + submitted_to = models.ForeignKey('journals.Journal', on_delete=models.CASCADE, + blank=True, null=True) proceedings = models.ForeignKey('proceedings.Proceedings', null=True, blank=True, related_name='submissions') title = models.CharField(max_length=300) diff --git a/submissions/plagiarism.py b/submissions/plagiarism.py index f09bab2c44e210a0af433c59f85e00d4601ad175..c4f0bcfffbb9e3f0c5a06ca2c8175640daa42305 100644 --- a/submissions/plagiarism.py +++ b/submissions/plagiarism.py @@ -49,7 +49,7 @@ class iThenticate: Generates a new folder and id if needed. """ - group_re = '{journal}_submissions'.format(journal=submission.submitted_to_journal) + group_re = '{journal}_submissions'.format(journal=submission.submitted_to.doi_label) folder_re = '{year}_{month}'.format( year=submission.submission_date.year, month=submission.submission_date.month diff --git a/submissions/templates/partials/submissions/pool/submission_info_table.html b/submissions/templates/partials/submissions/pool/submission_info_table.html index 642b35736cc326ba28c0263aac5e1b08c9db143e..3594325cb2603b6fa8e0a1f976bce104afc02848 100644 --- a/submissions/templates/partials/submissions/pool/submission_info_table.html +++ b/submissions/templates/partials/submissions/pool/submission_info_table.html @@ -20,7 +20,7 @@ {% endif %} <tr> <td>Submitted</td> - <td>{{submission.submission_date}} to {{submission.get_submitted_to_journal_display}}</td> + <td>{{submission.submission_date}} to {{submission.submitted_to}}</td> </tr> {% if submission.acceptance_date %} diff --git a/submissions/templates/partials/submissions/refereeing_pack_tex_template.html b/submissions/templates/partials/submissions/refereeing_pack_tex_template.html index fdfb3e290c3879e4c9950c2005819755fd762e03..c53d18f5d9ba24592d7953f9a412a68a168d62c8 100644 --- a/submissions/templates/partials/submissions/refereeing_pack_tex_template.html +++ b/submissions/templates/partials/submissions/refereeing_pack_tex_template.html @@ -49,7 +49,7 @@ Refereeing Package of\href{https://scipost.org{{submission.get_absolute_url|safe %%%%%%%%%% DATES \small{\ \\Received {{submission.submission_date|date:'d-m-Y'}}\newline {% if submission.acceptance_date %}Accepted {{submission.acceptance_date|date:'d-m-Y'}} \newline{% endif %} -Submitted to {{submission.get_submitted_to_journal_display}} +Submitted to {{submission.submitted_to}} } diff --git a/submissions/templates/partials/submissions/submission_card_content.html b/submissions/templates/partials/submissions/submission_card_content.html index 61269a9c316c6a9440d8a325b4d1b803d9bf4843..6a5f938774c555f3e3c1afa685c5f1786ce21a2b 100644 --- a/submissions/templates/partials/submissions/submission_card_content.html +++ b/submissions/templates/partials/submissions/submission_card_content.html @@ -7,7 +7,7 @@ {% if submission.publication and submission.publication.is_published %} Published as <a href="{{ submission.publication.get_absolute_url }}">{{ submission.publication.get_journal.abbreviation_citation }}{% if submission.publication.in_issue.in_volume %} <strong>{{ submission.publication.in_issue.in_volume.number }}</strong>,{% endif %} {{ submission.publication.get_paper_nr }} ({{ submission.publication.publication_date|date:'Y' }})</a> {% else %} - Submitted {{ submission.submission_date }} to {{ submission.get_submitted_to_journal_display }} + Submitted {{ submission.submission_date }} to {{ submission.submitted_to }} {% endif %} · latest activity: {{ submission.latest_activity }} </p> {% endblock %} diff --git a/submissions/templates/partials/submissions/submission_card_content_homepage.html b/submissions/templates/partials/submissions/submission_card_content_homepage.html index 18dac36ba2e7bd46f4829e9c6e25cf2bac0540c8..5f63d272edc3f1bb3789cdf471c7ea093b19272c 100644 --- a/submissions/templates/partials/submissions/submission_card_content_homepage.html +++ b/submissions/templates/partials/submissions/submission_card_content_homepage.html @@ -2,6 +2,6 @@ {% block card_footer %} <div class="meta"> - Submitted {{submission.submission_date}} to {{submission.get_submitted_to_journal_display}}</span> + Submitted {{submission.submission_date}} to {{submission.submitted_to}}</span> </div> {% endblock %} diff --git a/submissions/templates/partials/submissions/submission_summary.html b/submissions/templates/partials/submissions/submission_summary.html index 4c337c9aa871d83f8cbf4235f7fe53ba3462aaa2..b75a9ae12c54edc45721b19ab973fc99d3ff9de1 100644 --- a/submissions/templates/partials/submissions/submission_summary.html +++ b/submissions/templates/partials/submissions/submission_summary.html @@ -50,7 +50,7 @@ </tr> <tr> <td>Submitted to:</td> - <td>{{submission.get_submitted_to_journal_display}}</td> + <td>{{submission.submitted_to}}</td> </tr> <tr> <td>Domain(s):</td> diff --git a/submissions/templates/submissions/referee_form.html b/submissions/templates/submissions/select_referee.html similarity index 52% rename from submissions/templates/submissions/referee_form.html rename to submissions/templates/submissions/select_referee.html index 29dca70cb44666ec99490fde356eb194b541aa48..a74aff6d40f28a4d0a4ea123165c928aa8dd28d4 100644 --- a/submissions/templates/submissions/referee_form.html +++ b/submissions/templates/submissions/select_referee.html @@ -41,10 +41,9 @@ <div class="col-12"> <h2 class="highlight" id="form">Select an additional Referee</h2> - <form action="{% url 'submissions:select_referee' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}#form" method="post"> - {% csrf_token %} - {{ ref_search_form|bootstrap }} - <input class="btn btn-primary" type="submit" value="Find referee"> + <form action="{% url 'submissions:select_referee' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}" method="get"> + {{ referee_search_form|bootstrap }} + <input class="btn btn-primary" type="submit" value="Find referee"> </form> </div> </div> @@ -72,37 +71,48 @@ {% endif %} <div class="row"> - <div class="col-12"> - {% if contributors_found %} - <h3>Identified as contributor:</h3> - <table class="table"> - {% for contributor in contributors_found %} - <tr> - <td>{{ contributor.user.first_name }} {{ contributor.user.last_name }}</td> - <td> - <a href="{% url 'submissions:send_refereeing_invitation' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr contributor_id=contributor.id auto_reminders_allowed=1 %}">Send refereeing invitation with</a> or <a href="{% url 'submissions:send_refereeing_invitation' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr contributor_id=contributor.id auto_reminders_allowed=0 %}">without</a> auto reminders - {% include 'partials/submissions/refinv_auto_reminders_tooltip.html' %} - </td> - </tr> - {% endfor %} - </table> - {% elif ref_search_form.has_changed %} - <p>No Contributor with this last name could be identified.</p> - {% endif %} + <div class="col-12"> + {% if workdays_left_to_report < 15 %} + <h4 class="highlight p-1"><strong class="text-danger">Warning: there are {{ workdays_left_to_report }} working days left before the refereeing deadline.</strong><br/><br/><span class="text-muted">Standard refereeing period for {{ submission.submitted_to }}: <em>{{ submission.submitted_to.refereeing_period.days }} days</em></span><br/><br/>Consider resetting the refereeing deadline at the <a href="{% url 'submissions:editorial_page' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}">Editorial Page</a> before inviting a referee.</h4> + {% endif %} + {% if profiles_found %} + <h3>Matching people in our database:</h3> + <table class="table"> + <tr> + <th>Name</th> + <th>Registered<br/>Contributor?</th> + <th>Action<br/><span class="text-muted"><small>(Unregistered people will also receive a registration invitation)</small></span></th> + </tr> + {% for profile in profiles_found %} + <tr> + <td>{{ profile }}</td> + <td>{% if profile.contributor %}<i class="fa fa-check-circle text-success"></i>{% else %}<i class="fa fa-times-circle text-danger"></i>{% endif %}</td> + <td>Send refereeing invitation <a href="{% url 'submissions:invite_referee' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr profile_id=profile.id auto_reminders_allowed=1 %}">with</a> or <a href="{% url 'submissions:invite_referee' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr profile_id=profile.id auto_reminders_allowed=0 %}">without</a> auto-reminders {% include 'partials/submissions/refinv_auto_reminders_tooltip.html' %}</td> + </tr> + {% empty %} + <tr> + <td>No Profiles found</td> + <td></td> + </tr> + {% endfor %} + </table> + {% endif %} + </div> </div> -{% if ref_recruit_form %} - <div class="row"> - <div class="col-12"> - <h3 class="mb-2">If the referee you were looking for was not found by using the method above, you can send a registration and refereeing invitation by filling this form:</h3> - <form action="{% url 'submissions:recruit_referee' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}" method="post"> - {% csrf_token %} - {{ ref_recruit_form|bootstrap }} - <input type="submit" name="submit" value="Send invitation" class="btn btn-primary"> - </form> - </div> - </div> +{% if profile_form %} +<div class="row"> + <div class="col-12"> + <h3 class="mb-2">Not found? Then add to our database by filling this form:</h3> + <form action="{% url 'submissions:add_referee_profile' identifier_w_vn_nr=submission.preprint.identifier_w_vn_nr %}" method="post"> + {% csrf_token %} + {{ profile_form|bootstrap }} + <input type="submit" name="submit" value="Add to database" class="btn btn-primary"> + </form> + <h4>(does not send invitation yet: you will return to this page, from which you can then invite this referee)</h4> + </div> +</div> {% endif %} diff --git a/submissions/templates/submissions/submission_form.html b/submissions/templates/submissions/submission_form.html index 11a2d6adf88d487bad59e7881fee3ebf864e8cde..8d4c879e1e0a988d2488bd69a3c46e2148cf2994 100644 --- a/submissions/templates/submissions/submission_form.html +++ b/submissions/templates/submissions/submission_form.html @@ -11,16 +11,18 @@ {% block footer_script %} <script> - $(document).ready(function(){ - $('select#id_submitted_to_journal').on('change', function (){ + + $(document).ready(function(){ + + $('select#id_submitted_to').on('change', function (){ var selection = $(this).val(); $("#id_proceedings, #id_submission_type").parents('.form-group').hide() switch(selection){ - case "SciPostPhys": + case "{{ id_SciPostPhys }}": $("#id_submission_type").parents('.form-group').show() break; - case "SciPostPhysProc": + case "{{ id_SciPostPhysProc }}": $("#id_proceedings").parents('.form-group').show() break; } diff --git a/submissions/test_views.py b/submissions/test_views.py index 6b6bf63e7c3ed4ce9d2b90355fceb4505048ffbd..6f4b9fbdf9d9cc43b6c27a32b4db70bb9efa1fad 100644 --- a/submissions/test_views.py +++ b/submissions/test_views.py @@ -18,6 +18,8 @@ from .factories import UnassignedSubmissionFactory, EICassignedSubmissionFactory from .forms import RequestSubmissionForm, SubmissionIdentifierForm, ReportForm from .models import Submission, Report, RefereeInvitation +from journals.models import Journal + from faker import Faker @@ -139,7 +141,7 @@ class SubmitManuscriptTest(BaseContributorTestCase): params.update({ 'discipline': 'physics', 'subject_area': 'Phys:MP', - 'submitted_to_journal': 'SciPostPhys', + 'submitted_to': Journal.objects.filter(doi_label='SciPostPhys'), 'submission_type': 'Article', 'domain': 'T' }) @@ -179,7 +181,7 @@ class SubmitManuscriptTest(BaseContributorTestCase): params.update({ 'discipline': 'physics', 'subject_area': 'Phys:MP', - 'submitted_to_journal': 'SciPostPhys', + 'submitted_to': Journal.objects.get(doi_label='SciPostPhys'), 'submission_type': 'Article', 'domain': 'T' }) diff --git a/submissions/urls.py b/submissions/urls.py index 57711856f0e8f01895dce1c5fb10285626125496..9b2f9985cbf8c22e4f8c9bd2600efb1c94925991 100644 --- a/submissions/urls.py +++ b/submissions/urls.py @@ -102,12 +102,12 @@ urlpatterns = [ views.editorial_page, name='editorial_page'), url(r'^select_referee/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), 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]+)' + url(r'^add_referee_profile/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), + views.add_referee_profile, name='add_referee_profile'), + url(r'^invite_referee/{regex}/(?P<profile_id>[0-9]+)' '/(?P<auto_reminders_allowed>[0-1])$'.format( regex=SUBMISSIONS_COMPLETE_REGEX), - views.send_refereeing_invitation, name='send_refereeing_invitation'), + views.invite_referee, name='invite_referee'), url(r'^set_refinv_auto_reminder/(?P<invitation_id>[0-9]+)/(?P<auto_reminders>[0-1])$', views.set_refinv_auto_reminder, name='set_refinv_auto_reminder'), url(r'^accept_or_decline_ref_invitations/$', diff --git a/submissions/utils.py b/submissions/utils.py index f17b0c87d25e00810e44f93f57780b9c4d146da0..8855acd84ab628be25cbe6ac1bc550a682f2c3bd 100644 --- a/submissions/utils.py +++ b/submissions/utils.py @@ -319,7 +319,7 @@ class SubmissionUtils(BaseMailUtil): email_text = ('Dear ' + cls.submission.submitted_by.get_title_display() + ' ' + cls.submission.submitted_by.user.last_name + ', \n\nWe have received your Submission to ' + - cls.submission.get_submitted_to_journal_display() + ',\n\n' + + str(cls.submission.submitted_to) + ',\n\n' + cls.submission.title + ' by ' + cls.submission.author_list + '.' + '\n\nWe will update you on the results of the pre-screening process ' 'within the next 5 working days.' @@ -329,7 +329,7 @@ class SubmissionUtils(BaseMailUtil): '\n\nThe SciPost Team.') email_text_html = ( '<p>Dear {{ title }} {{ last_name }},</p>' - '<p>We have received your Submission to {{ submitted_to_journal }},</p>' + '<p>We have received your Submission to {{ submitted_to }},</p>' '<p>{{ sub_title }}</p>' '\n<p>by {{ author_list }}.</p>' '\n<p>We will update you on the results of the pre-screening process ' @@ -342,7 +342,7 @@ class SubmissionUtils(BaseMailUtil): 'title': cls.submission.submitted_by.get_title_display(), 'last_name': cls.submission.submitted_by.user.last_name, 'sub_title': cls.submission.title, - 'submitted_to_journal': cls.submission.get_submitted_to_journal_display(), + 'submitted_to': str(cls.submission.submitted_to), 'author_list': cls.submission.author_list, } email_text_html += '<br/>' + EMAIL_FOOTER @@ -1175,7 +1175,7 @@ class SubmissionUtils(BaseMailUtil): or cls.recommendation.recommendation == 3): email_text += ('We are pleased to inform you that your Submission ' 'has been accepted for publication in ' - + cls.submission.get_submitted_to_journal_display()) + + str(cls.submission.submitted_to)) email_text_html += ( '<p>We are pleased to inform you that your Submission ' 'has been accepted for publication in <strong>{{ journal }}</strong>') @@ -1232,7 +1232,7 @@ class SubmissionUtils(BaseMailUtil): 'sub_title': cls.submission.title, 'author_list': cls.submission.author_list, 'identifier_w_vn_nr': cls.submission.preprint.identifier_w_vn_nr, - 'journal': cls.submission.get_submitted_to_journal_display(), + 'journal': str(cls.submission.submitted_to), } email_text_html += '<br/>' + EMAIL_FOOTER html_template = Template(email_text_html) diff --git a/submissions/views.py b/submissions/views.py index b5674a000946cee10c741010afde91901ceb7144..583544cee207701ed035eaf21cecff19764bd2f7 100644 --- a/submissions/views.py +++ b/submissions/views.py @@ -4,6 +4,7 @@ __license__ = "AGPL v3" import datetime import feedparser +import json import strings from django.contrib import messages @@ -33,8 +34,10 @@ from .mixins import SubmissionAdminViewMixin from .forms import ( SubmissionIdentifierForm, RequestSubmissionForm, SubmissionSearchForm, RecommendationVoteForm, ConsiderAssignmentForm, InviteEditorialAssignmentForm, EditorialAssignmentForm, VetReportForm, - SetRefereeingDeadlineForm, RefereeSelectForm, iThenticateReportForm, VotingEligibilityForm, - RefereeRecruitmentForm, ConsiderRefereeInvitationForm, EditorialCommunicationForm, ReportForm, + SetRefereeingDeadlineForm, RefereeSearchForm, #RefereeSelectForm, + iThenticateReportForm, VotingEligibilityForm, + #RefereeRecruitmentForm, + ConsiderRefereeInvitationForm, EditorialCommunicationForm, ReportForm, SubmissionCycleChoiceForm, ReportPDFForm, SubmissionReportsForm, EICRecommendationForm, SubmissionPoolFilterForm, FixCollegeDecisionForm, SubmissionPrescreeningForm, PreassignEditorsFormSet, SubmissionReassignmentForm) @@ -45,15 +48,22 @@ from .utils import SubmissionUtils from colleges.permissions import fellowship_required, fellowship_or_admin_required from comments.forms import CommentForm +from common.helpers import get_new_secrets_key +from common.utils import workdays_between +from invitations.constants import STATUS_SENT +from invitations.models import RegistrationInvitation from journals.models import Journal from mails.views import MailEditingSubView from ontology.models import Topic from ontology.forms import SelectTopicForm from production.forms import ProofsDecisionForm from profiles.models import Profile +from profiles.forms import SimpleProfileForm +from scipost.constants import INVITATION_REFEREEING from scipost.forms import RemarkForm from scipost.mixins import PaginationMixin from scipost.models import Contributor, Remark +from submissions.models import RefereeInvitation # from notifications.views import is_test_user # Temporarily until release @@ -70,6 +80,12 @@ class RequestSubmissionView(LoginRequiredMixin, PermissionRequiredMixin, CreateV form_class = RequestSubmissionForm template_name = 'submissions/submission_form.html' + def get_context_data(self, *args, **kwargs): + context = super().get_context_data(*args, **kwargs) + context['id_SciPostPhys'] = get_object_or_404(Journal, doi_label='SciPostPhys').id + context['id_SciPostPhysProc'] = get_object_or_404(Journal, doi_label='SciPostPhysProc').id + return context + def get_form_kwargs(self): """Form requires extra kwargs.""" kwargs = super().get_form_kwargs() @@ -83,7 +99,7 @@ class RequestSubmissionView(LoginRequiredMixin, PermissionRequiredMixin, CreateV """Redirect and send out mails if all data is valid.""" submission = form.save() submission.add_general_event('The manuscript has been submitted to %s.' - % submission.get_submitted_to_journal_display()) + % str(submission.submitted_to)) text = ('<h3>Thank you for your Submission to SciPost</h3>' 'Your Submission will soon be handled by an Editor.') @@ -193,7 +209,7 @@ class SubmissionListView(PaginationMixin, ListView): if 'to_journal' in self.request.GET: queryset = queryset.filter( latest_activity__gte=timezone.now() + datetime.timedelta(days=-60), - submitted_to_journal=self.request.GET['to_journal'] + submitted_to__doi_label=self.request.GET['to_journal'] ) elif 'discipline' in self.kwargs and 'nrweeksback' in self.kwargs: discipline = self.kwargs['discipline'] @@ -718,9 +734,7 @@ def volunteer_as_EIC(request, identifier_w_vn_nr): status=STATUS_ACCEPTED, date_answered=timezone.now()) # Set deadlines - deadline = timezone.now() + datetime.timedelta(days=28) # for papers - if submission.submitted_to_journal == 'SciPostPhysLectNotes': - deadline += datetime.timedelta(days=28) + deadline = timezone.now() + submission.submitted_to.refereeing_period # Update Submission data Submission.objects.filter(id=submission.id).update( @@ -866,24 +880,19 @@ def cycle_form_submit(request, identifier_w_vn_nr): @login_required @fellowship_or_admin_required() def select_referee(request, identifier_w_vn_nr): - """Invite scientist to referee a Submission. - - Accessible for: Editor-in-charge and Editorial Administration. - It'll list possible already registered Contributors that match the search. If the scientist - is not yet registered, he will be invited using a RegistrationInvitation as well. In - addition the page will show possible conflicts of interests, with that information - coming from the ArXiv API. + """ + Search for a referee in the set of Profiles, and if none is found, + create a new Profile and return to this page for further processing. """ submission = get_object_or_404(Submission.objects.filter_for_eic(request.user), preprint__identifier_w_vn_nr=identifier_w_vn_nr) context = {} queryresults = '' - ref_search_form = RefereeSelectForm(request.POST or None) - if ref_search_form.is_valid(): - contributors_found = Contributor.objects.filter( - user__last_name__icontains=ref_search_form.cleaned_data['last_name']) - context['contributors_found'] = contributors_found - + referee_search_form = RefereeSearchForm(request.GET or None) + if referee_search_form.is_valid(): + profiles_found = Profile.objects.filter( + last_name__icontains=referee_search_form.cleaned_data['last_name']) + context['profiles_found'] = profiles_found # Check for recent co-authorship (thus referee disqualification) try: sub_auth_boolean_str = '((' + (submission @@ -892,7 +901,7 @@ def select_referee(request, identifier_w_vn_nr): for author in submission.metadata['entries'][0]['authors'][1:]: sub_auth_boolean_str += '+OR+' + author['name'].split()[-1] sub_auth_boolean_str += ')+AND+' - search_str = sub_auth_boolean_str + ref_search_form.cleaned_data['last_name'] + ')' + search_str = sub_auth_boolean_str + referee_search_form.cleaned_data['last_name'] + ')' queryurl = ('https://export.arxiv.org/api/query?search_query=au:%s' % search_str + '&sortBy=submittedDate&sortOrder=descending' '&max_results=5') @@ -900,116 +909,105 @@ def select_referee(request, identifier_w_vn_nr): queryresults = arxivquery except KeyError: pass - context['ref_recruit_form'] = RefereeRecruitmentForm() - + context['profile_form'] = SimpleProfileForm() context.update({ 'submission': submission, - 'ref_search_form': ref_search_form, - 'queryresults': queryresults + 'workdays_left_to_report': workdays_between(timezone.now(), submission.reporting_deadline), + 'referee_search_form': referee_search_form, + 'queryresults': queryresults, }) - return render(request, 'submissions/referee_form.html', context) + return render(request, 'submissions/select_referee.html', context) @login_required @fellowship_or_admin_required() @transaction.atomic -def recruit_referee(request, identifier_w_vn_nr): - """Invite a non-registered scientist to register and referee a Submission. - - Accessible for: Editor-in-charge and Editorial Administration - If the Editor-in-charge does not find the desired referee among Contributors - (otherwise, the method send_refereeing_invitation is used), he/she can invite somebody - by providing name + contact details. This function emails a registration invitation to this - person. The pending refereeing invitation is then recognized upon registration, using the - invitation token. - """ +def add_referee_profile(request, identifier_w_vn_nr): submission = get_object_or_404(Submission.objects.filter_for_eic(request.user), preprint__identifier_w_vn_nr=identifier_w_vn_nr) - - if request.method == 'GET': - # This leads to unexpected 500 errors - return redirect(reverse('submissions:select_referee', args=(identifier_w_vn_nr,))) - - ref_recruit_form = RefereeRecruitmentForm( - request.POST or None, request=request, submission=submission) - if ref_recruit_form.is_valid(): - referee_invitation, registration_invitation = ref_recruit_form.save(commit=False) - mail_request = MailEditingSubView(request, - mail_code='referees/invite_unregistered_to_referee', - instance=referee_invitation) - mail_request.add_form(ref_recruit_form) - if mail_request.is_valid(): - referee_invitation.save() - registration_invitation.save() - - messages.success(request, 'Referee {} invited'.format( - registration_invitation.last_name)) - submission.add_event_for_author('A referee has been invited.') - submission.add_event_for_eic('{} has been recruited and invited as a referee.'.format( - referee_invitation.last_name)) - - mail_request.send() - return redirect(reverse('submissions:editorial_page', - kwargs={'identifier_w_vn_nr': identifier_w_vn_nr})) - else: - return mail_request.return_render() - - ref_search_form = RefereeSelectForm(request.POST or None) - contributors_found = Contributor.objects.filter( - user__email=ref_recruit_form.cleaned_data['email_address']) - context = { - 'ref_recruit_form': ref_recruit_form, - 'ref_search_form': ref_search_form, - 'submission': submission, - 'queryresults': [], - 'contributors_found': contributors_found, - } - return render(request, 'submissions/referee_form.html', context) + profile_form = SimpleProfileForm(request.POST or None) + if profile_form.is_valid(): + profile_form.save() + messages.success(request, + 'Profile added, you can now invite this referee using the links above') + else: + messages.error(request, 'Could not create this Profile') + for error_messages in profile_form.errors.values(): + messages.warning(request, *error_messages) + return redirect(reverse( + 'submissions:select_referee', + kwargs={'identifier_w_vn_nr': submission.preprint.identifier_w_vn_nr}) + + ('?last_name=%s' % profile_form.cleaned_data['last_name'])) @login_required @fellowship_or_admin_required() @transaction.atomic -def send_refereeing_invitation(request, identifier_w_vn_nr, contributor_id, - auto_reminders_allowed): - """Send RefereeInvitation to a registered Contributor. - - This method is called by the EIC from the submission's editorial_page, - in the case where the referee is an identified Contributor. - For a referee who isn't a Contributor yet, the method recruit_referee above - is called instead. - - Accessible for: Editor-in-charge and Editorial Administration +def invite_referee(request, identifier_w_vn_nr, profile_id, auto_reminders_allowed): + """ + Invite a referee linked to a Profile. + If the Profile has a Contributor object, a simple invitation is sent. + If there is no associated Contributor, a registration invitation is included. """ submission = get_object_or_404(Submission.objects.filter_for_eic(request.user), preprint__identifier_w_vn_nr=identifier_w_vn_nr) - contributor = get_object_or_404(Contributor, pk=contributor_id) - profile = Profile.objects.get_unique_from_email_or_None(contributor.user.email) + profile = get_object_or_404(Profile, pk=profile_id) - if not contributor.is_currently_available: - errormessage = ('This Contributor is marked as currently unavailable. ' - 'Please go back and select another referee.') - return render(request, 'scipost/error.html', {'errormessage': errormessage}) + contributor = None + if hasattr(profile, 'contributor') and profile.contributor: + contributor = profile.contributor - invitation = RefereeInvitation( + referee_invitation, created = RefereeInvitation.objects.get_or_create( profile=profile, - submission=submission, referee=contributor, - title=contributor.title, - first_name=contributor.user.first_name, - last_name=contributor.user.last_name, - email_address=contributor.user.email, + submission=submission, + title=profile.title, + first_name=profile.first_name, + last_name=profile.last_name, + email_address=profile.email, auto_reminders_allowed=auto_reminders_allowed, - # the key is only used for inviting unregistered users - date_invited=timezone.now(), invited_by=request.user.contributor) - mail_request = MailEditingSubView(request, mail_code='referees/invite_contributor_to_referee', - invitation=invitation) + key = '' + if created: + key = get_new_secrets_key() + referee_invitation.invitation_key = key + referee_invitation.save() + + registration_invitation = None + if contributor: + if not profile.contributor.is_currently_available: + errormessage = ('This Contributor is marked as currently unavailable. ' + 'Please go back and select another referee.') + return render(request, 'scipost/error.html', {'errormessage': errormessage}) + + mail_request = MailEditingSubView(request, + mail_code='referees/invite_contributor_to_referee', + invitation=referee_invitation) + else: # no Contributor, so registration invitation + registration_invitation, reginv_created = RegistrationInvitation.objects.get_or_create( + profile=profile, + title=profile.title, + first_name=profile.first_name, + last_name=profile.last_name, + email=profile.email, + invitation_type=INVITATION_REFEREEING, + created_by=request.user, + invited_by=request.user, + invitation_key=referee_invitation.invitation_key) + mail_request = MailEditingSubView(request, + mail_code='referees/invite_unregistered_to_referee', + invitation=referee_invitation) + if mail_request.is_valid(): - invitation.save() + referee_invitation.date_invited = timezone.now() + referee_invitation.save() + if registration_invitation: + registration_invitation.status = STATUS_SENT + registration_invitation.key_expires = timezone.now() + datetime.timedelta(days=365) + registration_invitation.save() submission.add_event_for_author('A referee has been invited.') - submission.add_event_for_eic('Referee %s has been invited.' % contributor.user.last_name) + submission.add_event_for_eic('Referee %s has been invited.' % profile.last_name) messages.success(request, 'Invitation sent') mail_request.send() return redirect(reverse('submissions:editorial_page', @@ -1199,12 +1197,11 @@ def extend_refereeing_deadline(request, identifier_w_vn_nr, days): submission = get_object_or_404(Submission.objects.filter_for_eic(request.user), preprint__identifier_w_vn_nr=identifier_w_vn_nr) - submission.reporting_deadline += datetime.timedelta(days=int(days)) - submission.open_for_reporting = True - submission.open_for_commenting = True - submission.status = STATUS_EIC_ASSIGNED - submission.latest_activity = timezone.now() - submission.save() + Submission.objects.filter(pk=submission.id).update( + reporting_deadline=submission.reporting_deadline + datetime.timedelta(days=int(days)), + open_for_reporting=True, + open_for_commenting=True, + latest_activity=timezone.now()) messages.success(request, 'Refereeing deadline set to {0}.'.format( submission.reporting_deadline.strftime('%Y-%m-%d'))) @@ -1231,17 +1228,10 @@ def set_refereeing_deadline(request, identifier_w_vn_nr): form = SetRefereeingDeadlineForm(request.POST or None) if form.is_valid(): - submission.reporting_deadline = form.cleaned_data['deadline'] - if form.cleaned_data['deadline'] > timezone.now().date(): - submission.open_for_reporting = True - submission.open_for_commenting = True - submission.latest_activity = timezone.now() - else: - submission.open_for_reporting = False - submission.open_for_commenting = False - submission.latest_activity = timezone.now() - # submission.status = STATUS_EIC_ASSIGNED # This is dangerous as shit. - submission.save() + Submission.objects.filter(pk=submission.id).update( + reporting_deadline=form.cleaned_data['deadline'], + open_for_reporting=(form.cleaned_data['deadline'] >= timezone.now().date()), + latest_activity = timezone.now()) submission.add_general_event('A new refereeing deadline is set.') messages.success(request, 'New reporting deadline set.') else: diff --git a/templates/email/referees/invite_contributor_to_referee_reminder1.html b/templates/email/referees/invite_contributor_to_referee_reminder1.html index bc2d7809fa92ab650a597487bd7aea329ddea92b..3eda73474bd795b2de4f5b57862c359a82f12ddc 100644 --- a/templates/email/referees/invite_contributor_to_referee_reminder1.html +++ b/templates/email/referees/invite_contributor_to_referee_reminder1.html @@ -3,7 +3,7 @@ Dear {{invitation.referee.get_title_display}} {{invitation.referee.user.last_name}}, </p> <p> - Recently, on behalf of the Editor-in-charge {{ invitation.submission.editor_in_charge.get_title_display }} {{ invitation.submission.editor_in_charge.user.last_name }}, we invited you to consider refereeing a Submission to {{ invitation.submission.get_submitted_to_journal_display }}, namely<br><br> + Recently, on behalf of the Editor-in-charge {{ invitation.submission.editor_in_charge.get_title_display }} {{ invitation.submission.editor_in_charge.user.last_name }}, we invited you to consider refereeing a Submission to {{ invitation.submission.submitted_to }}, namely<br><br> {{ invitation.submission.title }}<br> by {{ invitation.submission.author_list }}<br> (see https://scipost.org{{ invitation.submission.get_absolute_url }} - first submitted {{ invitation.submission.original_submission_date|date:"d M Y" }}). diff --git a/templates/email/referees/invite_contributor_to_referee_reminder2.html b/templates/email/referees/invite_contributor_to_referee_reminder2.html index 6623646302f15ec1dd71e6f8eaec63d29c4e84df..3f26c5490069de7fc6a4d6c986d296a70a4dd908 100644 --- a/templates/email/referees/invite_contributor_to_referee_reminder2.html +++ b/templates/email/referees/invite_contributor_to_referee_reminder2.html @@ -3,7 +3,7 @@ Dear {{invitation.referee.get_title_display}} {{invitation.referee.user.last_name}}, </p> <p> - Recently, on behalf of the Editor-in-charge {{ invitation.submission.editor_in_charge.get_title_display }} {{ invitation.submission.editor_in_charge.user.last_name }}, we invited you to consider refereeing a Submission to {{ invitation.submission.get_submitted_to_journal_display }}, namely<br><br> + Recently, on behalf of the Editor-in-charge {{ invitation.submission.editor_in_charge.get_title_display }} {{ invitation.submission.editor_in_charge.user.last_name }}, we invited you to consider refereeing a Submission to {{ invitation.submission.submitted_to }}, namely<br><br> {{ invitation.submission.title }}<br> by {{ invitation.submission.author_list }}<br> (see https://scipost.org{{ invitation.submission.get_absolute_url }} - first submitted {{ invitation.submission.original_submission_date|date:"d M Y" }}). diff --git a/templates/email/referees/invite_unregistered_to_referee.html b/templates/email/referees/invite_unregistered_to_referee.html index cad3479466910922ee71e586130cd468cdd2f2fe..0080a835aa39cc95d790a036b30d9af8e7b12580 100644 --- a/templates/email/referees/invite_unregistered_to_referee.html +++ b/templates/email/referees/invite_unregistered_to_referee.html @@ -2,7 +2,7 @@ Dear {{ invitation.get_title_display }} {{ invitation.last_name }}, </p> <p> - On behalf of the Editor-in-charge {{ invitation.submission.editor_in_charge.get_title_display }} {{ invitation.submission.editor_in_charge.user.last_name }}, we would like to invite you to referee a Submission to {{ invitation.submission.get_submitted_to_journal_display }}, namely<br><br> + On behalf of the Editor-in-charge {{ invitation.submission.editor_in_charge.get_title_display }} {{ invitation.submission.editor_in_charge.user.last_name }}, we would like to invite you to referee a Submission to {{ invitation.submission.submitted_to }}, namely<br><br> {{ invitation.submission.title }}<br> by {{ invitation.submission.author_list }}<br> (see https://scipost.org{{ invitation.submission.get_absolute_url }} - first submitted {{ invitation.submission.original_submission_date|date:"d M Y" }}). diff --git a/templates/email/referees/invite_unregistered_to_referee_reminder1.html b/templates/email/referees/invite_unregistered_to_referee_reminder1.html index 0825b9042b7f7174824c0e91430f1d35037a2069..aa37c5ac735071bbcdd4bf9b7109b7c32ba27c5a 100644 --- a/templates/email/referees/invite_unregistered_to_referee_reminder1.html +++ b/templates/email/referees/invite_unregistered_to_referee_reminder1.html @@ -3,7 +3,7 @@ Dear {{ invitation.get_title_display }} {{ invitation.last_name }}, </p> <p> - Recently, on behalf of the Editor-in-charge {{ invitation.submission.editor_in_charge.get_title_display }} {{ invitation.submission.editor_in_charge.user.last_name }}, we invited you to consider refereeing a Submission to {{ invitation.submission.get_submitted_to_journal_display }}, namely<br><br> + Recently, on behalf of the Editor-in-charge {{ invitation.submission.editor_in_charge.get_title_display }} {{ invitation.submission.editor_in_charge.user.last_name }}, we invited you to consider refereeing a Submission to {{ invitation.submission.submitted_to }}, namely<br><br> {{ invitation.submission.title }}<br> by {{ invitation.submission.author_list }}<br> (see https://scipost.org{{ invitation.submission.get_absolute_url }} - first submitted {{ invitation.submission.original_submission_date|date:"d M Y" }}). diff --git a/templates/email/referees/invite_unregistered_to_referee_reminder2.html b/templates/email/referees/invite_unregistered_to_referee_reminder2.html index f2dd29280d189c442c1c9717892d79040ac441f3..6fa3f0048eaf30c47b7c1ed638ec033cecb23f26 100644 --- a/templates/email/referees/invite_unregistered_to_referee_reminder2.html +++ b/templates/email/referees/invite_unregistered_to_referee_reminder2.html @@ -3,7 +3,7 @@ Dear {{ invitation.get_title_display }} {{ invitation.last_name }}, </p> <p> - Recently, on behalf of the Editor-in-charge {{ invitation.submission.editor_in_charge.get_title_display }} {{ invitation.submission.editor_in_charge.user.last_name }}, we invited you to consider refereeing a Submission to {{ invitation.submission.get_submitted_to_journal_display }}, namely<br><br> + Recently, on behalf of the Editor-in-charge {{ invitation.submission.editor_in_charge.get_title_display }} {{ invitation.submission.editor_in_charge.user.last_name }}, we invited you to consider refereeing a Submission to {{ invitation.submission.submitted_to }}, namely<br><br> {{ invitation.submission.title }}<br> by {{ invitation.submission.author_list }}<br> (see https://scipost.org{{ invitation.submission.get_absolute_url }} - first submitted {{ invitation.submission.original_submission_date|date:"d M Y" }}).