__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
__license__ = "AGPL v3"
from django.db import models
from django.db.models import Count, Q
from django.db.models.functions import Concat, Lower
from django.utils import timezone
from .constants import (
NORMAL_CONTRIBUTOR,
NEWLY_REGISTERED,
DOUBLE_ACCOUNT,
AUTHORSHIP_CLAIM_PENDING,
)
[docs]class ContributorQuerySet(models.QuerySet):
"""Custom defined filters for the Contributor model."""
[docs] def active(self):
"""Return all validated and vetted Contributors."""
return self.filter(user__is_active=True, status=NORMAL_CONTRIBUTOR)
[docs] def nonduplicates(self):
"""
Filter out duplicate Contributors.
"""
return self.exclude(duplicate_of__isnull=False)
[docs] def available(self):
"""Filter out the Contributors that have active unavailability periods."""
today = timezone.now().date()
return self.exclude(
unavailability_periods__start__lte=today,
unavailability_periods__end__gte=today,
)
[docs] def awaiting_validation(self):
"""Filter Contributors that have not been validated by the user."""
return self.filter(user__is_active=False, status=NEWLY_REGISTERED)
[docs] def awaiting_vetting(self):
"""Filter Contributors that have not been vetted through."""
return self.filter(user__is_active=True, status=NEWLY_REGISTERED).exclude(
status=DOUBLE_ACCOUNT
)
[docs] def with_duplicate_names(self):
"""
Returns only potential duplicate Contributors (as identified by first and
last names).
Admins and superusers are explicitly excluded.
"""
contribs = (
self.exclude(status=DOUBLE_ACCOUNT)
.exclude(user__is_superuser=True)
.exclude(user__is_staff=True)
.annotate(full_name=Concat("user__last_name", "user__first_name"))
)
duplicates = (
contribs.values("full_name")
.annotate(nr_count=Count("full_name"))
.filter(nr_count__gt=1)
.values_list("full_name", flat=True)
)
return contribs.filter(full_name__in=duplicates).order_by(
"user__last_name", "user__first_name", "-id"
)
[docs] def with_duplicate_email(self):
"""
Return Contributors having duplicate emails.
"""
qs = (
self.exclude(status=DOUBLE_ACCOUNT)
.exclude(user__is_superuser=True)
.exclude(user__is_staff=True)
.annotate(lower_email=Lower("user__email"))
)
duplicates = (
qs.values("lower_email")
.annotate(Count("id"))
.filter(id__count__gt=1)
.values_list("lower_email", flat=True)
)
return qs.filter(lower_email__in=duplicates)
[docs] def specialties_overlap(self, specialties_slug_list):
"""
Returns all Contributors whose specialties overlap with those specified in the slug list.
This method is also separately implemented for Profile and PotentialFellowship objects.
"""
return self.filter(profile__specialties__slug__in=specialties_slug_list)
[docs]class UnavailabilityPeriodManager(models.Manager):
[docs] def today(self):
today = timezone.now().date()
return self.filter(start__lte=today, end__gte=today)
[docs] def future(self):
today = timezone.now().date()
return self.filter(end__gte=today)
[docs]class AuthorshipClaimQuerySet(models.QuerySet):
[docs] def awaiting_vetting(self):
return self.filter(status=AUTHORSHIP_CLAIM_PENDING)