Source code for nkdsu.apps.vote.voter

from __future__ import annotations

import datetime
from typing import Iterable, Optional, Protocol, TYPE_CHECKING, _ProtocolMeta

from django.db.models import BooleanField, CharField, QuerySet
from django.db.models.base import ModelBase
from django.utils import timezone
from .utils import memoize

if TYPE_CHECKING:
    from .models import UserBadge, Vote, Show, Track, Profile, TwitterUser, UserWebsite

    # to check to see that their VoterProtocol implementations are complete:
    Profile()
    TwitterUser()


[docs] class ModelVoterMeta(_ProtocolMeta, ModelBase): pass
[docs] class Voter(Protocol, metaclass=ModelVoterMeta): name: str | CharField pronouns: str | CharField = '' pk: int is_abuser: bool | BooleanField is_patron: bool | BooleanField
[docs] def _twitter_user_and_profile(
self, ) -> tuple[Optional[TwitterUser], Optional[Profile]]: ... @property def username(self) -> str: ... @property def voter_id(self) -> tuple[Optional[int], Optional[int]]: """ A unique identifier that will be the same for TwitterUser and Profile instances that represent the same accounts. """ twu, pr = self._twitter_user_and_profile() return (None if twu is None else twu.pk, None if pr is None else pr.pk) @property @memoize def badges(self) -> QuerySet[UserBadge]: from .models import UserBadge return UserBadge.for_voter(self)
[docs] def unordered_votes(self) -> QuerySet[Vote]: ...
[docs] def get_toggle_abuser_url(self) -> str: ...
[docs] def votes(self) -> QuerySet[Vote]: return self.unordered_votes().order_by('-date').prefetch_related('tracks')
[docs] @memoize def votes_with_liberal_preselection(self) -> QuerySet[Vote]: return self.votes().prefetch_related( 'show', 'show__play_set', 'show__play_set__track', # doesn't actually appear to work :< )
[docs] @memoize def votes_for(self, show: Show) -> QuerySet[Vote]: return self.votes().filter(show=show)
[docs] @memoize def tracks_voted_for_for(self, show: Show) -> list[Track]: tracks = [] track_pk_set = set() for vote in self.votes_for(show): for track in vote.tracks.all(): if track.pk not in track_pk_set: track_pk_set.add(track.pk) tracks.append(track) return tracks
[docs] @memoize def is_new(self) -> bool: from .models import Show return not self.votes().exclude(show=Show.current()).exists()
[docs] @memoize def is_placated(self) -> bool: from .models import Show return ( self.votes() .filter( tracks__play__show=Show.current(), show=Show.current(), ) .exists() )
[docs] @memoize def is_shortlisted(self) -> bool: from .models import Show return ( self.votes() .filter( tracks__shortlist__show=Show.current(), show=Show.current(), ) .exists() )
[docs] def has_max_websites(self) -> bool: return True
[docs] def get_websites(self) -> Iterable[UserWebsite]: return []
[docs] def _batting_average( self, cutoff: Optional[datetime.datetime] = None, minimum_weight: float = 1, ) -> Optional[float]: from .models import Show def ba( pk: int, current_show_pk: int, cutoff: Optional[datetime.datetime] ) -> tuple[float, float]: score: float = 0 weight: float = 0 for vote in self.votes().filter(date__gt=cutoff).prefetch_related('tracks'): success = vote.success() if success is not None: score += success * vote.weight() weight += vote.weight() return (score, weight) score, weight = ba(self.pk, Show.current().pk, cutoff) if weight >= minimum_weight: return score / weight else: # there were no worthwhile votes return None return score
[docs] @memoize def batting_average(self, minimum_weight: float = 1) -> Optional[float]: """ Return a user's batting average for the past six months. """ from .models import Show return self._batting_average( cutoff=Show.at(timezone.now() - datetime.timedelta(days=31 * 6)).end, minimum_weight=minimum_weight, )
[docs] def _streak(self, ls=[]) -> int: from .models import Show show = Show.current().prev() streak = 0 while True: if show is None: return streak elif not show.voting_allowed: show = show.prev() elif self.votes().filter(show=show).exists(): streak += 1 show = show.prev() else: break return streak
[docs] @memoize def streak(self) -> int: from .models import Show def streak(pk, current_show): return self._streak() return streak(self.pk, Show.current())
[docs] def all_time_batting_average(self, minimum_weight: float = 1) -> Optional[float]: return self._batting_average(minimum_weight=minimum_weight)