From f3d44b743b399a2a4a71346e9648072998dd1f09 Mon Sep 17 00:00:00 2001 From: Xeniac Date: Wed, 1 Nov 2017 09:53:52 +0100 Subject: [PATCH] recalculating only the new hanchans shoud do the trick now. the racalc cronjob reports erronous partly recalculations now. A lot of code cleanups --- package.json | 8 +- src/content/feeds.py | 4 +- src/content/views.py | 2 +- src/events/mixins.py | 1 - src/kasu/settings.py | 6 +- src/kasu/static/css/kasu.css | 8 +- src/mahjong_ranking/__init__.py | 2 +- .../management/commands/update_ranking.py | 13 +++ src/mahjong_ranking/middleware.py | 4 +- src/mahjong_ranking/models.py | 85 ++++++++++--------- src/mahjong_ranking/tests.py | 6 +- src/mahjong_ranking/views.py | 2 + src/maistar_ranking/models.py | 5 ++ src/maistar_ranking/views.py | 2 + src/utils/massmailer.py | 1 - 15 files changed, 87 insertions(+), 62 deletions(-) diff --git a/package.json b/package.json index 140eae2..7fac8e7 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,10 @@ "author": "Christian Berg", "license": "GPLv3", "devDependencies": { - "grunt": "^0.4.5", - "grunt-contrib-less": "^1.0.1", - "grunt-contrib-watch": "^0.6.1", - "grunt-more-css": "^0.1.0" + "grunt": ">=0.4.5", + "grunt-contrib-less": ">=1.0.1", + "grunt-contrib-watch": ">=0.6.1", + "grunt-more-css": ">=0.1.0" }, "dependencies": { "ckeditor-dev": "git://github.com/ckeditor/ckeditor-dev.git" diff --git a/src/content/feeds.py b/src/content/feeds.py index 8e0435b..1323c5a 100644 --- a/src/content/feeds.py +++ b/src/content/feeds.py @@ -7,8 +7,8 @@ from django.utils.translation import ugettext as _ from content.models import Article -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_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. # Start ignoring PyLintBear (R0201) diff --git a/src/content/views.py b/src/content/views.py index 5ac5d1b..6025650 100644 --- a/src/content/views.py +++ b/src/content/views.py @@ -31,7 +31,7 @@ class WYSIWYGEditorMixin(PermissionRequiredMixin): request, *args, **kwargs) -class ArticleArchiveMixin(): +class ArticleArchiveMixin(object): """Mixin to add common context data to Views of the news archive.""" category = None diff --git a/src/events/mixins.py b/src/events/mixins.py index 4cfc1a6..cb8f372 100644 --- a/src/events/mixins.py +++ b/src/events/mixins.py @@ -39,5 +39,4 @@ class EventDetailMixin(object): context['event'] = self.object elif hasattr(self, 'object') and hasattr(self.object, 'event'): context['event'] = self.object.event - print(context) return context diff --git a/src/kasu/settings.py b/src/kasu/settings.py index 3fcf4f5..3ffc8f3 100644 --- a/src/kasu/settings.py +++ b/src/kasu/settings.py @@ -212,11 +212,11 @@ LOGGING = { }, 'handlers': { 'null': { - 'level': 'DEBUG', + 'level': 'INFO', 'class': 'logging.NullHandler', }, 'console': { - 'level': 'DEBUG', + 'level': 'INFO', 'class': 'logging.StreamHandler', 'formatter': 'simple' }, @@ -239,7 +239,7 @@ LOGGING = { }, 'kasu': { 'handlers': ['console', 'mail_admins'], - 'level': 'DEBUG', + 'level': 'INFO', 'propagate': False, }, diff --git a/src/kasu/static/css/kasu.css b/src/kasu/static/css/kasu.css index 6f5d64a..b889b3e 100644 --- a/src/kasu/static/css/kasu.css +++ b/src/kasu/static/css/kasu.css @@ -236,7 +236,7 @@ ul.tabs { padding: 0.5em 0 0 0; border-radius: 10px 10px 0 0; background-color: #bc0a19; - background: linear-gradient(to bottom, #000000 0%, #45484d 100%); + background: linear-gradient(to bottom, #000 0%, #45484d 100%); } ul.tabs li { background-color: #fa665a; @@ -325,7 +325,7 @@ ul.tabs li.active a { min-height: 500px; } .disabled { - color: #cccccc; + color: #ccc; } .comment { display: table; @@ -425,7 +425,7 @@ ul.errorlist li { } .pagination a.disabled, .pagination a.disabled:hover { - color: #666666; + color: #666; } .pagination a.previous { float: left; @@ -732,7 +732,7 @@ ul.errorlist li { } #toggle:checked ~ .toggle, .toggle:hover { - background: #45abd6; + background: #45ABD6; } #usernav a { display: inline-block; diff --git a/src/mahjong_ranking/__init__.py b/src/mahjong_ranking/__init__.py index afc78f3..90c98ca 100644 --- a/src/mahjong_ranking/__init__.py +++ b/src/mahjong_ranking/__init__.py @@ -29,7 +29,7 @@ DAN_RANKS = ( DAN_RANKS_DICT = dict([(dan, points) for points, dan in DAN_RANKS]) 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): diff --git a/src/mahjong_ranking/management/commands/update_ranking.py b/src/mahjong_ranking/management/commands/update_ranking.py index 2b12dbc..692a8e1 100644 --- a/src/mahjong_ranking/management/commands/update_ranking.py +++ b/src/mahjong_ranking/management/commands/update_ranking.py @@ -5,6 +5,8 @@ Recalculate Mahjong Rankings... """ from django.core.management.base import BaseCommand + +from mahjong_ranking import LOGGER from mahjong_ranking import models @@ -14,5 +16,16 @@ class Command(BaseCommand): help = "Recalculate all Kyu/Dan Rankings" 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(): + old_attr = {attr: getattr(ranking, attr) for attr in old_attr.keys()} 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)} + ) diff --git a/src/mahjong_ranking/middleware.py b/src/mahjong_ranking/middleware.py index 630d977..299edc1 100644 --- a/src/mahjong_ranking/middleware.py +++ b/src/mahjong_ranking/middleware.py @@ -2,7 +2,7 @@ from django.core.cache import cache 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) @@ -57,7 +57,7 @@ class DenormalizationUpdateMiddleware(object): # Ignore PyLintBear (R0903) ranking.save(force_update=True) 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=season, hanchan_count__gt=MIN_HANCHANS_FOR_LADDER ).order_by('avg_placement', '-avg_score') diff --git a/src/mahjong_ranking/models.py b/src/mahjong_ranking/models.py index a3dccb6..eb452a6 100644 --- a/src/mahjong_ranking/models.py +++ b/src/mahjong_ranking/models.py @@ -13,7 +13,7 @@ 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, logger, set_dirty +from . import KYU_RANKS, DAN_RANKS, DAN_RANKS_DICT, LOGGER, set_dirty from . import managers kyu_dan_rankings = set() @@ -50,7 +50,7 @@ class EventRanking(models.Model): können zwar sehr leicht errechnet werden, es macht trotzdem Sinn sie zwischen zu speichern. """ - logger.info( + LOGGER.info( u'Recalculate EventRanking for Player %s in %s', self.user, self.event.name ) @@ -235,7 +235,7 @@ class Hanchan(models.Model): Bestimmt die Platzierung eines der Spieler einer Hanchan und speichert diese beim jeweiligen Spieler ab. """ - logger.debug("Berechne die Platzierungen neu...") + LOGGER.debug("Berechne die Platzierungen neu...") player_names = [] other_player_placement = 0 other_player_game_score = 0 @@ -370,18 +370,18 @@ class KyuDanRanking(models.Model): self.wins_in_a_row = 0 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) 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( + 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) - logger.debug("bonus points to add: %d", bonus_points) + LOGGER.debug("bonus points to add: %d", bonus_points) hanchan.dan_points += bonus_points hanchan.bonus_points += bonus_points @@ -444,40 +444,43 @@ class KyuDanRanking(models.Model): self.kyu = None self.kyu_points = self.legacy_kyu_points or 0 self.hanchan_count = self.legacy_hanchan_count or 0 - self.good_hanchans = 0 self.won_hanchans = 0 + self.update_rank() - logger.info( + LOGGER.info( "recalculating Kyu/Dan points for %s since %s...", self.user, str(hanchan_start) ) - self.update_rank() - valid_hanchans = Hanchan.objects.confirmed_hanchans(user=self.user) - valid_hanchans = valid_hanchans.order_by('start') + valid_hanchans = Hanchan.objects.confirmed_hanchans( + user=self.user).order_by('start') if 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.""" - if hanchan_start: - valid_hanchans = valid_hanchans.filter(start__gte=hanchan_start) self.hanchan_count += valid_hanchans.count() for hanchan in valid_hanchans: hanchan.get_playerdata(self.user) - hanchan.bonus_points = 0 - hanchan.player_comment = u"" - 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() + if hanchan_start and hanchan_start < hanchan.start: + self.dan_points += hanchan.dan_points or 0 + self.kyu_points += hanchan.kyu_points or 0 + self.update_rank() + else: + hanchan.bonus_points = 0 + hanchan.player_comment = u"" + 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.good_hanchans += 1 if hanchan.placement == 2 else 0 - hanchan.update_playerdata(self.user) - hanchan.save(recalculate=False) - logger.debug( + LOGGER.debug( 'id: %(id)d, start: %(start)s, placement: %(placement)d, ' 'score: %(score)d, kyu points: %(kyu_points)d, dan points: ' '%(dan_points)d, bonus points: %(bonus_points)d', @@ -498,11 +501,11 @@ class KyuDanRanking(models.Model): hanchan.kyu_points = None hanchan.dan_points = None if hanchan.event.mahjong_tournament: - # Für Turniere gelten andere Regeln zur Punktevergabe: - # 1. Platz 4 Punkte - # 2. Platz 3 Punkte - # 3. Platz 2 Punkte - # 4. Platz 1 Punkt + """Für Turniere gelten andere Regeln zur Punktevergabe: + 1. Platz 4 Punkte + 2. Platz 3 Punkte + 3. Platz 2 Punkte + 4. Platz 1 Punkt""" tourney_points = 4 - hanchan.placement if self.dan: hanchan.dan_points = tourney_points @@ -598,7 +601,7 @@ class SeasonRanking(models.Model): self.good_hanchans = 0 self.won_hanchans = 0 - logger.info( + LOGGER.info( u'Recalculate LadderRanking for Player %s in Season %s', self.user, self.season) for hanchan in season_hanchans: @@ -616,22 +619,24 @@ class SeasonRanking(models.Model): def update_ranking(sender, instance, **kwargs): - for user in (instance.player1, instance.player2, instance.player3, instance.player4): - logger.debug( + for user in ( + instance.player1, instance.player2, instance.player3, + instance.player4): + LOGGER.debug( "marking %(user)s's kyu/dan for recalculation since %(start)s", {'user': user, 'start': str(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) if instance.season: - logger.debug( + LOGGER.debug( "marking %s's ladder %i season for recalculation.", user, 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) diff --git a/src/mahjong_ranking/tests.py b/src/mahjong_ranking/tests.py index d33a08b..63f1a86 100644 --- a/src/mahjong_ranking/tests.py +++ b/src/mahjong_ranking/tests.py @@ -25,7 +25,7 @@ class KyuDanTest(TestCase): '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. @@ -58,10 +58,10 @@ class KyuDanTest(TestCase): user=ranking.user, since=ranking.legacy_date ) + if not confirmed_hanchans.count(): + continue rnd = random.randrange(confirmed_hanchans.count()) since = confirmed_hanchans[rnd].start - print(since) - ranking.recalculate(hanchan_start=since) for attr in self.equal_attrs: self.assertEqual( diff --git a/src/mahjong_ranking/views.py b/src/mahjong_ranking/views.py index 31976aa..44c2dcd 100644 --- a/src/mahjong_ranking/views.py +++ b/src/mahjong_ranking/views.py @@ -188,9 +188,11 @@ class SeasonRankingList(MahjongMixin, generic.ListView): class PlayerScore(LoginRequiredMixin, generic.ListView): paginate_by = 25 + user = auth.get_user_model() def get(self, request, *args, **kwargs): user_model = auth.get_user_model() + username = kwargs.get('username') try: self.user = user_model.objects.get( username=self.kwargs.get('username')) diff --git a/src/maistar_ranking/models.py b/src/maistar_ranking/models.py index cf1c284..42e1d6e 100644 --- a/src/maistar_ranking/models.py +++ b/src/maistar_ranking/models.py @@ -63,6 +63,11 @@ class Game(models.Model): objects = managers.GameManager() + class Meta(object): + """Display rankings by placement, best players first.""" + ordering = ('-event__start', '-id') + + def __str__(self): return _("Mai-Star Game with {0} from {1:%Y-%m-%d}").format( self.player_names, self.event.start diff --git a/src/maistar_ranking/views.py b/src/maistar_ranking/views.py index 1b839d7..993aa75 100644 --- a/src/maistar_ranking/views.py +++ b/src/maistar_ranking/views.py @@ -78,6 +78,8 @@ class ListPlayerGames(PlayerScore): template_name = 'maistar_ranking/player_game_list.html' paginate_by = 25 context_object_name = 'game_list' + user = None + season = None def get_context_data(self, **kwargs): context = super(ListPlayerGames, self).get_context_data() diff --git a/src/utils/massmailer.py b/src/utils/massmailer.py index 6e7b6bd..c0e517b 100644 --- a/src/utils/massmailer.py +++ b/src/utils/massmailer.py @@ -50,7 +50,6 @@ class MassMailer(object): LOGGER.warning('%s is not a User Object!', recipient) continue mail_context['recipient'] = recipient - print(recipient.__dict__) mail_to = "{first_name!s} {last_name!s} <{email!s}>".format( **recipient.__dict__) message = django.core.mail.EmailMultiAlternatives(