neue Verzeichnissstruktur
This commit is contained in:
@@ -1,602 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from datetime import date, timedelta
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.cache import cache
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import models
|
||||
from django.db.models.aggregates import Sum
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext as _
|
||||
from events.models import Event
|
||||
|
||||
from . import KYU_RANKS, DAN_RANKS, DAN_RANKS_DICT, MIN_HANCHANS_FOR_LADDER
|
||||
from . import logger, set_dirty
|
||||
|
||||
kyu_dan_rankings = set()
|
||||
ladder_rankings = set()
|
||||
ladder_seasons = set()
|
||||
|
||||
|
||||
class EventRanking(models.Model):
|
||||
'''
|
||||
Event Rankings funktionieren genauso wie Season Rankings.
|
||||
Sie beschränken sich aber auf einen Event und werden nur dann angestossen,
|
||||
wenn der Event als Turnier markiert wurde.
|
||||
'''
|
||||
user = models.ForeignKey(User)
|
||||
event = models.ForeignKey(Event)
|
||||
placement = models.PositiveIntegerField(blank=True, null=True)
|
||||
avg_placement = models.FloatField(default=4)
|
||||
avg_score = models.FloatField(default=0)
|
||||
hanchan_count = models.PositiveIntegerField(default=0)
|
||||
good_hanchans = models.PositiveIntegerField(default=0)
|
||||
won_hanchans = models.PositiveIntegerField(default=0)
|
||||
dirty = models.BooleanField(default=True, editable=False)
|
||||
|
||||
class Meta(object):
|
||||
ordering = ('placement', 'avg_placement', '-avg_score',)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('event-ranking', args=[self.tourney_id])
|
||||
|
||||
def recalculate(self):
|
||||
'''
|
||||
Berechnet die durschnittliche Platzierung und Punkte, u.v.m. neu.
|
||||
|
||||
Diese Daten werden benötigt um die Platzierung zu erstellen. Sie
|
||||
können zwar sehr leicht errechnet werden, es macht trotzdem Sinn
|
||||
sie zwischen zu speichern.
|
||||
|
||||
Das Eigenschaft dirty ist ein altes Überbleibsel, um das Objekt
|
||||
zur neuberrechnung zu markieren. Mittlerweile wird ein lokaler
|
||||
Cache dafür verwendet, das ist schneller.
|
||||
'''
|
||||
logger.info(u'Recalculate EventRanking for Player %s in %s', self.user, self.event.name) # @IgnorePep8
|
||||
event_hanchans = Player.objects.valid_hanchans(user=self.user_id, event=self.event_id) # @IgnorePep8
|
||||
aggregator = event_hanchans.aggregate(
|
||||
models.Avg('placement'),
|
||||
models.Avg('score'),
|
||||
models.Count('pk'))
|
||||
self.avg_placement = aggregator['placement__avg']
|
||||
self.avg_score = aggregator['score__avg']
|
||||
self.hanchan_count = aggregator['pk__count']
|
||||
self.good_hanchans = event_hanchans.filter(placement__lt=3).count()
|
||||
self.won_hanchans = event_hanchans.filter(placement=1).count()
|
||||
self.dirty = False
|
||||
if self.hanchan_count <= 0:
|
||||
self.delete()
|
||||
else:
|
||||
self.save()
|
||||
|
||||
|
||||
class Hanchan(models.Model):
|
||||
'''
|
||||
Ein komplette Runde Mahjong, die aus genau 4 Spielern bestehen muss.
|
||||
Es werden aber noch andere Tests durchgeführt, ob sie gültig ist.
|
||||
Außerdem gehört jede Hanchan zu einer Veranstaltung.
|
||||
'''
|
||||
comment = models.TextField(_('Comment'), blank=True)
|
||||
confirmed = models.BooleanField(_('Has been Confirmed'), default=True, help_text=_('Only valid and confirmed Hanchans will be counted in the rating.')) # @IgnorePep8
|
||||
event = models.ForeignKey(Event)
|
||||
player_names = models.CharField(max_length=127, editable=False)
|
||||
players = models.ManyToManyField(User, through='Player',verbose_name=_('Players')) # @IgnorePep8
|
||||
season = models.ForeignKey('LadderSeason', blank=True, null=True, editable=False) # @IgnorePep8
|
||||
start = models.DateTimeField(_('Start'), help_text=_('This is crucial to get the right Hanchans that scores')) # @IgnorePep8
|
||||
valid = models.BooleanField(_('Is Valid'), default=False)
|
||||
|
||||
class Meta(object):
|
||||
ordering = ('-start',)
|
||||
verbose_name = _(u'Hanchan')
|
||||
verbose_name_plural = _(u'Hanchans')
|
||||
|
||||
def __str__(self):
|
||||
return "Hanchan am {0:%d.%m.%Y} um {0:%H:%M} ({1})".format(self.start, self.player_names)
|
||||
|
||||
def check_validity(self):
|
||||
'''
|
||||
Prüft ob die Hanchan gültig ist.
|
||||
|
||||
4 Spieler müssen genau 100.000 Punkte erreichen, mehr sind nur erlaubt
|
||||
wenn midestens ein Spieler ins Minus (auf 0) geraten ist. Ansonsten
|
||||
wird die Hanchan als ungültig markiert aber trotzdem abgespeichert,
|
||||
außerdem wird die Begründung zurück gegeben, was nicht gestimmt hat.
|
||||
'''
|
||||
logger.debug("Hanchan wird geprüft ob er valide ist...")
|
||||
if not self.pk:
|
||||
self.valid = False
|
||||
return
|
||||
elif self.player_set.distinct().count() != 4:
|
||||
self.valid = False
|
||||
return _('For a Hanchan exactly 4 players are needed.')
|
||||
|
||||
score_sum = self.player_set.aggregate(Sum('score'))['score__sum']
|
||||
if score_sum == 100000:
|
||||
self.valid = True
|
||||
return '4 Spieler, 100.000 Endpunktestand, die Hanchan ist \
|
||||
korrekt!'
|
||||
elif score_sum > 100000 and self.player_set.filter(score=0):
|
||||
self.valid = True
|
||||
return 'Endpunktestand über 100.000, aber jemand ist auf 0 \
|
||||
gefallen. Die Hanchan stimmt.'
|
||||
elif score_sum < 100000:
|
||||
self.valid = False
|
||||
return 'Endpunktestand weniger als 100.000 Punkte.'
|
||||
elif score_sum > 100000 and not self.player_set.filter(score=0):
|
||||
self.valid = False
|
||||
return 'Endpunktestand über 100.000, aber niemand ist auf 0 \
|
||||
gefallen.'
|
||||
else:
|
||||
self.valid = False
|
||||
return 'Wir wissen nicht warum, aber das kann nicht passen...'
|
||||
|
||||
def clean(self):
|
||||
'''
|
||||
Prüft ob wichtige Vorrausetzungen gegeben sind und aktualisiert ein
|
||||
paar Zwischenspeicher, bevor gespeichert wird.
|
||||
|
||||
Die Hanchan muss 4 (unterschiedliche) Spieler haben, muss in der
|
||||
Veranstaltungzeit liegen, und darf nicht in der Zukunft liegen.
|
||||
Ansonsten wird ein ValidationError ausgegeben und die Hanchan nicht
|
||||
gespeichert.
|
||||
|
||||
Die Gültigkeit wird geprüft und die Sasion in der die Hanchan liegt
|
||||
wird aktualisert.
|
||||
'''
|
||||
logger.debug("Hanchan clean() wurde getriggert!")
|
||||
|
||||
# if self.pk and self.player_set.distinct().count() != 4:
|
||||
# raise ValidationError(
|
||||
# _('For a Hanchan exactly 4 players are needed.'))
|
||||
if self.start and self.start > timezone.now():
|
||||
raise ValidationError(_("It's not allowed to enter future games."))
|
||||
elif not (self.event.start <= self.start <= self.event.end):
|
||||
raise ValidationError(_("Only games during the event are allowed"))
|
||||
return self
|
||||
|
||||
def compute_player_placements(self):
|
||||
u'''
|
||||
Bestimmt die Platzierung eines der Spieler einer Hanchan und speichert
|
||||
diese beim jeweiligen Spieler ab.
|
||||
'''
|
||||
logger.debug("Berechne die Platzierungen neu...")
|
||||
attending_players = self.player_set.select_related('hanchan', 'user')
|
||||
attending_players = attending_players.order_by('-score')
|
||||
other_player_placement = 0
|
||||
other_player_score = 0
|
||||
placement = 1
|
||||
player_list = []
|
||||
logger.info("Compute player pacements for Hanchan Nr. %d", self.pk)
|
||||
for player in attending_players:
|
||||
player_list.append(player.user.username)
|
||||
if player.score <= 0:
|
||||
player.placement = 4
|
||||
elif player.score == other_player_score:
|
||||
player.placement = other_player_placement
|
||||
else:
|
||||
player.placement = placement
|
||||
placement += 1
|
||||
other_player_placement = player.placement
|
||||
other_player_score = player.score
|
||||
player.save(season_id=self.season_id, mark_dirty=True)
|
||||
|
||||
def get_absolute_url(self):
|
||||
'''
|
||||
URL zur Hanchanliste des Events wo diese Hanchan gelistet wurde.
|
||||
'''
|
||||
url = reverse('event-hanchan-list', kwargs={'event': self.event.pk})
|
||||
return u'%(url)s#%(pk)d' % {'url': url, 'pk': self.pk}
|
||||
|
||||
def save(self, **kwargs):
|
||||
logger.debug("Hanchan save() wurde getriggert!")
|
||||
|
||||
self.season = self.season or LadderSeason.objects.get_by_date(self.start)
|
||||
if self.pk:
|
||||
self.check_validity()
|
||||
self.compute_player_placements()
|
||||
self.player_names = ', '.join(self.player_set.values_list(
|
||||
'user__username', flat=True).order_by('-score'))
|
||||
return models.Model.save(self, **kwargs)
|
||||
|
||||
|
||||
class KyuDanRanking(models.Model):
|
||||
u'''
|
||||
Die Einstufung des Spieles im Kyu bzw. Dan System.
|
||||
Im Gegensatz zum Ladder Ranking ist das nicht Saison gebunden.
|
||||
daher läuft es getrennt.
|
||||
'''
|
||||
user = models.OneToOneField(User)
|
||||
dan = models.PositiveSmallIntegerField(blank=True, null=True)
|
||||
dan_points = models.PositiveIntegerField(default=0)
|
||||
kyu = models.PositiveSmallIntegerField(default=10, blank=True, null=True)
|
||||
kyu_points = models.PositiveIntegerField(default=0)
|
||||
won_hanchans = models.PositiveIntegerField(default=0)
|
||||
good_hanchans = models.PositiveIntegerField(default=0)
|
||||
hanchan_count = models.PositiveIntegerField(default=0)
|
||||
dirty = models.BooleanField(default=True, editable=False)
|
||||
wins_in_a_row = 0
|
||||
|
||||
class Meta(object):
|
||||
ordering = ('-dan_points', '-kyu_points',)
|
||||
verbose_name = _(u'Kyū/Dan Ranking')
|
||||
verbose_name_plural = _(u'Kyū/Dan Rankings')
|
||||
|
||||
def __unicode__(self):
|
||||
if self.dan_points:
|
||||
return u"%s - %d. Dan" % (self.user.username, self.dan)
|
||||
else:
|
||||
return u"%s - %d. Kyu" % (self.user.username, self.kyu)
|
||||
|
||||
def append_3_in_a_row_bonuspoints(self, hanchan):
|
||||
u'''
|
||||
Wenn der Spieler 3 Siege in folge hatte, bekommt er so viele Punkte
|
||||
das er einen Dan Rang aufsteigt. Dies wird als Kommentar abgespeichert,
|
||||
um es besser nachvollziehen zu können.
|
||||
'''
|
||||
|
||||
if self.dan and hanchan.placement == 1:
|
||||
self.wins_in_a_row += 1
|
||||
else:
|
||||
self.wins_in_a_row = 0
|
||||
|
||||
if self.dan and self.wins_in_a_row > 2:
|
||||
logger.info('adding bonuspoints for 3 wins in a row for %s', self.user) # @IgnorePep8
|
||||
new_dan_rank = self.dan + 1
|
||||
new_dan_points = DAN_RANKS_DICT[new_dan_rank] + 1
|
||||
bonus_points = new_dan_points - self.dan_points
|
||||
|
||||
logger.debug("Stats for %s:", self.user)
|
||||
logger.debug("current dan_points: %d", self.dan_points)
|
||||
logger.debug("current dan: %d", self.dan)
|
||||
logger.debug("min required points for the next dan: %d", new_dan_points) # @IgnorePep8
|
||||
logger.debug("bonus points to add: %d", bonus_points)
|
||||
|
||||
hanchan.dan_points += bonus_points
|
||||
hanchan.bonus_points += bonus_points
|
||||
hanchan.comment += '3 Siege in Folge: +%d Punkte auf den \
|
||||
%d. Dan. ' % (bonus_points, new_dan_rank)
|
||||
self.dan_points += bonus_points
|
||||
|
||||
def append_tournament_bonuspoints(self, player):
|
||||
'''
|
||||
Prüft ob es die letzte Hanchan in einem Turnier war. Wenn ja werden
|
||||
bei Bedarf Bonuspunkte vergeben, falls der Spieler das Turnier gewonnen
|
||||
hat.
|
||||
:param player: Ein Player Objekt
|
||||
'''
|
||||
bonus_points = 0
|
||||
current_event = player.hanchan.event
|
||||
if not current_event.is_tournament:
|
||||
return False # Kein Tunrier, den rest können wir uns sparen.
|
||||
hanchans_this_event = Player.objects.filter(
|
||||
user=self.user, hanchan__event=current_event
|
||||
)
|
||||
hanchans_this_event = hanchans_this_event.order_by('-hanchan__start')
|
||||
last_hanchan_this_event = hanchans_this_event[0].hanchan
|
||||
if player.hanchan != last_hanchan_this_event:
|
||||
return # Das bruacht nur am Ende eines Turnieres gemacht werden.
|
||||
event_ranking = EventRanking.objects.get(
|
||||
user=self.user,
|
||||
event=current_event)
|
||||
if event_ranking.placement == 1:
|
||||
bonus_points += 4
|
||||
player.comment += '+4 Punkte für den Sieg des Turnieres. '
|
||||
if event_ranking.avg_placement == 1:
|
||||
bonus_points += 8
|
||||
player.comment += '+8 Pkt: alle Spiele des Turnieres gewonnen. '
|
||||
player.bonus_points += bonus_points
|
||||
if bonus_points and self.dan:
|
||||
player.dan_points += bonus_points
|
||||
self.dan_points += bonus_points
|
||||
elif bonus_points:
|
||||
player.kyu_points += bonus_points
|
||||
self.kyu_points += bonus_points
|
||||
|
||||
def get_absolute_url(self):
|
||||
if self.dan:
|
||||
return reverse('player-dan-score', args=[self.user.username])
|
||||
else:
|
||||
return reverse('player-kyu-score', args=[self.user.username])
|
||||
|
||||
def recalculate(self):
|
||||
'''
|
||||
Fetches all valid Hanchans from this Player and recalculates his
|
||||
Kyu/Dan Ranking.
|
||||
'''
|
||||
logger.debug("recalculating Kyu/Dan punkte for %s...", self.user)
|
||||
valid_hanchans = Player.objects.valid_hanchans(user=self.user_id)
|
||||
valid_hanchans = valid_hanchans.order_by('hanchan__start')
|
||||
self.kyu_points = 0
|
||||
self.dan_points = 0
|
||||
self.dan = None
|
||||
self.kyu = 10
|
||||
self.hanchan_count = valid_hanchans.count()
|
||||
self.won_hanchans = valid_hanchans.filter(placement=1).count()
|
||||
self.good_hanchans = valid_hanchans.filter(placement=2).count()
|
||||
logger.info("Neuberechnung der Punkte von %s", self.user)
|
||||
|
||||
for hanchan in valid_hanchans:
|
||||
self.update_points(hanchan)
|
||||
self.append_tournament_bonuspoints(hanchan)
|
||||
self.update_rank()
|
||||
self.append_3_in_a_row_bonuspoints(hanchan)
|
||||
self.update_rank()
|
||||
hanchan.save(force_update=True, mark_dirty=False)
|
||||
self.save(force_update=True)
|
||||
|
||||
def update_points(self, player):
|
||||
'''
|
||||
Berechne die Kyu bzw. Dan Punkte für ein Spiel neu.
|
||||
:param player: Das Player Objekt das neuberechnet werden soll.
|
||||
'''
|
||||
player.bonus_points = 0
|
||||
player.comment = ""
|
||||
player.dan_points = None
|
||||
player.kyu_points = None
|
||||
|
||||
if player.hanchan.event.is_tournament:
|
||||
tourney_points = 4 - player.placement
|
||||
if self.dan:
|
||||
player.dan_points = tourney_points
|
||||
else:
|
||||
player.kyu_points = tourney_points
|
||||
elif self.dan:
|
||||
if player.score >= 60000:
|
||||
player.dan_points = 3
|
||||
elif player.score == 0:
|
||||
player.dan_points = -3
|
||||
elif player.placement == 1:
|
||||
player.dan_points = 2
|
||||
elif player.placement == 2:
|
||||
player.dan_points = 1
|
||||
elif player.placement == 3:
|
||||
player.dan_points = -1
|
||||
elif player.placement == 4:
|
||||
player.dan_points = -2
|
||||
elif player.score >= 60000:
|
||||
player.kyu_points = 3
|
||||
elif player.score >= 30000:
|
||||
player.kyu_points = 1
|
||||
elif player.score < 10000:
|
||||
player.kyu_points = -1
|
||||
else:
|
||||
player.kyu_points = 0
|
||||
|
||||
# Add the hanchans points to the players score
|
||||
if player.dan_points:
|
||||
if self.dan_points + player.dan_points < 0:
|
||||
# Only substract so much points that player has 0 Points:
|
||||
player.dan_points -= (self.dan_points + player.dan_points)
|
||||
self.dan_points += player.dan_points
|
||||
elif player.kyu_points:
|
||||
if self.kyu_points + player.kyu_points < 0:
|
||||
# Only substract so much points that player has 0 Points:
|
||||
player.kyu_points -= (self.kyu_points + player.kyu_points)
|
||||
self.kyu_points += player.kyu_points
|
||||
|
||||
def update_rank(self):
|
||||
if self.dan and self.dan_points < 0:
|
||||
self.dan_points = 0
|
||||
self.dan = 1
|
||||
elif self.dan:
|
||||
old_dan = self.dan
|
||||
for min_points, dan_rank in DAN_RANKS:
|
||||
if self.dan_points > min_points:
|
||||
self.dan = dan_rank
|
||||
break
|
||||
if self.dan > old_dan:
|
||||
self.wins_in_a_row = 0
|
||||
elif self.kyu_points < 1:
|
||||
self.kyu_points = 0
|
||||
self.kyu = 10
|
||||
elif self.kyu_points > 50:
|
||||
self.dan = 1
|
||||
self.kyu = None
|
||||
self.dan_points = 0
|
||||
self.kyu_points = 0
|
||||
else:
|
||||
for min_points, kyu_rank in KYU_RANKS:
|
||||
if self.kyu_points > min_points:
|
||||
self.kyu = kyu_rank
|
||||
break
|
||||
|
||||
|
||||
class LadderRanking(models.Model):
|
||||
user = models.ForeignKey(User)
|
||||
season = models.ForeignKey('LadderSeason')
|
||||
placement = models.PositiveIntegerField(blank=True, null=True)
|
||||
avg_placement = models.FloatField(blank=True, null=True)
|
||||
avg_score = models.FloatField(blank=True, null=True)
|
||||
hanchan_count = models.PositiveIntegerField(default=0)
|
||||
good_hanchans = models.PositiveIntegerField(default=0)
|
||||
won_hanchans = models.PositiveIntegerField(default=0)
|
||||
dirty = models.BooleanField(default=True, editable=False)
|
||||
|
||||
class Meta(object):
|
||||
ordering = ('placement', 'avg_placement', '-avg_score',)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('player-ladder-score', args=[self.user.username])
|
||||
|
||||
def recalculate(self):
|
||||
logger.info(u'Recalculate LadderRanking for Player %s in Season %s', self.user, self.season) # @IgnorePep8
|
||||
ladder_hanchans = Player.objects.ladder_hanchans(
|
||||
self.user_id, self.season_id)
|
||||
aggregate = ladder_hanchans.aggregate(
|
||||
models.Avg('placement'),
|
||||
models.Avg('score'),
|
||||
models.Count('pk')
|
||||
)
|
||||
self.dirty = False
|
||||
self.placement = None
|
||||
self.hanchan_count = aggregate['pk__count']
|
||||
self.avg_placement = aggregate['placement__avg']
|
||||
self.avg_score = aggregate['score__avg']
|
||||
self.good_hanchans = ladder_hanchans.filter(placement=2).count()
|
||||
self.won_hanchans = ladder_hanchans.filter(placement=1).count()
|
||||
self.save(force_update=True)
|
||||
|
||||
|
||||
class LadderSeasonManager(models.Manager):
|
||||
|
||||
def current(self):
|
||||
'''
|
||||
Returns the current season and caches the result for 12 hours
|
||||
'''
|
||||
current_season = cache.get('current_mahjong_season')
|
||||
if not current_season:
|
||||
try:
|
||||
today = date.today()
|
||||
current_season = self.filter(start__lte=today, end__gte=today)
|
||||
current_season = current_season[0]
|
||||
except IndexError:
|
||||
current_season = None
|
||||
cache.set('current_mahjong_season', current_season, 4320)
|
||||
return current_season
|
||||
|
||||
def get_by_date(self, deadline):
|
||||
'''
|
||||
returns the season that where running on the given date.
|
||||
:param deadline: the date you're intrested in
|
||||
'''
|
||||
try:
|
||||
season = self.filter(start__lte=deadline, end__gte=deadline)
|
||||
return season[0]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
|
||||
class LadderSeason(models.Model):
|
||||
u'''
|
||||
Eine Saison für das Kasu interne Ladder-Ranking.
|
||||
'''
|
||||
name = models.CharField(max_length=100)
|
||||
start = models.DateField()
|
||||
end = models.DateField()
|
||||
objects = LadderSeasonManager()
|
||||
dirty = models.BooleanField(default=True, editable=False)
|
||||
|
||||
class Meta(object):
|
||||
ordering = ('start',)
|
||||
verbose_name = _('Ladder Season')
|
||||
verbose_name_plural = _('Ladder Seasons')
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
def recalculate(self):
|
||||
logger.info(u'Recalculate LadderSeason %s', self.name)
|
||||
self.ladderranking_set.update(placement=None)
|
||||
placement = 1
|
||||
ladder_rankings = self.ladderranking_set.filter(hanchan_count__gt=MIN_HANCHANS_FOR_LADDER) # @IgnorePep8
|
||||
ladder_rankings = ladder_rankings.order_by('avg_placement', '-avg_score') # @IgnorePep8
|
||||
for ranking in ladder_rankings:
|
||||
ranking.placement = placement
|
||||
ranking.save(force_update=True)
|
||||
placement += 1
|
||||
self.dirty = False
|
||||
self.save(force_update=True)
|
||||
|
||||
|
||||
class PlayerManager(models.Manager):
|
||||
use_for_related_fields = True
|
||||
|
||||
def dan_hanchans(self, user=None):
|
||||
dan_hanchans = self.valid_hanchans(user)
|
||||
dan_hanchans = dan_hanchans.filter(dan_points__isnull=False)
|
||||
return dan_hanchans.select_related()
|
||||
|
||||
def kyu_hanchans(self, user=None):
|
||||
queryset = self.valid_hanchans(user).order_by('-hanchan__start')
|
||||
queryset = queryset.filter(kyu_points__isnull=False).select_related()
|
||||
return queryset
|
||||
|
||||
def ladder_hanchans(self, user=None, season=None, num_hanchans=None, max_age=None): # @IgnorePep8
|
||||
queryset = self.valid_hanchans(user).order_by('-hanchan__start')
|
||||
queryset = queryset.select_related()
|
||||
season = season or LadderSeason.objects.current()
|
||||
|
||||
if season:
|
||||
queryset = queryset.filter(hanchan__season_id=season)
|
||||
if user:
|
||||
queryset = queryset.filter(user=user)
|
||||
if max_age:
|
||||
expiration_date = date.today() - timedelta(days=max_age)
|
||||
queryset = queryset.filter(start__gt=expiration_date)
|
||||
if num_hanchans:
|
||||
hanchan_list = queryset.values_list('id', flat=True)[:num_hanchans]
|
||||
hanchan_list = set(hanchan_list)
|
||||
queryset = self.valid_hanchans(user).filter(id__in=hanchan_list)
|
||||
queryset = queryset.select_related().order_by('-hanchan__start')
|
||||
return queryset
|
||||
|
||||
def hanchan_stats(self, queryset=None):
|
||||
queryset = queryset or self.get_query_set()
|
||||
self.num_hanchans = queryset.count()
|
||||
self.won_hanchans = queryset.filter(placement=1).count()
|
||||
self.good_hanchans = queryset.filter(placement__lt=3).count()
|
||||
|
||||
def non_counting_hanchans(self, user=None):
|
||||
queryset = self.exclude(hanchan__valid=True, hanchan__confirmed=True)
|
||||
if user:
|
||||
queryset = queryset.filter(user=user)
|
||||
queryset = queryset.select_related()
|
||||
return queryset.order_by('-hanchan__start')
|
||||
|
||||
def valid_hanchans(self, user=None, event=None):
|
||||
queryset = self.filter(hanchan__valid=True, hanchan__confirmed=True)
|
||||
if user:
|
||||
queryset = queryset.filter(user=user)
|
||||
if event:
|
||||
queryset = queryset.filter(hanchan__event_id=event)
|
||||
queryset = queryset.select_related()
|
||||
return queryset.order_by('-hanchan__start')
|
||||
|
||||
|
||||
class Player(models.Model):
|
||||
hanchan = models.ForeignKey(Hanchan)
|
||||
user = models.ForeignKey(User)
|
||||
score = models.PositiveIntegerField(default=0)
|
||||
placement = models.PositiveSmallIntegerField(blank=True, null=True, default=None) # @IgnorePep8
|
||||
kyu_points = models.PositiveSmallIntegerField(blank=True, null=True, default=None) # @IgnorePep8
|
||||
dan_points = models.PositiveSmallIntegerField(blank=True, null=True, default=None) # @IgnorePep8
|
||||
bonus_points = models.PositiveSmallIntegerField(blank=True, null=True, default=0) # @IgnorePep8
|
||||
comment = models.TextField(_('Comment'), blank=True)
|
||||
objects = PlayerManager()
|
||||
|
||||
class Meta(object):
|
||||
unique_together = ('hanchan', 'user')
|
||||
ordering = ['-score']
|
||||
|
||||
def __str__(self):
|
||||
return "{0}'s Punkte vom {1: %d.%m.%Y um %H:%M}".format(self.user.username, self.hanchan.start)
|
||||
|
||||
def save(self, mark_dirty=True, season_id=None, *args, **kwargs):
|
||||
season_id = season_id or self.hanchan.season_id
|
||||
super(Player, self).save(*args, **kwargs)
|
||||
|
||||
# Set the correct Ladder Ranking to dirty, create it if necessary
|
||||
if mark_dirty:
|
||||
logger.debug("Marking %s's kyu/dan for recalculation", self.user)
|
||||
set_dirty(user=self.user_id)
|
||||
else:
|
||||
return self
|
||||
if season_id:
|
||||
logger.debug("Marking %s's season no. %i ranking for recalculation.", self.user, season_id) # @IgnorePep8
|
||||
set_dirty(season=season_id, user=self.user_id)
|
||||
logger.debug("Marking season no %i for recalculation.", season_id)
|
||||
set_dirty(season=season_id)
|
||||
if self.hanchan.event.is_tournament:
|
||||
logger.debug("Marking tournament %s for recalculation.", self.hanchan.event) # @IgnorePep8
|
||||
set_dirty(event=self.hanchan.event_id, user=self.user_id)
|
||||
return self
|
||||
|
||||
|
||||
def update_ranking_delete(sender, instance, **kwargs): # @UnusedVariable
|
||||
for player in instance.player_set.all():
|
||||
set_dirty(user=player.user_id)
|
||||
if instance.season_id:
|
||||
set_dirty(season=instance.season_id, user=player.user_id)
|
||||
|
||||
models.signals.pre_delete.connect(update_ranking_delete, sender=Hanchan)
|
||||
Reference in New Issue
Block a user