commitbb5081a78bAuthor: Xeniac <xeniac@posteo.at> Date: Thu Nov 23 22:02:40 2017 +0100 Added a setting where the exported excel files should be stored. Added a option to send the exported excel as mail attachment. commit854fd38740Author: Xeniac <xeniac@posteo.at> Date: Thu Nov 23 22:01:38 2017 +0100 Fixed: enumerate the Seasonrankings starting with 1 Fixed: Logging error when a value changed from/to None commit6de1ecb102Author: Christian Berg <xeniac@posteo.at> Date: Thu Nov 23 14:15:36 2017 +0100 add a latest method to query the latest x events commitbf12060c3bAuthor: Christian Berg <xeniac@posteo.at> Date: Thu Nov 23 14:15:12 2017 +0100 add a latest method to query the latest x events commit5ad628f33aAuthor: Christian Berg <xeniac@posteo.at> Date: Mon Nov 20 07:47:47 2017 +0100 Changed PlayerDanScore to only list non-legacy hanchans commit36272c60d6Author: Christian Berg <xeniac@posteo.at> Date: Mon Nov 20 07:42:44 2017 +0100 fixed import of MIN_HANCHANS_FOR_LADDER that moved to settings commitc428f6ed1fAuthor: Christian Berg <xeniac@posteo.at> Date: Mon Nov 20 07:41:04 2017 +0100 Updated docstrings for new since and until kwargs commit9276e97c36Author: Christian Berg <xeniac@posteo.at> Date: Mon Nov 20 07:33:54 2017 +0100 added a since parameter to the hanchan queries to return only hanchans since the give date and time commitfd244f10e8Author: Christian Berg <xeniac@posteo.at> Date: Sun Nov 19 16:55:10 2017 +0100 new command: resetdanranking YYYY-MM-DD, sets every dan player to 1st dan with zero dan_points at the given date. commit0a45cf1fd8Author: Christian Berg <xeniac@posteo.at> Date: Sun Nov 19 16:14:59 2017 +0100 added new fields to KyuDanRanking that allow to pick up the calculation from the last state of the KyuDanRanking. last_hanchan_date: it contains the start of the latest hanchan content for this players ranking. wins_in_row: to save the currents wins in a row Added option to calcuclate rankings until a given datetime.
254 lines
11 KiB
Python
254 lines
11 KiB
Python
"""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=None, season=None, until=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.
|
|
: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 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 processings.
|
|
|
|
:param season: Season that should be exported, current season if empty
|
|
:return: a list() of dict() objects suiteable 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}
|
|
)
|