recalculating only the new hanchans shoud do the trick now.

the racalc cronjob reports erronous partly recalculations now.
A lot of code cleanups
This commit is contained in:
2017-11-01 09:53:52 +01:00
parent 42a6ebedf9
commit abeb86d48f
15 changed files with 87 additions and 62 deletions

View File

@@ -5,10 +5,10 @@
"author": "Christian Berg", "author": "Christian Berg",
"license": "GPLv3", "license": "GPLv3",
"devDependencies": { "devDependencies": {
"grunt": "^0.4.5", "grunt": ">=0.4.5",
"grunt-contrib-less": "^1.0.1", "grunt-contrib-less": ">=1.0.1",
"grunt-contrib-watch": "^0.6.1", "grunt-contrib-watch": ">=0.6.1",
"grunt-more-css": "^0.1.0" "grunt-more-css": ">=0.1.0"
}, },
"dependencies": { "dependencies": {
"ckeditor-dev": "git://github.com/ckeditor/ckeditor-dev.git" "ckeditor-dev": "git://github.com/ckeditor/ckeditor-dev.git"

View File

@@ -7,8 +7,8 @@ from django.utils.translation import ugettext as _
from content.models import Article from content.models import Article
MAX_ARTICLE_ITEMS = 10 # Maximum count of articles in the news RSS feed. MAX_ARTICLE_ITEMS = 10 # Maximum count of articles in the news RSS feed.
MAX_COMMENT_ITEMS = 40 # Maximum count of comments in the comments RSS feed. MAX_COMMENT_ITEMS = 40 # Maximum count of comments in the comments RSS feed.
# Start ignoring PyLintBear (R0201) # Start ignoring PyLintBear (R0201)

View File

@@ -31,7 +31,7 @@ class WYSIWYGEditorMixin(PermissionRequiredMixin):
request, *args, **kwargs) request, *args, **kwargs)
class ArticleArchiveMixin(): class ArticleArchiveMixin(object):
"""Mixin to add common context data to Views of the news archive.""" """Mixin to add common context data to Views of the news archive."""
category = None category = None

View File

@@ -39,5 +39,4 @@ class EventDetailMixin(object):
context['event'] = self.object context['event'] = self.object
elif hasattr(self, 'object') and hasattr(self.object, 'event'): elif hasattr(self, 'object') and hasattr(self.object, 'event'):
context['event'] = self.object.event context['event'] = self.object.event
print(context)
return context return context

View File

@@ -212,11 +212,11 @@ LOGGING = {
}, },
'handlers': { 'handlers': {
'null': { 'null': {
'level': 'DEBUG', 'level': 'INFO',
'class': 'logging.NullHandler', 'class': 'logging.NullHandler',
}, },
'console': { 'console': {
'level': 'DEBUG', 'level': 'INFO',
'class': 'logging.StreamHandler', 'class': 'logging.StreamHandler',
'formatter': 'simple' 'formatter': 'simple'
}, },
@@ -239,7 +239,7 @@ LOGGING = {
}, },
'kasu': { 'kasu': {
'handlers': ['console', 'mail_admins'], 'handlers': ['console', 'mail_admins'],
'level': 'DEBUG', 'level': 'INFO',
'propagate': False, 'propagate': False,
}, },

View File

@@ -236,7 +236,7 @@ ul.tabs {
padding: 0.5em 0 0 0; padding: 0.5em 0 0 0;
border-radius: 10px 10px 0 0; border-radius: 10px 10px 0 0;
background-color: #bc0a19; background-color: #bc0a19;
background: linear-gradient(to bottom, #000000 0%, #45484d 100%); background: linear-gradient(to bottom, #000 0%, #45484d 100%);
} }
ul.tabs li { ul.tabs li {
background-color: #fa665a; background-color: #fa665a;
@@ -325,7 +325,7 @@ ul.tabs li.active a {
min-height: 500px; min-height: 500px;
} }
.disabled { .disabled {
color: #cccccc; color: #ccc;
} }
.comment { .comment {
display: table; display: table;
@@ -425,7 +425,7 @@ ul.errorlist li {
} }
.pagination a.disabled, .pagination a.disabled,
.pagination a.disabled:hover { .pagination a.disabled:hover {
color: #666666; color: #666;
} }
.pagination a.previous { .pagination a.previous {
float: left; float: left;
@@ -732,7 +732,7 @@ ul.errorlist li {
} }
#toggle:checked ~ .toggle, #toggle:checked ~ .toggle,
.toggle:hover { .toggle:hover {
background: #45abd6; background: #45ABD6;
} }
#usernav a { #usernav a {
display: inline-block; display: inline-block;

View File

@@ -29,7 +29,7 @@ DAN_RANKS = (
DAN_RANKS_DICT = dict([(dan, points) for points, dan in DAN_RANKS]) DAN_RANKS_DICT = dict([(dan, points) for points, dan in DAN_RANKS])
MIN_HANCHANS_FOR_LADDER = 5 MIN_HANCHANS_FOR_LADDER = 5
logger = logging.getLogger('kasu.mahjong_ranking') LOGGER = logging.getLogger('kasu.mahjong_ranking')
def set_dirty(event=None, season=None, user=None, hanchan_date=None): def set_dirty(event=None, season=None, user=None, hanchan_date=None):

View File

@@ -5,6 +5,8 @@ Recalculate Mahjong Rankings...
""" """
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from mahjong_ranking import LOGGER
from mahjong_ranking import models from mahjong_ranking import models
@@ -14,5 +16,16 @@ class Command(BaseCommand):
help = "Recalculate all Kyu/Dan Rankings" help = "Recalculate all Kyu/Dan Rankings"
def handle(self, *args, **options): def handle(self, *args, **options):
old_attr = {'dan': None, 'dan_points': None,
'kyu': None, 'kyu_points': None, 'won_hanchans': None,
'good_hanchans': None, 'hanchan_count': None}
for ranking in models.KyuDanRanking.objects.all(): for ranking in models.KyuDanRanking.objects.all():
old_attr = {attr: getattr(ranking, attr) for attr in old_attr.keys()}
ranking.recalculate() ranking.recalculate()
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, 'new': getattr(ranking, attr)}
)

View File

@@ -2,7 +2,7 @@
from django.core.cache import cache from django.core.cache import cache
from mahjong_ranking import models from mahjong_ranking import models
from . import logger, MIN_HANCHANS_FOR_LADDER from . import LOGGER, MIN_HANCHANS_FOR_LADDER
class DenormalizationUpdateMiddleware(object): # Ignore PyLintBear (R0903) class DenormalizationUpdateMiddleware(object): # Ignore PyLintBear (R0903)
@@ -57,7 +57,7 @@ class DenormalizationUpdateMiddleware(object): # Ignore PyLintBear (R0903)
ranking.save(force_update=True) ranking.save(force_update=True)
for season in season_queue: for season in season_queue:
logger.info(u'Recalculate placements for Season %d', season) LOGGER.info(u'Recalculate placements for Season %d', season)
season_rankings = models.SeasonRanking.objects.filter( season_rankings = models.SeasonRanking.objects.filter(
season=season, hanchan_count__gt=MIN_HANCHANS_FOR_LADDER season=season, hanchan_count__gt=MIN_HANCHANS_FOR_LADDER
).order_by('avg_placement', '-avg_score') ).order_by('avg_placement', '-avg_score')

View File

@@ -13,7 +13,7 @@ from django.utils import timezone
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from events.models import Event from events.models import Event
from . import KYU_RANKS, DAN_RANKS, DAN_RANKS_DICT, logger, set_dirty from . import KYU_RANKS, DAN_RANKS, DAN_RANKS_DICT, LOGGER, set_dirty
from . import managers from . import managers
kyu_dan_rankings = set() kyu_dan_rankings = set()
@@ -50,7 +50,7 @@ class EventRanking(models.Model):
können zwar sehr leicht errechnet werden, es macht trotzdem Sinn können zwar sehr leicht errechnet werden, es macht trotzdem Sinn
sie zwischen zu speichern. sie zwischen zu speichern.
""" """
logger.info( LOGGER.info(
u'Recalculate EventRanking for Player %s in %s', u'Recalculate EventRanking for Player %s in %s',
self.user, self.event.name self.user, self.event.name
) )
@@ -235,7 +235,7 @@ class Hanchan(models.Model):
Bestimmt die Platzierung eines der Spieler einer Hanchan und speichert Bestimmt die Platzierung eines der Spieler einer Hanchan und speichert
diese beim jeweiligen Spieler ab. diese beim jeweiligen Spieler ab.
""" """
logger.debug("Berechne die Platzierungen neu...") LOGGER.debug("Berechne die Platzierungen neu...")
player_names = [] player_names = []
other_player_placement = 0 other_player_placement = 0
other_player_game_score = 0 other_player_game_score = 0
@@ -370,18 +370,18 @@ class KyuDanRanking(models.Model):
self.wins_in_a_row = 0 self.wins_in_a_row = 0
if self.dan and self.wins_in_a_row >= 3 and self.dan < 9: if self.dan and self.wins_in_a_row >= 3 and self.dan < 9:
logger.info( LOGGER.info(
'adding bonuspoints for 3 wins in a row for %s', self.user) 'adding bonuspoints for 3 wins in a row for %s', self.user)
new_dan_rank = self.dan + 1 new_dan_rank = self.dan + 1
new_dan_points = DAN_RANKS_DICT[new_dan_rank] + 1 new_dan_points = DAN_RANKS_DICT[new_dan_rank] + 1
bonus_points = new_dan_points - self.dan_points bonus_points = new_dan_points - self.dan_points
logger.debug("Stats for %s:", self.user) LOGGER.debug("Stats for %s:", self.user)
logger.debug("current dan_points: %d", self.dan_points) LOGGER.debug("current dan_points: %d", self.dan_points)
logger.debug("current dan: %d", self.dan) LOGGER.debug("current dan: %d", self.dan)
logger.debug( LOGGER.debug(
"min required points for the next dan: %d", new_dan_points) "min required points for the next dan: %d", new_dan_points)
logger.debug("bonus points to add: %d", bonus_points) LOGGER.debug("bonus points to add: %d", bonus_points)
hanchan.dan_points += bonus_points hanchan.dan_points += bonus_points
hanchan.bonus_points += bonus_points hanchan.bonus_points += bonus_points
@@ -444,40 +444,43 @@ class KyuDanRanking(models.Model):
self.kyu = None self.kyu = None
self.kyu_points = self.legacy_kyu_points or 0 self.kyu_points = self.legacy_kyu_points or 0
self.hanchan_count = self.legacy_hanchan_count or 0 self.hanchan_count = self.legacy_hanchan_count or 0
self.good_hanchans = 0 self.good_hanchans = 0
self.won_hanchans = 0 self.won_hanchans = 0
self.update_rank()
logger.info( LOGGER.info(
"recalculating Kyu/Dan points for %s since %s...", "recalculating Kyu/Dan points for %s since %s...",
self.user, str(hanchan_start) self.user, str(hanchan_start)
) )
self.update_rank() valid_hanchans = Hanchan.objects.confirmed_hanchans(
valid_hanchans = Hanchan.objects.confirmed_hanchans(user=self.user) user=self.user).order_by('start')
valid_hanchans = valid_hanchans.order_by('start')
if self.legacy_date: if self.legacy_date:
valid_hanchans = valid_hanchans.filter(start__gt=self.legacy_date) valid_hanchans = valid_hanchans.filter(start__gt=self.legacy_date)
""" TODO: Hanchan Punkte nur neu berechnen wenn sie vor hachan_start
lag. Es müssen aber alle durch die Schleife rennen, damit die Punkte """ TODO: Hanchan Punkte nur neu berechnen wenn sie nach hachan_start
lagen. Es müssen aber alle durch die Schleife rennen, damit die Punkte
richtig gezählt werden.""" richtig gezählt werden."""
if hanchan_start:
valid_hanchans = valid_hanchans.filter(start__gte=hanchan_start)
self.hanchan_count += valid_hanchans.count() self.hanchan_count += valid_hanchans.count()
for hanchan in valid_hanchans: for hanchan in valid_hanchans:
hanchan.get_playerdata(self.user) hanchan.get_playerdata(self.user)
hanchan.bonus_points = 0 if hanchan_start and hanchan_start < hanchan.start:
hanchan.player_comment = u"" self.dan_points += hanchan.dan_points or 0
self.update_hanchan_points(hanchan) self.kyu_points += hanchan.kyu_points or 0
if hanchan.event.mahjong_tournament: self.update_rank()
self.append_tournament_bonuspoints(hanchan) else:
self.update_rank() hanchan.bonus_points = 0
self.append_3_in_a_row_bonuspoints(hanchan) hanchan.player_comment = u""
self.update_rank() self.update_hanchan_points(hanchan)
if hanchan.event.mahjong_tournament:
self.append_tournament_bonuspoints(hanchan)
self.update_rank()
self.append_3_in_a_row_bonuspoints(hanchan)
self.update_rank()
hanchan.update_playerdata(self.user)
hanchan.save(recalculate=False)
self.won_hanchans += 1 if hanchan.placement == 1 else 0 self.won_hanchans += 1 if hanchan.placement == 1 else 0
self.good_hanchans += 1 if hanchan.placement == 2 else 0 self.good_hanchans += 1 if hanchan.placement == 2 else 0
hanchan.update_playerdata(self.user) LOGGER.debug(
hanchan.save(recalculate=False)
logger.debug(
'id: %(id)d, start: %(start)s, placement: %(placement)d, ' 'id: %(id)d, start: %(start)s, placement: %(placement)d, '
'score: %(score)d, kyu points: %(kyu_points)d, dan points: ' 'score: %(score)d, kyu points: %(kyu_points)d, dan points: '
'%(dan_points)d, bonus points: %(bonus_points)d', '%(dan_points)d, bonus points: %(bonus_points)d',
@@ -498,11 +501,11 @@ class KyuDanRanking(models.Model):
hanchan.kyu_points = None hanchan.kyu_points = None
hanchan.dan_points = None hanchan.dan_points = None
if hanchan.event.mahjong_tournament: if hanchan.event.mahjong_tournament:
# Für Turniere gelten andere Regeln zur Punktevergabe: """Für Turniere gelten andere Regeln zur Punktevergabe:
# 1. Platz 4 Punkte 1. Platz 4 Punkte
# 2. Platz 3 Punkte 2. Platz 3 Punkte
# 3. Platz 2 Punkte 3. Platz 2 Punkte
# 4. Platz 1 Punkt 4. Platz 1 Punkt"""
tourney_points = 4 - hanchan.placement tourney_points = 4 - hanchan.placement
if self.dan: if self.dan:
hanchan.dan_points = tourney_points hanchan.dan_points = tourney_points
@@ -598,7 +601,7 @@ class SeasonRanking(models.Model):
self.good_hanchans = 0 self.good_hanchans = 0
self.won_hanchans = 0 self.won_hanchans = 0
logger.info( LOGGER.info(
u'Recalculate LadderRanking for Player %s in Season %s', u'Recalculate LadderRanking for Player %s in Season %s',
self.user, self.season) self.user, self.season)
for hanchan in season_hanchans: for hanchan in season_hanchans:
@@ -616,22 +619,24 @@ class SeasonRanking(models.Model):
def update_ranking(sender, instance, **kwargs): def update_ranking(sender, instance, **kwargs):
for user in (instance.player1, instance.player2, instance.player3, instance.player4): for user in (
logger.debug( instance.player1, instance.player2, instance.player3,
instance.player4):
LOGGER.debug(
"marking %(user)s's kyu/dan for recalculation since %(start)s", "marking %(user)s's kyu/dan for recalculation since %(start)s",
{'user': user, 'start': str(instance.start.date())} {'user': user, 'start': str(instance.start.date())}
) )
set_dirty(user=user.id, hanchan_date=instance.start.date()) set_dirty(user=user.id, hanchan_date=instance.start.date())
logger.debug("marking event %s for recalculation.", instance.event) LOGGER.debug("marking event %s for recalculation.", instance.event)
set_dirty(event=instance.event_id, user=user.id) set_dirty(event=instance.event_id, user=user.id)
if instance.season: if instance.season:
logger.debug( LOGGER.debug(
"marking %s's ladder %i season for recalculation.", "marking %s's ladder %i season for recalculation.",
user, instance.season user, instance.season
) )
set_dirty(user=user.id, season=instance.season) set_dirty(user=user.id, season=instance.season)
logger.debug("marking season %d for recalculation.", instance.season) LOGGER.debug("marking season %d for recalculation.", instance.season)
set_dirty(season=instance.season) set_dirty(season=instance.season)

View File

@@ -25,7 +25,7 @@ class KyuDanTest(TestCase):
'test_kyu_dan_rankings.json' 'test_kyu_dan_rankings.json'
] ]
def recalc(self): def _test_recalc(self):
""" """
Test if a Kyu/Dan Ranking recalculation gives the same result as stored. Test if a Kyu/Dan Ranking recalculation gives the same result as stored.
@@ -58,10 +58,10 @@ class KyuDanTest(TestCase):
user=ranking.user, user=ranking.user,
since=ranking.legacy_date since=ranking.legacy_date
) )
if not confirmed_hanchans.count():
continue
rnd = random.randrange(confirmed_hanchans.count()) rnd = random.randrange(confirmed_hanchans.count())
since = confirmed_hanchans[rnd].start since = confirmed_hanchans[rnd].start
print(since)
ranking.recalculate(hanchan_start=since) ranking.recalculate(hanchan_start=since)
for attr in self.equal_attrs: for attr in self.equal_attrs:
self.assertEqual( self.assertEqual(

View File

@@ -188,9 +188,11 @@ class SeasonRankingList(MahjongMixin, generic.ListView):
class PlayerScore(LoginRequiredMixin, generic.ListView): class PlayerScore(LoginRequiredMixin, generic.ListView):
paginate_by = 25 paginate_by = 25
user = auth.get_user_model()
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
user_model = auth.get_user_model() user_model = auth.get_user_model()
username = kwargs.get('username')
try: try:
self.user = user_model.objects.get( self.user = user_model.objects.get(
username=self.kwargs.get('username')) username=self.kwargs.get('username'))

View File

@@ -63,6 +63,11 @@ class Game(models.Model):
objects = managers.GameManager() objects = managers.GameManager()
class Meta(object):
"""Display rankings by placement, best players first."""
ordering = ('-event__start', '-id')
def __str__(self): def __str__(self):
return _("Mai-Star Game with {0} from {1:%Y-%m-%d}").format( return _("Mai-Star Game with {0} from {1:%Y-%m-%d}").format(
self.player_names, self.event.start self.player_names, self.event.start

View File

@@ -78,6 +78,8 @@ class ListPlayerGames(PlayerScore):
template_name = 'maistar_ranking/player_game_list.html' template_name = 'maistar_ranking/player_game_list.html'
paginate_by = 25 paginate_by = 25
context_object_name = 'game_list' context_object_name = 'game_list'
user = None
season = None
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(ListPlayerGames, self).get_context_data() context = super(ListPlayerGames, self).get_context_data()

View File

@@ -50,7 +50,6 @@ class MassMailer(object):
LOGGER.warning('%s is not a User Object!', recipient) LOGGER.warning('%s is not a User Object!', recipient)
continue continue
mail_context['recipient'] = recipient mail_context['recipient'] = recipient
print(recipient.__dict__)
mail_to = "{first_name!s} {last_name!s} <{email!s}>".format( mail_to = "{first_name!s} {last_name!s} <{email!s}>".format(
**recipient.__dict__) **recipient.__dict__)
message = django.core.mail.EmailMultiAlternatives( message = django.core.mail.EmailMultiAlternatives(