Source code for invitations.forms

__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
__license__ = "AGPL v3"


from django import forms
from django.contrib import messages
from django.db.models import Q

from journals.models import Publication
from scipost.models import Contributor
from submissions.models import Submission

from . import constants
from .models import RegistrationInvitation, CitationNotification

from profiles.models import Profile
from submissions.models import Submission

from dal import autocomplete


[docs]class AcceptRequestMixin: def __init__(self, *args, **kwargs): self.request = kwargs.pop("request") super().__init__(*args, **kwargs)
[docs]class RegistrationInvitationFilterForm(forms.Form): term = forms.CharField( help_text="You may search on arXiv identifier, DOI or last name." )
[docs] 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 ) | Q(citation_notifications__publication__doi_label__icontains=term) )
[docs]class SuggestionSearchForm(forms.Form): last_name = forms.CharField()
[docs] 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()
[docs]class CitationNotificationForm(AcceptRequestMixin, forms.ModelForm): submission = forms.ModelChoiceField( queryset=Submission.objects.all(), widget=autocomplete.ModelSelect2( url="/submissions/submission-autocomplete", attrs={"data-html": True} ), required=False, ) publication = forms.ModelChoiceField( queryset=Publication.objects.all(), widget=autocomplete.ModelSelect2( url="/journals/publication-autocomplete", attrs={"data-html": True} ), required=False, )
[docs] 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()
[docs] 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" )
[docs] def save(self, *args, **kwargs): if not hasattr(self.instance, "created_by"): self.instance.created_by = self.request.user return super().save(*args, **kwargs)
[docs]class CitationNotificationProcessForm(AcceptRequestMixin, forms.ModelForm):
[docs] class Meta: model = CitationNotification fields = ()
[docs] def get_all_notifications(self): return self.instance.related_notifications().unprocessed()
[docs]class RegistrationInvitationAddCitationForm(AcceptRequestMixin, forms.ModelForm): cited_in_submissions = forms.ModelMultipleChoiceField( queryset=Submission.objects.all(), widget=autocomplete.ModelSelect2Multiple( url="/submissions/submission-autocomplete", attrs={"data-html": True} ), required=False, ) cited_in_publications = forms.ModelMultipleChoiceField( queryset=Publication.objects.all(), widget=autocomplete.ModelSelect2Multiple( url="/journals/publication-autocomplete", attrs={"data-html": True} ), required=False, )
[docs] class Meta: model = RegistrationInvitation fields = ()
[docs] 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
[docs]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" )
[docs] 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) )
[docs] 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 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
[docs]class RegistrationInvitationForm(AcceptRequestMixin, forms.ModelForm): cited_in_submissions = forms.ModelMultipleChoiceField( queryset=Submission.objects.all(), widget=autocomplete.ModelSelect2Multiple( url="/submissions/submission-autocomplete", attrs={"data-html": True} ), required=False, ) cited_in_publications = forms.ModelMultipleChoiceField( queryset=Publication.objects.all(), widget=autocomplete.ModelSelect2Multiple( url="/journals/publication-autocomplete", attrs={"data-html": True} ), required=False, )
[docs] class Meta: model = RegistrationInvitation fields = ( "profile", "title", "first_name", "last_name", "email", "message_style", "invitation_type", "personal_message", ) widgets = { "profile": forms.HiddenInput(), }
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 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
[docs] 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
[docs] def save(self, *args, **kwargs): if not hasattr(self.instance, "created_by"): self.instance.created_by = self.request.user if not hasattr(self.instance, "invited_by"): self.instance.invited_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
[docs]class RegistrationInvitationReminderForm(AcceptRequestMixin, forms.ModelForm):
[docs] class Meta: model = RegistrationInvitation fields = ()
[docs] def save(self, *args, **kwargs): if kwargs.get("commit", True): self.instance.mail_sent() return super().save(*args, **kwargs)
[docs]class RegistrationInvitationMapToContributorForm(AcceptRequestMixin, forms.ModelForm): contributor = None
[docs] class Meta: model = RegistrationInvitation fields = ()
[docs] 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 {}
[docs] def get_contributor(self): if not self.contributor: self.clean() return self.contributor
[docs] def save(self, *args, **kwargs): if kwargs.get("commit", True): self.instance.citation_notifications.update(contributor=self.contributor) self.instance.delete() return self.instance
[docs]class RegistrationInvitationMarkForm(AcceptRequestMixin, forms.ModelForm):
[docs] class Meta: model = RegistrationInvitation fields = ()
[docs] def save(self, *args, **kwargs): if kwargs.get("commit", True): self.instance.mail_sent() return self.instance