SciPost Code Repository

Skip to content
Snippets Groups Projects
forms.py 12.7 KiB
Newer Older
__copyright__ = "Copyright 2016-2018, Stichting SciPost (SciPost Foundation)"
__license__ = "AGPL v3"


Jorran de Wit's avatar
Jorran de Wit committed
from django import forms
from django.contrib import messages
Jorran de Wit's avatar
Jorran de Wit committed
from django.db.models import Q
Jorran de Wit's avatar
Jorran de Wit committed

from journals.models import Publication
from scipost.models import Contributor
from submissions.models import Submission
Jorran de Wit's avatar
Jorran de Wit committed

from . import constants
from .models import RegistrationInvitation, CitationNotification
Jorran de Wit's avatar
Jorran de Wit committed

from profiles.models import Profile

from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
Jorran de Wit's avatar
Jorran de Wit committed


class AcceptRequestMixin:
    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request')
        super().__init__(*args, **kwargs)


Jorran de Wit's avatar
Jorran de Wit committed
class RegistrationInvitationFilterForm(forms.Form):
    term = forms.CharField(help_text="You may search on arXiv identifier, DOI or last name.")

    def search(self, qs):
        term = self.cleaned_data.get('term')
        return qs.filter(
            Q(last_name__icontains=term) |
            Q(citation_notifications__submission__preprint__identifier_w_vn_nr__icontains=term) |
Jorran de Wit's avatar
Jorran de Wit committed
            Q(citation_notifications__publication__doi_label__icontains=term))


class SuggestionSearchForm(forms.Form):
    last_name = forms.CharField()

    def search(self):
        last_name = self.cleaned_data.get('last_name')

        if last_name:
            contributors = Contributor.objects.filter(user__last_name__icontains=last_name)
            invitations = RegistrationInvitation.objects.filter(last_name__icontains=last_name)
            declines = RegistrationInvitation.objects.declined().filter(
                last_name__icontains=last_name)
            return contributors, invitations, declines
        return Contributor.objects.none(), RegistrationInvitation.objects.none()


class CitationNotificationForm(AcceptRequestMixin, forms.ModelForm):
    submission = AutoCompleteSelectField('submissions_lookup', required=False)
    publication = AutoCompleteSelectField('publication_lookup', required=False)

    class Meta:
        model = CitationNotification
        fields = (
            'contributor',
            'submission',
            'publication')

    def __init__(self, *args, **kwargs):
        contributors = kwargs.pop('contributors')
        super().__init__(*args, **kwargs)
        if contributors:
            self.fields['contributor'].queryset = contributors
            self.fields['contributor'].empty_label = None
        else:
            self.fields['contributor'].queryset = Contributor.objects.none()

Jorran de Wit's avatar
Jorran de Wit committed
    def clean(self, *args, **kwargs):
        data = super().clean(*args, **kwargs)
        if not data.get('submission') and not data.get('publication'):
            self.add_error('submission', 'Either a Submission or Publication has to be filled out')
            self.add_error('publication', 'Either a Submission or Publication has to be filled out')

    def save(self, *args, **kwargs):
        if not hasattr(self.instance, 'created_by'):
            self.instance.created_by = self.request.user
        return super().save(*args, **kwargs)


class CitationNotificationProcessForm(AcceptRequestMixin, forms.ModelForm):
    class Meta:
        model = CitationNotification
        fields = ()

    def get_all_notifications(self):
        return self.instance.related_notifications().unprocessed()


class RegistrationInvitationAddCitationForm(AcceptRequestMixin, forms.ModelForm):
    cited_in_submissions = AutoCompleteSelectMultipleField('submissions_lookup', required=False)
    cited_in_publications = AutoCompleteSelectMultipleField('publication_lookup', required=False)

    class Meta:
        model = RegistrationInvitation
        fields = ()

    def save(self, *args, **kwargs):
        if kwargs.get('commit', True):
            updated = 0
            # Save the Submission notifications
            submissions = Submission.objects.filter(
                id__in=self.cleaned_data['cited_in_submissions'])
            for submission in submissions:
                __, _updated = CitationNotification.objects.get_or_create(
                    invitation=self.instance,
                    submission=submission,
                    defaults={'created_by': self.request.user})
                updated += 1 if _updated else 0

            # Save the Publication notifications
            publications = Publication.objects.filter(
                id__in=self.cleaned_data['cited_in_publications'])
            for publication in publications:
                __, _updated = CitationNotification.objects.get_or_create(
                    invitation=self.instance,
                    publication=publication,
                    defaults={'created_by': self.request.user})
                updated += 1 if _updated else 0
            if updated > 0:
                self.instance.status = constants.STATUS_SENT_AND_EDITED
                self.instance.save()
            messages.success(self.request, '{} Citation Notification(s) added.'.format(updated))
        return self.instance


class RegistrationInvitationMergeForm(AcceptRequestMixin, forms.ModelForm):
    """Merge RegistrationInvitations.

    This form will merge the instance with any other RegistrationInvitation selected
    into a single RegistrationInvitation.
    """

    invitation = forms.ModelChoiceField(queryset=RegistrationInvitation.objects.none(),
                                        label="Invitation to merge with")

    class Meta:
        model = RegistrationInvitation
        fields = ()

    def __init__(self, *args, **kwargs):
        """Update queryset according to the passed instance."""
        super().__init__(*args, **kwargs)
        self.fields['invitation'].queryset = RegistrationInvitation.objects.no_response().filter(
            last_name__icontains=self.instance.last_name).exclude(id=self.instance.id)

    def save(self, *args, **kwargs):
        """Merge the two RegistationInvitations into one."""
        if kwargs.get('commit', True):
            # Pick the right Invitation, with the most up-to-date invitation_key
            selected_invitation = self.cleaned_data['invitation']
            if not selected_invitation.date_sent_last:
                # Selected Invitation has never been sent yet.
                leading_invitation = self.instance
                deprecated_invitation = selected_invitation
            elif not self.instance.date_sent_last:
                # Instance has never been sent yet.
                leading_invitation = selected_invitation
                deprecated_invitation = self.instance
            elif selected_invitation.date_sent_last > self.instance.date_sent_last:
                # Lastest reminder: selected Invitation
                leading_invitation = selected_invitation
                deprecated_invitation = self.instance
            else:
                # Lastest reminder: instance
                leading_invitation = self.instance
                deprecated_invitation = selected_invitation

            # Move CitationNotification to the new leading Invitation
            deprecated_invitation.citation_notifications.update(invitation=leading_invitation)
            leading_invitation.times_sent += deprecated_invitation.times_sent   # Update counts
Jorran de Wit's avatar
Jorran de Wit committed
            leading_invitation.save()

            qs_contributor = deprecated_invitation.citation_notifications.filter(
                contributor__isnull=False).values_list('contributor', flat=True)
            if qs_contributor:
                if not leading_invitation.citation_notifications.filter(contributor__isnull=False):
                    # Contributor is already assigned in "old" RegistrationInvitation, copy it.
                    leading_invitation.citation_notifications.filter(contributor=qs_contributor[0])

            # Magic.
            deprecated_invitation.delete()
        return self.instance



class RegistrationInvitationForm(AcceptRequestMixin, forms.ModelForm):
    cited_in_submissions = AutoCompleteSelectMultipleField('submissions_lookup', required=False)
    cited_in_publications = AutoCompleteSelectMultipleField('publication_lookup', required=False)
Jorran de Wit's avatar
Jorran de Wit committed

    class Meta:
        model = RegistrationInvitation
        fields = (
Jorran de Wit's avatar
Jorran de Wit committed
            'title',
            'first_name',
            'last_name',
            'email',
            'message_style',
            'invitation_type',
            'personal_message')
        widgets = {
            'profile': forms.HiddenInput(),
        }
Jorran de Wit's avatar
Jorran de Wit committed

    def __init__(self, *args, **kwargs):
        # Find Submissions/Publications related to the invitation and fill the autocomplete fields
        initial = kwargs.get('initial', {})
        invitation = kwargs.get('instance', None)
        if invitation:
            submission_ids = invitation.citation_notifications.for_submissions().values_list(
                'submission_id', flat=True)
            publication_ids = invitation.citation_notifications.for_publications().values_list(
                'publication_id', flat=True)
            initial['cited_in_submissions'] = Submission.objects.filter(id__in=submission_ids)
            initial['cited_in_publications'] = Publication.objects.filter(id__in=publication_ids)
        kwargs['initial'] = initial
Jorran de Wit's avatar
Jorran de Wit committed
        super().__init__(*args, **kwargs)
        if not self.request.user.has_perm('scipost.can_manage_registration_invitations'):
            del self.fields['message_style']
            del self.fields['personal_message']
        if not self.request.user.has_perm('scipost.can_invite_fellows'):
            del self.fields['invitation_type']  # Only admins can invite fellows

    def clean_email(self):
        email = self.cleaned_data['email']
        if Contributor.objects.filter(user__email=email).exists():
            self.add_error('email', 'This email address is already associated to a Contributor')
        elif RegistrationInvitation.objects.declined().filter(email=email).exists():
            self.add_error('email', 'This person has already declined an earlier invitation')

        return email

Jorran de Wit's avatar
Jorran de Wit committed
    def save(self, *args, **kwargs):
        if not hasattr(self.instance, 'created_by'):
            self.instance.created_by = self.request.user

        # Try to associate an existing Profile to invitation:
        profile = Profile.objects.get_unique_from_email_or_None(
            email=self.cleaned_data['email'])
        self.instance.profile = profile

        invitation = super().save(*args, **kwargs)
        if kwargs.get('commit', True):
            # Save the Submission notifications
            submissions = Submission.objects.filter(
                id__in=self.cleaned_data['cited_in_submissions'])
            for submission in submissions:
                CitationNotification.objects.get_or_create(
                    invitation=self.instance,
                    submission=submission,
                    defaults={
                        'created_by': self.instance.created_by
                    })

            # Save the Publication notifications
            publications = Publication.objects.filter(
                id__in=self.cleaned_data['cited_in_publications'])
            for publication in publications:
                CitationNotification.objects.get_or_create(
                    invitation=self.instance,
                    publication=publication,
                    defaults={
                        'created_by': self.instance.created_by
                    })
        return invitation


class RegistrationInvitationReminderForm(AcceptRequestMixin, forms.ModelForm):
    class Meta:
        model = RegistrationInvitation
        fields = ()

    def save(self, *args, **kwargs):
        if kwargs.get('commit', True):
            self.instance.mail_sent()
Jorran de Wit's avatar
Jorran de Wit committed
        return super().save(*args, **kwargs)


class RegistrationInvitationMapToContributorForm(AcceptRequestMixin, forms.ModelForm):
    contributor = None

Jorran de Wit's avatar
Jorran de Wit committed
    class Meta:
        model = RegistrationInvitation
        fields = ()

    def clean(self, *args, **kwargs):
        try:
            self.contributor = Contributor.objects.get(
                id=self.request.resolver_match.kwargs['contributor_id'])
        except Contributor.DoesNotExist:
            self.add_error(None, 'Contributor does not exist.')
        return {}

    def get_contributor(self):
        if not self.contributor:
            self.clean()
        return self.contributor
Jorran de Wit's avatar
Jorran de Wit committed

    def save(self, *args, **kwargs):
        if kwargs.get('commit', True):
            self.instance.citation_notifications.update(contributor=self.contributor)
            self.instance.delete()
        return self.instance


class RegistrationInvitationMarkForm(AcceptRequestMixin, forms.ModelForm):
    class Meta:
        model = RegistrationInvitation
        fields = ()

    def save(self, *args, **kwargs):
        if kwargs.get('commit', True):
            self.instance.mail_sent()
        return self.instance