Source code for literature.managers

from datetime import date

from dateutil.relativedelta import relativedelta
from django.db.models import Count, Max, Q
from django.db.models.query import QuerySet
from django.utils.module_loading import import_string

from literature.conf import settings

from .exceptions import AdaptorError, RemoteAdaptorError


[docs]class AuthorQuerySet(QuerySet):
[docs] def with_work_counts(self): """Convenience filter for retrieving authors with annotated counts of works published as either lead or supporting author. These count attributes can be accessed on the queryset as `as_lead` or `as_supporting`. Further filtering/manipulation is possible on both fields afterwards. Example: Get authors that have published at least five works as lead author. >>> Author.objects.with_work_counts().filter(as_lead__gte=5) Get authors that have published only once but have been a supporting author on at least three. >>> Author.objects.with_work_counts().filter(as_lead=1, as_supporting__gte=3) """ return self.prefetch_related("literature").annotate( as_lead=Count("position", filter=Q(position__position=1)), as_supporting=Count("position", filter=Q(position__position__gt=1)), )
[docs] def as_lead(self): """Convenience filter for retrieving only authors that are listed as the lead author on a publication.""" return ( self.prefetch_related("literature") .annotate(as_lead=Count("position", filter=Q(position__position=1))) .filter(as_lead__gt=0) )
[docs] def with_last_published(self): return self.prefetch_related("literature").annotate(last_published=Max("literature__published"))
[docs] def is_active(self): cutoff = date.today() - relativedelta(years=settings.LITERATURE_INACTIVE_AFTER) return self.with_last_published().filter(last_published__gt=cutoff)
AuthorManager = AuthorQuerySet.as_manager
[docs]class LiteratureQuerySet(QuerySet):
[docs] def resolve_doi(self, doi, adaptor=None): """Attempts to fetch a doi from a remote data source. Loops through the available remote adaptors until the doi is succesfully resolved. If the doi registrar (source) is known, you may supply the appropriate adaptor to prevent searching other registries. Args: doi (_type_): _description_ adaptor (_type_, optional): _description_. Defaults to None. Returns: _type_: _description_ """ adaptors = [adaptor] if adaptor else [import_string(ac) for ac in settings.LITERATURE_ADAPTORS] for adaptor in adaptors: try: return adaptor(doi=doi).get_data() except AdaptorError: # raised if the adaptor is not remote pass except RemoteAdaptorError: # raised if the adaptor returns a 404 pass
[docs] def get_or_resolve(self, doi): """Loops through all available remote adaptors and attempts to resolve the given DOI until succesful. Args: doi (_type_): _description_ """ try: return self.get(doi=doi), False except self.model.DoesNotExist: for adaptor_class in settings.LITERATURE_ADAPTORS: if adaptor_class.is_remote: obj, created = self.resolve_doi_for_adaptor(doi, adaptor_class)
# def _resolve_doi(self, doi, adaptor): # """ # For a given adaptor, attempt to resolve a doi by querying the remote data source. # """ # # if not adaptor.is_remote: # # raise AdaptorError(_(f"{adaptor} cannot resolve remote sources.")) # # initialize the adaptor with the given doi in an attempt to # # fetch data from the adaptor's API # return adaptor(doi=doi).get_data() # async def aresolve_doi_for_adaptor(self, doi): # return await sync_to_async(self.resolve_doi_for_adaptor)(doi) LiteratureManager = LiteratureQuerySet.as_manager