Another Step in the Quest to clean up the code base.

This commit is contained in:
2017-09-08 07:19:50 +02:00
parent ce218080b2
commit b3ab9798b5
229 changed files with 1915 additions and 15175 deletions

View File

@@ -1,18 +1,13 @@
# -*- encoding: utf-8 -*-
""" Adds management of the mahong ranking system to the admin interface. """
"""
Created on 19.09.2011
@author: christian
"""
# import stuff we need from django
from django.contrib import admin
from django.utils.translation import ugettext as _
from . import models, set_dirty
def recalculate(modeladmin, request, queryset):
def recalculate(modeladmin, request, queryset): # Ignore PyLintBear (W0613)
""" An admin action to force recalculation of the selected items. """
if isinstance(modeladmin, HanchanAdmin):
for hanchan in queryset:
hanchan.save()
@@ -31,7 +26,8 @@ def recalculate(modeladmin, request, queryset):
recalculate.short_description = _("Recalculate")
def confirm(modeladmin, request, queryset):
def confirm(modeladmin, request, queryset): # Ignore PyLintBear (W0613)
"""An admin action to quickly set selected hanchans to confirmed. """
for hanchan in queryset:
hanchan.confirmed = True
hanchan.save()
@@ -40,7 +36,8 @@ def confirm(modeladmin, request, queryset):
confirm.short_description = _("Confirm")
def unconfirm(modeladmin, request, queryset):
def unconfirm(modeladmin, request, queryset): # Ignore PyLintBear (W0613)
"""An admin action to quickly set selected hanchans to unconfirmed. """
for hanchan in queryset:
hanchan.confirmed = False
hanchan.save()
@@ -50,6 +47,7 @@ unconfirm.short_description = _('Set unconfirmed')
class EventRankingAdmin(admin.ModelAdmin):
""" Lists the Event Rankings and allows to recalculate them. """
list_filter = ['event']
list_display = ('placement', 'user', 'event', 'avg_placement', 'avg_score',
'hanchan_count', 'good_hanchans', 'won_hanchans')
@@ -58,6 +56,7 @@ class EventRankingAdmin(admin.ModelAdmin):
class HanchanAdmin(admin.ModelAdmin):
""" To administrate the stored Hanchans. """
actions = [recalculate, confirm, unconfirm]
date_hierarchy = 'start'
list_filter = ('season', 'event', 'confirmed')
@@ -82,6 +81,7 @@ class HanchanAdmin(admin.ModelAdmin):
class KyuDanAdmin(admin.ModelAdmin):
""" Lists the Kyu/dan Rankings and allows to recalculate them. """
actions = [recalculate]
list_display = ('user', 'kyu', 'kyu_points', 'dan', 'dan_points',
'hanchan_count')
@@ -92,13 +92,15 @@ class KyuDanAdmin(admin.ModelAdmin):
'classes': ('grp-collapse grp-open',),
}),
('Frühere Aufzeichnungen', {
'fields': ('legacy_date', 'legacy_hanchan_count', ('legacy_kyu_points', 'legacy_dan_points')),
'fields': ('legacy_date', 'legacy_hanchan_count',
('legacy_kyu_points', 'legacy_dan_points')),
'classes': ('grp-collapse grp-closed',),
}),
)
class SeasonRankingAdmin(admin.ModelAdmin):
""" Lists the Season Rankings and allows to recalculate them. """
actions = [recalculate]
list_display = ('placement', 'season', 'user', 'avg_placement',
'avg_score', 'hanchan_count', 'good_hanchans',
@@ -108,6 +110,7 @@ class SeasonRankingAdmin(admin.ModelAdmin):
class LadderSeasonAdmin(admin.ModelAdmin):
""" Seasons will be generated automaticly, but you can examine them here."""
actions = [recalculate]
list_display = ('name', 'start', 'end')

View File

@@ -15,11 +15,13 @@ USER_MODEL = get_user_model()
class HanchanForm(forms.ModelForm):
""" Form to add/edit mahjong hanchans, grouped with formsets. """
error_css_class = 'error'
required_css_class = 'required'
start = forms.SplitDateTimeField(label=_('start'), required=True)
class Meta(object):
""" Medadata for better usability of the HanchanForm """
model = models.Hanchan
fields = ('start',
'player1', 'player1_input_score',
@@ -30,12 +32,14 @@ class HanchanForm(forms.ModelForm):
widgets = {
'event': forms.HiddenInput(),
'comment': forms.widgets.Textarea(attrs={'rows': 4, 'cols': 40})
}
}
def __init__(self, *args, **kwargs):
""" Overwrites some standard widgets for better usability. """
super(HanchanForm, self).__init__(*args, **kwargs)
player_queryset = USER_MODEL.objects.filter(is_active=True,
membership=True)
player_queryset = USER_MODEL.objects.filter(
is_active=True,
membership=True)
for i in range(1, 4):
player = 'player%d' % i
player_input_score = 'player%d_input_score' % i
@@ -45,18 +49,12 @@ class HanchanForm(forms.ModelForm):
class HanchanAdminForm(HanchanForm):
""" Extends the HanchanForm for users with admin privileges.
They are allowed to confirm/unconfirm Hanchans, this could be userful if
one games smells fishy and needs the opinion of an referee."""
class Meta(object):
""" Extend the formfields to add the confirmed checkbox. """
model = models.Hanchan
fields = HanchanForm.Meta.fields + ('confirmed',)
class SeasonSelectForm(forms.Form):
season = forms.ChoiceField(label='', choices=('a', 'b', 'c'))
def __init__(self, user, *args, **kwargs):
super(SeasonSelectForm, self).__init__(args, kwargs)
season_list = models.LadderRanking.objects.filter(user=user)
season_list = season_list.select_related('user')
season_list = season_list.values_list('season__id', 'season__name')
self.fields['season'] = forms.ChoiceField(choices=season_list)

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: kasu.mahjong_ranking\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-05-10 23:16+0200\n"
"POT-Creation-Date: 2017-06-19 22:46+0200\n"
"PO-Revision-Date: 2016-09-28 00:24+0200\n"
"Last-Translator: Christian Berg <xeniac.at@gmail.com>\n"
"Language-Team: Kasu <verein@kasu.at>\n"
@@ -19,7 +19,7 @@ msgstr ""
"X-Generator: Poedit 1.8.9\n"
"X-Translated-Using: django-rosetta 0.7.6\n"
#: mahjong_ranking/admin.py:29
#: mahjong_ranking/admin.py:26
msgid "Recalculate"
msgstr "Neuberechnen"
@@ -27,15 +27,15 @@ msgstr "Neuberechnen"
msgid "Confirm"
msgstr "Bestätigen"
#: mahjong_ranking/admin.py:43
#: mahjong_ranking/admin.py:46
msgid "Set unconfirmed"
msgstr "Als unbestätigt markieren"
#: mahjong_ranking/forms.py:22
#: mahjong_ranking/forms.py:21
msgid "start"
msgstr "Beginn"
#: mahjong_ranking/models.py:90
#: mahjong_ranking/models.py:89
#: mahjong_ranking/templates/mahjong_ranking/player_dan_score.html:14
#: mahjong_ranking/templates/mahjong_ranking/player_invalid_score.html:13
#: mahjong_ranking/templates/mahjong_ranking/player_kyu_score.html:15
@@ -44,18 +44,18 @@ msgstr "Beginn"
msgid "Start"
msgstr "Beginn"
#: mahjong_ranking/models.py:91
#: mahjong_ranking/models.py:90
msgid "This is crucial to get the right Hanchans that scores"
msgstr "Wichtig damit die richtigen Hanchans in die Wertung kommen."
#: mahjong_ranking/models.py:98
#: mahjong_ranking/models.py:97
msgid "Player 1"
msgstr "Spieler 1"
#: mahjong_ranking/models.py:99 mahjong_ranking/models.py:101
#: mahjong_ranking/models.py:118 mahjong_ranking/models.py:120
#: mahjong_ranking/models.py:137 mahjong_ranking/models.py:139
#: mahjong_ranking/models.py:156 mahjong_ranking/models.py:158
#: mahjong_ranking/models.py:98 mahjong_ranking/models.py:100
#: mahjong_ranking/models.py:117 mahjong_ranking/models.py:119
#: mahjong_ranking/models.py:136 mahjong_ranking/models.py:138
#: mahjong_ranking/models.py:155 mahjong_ranking/models.py:157
#: mahjong_ranking/templates/mahjong_ranking/eventhanchan_list.html:19
#: mahjong_ranking/templates/mahjong_ranking/eventranking_list.html:21
#: mahjong_ranking/templates/mahjong_ranking/hanchan_confirm_delete.html:16
@@ -65,80 +65,80 @@ msgstr "Spieler 1"
msgid "Score"
msgstr "Punkte"
#: mahjong_ranking/models.py:111 mahjong_ranking/models.py:130
#: mahjong_ranking/models.py:149 mahjong_ranking/models.py:168
#: mahjong_ranking/models.py:170
#: mahjong_ranking/models.py:110 mahjong_ranking/models.py:129
#: mahjong_ranking/models.py:148 mahjong_ranking/models.py:167
#: mahjong_ranking/models.py:169
#: mahjong_ranking/templates/mahjong_ranking/hanchan_form.html:20
#: mahjong_ranking/templates/mahjong_ranking/player_dan_score.html:18
#: mahjong_ranking/templates/mahjong_ranking/player_invalid_score.html:17
msgid "Comment"
msgstr "Kommentar"
#: mahjong_ranking/models.py:117
#: mahjong_ranking/models.py:116
msgid "Player 2"
msgstr "Spieler 2"
#: mahjong_ranking/models.py:136
#: mahjong_ranking/models.py:135
msgid "Player 3"
msgstr "Spieler 3"
#: mahjong_ranking/models.py:155
#: mahjong_ranking/models.py:154
msgid "Player 4"
msgstr "Spieler 4"
#: mahjong_ranking/models.py:171
#: mahjong_ranking/models.py:170
msgid "Has been Confirmed"
msgstr "Wurde bestätigt"
#: mahjong_ranking/models.py:173
#: mahjong_ranking/models.py:172
msgid "Only valid and confirmed Hanchans will be counted in the rating."
msgstr "Nur gültige und bestätigte Hanchans kommen in die Wertung."
#: mahjong_ranking/models.py:178 mahjong_ranking/models.py:565
#: mahjong_ranking/models.py:177 mahjong_ranking/models.py:576
#: mahjong_ranking/templates/mahjong_ranking/ladder_redbox.html:29
#: mahjong_ranking/templates/mahjong_ranking/player_ladder_score.html:63
msgid "Season"
msgstr "Saison"
#: mahjong_ranking/models.py:183
#: mahjong_ranking/models.py:182
msgid "Hanchan"
msgstr "Hanchan"
#: mahjong_ranking/models.py:184
#: mahjong_ranking/models.py:183
#: mahjong_ranking/templates/mahjong_ranking/eventranking_list.html:17
msgid "Hanchans"
msgstr "Hanchans"
#: mahjong_ranking/models.py:187
#: mahjong_ranking/models.py:186
msgid "Hanchan from {0:%Y-%m-%d} at {0:%H:%M} with {1}"
msgstr "Hanchan vom {0:%Y-%m-%d} um {0:%H:%M} mit {1}"
#: mahjong_ranking/models.py:214
#: mahjong_ranking/models.py:213
#, python-format
msgid "%s can't attend the same game multiple times"
msgstr "%s kann an einem Spiel nicht mehrfach teilnehmen."
#: mahjong_ranking/models.py:222
#: mahjong_ranking/models.py:221
msgid "Games in the future may not be added, Dr. Brown"
msgstr "Spiele aus der Zukunft dürfen noch nicht erfasst werden. Dr. Brown."
#: mahjong_ranking/models.py:224
#: mahjong_ranking/models.py:223
msgid "Only games during the event are allowed"
msgstr "Nur Spiele während der Veranstaltung zählen."
#: mahjong_ranking/models.py:227
#: mahjong_ranking/models.py:226
msgid "Gamescore is lower then 100.000 Pt."
msgstr "Spielstand ist weniger als 100.000 Punkte"
#: mahjong_ranking/models.py:229
#: mahjong_ranking/models.py:228
msgid "Gamescore is over 100.000 Pt."
msgstr "Spielstand ist über 100.000 Punkte."
#: mahjong_ranking/models.py:353
#: mahjong_ranking/models.py:352
msgid "Kyū/Dan Ranking"
msgstr "Kyū/Dan Wertung"
#: mahjong_ranking/models.py:354
#: mahjong_ranking/models.py:353
msgid "Kyū/Dan Rankings"
msgstr "Kyū/Dan Wertungen"
@@ -362,7 +362,7 @@ msgstr "Ladder Wertung für"
msgid "Hanchans that apply to the Ladder Score"
msgstr "Hanchans welche in der Ladder zählen"
#: mahjong_ranking/templates/mahjong_ranking/player_ladder_score.html:69
#: mahjong_ranking/templates/mahjong_ranking/player_ladder_score.html:71
msgid "Go"
msgstr "Los"
@@ -374,17 +374,17 @@ msgstr "Ende"
msgid "Participants"
msgstr "Teilnehmer"
#: mahjong_ranking/views.py:97
#: mahjong_ranking/views.py:98
#, python-format
msgid "%s has been updated successfully."
msgstr "%s wurde erfolgreich aktualisiert."
#: mahjong_ranking/views.py:100
#: mahjong_ranking/views.py:101
#, python-format
msgid "%s has been added successfully. You can now add a new one."
msgstr "%s wurde erfolgreich hinzugefügt. Du kannst eine neue eintragen."
#: mahjong_ranking/views.py:118 mahjong_ranking/views.py:134
#: mahjong_ranking/views.py:119 mahjong_ranking/views.py:135
msgid "Event does not exist"
msgstr "Veranstaltung existiert nicht"

View File

@@ -1,18 +1,20 @@
# -*- coding: utf-8 -*-
"""
Export Mahjong Rankings...
"""
"""Export Mahjong Rankings as excel files."""
from operator import itemgetter
from django.core.management.base import BaseCommand
from mahjong_ranking.models import SeasonRanking
from openpyxl import Workbook
from mahjong_ranking.models import SeasonRanking
def geneate_seasonexcel(json_data):
wb = Workbook()
worksheet = wb.active
"""Generate an excel .xlsx spreadsheet from json data of the kyu/dan
rankings.
:param json_data: The ladder ranking as JSON export."""
workbook = Workbook()
worksheet = workbook.active
worksheet.append([
'Rang', 'Spitzname',
@@ -20,7 +22,6 @@ def geneate_seasonexcel(json_data):
'Hanchans', 'Gut', 'Gewonnen'
])
json_data = json_data.values()
json_data = sorted(json_data, key=itemgetter('placement'))
for row in json_data:
worksheet.append([
@@ -29,13 +30,13 @@ def geneate_seasonexcel(json_data):
row['hanchan_count'],
row['good_hanchans'], row['won_hanchans']
])
wb.save("sample.xlsx")
workbook.save("sample.xlsx")
class Command(BaseCommand):
help = "Exports the SeasonRankings"
"""Exports the SeasonRankings"""
def handle(self, *args, **options):
season_json = SeasonRanking.objects.json_data()
geneate_seasonexcel(season_json)
"""Exports the current ladder ranking in a spreadsheet.
This is useful as a backup in form of a hardcopy."""
geneate_seasonexcel(SeasonRanking.objects.json_data())

View File

@@ -9,7 +9,9 @@ from mahjong_ranking import models
class Command(BaseCommand):
help = "Recalculate all Rankings"
""" Recalculate all Kyu/Dan Rankings """
help = "Recalculate all Kyu/Dan Rankings"
def handle(self, *args, **options):
for ranking in models.KyuDanRanking.objects.all():

View File

@@ -1,100 +1,150 @@
__author__ = 'christian'
"""ObjectManagers for the Django Models used in the Mahjong-Ranking."""
from datetime import date
from django.db import models
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_hanchans(self, user=None, **kwargs):
def confirmed_hanchans(self, user=None, **filter_args):
""" Return all valid and confirmed Hanchans.
: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=True, **kwargs)
return self.user_hanchans(user, confirmed=True, **filter_args)
else:
return self.filter(confirmed=True, **kwargs)
return self.filter(confirmed=True, **filter_args)
def dan_hanchans(self, user, **kwargs):
def dan_hanchans(self, user, **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 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, **kwargs)
).filter(confirmed=True, **filter_args)
queryset = queryset.select_related().order_by('-start')
for hanchan in queryset:
hanchan.get_playerdata(user)
[ hanchan.get_playerdata(user) for hanchan in queryset ]
return queryset
def kyu_hanchans(self, user, **kwargs):
def kyu_hanchans(self, user, **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 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, **kwargs)
).filter(confirmed=True, **filter_args)
queryset = queryset.select_related().order_by('-start')
for hanchan in queryset:
hanchan.get_playerdata(user)
[ hanchan.get_playerdata(user) for hanchan in queryset ]
return queryset
def season_hanchans(self, user=None, season=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
"""
season = season or date.today().year
return self.confirmed_hanchans(user=user, season=season)
def user_hanchans(self, user, since=None, **kwargs):
"""
:param user: User Object, or an player_id as integer
def user_hanchans(self, user, since=None, **filter_args):
"""Return all Hanchans where a specific user has participated.
:param user: Return Hanchans where this user participated.
:param since: optional a date value since when you want to hanchans
:return:
: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)
)
if since:
queryset = queryset.filter(start__gte=since)
if kwargs:
queryset = queryset.filter(**kwargs)
queryset = queryset.filter(start__gte=since, **filter_args)
else:
queryset = queryset.filter(**filter_args)
queryset = queryset.select_related().order_by('-start')
for hanchan in queryset:
hanchan.get_playerdata(user)
[ hanchan.get_playerdata(user) for hanchan in queryset ]
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 unconfirmed_hanchans(self, user=None, **filter_args):
""" Return all Hanchans that have been set to unconfirmed.
def unconfirmed_hanchans(self, user=None, **kwargs):
: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, **kwargs)
return self.user_hanchans(user, confirmed=False, **filter_args)
else:
return self.filter(confirmed=False, **kwargs)
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 = {}
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',
'user__first_name', 'user__last_name',
'avg_placement', 'avg_score',
'hanchan_count', 'good_hanchans', 'won_hanchans')
for user in values:
json_data[user['user_id']] = {
json_data.append({
'placement': user['placement'],
'user_id': user['user_id'],
'username': user['user__username'],
@@ -105,5 +155,5 @@ class SeasonRankingManager(models.Manager):
'hanchan_count': user['hanchan_count'],
'good_hanchans': user['good_hanchans'],
'won_hanchans': user['won_hanchans']
}
})
return json_data

View File

@@ -1,78 +1,53 @@
# -*- coding: utf-8 -*-
"""
Created on 23.05.2011
@author: christian
"""
"""Middleware to defer slow denormalization at the end of a request."""
from django.core.cache import cache
from mahjong_ranking import models
from . import logger, MIN_HANCHANS_FOR_LADDER
class DenormalizationUpdateMiddleware(object):
"""
This Class deferres the recalculation for the Otaku XP at the end of a
response.
"""
event_queue = set()
season_queue = set()
class DenormalizationUpdateMiddleware(object): # Ignore PyLintBear (R0903)
"""To recalculate everything in the queues at the end of a POST request."""
def process_response(self, request, response):
# We only do this in POST request, to speedup the responsetime.
if request.method == 'POST':
print('event_ranking_queue', cache.get(
'event_ranking_queue', set()))
print('kyu_dan_ranking_queue', cache.get(
'kyu_dan_ranking_queue', set()))
print('ladder_ranking_queue', cache.get(
'ladder_ranking_queue', set()))
queue = cache.get('event_ranking_queue', set())
if len(queue) > 0:
self.recalculate_event_rankings(queue)
cache.set('event_ranking_queue', set(), 360)
def process_response(self, request, response): # Ignore PyLintBear (R0201)
"""Check and process the recalculation queues on each POST request.
queue = cache.get('kyu_dan_ranking_queue', set())
if len(queue) > 0:
self.recalulate_kyu_dan_ranking(queue)
cache.set('kyu_dan_ranking_queue', set(), 360)
:param request: the django HttpRequest object
:param response: the django HttpResponse object
:return: the django HttpResponse object
"""
event_queue = set()
season_queue = set()
queue = cache.get('ladder_ranking_queue', set())
if len(queue) > 0:
self.recalculate_ladder_ranking(queue)
cache.set('ladder_ranking_queue', set(), 360)
if request.method != 'POST':
return response
for event_id in self.event_queue:
self.update_event_placements()
for season in self.season_queue:
self.update_season_placements()
return response
def recalculate_event_rankings(self, queue):
# recalculate tournament (event) rankings:
for event_id, user_id in queue:
ranking = models.EventRanking.objects.get_or_create(
event_ranking_queue = cache.get('event_ranking_queue', set())
while event_ranking_queue:
event_id, user_id = event_ranking_queue.pop()
event_ranking = models.EventRanking.objects.get_or_create(
event_id=event_id, user_id=user_id)[0]
ranking.recalculate()
self.event_queue.add(event_id)
return queue
event_ranking.recalculate()
event_queue.add(event_id)
cache.set('event_ranking_queue', set(), 360)
def recalulate_kyu_dan_ranking(self, queue):
for user_id, hanchan_start in queue:
ranking = models.KyuDanRanking.objects.get_or_create(
kyu_dan_ranking_queue = cache.get('kyu_dan_ranking_queue', set())
while kyu_dan_ranking_queue:
user_id, hanchan_start = kyu_dan_ranking_queue.pop()
kyu_dan_ranking = models.KyuDanRanking.objects.get_or_create(
user_id=user_id)[0]
ranking.recalculate(hanchan_start)
return queue
kyu_dan_ranking.recalculate(hanchan_start)
cache.set('kyu_dan_ranking_queue', set(), 360)
def recalculate_ladder_ranking(self, queue):
for season, user_id in queue:
ladder = models.SeasonRanking.objects.get_or_create(
ladder_ranking_queue = cache.get('ladder_ranking_queue', set())
while ladder_ranking_queue:
season, user_id = ladder_ranking_queue.pop()
ladder_ranking = models.SeasonRanking.objects.get_or_create(
user_id=user_id, season=season)[0]
ladder.recalculate()
self.season_queue.add(season)
ladder_ranking.recalculate()
season_queue.add(season)
cache.set('ladder_ranking_queue', ladder_ranking_queue, 360)
def update_event_placements(self):
for event_id in self.event_queue:
for event_id in event_queue:
eventranking_set = models.EventRanking.objects.filter(
event_id=event_id).order_by('avg_placement', '-avg_score')
placement = 1
@@ -81,8 +56,7 @@ class DenormalizationUpdateMiddleware(object):
placement += 1
ranking.save(force_update=True)
def update_season_placements(self):
for season in self.season_queue:
for season in season_queue:
logger.info(u'Recalculate placements for Season %d', season)
season_rankings = models.SeasonRanking.objects.filter(
season=season, hanchan_count__gt=MIN_HANCHANS_FOR_LADDER
@@ -92,3 +66,4 @@ class DenormalizationUpdateMiddleware(object):
ranking.placement = placement
ranking.save(force_update=True)
placement += 1
return response

View File

@@ -167,11 +167,11 @@ class Hanchan(models.Model):
_('Comment'), blank=True, max_length=255, editable=False)
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.')
)
confirmed = models.BooleanField(
_('Has been Confirmed'), default=True,
help_text=_('Only valid and confirmed Hanchans will be counted in the '
'rating.')
)
player_names = models.CharField(max_length=255, editable=False)
season = models.PositiveSmallIntegerField(
_('Season'), editable=False, db_index=True)
@@ -622,17 +622,17 @@ def update_ranking(sender, instance, **kwargs):
{'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)
set_dirty(event=instance.event_id, user=user.id)
if instance.season:
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)
set_dirty(season=instance.season)
logger.debug("marking event %s for recalculation.", instance.event)
set_dirty(event=instance.event_id, user=user.id)
models.signals.pre_delete.connect(update_ranking, sender=Hanchan)
# models.signals.post_save.connect(update_ranking, sender=Hanchan)

View File

@@ -25,12 +25,11 @@ class KyuDanTest(TestCase):
'test_kyu_dan_rankings.json'
]
def recalc(self):
"""
Test if a Kyu/Dan Ranking recalculation gives the same result as stored.
:return:
Test if a Kyu/Dan Ranking recalculation gives the same result as stored.
:return:
"""
for ranking in KyuDanRanking.objects.all():
@@ -49,8 +48,8 @@ class KyuDanTest(TestCase):
def test_partial_recalc(self):
"""
Test if partial recalclulation gives the same results.
:return:
:return:
"""
for ranking in KyuDanRanking.objects.all():
@@ -78,8 +77,8 @@ class KyuDanTest(TestCase):
def test_points_sum(self):
"""
Test if the sum of the kyu / dan points equals the value in the Ranking.
:return: None
:return: None
"""
for ranking in KyuDanRanking.objects.all():
dan_kyu = 'dan_points' if ranking.dan else 'kyu_points'

View File

@@ -1,19 +1,13 @@
# -*- encoding: utf-8 -*-
""" URLS to display the Riichi Mahjong Rankings and the Ladder system."""
"""
Created on 03.10.2011
@author: christian
"""
from django.views.generic import RedirectView
from django.conf.urls import url
from django.views.generic import RedirectView
from . import views
urlpatterns = [
url(r'^$', RedirectView.as_view(
url='/ranking/mahjong-ladder/', permanent=True)
),
urlpatterns = [ # Ignore PyLintBear (C0103)
url(r'^$',
RedirectView.as_view(url='/ranking/mahjong-ladder/', permanent=True)),
url(r'^event/(?P<event>[\d]+)/mahjong/$',
views.EventHanchanList.as_view(), name="event-hanchan-list"),
url(r'^event/(?P<event>[\d]+)/add-hanchan/$',

View File

@@ -1,18 +1,19 @@
# -*- encoding: utf-8 -*-
from datetime import date
from django.contrib import auth
from django.core.urlresolvers import reverse
import django.forms
import django.http
from django.contrib import auth
from django.contrib.auth.mixins import LoginRequiredMixin, \
PermissionRequiredMixin
from django.contrib.messages.views import SuccessMessageMixin
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
from django.views import generic
from events.models import Event
from events.views import EventDetailMixin
from events.mixins import EventDetailMixin
from . import forms, models
from utils.mixins import LoginRequiredMixin, PermissionRequiredMixin
kyu_dan_order = {
'+full_name': ('user__last_name', 'user__first_name'),
@@ -135,7 +136,6 @@ class EventRankingList(EventDetailMixin, generic.ListView):
class MahjongMixin(object):
def get_context_data(self, **kwargs):
context = super(MahjongMixin, self).get_context_data(**kwargs)
try:
@@ -149,7 +149,7 @@ class MahjongMixin(object):
'latest_hanchan_list'] = \
models.Hanchan.objects.confirmed_hanchans()[
:3]
context['latest_event_list'] = Event.objects.latest_events(num=3)
context['latest_event_list'] = Event.objects.upcoming(limit=3)
return context
@@ -239,6 +239,7 @@ class PlayerKyuScore(PlayerScore):
class PlayerLadderScore(PlayerScore):
season = None
template_name = 'mahjong_ranking/player_ladder_score.html'
def get_context_data(self, **kwargs):
@@ -250,10 +251,9 @@ class PlayerLadderScore(PlayerScore):
return context
def get_queryset(self, **kwargs):
try:
self.season = int(self.request.GET.get('season'))
except:
self.season = date.today().year
self.season = int(self.request.GET.get('season', date.today().year))
hanchan_list = models.Hanchan.objects.season_hanchans(
user=self.user, season=self.season)
user=self.user,
season=self.season
)
return hanchan_list