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 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] 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"
)
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] 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] 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 save(self, *args, **kwargs):
if kwargs.get("commit", True):
self.instance.citation_notifications.update(contributor=self.contributor)
self.instance.delete()
return self.instance