"""ObjectManagers for the Django Models used in the Mahjong-Ranking.""" from datetime import date from . import LOGGER from django.db import models from django.conf import settings class HanchanManager(models.Manager): """ The ObjectManager for models.Hanchan QuerySets. It adds many specific filters that makes many queries much easier. """ use_for_related_fields = True def confirmed(self, user=None, since=None, until=None, **filter_args): """ Return all valid and confirmed Hanchans. :param user: Only return Hanchans where this user participated. :param since: only return Hanchans played since the given datetime :param until: only return Hanchans played until the given datetime :param filter_args: To add specific arguments to the Django filter. :return: QuerySet Object """ if user: return self.user_hanchans(user, confirmed=True, until=until, **filter_args) hanchans = self.filter(confirmed=True, **filter_args) if since: hanchans = hanchans.filter(start__gt=since) if until: hanchans = hanchans.filter(start__lte=until) return hanchans def dan_hanchans(self, user, since=None, **filter_args): """ Return all Hanchans where a specific user has participated and had gain dan points and make his gamestats availabale. :param user: Only return Hanchans where this user participated. :param since: only return Hanchans played since the given datetime :param filter_args: To add specific arguments to the Django filter. :return: QuerySet Object """ queryset = self.filter( models.Q(player1=user, player1_dan_points__isnull=False) | models.Q(player2=user, player2_dan_points__isnull=False) | models.Q(player3=user, player3_dan_points__isnull=False) | models.Q(player4=user, player4_dan_points__isnull=False) ).filter(confirmed=True, **filter_args) if since: queryset = queryset.filter(start__gt=since) queryset = queryset.select_related().order_by('-start') [hanchan.get_playerdata(user) for hanchan in queryset] return queryset def kyu_hanchans(self, user, since=None, **filter_args): """ Return all Hanchans where a specific user has participated and had gain kyū points and make his gamestats availabale. :param user: Only return Hanchans where this user participated. :param since: only return Hanchans played since the given datetime :param filter_args: To add specific arguments to the Django filter. :return: QuerySet Object """ queryset = self.filter( models.Q(player1=user, player1_kyu_points__isnull=False) | models.Q(player2=user, player2_kyu_points__isnull=False) | models.Q(player3=user, player3_kyu_points__isnull=False) | models.Q(player4=user, player4_kyu_points__isnull=False) ).filter(confirmed=True, **filter_args) if since: queryset = queryset.filter(start__gt=since) queryset = queryset.select_related().order_by('-start') [hanchan.get_playerdata(user) for hanchan in queryset] return queryset def season_hanchans(self, user: object = None, season: int = None, until: date = None): """Return all Hanchans that belong to a given or the current season. :param user: Only return Hanchans where this user participated. :param season: the year of the wanted season, current year if None. :param until: only return hanchans played until the given date. :return: QuerySet Object """ try: season = season or until.year except AttributeError: season = date.today().year return self.confirmed(user=user, season=season, until=until) def user_hanchans(self, user, since=None, until=None, **filter_args): """Return all Hanchans where a specific user has participated. :param user: Return Hanchans where this user participated. :param since: only return Hanchans played since the given datetime :param until: only return hanchans played until the given date. :param filter_args: To add specific arguments to the Django filter. :return: a QuerySet Object """ queryset = self.filter( models.Q(player1=user) | models.Q(player2=user) | models.Q(player3=user) | models.Q(player4=user) ) queryset = queryset.filter(**filter_args) if since: queryset = queryset.filter(start__gte=since) if until: queryset = queryset.filter(start__lte=until) queryset = queryset.select_related().order_by('-start') [hanchan.get_playerdata(user) for hanchan in queryset] return queryset def unconfirmed(self, user=None, **filter_args): """ Return all Hanchans that have been set to unconfirmed. :param user: Only return Hanchans where this user participated. :param filter_args: To add specific arguments to the Django filter. :return: QuerySet Object """ if user: return self.user_hanchans(user, confirmed=False, **filter_args) else: return self.filter(confirmed=False, **filter_args) class SeasonRankingManager(models.Manager): """ The ObjectManager for models.SeasonRanking QuerySets to handle the Ladderrankings. It adds many specific filters that makes many queries much easier. """ def current_rankings(self): """ Returns the Rankings for the current year/season. :return: models.QuerySet """ current_season = date.today().year return self.filter(season=current_season) @property def season_list(self): """ Return a list of all availables years/seasons. It's useful for select boxes and choice fields. :return: list() """ values_list = self.model.objects.values_list('season', flat=True) return values_list.order_by('season').distinct() def json_data(self, season=None): """ Get all Rankings for a given Season and return them as a list of dict objects, suitable for JSON exports and other processings. :param season: Season that should be exported, current season if empty :return: a list() of dict() objects suiteable for JSON export. """ season = season or date.today().year json_data = list() values = self.filter(season=season, placement__isnull=False) values = values.values('placement', 'user_id', 'user__username', 'user__first_name', 'user__last_name', 'avg_placement', 'avg_score', 'hanchan_count', 'good_hanchans', 'won_hanchans') for user in values: json_data.append({ 'placement': user['placement'], 'user_id': user['user_id'], 'username': user['user__username'], 'first_name': user['user__first_name'], 'last_name': user['user__last_name'], 'avg_placement': user['avg_placement'], 'avg_score': user['avg_score'], 'hanchan_count': user['hanchan_count'], 'good_hanchans': user['good_hanchans'], 'won_hanchans': user['won_hanchans'] }) return json_data def update(self, season=None, until=None, force_recalc=False): try: season = season or until.year except AttributeError: season = date.today().year if until or force_recalc: for ranking in self.filter(season=season): ranking.recalculate(until=until) for placement, ranking in enumerate(self.season_rankings(season), start=1): ranking.placement = placement ranking.save(force_update=True, update_fields=['placement']) def season_rankings(self, season=None): season = season or date.today().year rankings = self.filter( season=season, hanchan_count__gt=settings.MIN_HANCHANS_FOR_LADDER) return rankings.order_by('avg_placement', '-avg_score') class KyuDanRankingManager(models.Manager): def json_data(self): """ Get all Rankings for a given Season and return them as a list of dict objects, suitable for JSON exports and other processing. :return: a list() of dict() objects suitable for JSON export. """ json_data = list() values = self.all() values = values.values('user_id', 'user__username', 'user__first_name', 'user__last_name', 'dan', 'dan_points', 'kyu', 'kyu_points', 'hanchan_count', 'won_hanchans', 'good_hanchans', 'last_hanchan_date') for user in values: if user['hanchan_count'] == 0: continue elif user['dan']: rank = '{}. Dan'.format(user['dan']) points = user['dan_points'] else: rank = '{}. Kyū'.format(user['kyu']) points = user['kyu_points'] json_data.append({ 'user_id': user['user_id'], 'username': user['user__username'], 'full_name': " ".join( [user['user__last_name'], user['user__first_name']]), 'rank': rank, 'points': points, 'hanchan_count': user['hanchan_count'], 'good_hanchans': user['good_hanchans'], 'won_hanchans': user['won_hanchans'], 'last_hanchan_date': user['last_hanchan_date'] }) return json_data def update(self, since=None, until=None, force_recalc=False): old_attr = {'dan': None, 'dan_points': None, 'kyu': None, 'kyu_points': None, 'won_hanchans': None, 'good_hanchans': None, 'hanchan_count': None} for ranking in self.all(): old_attr = {attr: getattr(ranking, attr) for attr in old_attr.keys()} ranking.calculate(since=since, until=until, force_recalc=force_recalc) for attr, old_value in old_attr.items(): if getattr(ranking, attr) != old_value: LOGGER.warning( "%(user)s recalc shows differences in %(attr)s! old: %(old)d, new: %(new)d", {'user': ranking.user, 'attr': attr, 'old': old_value or 0, 'new': getattr(ranking, attr) or 0} )