Fehlerbereinigung.

Mahjong Ranking berechnet nun die richtigen Ränge zu den legendären Kyu/Dan Punkten.

Stablie Version bevor die Datenstruktur des Mahjongrankings vereinfacht wird.
This commit is contained in:
Christian Berg
2015-08-18 20:25:35 +02:00
committed by Christian Berg
parent bafbf38612
commit 79eaeb34ad
1172 changed files with 1713 additions and 10150 deletions

View File

@@ -1,5 +1,7 @@
# -*- encoding: utf-8 -*-
# TODO: Rankings archiv Flag erstellen, womit sie nicht mehr neuberechnet werden dürfen.
from datetime import date, timedelta
from django.conf import settings
@@ -33,7 +35,6 @@ class EventRanking(models.Model):
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',)
@@ -48,10 +49,6 @@ class EventRanking(models.Model):
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
@@ -59,14 +56,13 @@ class EventRanking(models.Model):
event=self.event_id) # @IgnorePep8
aggregator = event_hanchans.aggregate(
models.Avg('placement'),
models.Avg('score'),
models.Avg('game_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:
@@ -80,20 +76,16 @@ class Hanchan(models.Model):
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
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)
player_names = models.CharField(max_length=255, editable=False)
players = models.ManyToManyField(
settings.AUTH_USER_MODEL,
through='Player',
verbose_name=_('Players')
)
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
season = models.ForeignKey('LadderSeason', blank=True, null=True, editable=False)
start = models.DateTimeField(_('Start'), help_text=_('This is crucial to get the right Hanchans that scores'))
valid = models.BooleanField(_('Is Valid'), default=False)
class Meta(object):
@@ -122,22 +114,26 @@ class Hanchan(models.Model):
self.valid = False
return _('For a Hanchan exactly 4 players are needed.')
score_sum = self.player_set.aggregate(Sum('score'))['score__sum']
score_sum = self.player_set.aggregate(Sum('input_score'))['input_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 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):
elif score_sum > 100000:
self.valid = False
return 'Endpunktestand über 100.000, aber niemand ist auf 0 \
gefallen.'
return 'Endpunktestand mehr 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...'
@@ -177,7 +173,7 @@ class Hanchan(models.Model):
"""
logger.debug("Berechne die Platzierungen neu...")
attending_players = self.player_set.select_related('hanchan', 'user')
attending_players = attending_players.order_by('-score')
attending_players = attending_players.order_by('-game_score')
other_player_placement = 0
other_player_score = 0
placement = 1
@@ -185,15 +181,15 @@ class Hanchan(models.Model):
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:
if player.game_score <= 0:
player.placement = 4
elif player.score == other_player_score:
elif player.game_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
other_player_score = player.game_score
player.save(season_id=self.season_id, mark_dirty=True)
def get_absolute_url(self):
@@ -206,21 +202,20 @@ class Hanchan(models.Model):
def save(self, **kwargs):
logger.debug("Hanchan save() wurde getriggert!")
self.season = self.season or LadderSeason.objects.get_by_date(
self.start)
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'))
'user__username', flat=True).order_by('-input_score'))
return models.Model.save(self, **kwargs)
class KyuDanRanking(models.Model):
u"""
Die Einstufung des Spieles im Kyu bzw. Dan System.
Die Einstufung des Spielers im Kyu bzw. Dan System.
Im Gegensatz zum Ladder Ranking ist das nicht Saison gebunden.
daher läuft es getrennt.
Deswegen läuft es getrennt.
"""
user = models.OneToOneField(settings.AUTH_USER_MODEL)
dan = models.PositiveSmallIntegerField(blank=True, null=True)
@@ -230,8 +225,10 @@ class KyuDanRanking(models.Model):
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
legacy_date = models.DateField(blank=True, null=True)
legacy_dan_points = models.PositiveIntegerField(default=0)
legacy_kyu_points = models.PositiveIntegerField(default=0)
class Meta(object):
ordering = ('-dan_points', '-kyu_points',)
@@ -239,10 +236,10 @@ class KyuDanRanking(models.Model):
verbose_name_plural = _(u'Kyū/Dan Rankings')
def __unicode__(self):
if self.dan_points:
return u"%s - %d. Dan" % (self.user.username, self.dan)
if self.dan_points is not None:
return u"%s - %d. Dan" % (self.user.username, self.dan or 1)
else:
return u"%s - %d. Kyu" % (self.user.username, self.kyu)
return u"%s - %d. Kyu" % (self.user.username, self.kyu or 10)
def append_3_in_a_row_bonuspoints(self, hanchan):
u"""
@@ -312,7 +309,7 @@ class KyuDanRanking(models.Model):
self.kyu_points += bonus_points
def get_absolute_url(self):
if self.dan:
if self.dan or self.dan_points > 0:
return reverse('player-dan-score', args=[self.user.username])
else:
return reverse('player-kyu-score', args=[self.user.username])
@@ -324,80 +321,90 @@ class KyuDanRanking(models.Model):
"""
logger.debug("recalculating Kyu/Dan punkte for %s...", self.user)
valid_hanchans = Player.objects.valid_hanchans(user=self.user_id)
if self.legacy_date:
valid_hanchans = valid_hanchans.filter(hanchan__start__gt=self.legacy_date)
valid_hanchans = valid_hanchans.order_by('hanchan__start')
self.kyu_points = 0
self.dan_points = 0
self.kyu_points = self.legacy_kyu_points or 0
self.dan_points = self.legacy_dan_points or 0
self.dan = None
self.kyu = 10
self.update_rank()
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)
for played_hanchan in valid_hanchans:
self.update_hanchan_points(played_hanchan)
self.append_tournament_bonuspoints(played_hanchan)
self.update_rank()
self.append_3_in_a_row_bonuspoints(hanchan)
self.append_3_in_a_row_bonuspoints(played_hanchan)
self.update_rank()
hanchan.save(force_update=True, mark_dirty=False)
played_hanchan.save(force_update=True, mark_dirty=False)
self.save(force_update=True)
def update_points(self, player):
def update_hanchan_points(self, played_hanchan):
"""
Berechne die Kyu bzw. Dan Punkte für ein Spiel neu.
:param player: Das Player Objekt das neuberechnet werden soll.
Berechne die Kyu bzw. Dan Punkte für eine Hanchan neu.
Für Turniere gelten andere Regeln zur Punktevergabe:
1. Platz 4 Punkte
2. Platz 3 Punkte
3. Platz 2 Punkte
4. Platz 1 Punkt
:param played_hanchan: 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
played_hanchan.bonus_points = 0
played_hanchan.comment = ""
played_hanchan.dan_points = None
played_hanchan.kyu_points = None
print self.user, played_hanchan.input_score, played_hanchan.game_score
if played_hanchan.hanchan.event.is_tournament:
tourney_points = 4 - played_hanchan.placement
if self.dan:
player.dan_points = tourney_points
played_hanchan.dan_points = tourney_points
else:
player.kyu_points = tourney_points
played_hanchan.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
if played_hanchan.game_score >= 60000:
played_hanchan.dan_points = 3
elif played_hanchan.game_score == 0:
played_hanchan.dan_points = -3
elif played_hanchan.placement == 1:
played_hanchan.dan_points = 2
elif played_hanchan.placement == 2:
played_hanchan.dan_points = 1
elif played_hanchan.placement == 3:
played_hanchan.dan_points = -1
elif played_hanchan.placement == 4:
played_hanchan.dan_points = -2
#Kein Turnier, Spieler nicht im Dan, dann muss er Sie Kyu Punkte bekommen
elif played_hanchan.game_score >= 60000:
played_hanchan.kyu_points = 3
elif played_hanchan.game_score >= 30000:
played_hanchan.kyu_points = 1
elif played_hanchan.game_score < 10000:
played_hanchan.kyu_points = -1
else:
player.kyu_points = 0
played_hanchan.kyu_points = 0
# Add the hanchans points to the players score
if player.dan_points:
if self.dan_points + player.dan_points < 0:
# Add the hanchans points to the players points
if self.dan:
if self.dan_points + played_hanchan.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:
played_hanchan.dan_points -= (self.dan_points + played_hanchan.dan_points)
self.dan_points += played_hanchan.dan_points
print "Danpunkte des Spielers: {}, Danpunkte der Hanchan {}".format(self.dan_points, played_hanchan.dan_points)
else:
if self.kyu_points + played_hanchan.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
played_hanchan.kyu_points -= (self.kyu_points + played_hanchan.kyu_points)
self.kyu_points += played_hanchan.kyu_points
def update_rank(self):
if self.dan and self.dan_points < 0:
self.dan_points = 0
self.dan = 1
elif self.dan:
elif self.dan or self.dan_points > 0:
old_dan = self.dan
for min_points, dan_rank in DAN_RANKS:
if self.dan_points > min_points:
@@ -429,7 +436,6 @@ class LadderRanking(models.Model):
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',)
@@ -443,15 +449,14 @@ class LadderRanking(models.Model):
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')
avg_placement = models.Avg('placement'),
avg_score = models.Avg('game_score'),
hanchan_count = 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.hanchan_count = aggregate['hanchan_count']
self.avg_placement = aggregate['avg_placement']
self.avg_score = aggregate['avg_score']
self.good_hanchans = ladder_hanchans.filter(placement=2).count()
self.won_hanchans = ladder_hanchans.filter(placement=1).count()
self.save(force_update=True)
@@ -464,12 +469,12 @@ class LadderSeasonManager(models.Manager):
"""
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
current_year = date.today().year
current_season = self.get_or_create(name=current_year, defaults={
'name': current_year,
'start': date(year=current_year,month=1, day=1),
'end': date(year=current_year, month=12, day=31)
})[0]
cache.set('current_mahjong_season', current_season, 4320)
return current_season
@@ -489,11 +494,10 @@ class LadderSeason(models.Model):
u"""
Eine Saison für das Kasu interne Ladder-Ranking.
"""
name = models.CharField(max_length=100)
name = models.PositiveSmallIntegerField()
start = models.DateField()
end = models.DateField()
objects = LadderSeasonManager()
dirty = models.BooleanField(default=True, editable=False)
class Meta(object):
ordering = ('start',)
@@ -501,7 +505,7 @@ class LadderSeason(models.Model):
verbose_name_plural = _('Ladder Seasons')
def __unicode__(self):
return self.name
return str(self.name)
def get_absolute_url(self):
"""
@@ -526,7 +530,6 @@ class LadderSeason(models.Model):
ranking.placement = placement
ranking.save(force_update=True)
placement += 1
self.dirty = False
self.save(force_update=True)
@@ -544,7 +547,7 @@ class PlayerManager(models.Manager):
return queryset
def ladder_hanchans(self, user=None, season=None, num_hanchans=None,
max_age=None): # @IgnorePep8
max_age=None):
queryset = self.valid_hanchans(user).order_by('-hanchan__start')
queryset = queryset.select_related()
season = season or LadderSeason.objects.current()
@@ -589,18 +592,19 @@ class PlayerManager(models.Manager):
class Player(models.Model):
hanchan = models.ForeignKey(Hanchan)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
score = models.PositiveIntegerField(default=0)
input_score = models.IntegerField(default=0)
game_score = models.PositiveIntegerField(default=0)
placement = models.PositiveSmallIntegerField(
blank=True,
null=True,
default=None
)
kyu_points = models.PositiveSmallIntegerField(
kyu_points = models.SmallIntegerField(
blank=True,
null=True,
default=None
)
dan_points = models.PositiveSmallIntegerField(
dan_points = models.SmallIntegerField(
blank=True,
null=True,
default=None
@@ -615,13 +619,14 @@ class Player(models.Model):
class Meta(object):
unique_together = ('hanchan', 'user')
ordering = ['-score']
ordering = ['-input_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):
self.game_score = self.input_score if self.input_score > 0 else 0
season_id = season_id or self.hanchan.season_id
super(Player, self).save(*args, **kwargs)
@@ -634,7 +639,7 @@ class Player(models.Model):
if season_id:
logger.debug(
"Marking %s's season no. %i ranking for recalculation.",
self.user, season_id) # @IgnorePep8
self.user, season_id)
set_dirty(season=season_id, user=self.user_id)
logger.debug("Marking season no %i for recalculation.", season_id)
set_dirty(season=season_id)