Milestone 08-14
* Mahjong Ranking wurde stark vereinfacht um Fehler besser vorzubeugen. * Online WYSIWYG Editor auf CKEditor umgeändert, damit online bearbeiten für unbedarfte besser funktioniert. * Viele kleine Optimierungen am CSS für bessere Performance. * CSS wird jetzt aus LESS Code generiert * Für dise Arbeit wird jetzt grunt und node package management lokal verwendet.
This commit is contained in:
@@ -10,7 +10,6 @@ from django.contrib import admin
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from . import models, set_dirty
|
||||
from forms import PlayerFormSet
|
||||
|
||||
|
||||
def recalculate(modeladmin, request, queryset):
|
||||
@@ -23,27 +22,25 @@ def recalculate(modeladmin, request, queryset):
|
||||
elif isinstance(modeladmin, KyuDanAdmin):
|
||||
for kyu_dan_ranking in queryset:
|
||||
set_dirty(user=kyu_dan_ranking.user_id)
|
||||
elif isinstance(modeladmin, LadderSeasonAdmin):
|
||||
for ranking_season in queryset:
|
||||
set_dirty(season=ranking_season.id)
|
||||
elif isinstance(modeladmin, LadderRankingAdmin):
|
||||
elif isinstance(modeladmin, SeasonRankingAdmin):
|
||||
for ladder_ranking in queryset:
|
||||
set_dirty(user=ladder_ranking.user_id,
|
||||
season=ladder_ranking.season_id) # @IgnorePep8
|
||||
|
||||
|
||||
set_dirty(user=ladder_ranking.user_id, season=ladder_ranking.season)
|
||||
recalculate.short_description = _("Recalculate")
|
||||
|
||||
|
||||
class PlayerInline(admin.TabularInline):
|
||||
extra = 4
|
||||
formset = PlayerFormSet
|
||||
readonly_fields = ('game_score', 'placement', 'kyu_points', 'dan_points', 'bonus_points',
|
||||
'comment',)
|
||||
max_num = 4
|
||||
model = models.Player
|
||||
def confirm(modeladmin, request, queryset):
|
||||
for hanchan in queryset:
|
||||
hanchan.confirmed = True
|
||||
hanchan.save()
|
||||
confirm.short_description = _("Confirm")
|
||||
|
||||
|
||||
def unconfirm(modeladmin, request, queryset):
|
||||
for hanchan in queryset:
|
||||
hanchan.confirmed = False
|
||||
hanchan.save()
|
||||
unconfirm.short_description = _('Set unconfirmed')
|
||||
|
||||
class EventRankingAdmin(admin.ModelAdmin):
|
||||
list_filter = ['event']
|
||||
list_display = ('placement', 'user', 'event', 'avg_placement', 'avg_score',
|
||||
@@ -53,18 +50,27 @@ class EventRankingAdmin(admin.ModelAdmin):
|
||||
|
||||
|
||||
class HanchanAdmin(admin.ModelAdmin):
|
||||
actions = [recalculate]
|
||||
actions = [recalculate, confirm, unconfirm]
|
||||
date_hierarchy = 'start'
|
||||
list_filter = ['season', 'event']
|
||||
list_display = ('event', 'start', 'player_names', 'comment',
|
||||
'confirmed', 'valid', 'check_validity')
|
||||
inlines = (PlayerInline,)
|
||||
readonly_fields = ('valid', 'check_validity')
|
||||
|
||||
def save_formset(self, request, form, formset, change):
|
||||
formset.save()
|
||||
form.is_valid()
|
||||
form.save()
|
||||
list_filter = ('season', 'event', 'confirmed')
|
||||
search_fields = ('player_names',)
|
||||
list_display = ('event', 'start', 'player_names', 'comment', 'confirmed')
|
||||
readonly_fields = ('season', 'player1_comment', 'player2_comment',
|
||||
'player3_comment', 'player4_comment')
|
||||
fieldsets = (
|
||||
('Hanchan', {
|
||||
'fields': (
|
||||
('event', 'season'),
|
||||
'start',
|
||||
('player1', 'player1_input_score', 'player1_comment'),
|
||||
('player2', 'player2_input_score', 'player2_comment'),
|
||||
('player3', 'player3_input_score', 'player3_comment'),
|
||||
('player4', 'player4_input_score', 'player4_comment'),
|
||||
'comment', 'confirmed'
|
||||
),
|
||||
'classes': ('grp-collapse grp-open',),
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
class KyuDanAdmin(admin.ModelAdmin):
|
||||
@@ -84,7 +90,7 @@ class KyuDanAdmin(admin.ModelAdmin):
|
||||
)
|
||||
|
||||
|
||||
class LadderRankingAdmin(admin.ModelAdmin):
|
||||
class SeasonRankingAdmin(admin.ModelAdmin):
|
||||
actions = [recalculate]
|
||||
list_display = ('placement', 'season', 'user', 'avg_placement',
|
||||
'avg_score', 'hanchan_count', 'good_hanchans',
|
||||
@@ -98,8 +104,7 @@ class LadderSeasonAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'start', 'end')
|
||||
|
||||
|
||||
admin.site.register(models.EventRanking, EventRankingAdmin)
|
||||
admin.site.register(models.Hanchan, HanchanAdmin)
|
||||
admin.site.register(models.KyuDanRanking, KyuDanAdmin)
|
||||
admin.site.register(models.LadderSeason, LadderSeasonAdmin)
|
||||
admin.site.register(models.LadderRanking, LadderRankingAdmin)
|
||||
admin.site.register(models.SeasonRanking, SeasonRankingAdmin)
|
||||
admin.site.register(models.EventRanking, EventRankingAdmin)
|
||||
|
||||
@@ -7,14 +7,12 @@ Created on 04.10.2011
|
||||
"""
|
||||
from django.contrib.auth import get_user_model
|
||||
import django.forms
|
||||
from django.forms.models import BaseInlineFormSet, inlineformset_factory
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from utils.html5 import forms
|
||||
from . import models
|
||||
|
||||
|
||||
USER_MODEL = get_user_model()
|
||||
|
||||
|
||||
@@ -25,12 +23,24 @@ class HanchanForm(forms.ModelForm):
|
||||
|
||||
class Meta(object):
|
||||
model = models.Hanchan
|
||||
fields = ('event', 'start', 'comment')
|
||||
widgets = {
|
||||
'event': forms.HiddenInput(),
|
||||
'comment': forms.widgets.Textarea(attrs={'rows': 4, 'cols': 40})
|
||||
}
|
||||
fields = ( 'start',
|
||||
'player1', 'player1_input_score',
|
||||
'player2', 'player2_input_score',
|
||||
'player3', 'player3_input_score',
|
||||
'player4', 'player4_input_score',
|
||||
'comment')
|
||||
widgets = {'event': forms.HiddenInput(),
|
||||
'comment': forms.widgets.Textarea(attrs={'rows': 4, 'cols': 40})
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(HanchanForm, self).__init__(*args, **kwargs)
|
||||
# self.fields['event'].widget.attrs['disabled'] = True
|
||||
for i in xrange(1, 4):
|
||||
self.fields['player%d_input_score' % i].widget.attrs['size'] = 6
|
||||
self.fields['player%d_input_score' % i].widget.attrs['type'] = 'number'
|
||||
|
||||
'''
|
||||
def clean_start(self):
|
||||
u"""Das Datum darf nicht in der Zukunft liegen und es muss innerhalb
|
||||
der Dauer des Events liegen."""
|
||||
@@ -43,55 +53,14 @@ class HanchanForm(forms.ModelForm):
|
||||
raise django.forms.ValidationError(
|
||||
_("Only games running during this event are allowed."))
|
||||
return start
|
||||
|
||||
'''
|
||||
|
||||
class HanchanAdminForm(HanchanForm):
|
||||
class Meta(object):
|
||||
model = models.Hanchan
|
||||
fields = ('event', 'start', 'comment', 'confirmed')
|
||||
widgets = {
|
||||
'event': forms.HiddenInput(),
|
||||
'comment': forms.widgets.Textarea(attrs={'rows': 4, 'cols': 40})
|
||||
}
|
||||
fields = HanchanForm.Meta.fields + ('confirmed',)
|
||||
|
||||
|
||||
class PlayerForm(forms.ModelForm):
|
||||
error_css_class = 'error'
|
||||
required_css_class = 'required'
|
||||
comment = forms.CharField(
|
||||
widget=forms.widgets.TextInput(attrs={'maxlength': 255}),
|
||||
required=False
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PlayerForm, self).__init__(*args, **kwargs)
|
||||
self.fields['input_score'].widget.attrs['size'] = 6
|
||||
self.fields['input_score'].widget.attrs['type'] = 'number'
|
||||
self.fields['bonus_points'].widget.attrs['readonly'] = True
|
||||
self.fields['bonus_points'].widget.attrs['size'] = 2
|
||||
self.fields['comment'].widget.attrs['readonly'] = True
|
||||
|
||||
class Meta(object):
|
||||
model = models.Player
|
||||
fields = ('hanchan', 'user', 'input_score', 'bonus_points', 'comment')
|
||||
|
||||
|
||||
class PlayerInlineFormSet(BaseInlineFormSet):
|
||||
def clean(self):
|
||||
if any(self.errors):
|
||||
return
|
||||
playerset = set()
|
||||
for form in self.forms:
|
||||
if form.is_valid() and not form.cleaned_data.get('user'):
|
||||
raise forms.ValidationError(_("A valid Hanchan needs 4 players"))
|
||||
player = form.cleaned_data['user']
|
||||
if player in playerset:
|
||||
message = _("Player %s cant attend the game multiple times.") % player
|
||||
raise forms.ValidationError(message)
|
||||
playerset.add(player)
|
||||
self.validate_unique()
|
||||
return False
|
||||
|
||||
|
||||
class SeasonSelectForm(django.forms.Form):
|
||||
season = django.forms.ChoiceField(label='', choices=('a', 'b', 'c'))
|
||||
@@ -102,13 +71,3 @@ class SeasonSelectForm(django.forms.Form):
|
||||
season_list = season_list.select_related('user')
|
||||
season_list = season_list.values_list('season__id', 'season__name')
|
||||
self.fields['season'] = django.forms.ChoiceField(choices=season_list)
|
||||
|
||||
|
||||
PlayerFormSet = inlineformset_factory(
|
||||
models.Hanchan,
|
||||
models.Player,
|
||||
max_num=4,
|
||||
extra=4,
|
||||
form=PlayerForm,
|
||||
formset=PlayerInlineFormSet
|
||||
)
|
||||
|
||||
Binary file not shown.
@@ -7,167 +7,173 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: kasu.mahjong_ranking\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-08-16 11:34+0200\n"
|
||||
"PO-Revision-Date: 2015-08-16 11:39+0200\n"
|
||||
"Last-Translator: Christian Berg <xeniac.at@gmail.com>\n"
|
||||
"POT-Creation-Date: 2015-08-22 23:28+0200\n"
|
||||
"PO-Revision-Date: 2015-08-22 15:09+0100\n"
|
||||
"Last-Translator: Christian Berg <xeniac@posteo.at>\n"
|
||||
"Language-Team: Kasu <verein@kasu.at>\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Translated-Using: django-rosetta 0.7.2\n"
|
||||
"X-Generator: Poedit 1.8.3\n"
|
||||
"X-Translated-Using: django-rosetta 0.7.6\n"
|
||||
|
||||
#: admin.py:35
|
||||
#: admin.py:28
|
||||
msgid "Recalculate"
|
||||
msgstr "Neuberechnen"
|
||||
|
||||
#: forms.py:24
|
||||
#: admin.py:35
|
||||
msgid "Confirm"
|
||||
msgstr "Bestätigen"
|
||||
|
||||
#: admin.py:42
|
||||
msgid "Set unconfirmed"
|
||||
msgstr "Als unbestätigt markieren"
|
||||
|
||||
#: forms.py:22
|
||||
msgid "start"
|
||||
msgstr "Beginn"
|
||||
|
||||
#: forms.py:41 models.py:164
|
||||
msgid "It's not allowed to enter future games."
|
||||
msgstr "Spiele in der Zukunft sind nicht erlaubt."
|
||||
|
||||
#: forms.py:44
|
||||
msgid "Only games running during this event are allowed."
|
||||
msgstr "Nur Spiele während der Veranstaltung sind erlaubt."
|
||||
|
||||
#: forms.py:86
|
||||
msgid "A valid Hanchan needs 4 players"
|
||||
msgstr "Eine gültige Hanchan braucht 4 Spieler"
|
||||
|
||||
#: forms.py:89
|
||||
#, python-format
|
||||
msgid "Player %s cant attend the game multiple times."
|
||||
msgstr "%s kann an einem Spiel nicht mehrfach teilnehmen."
|
||||
|
||||
#: models.py:78 models.py:617 templates/mahjong_ranking/hanchan_form.html:26
|
||||
#: templates/mahjong_ranking/player_dan_score.html:19
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:18
|
||||
msgid "Comment"
|
||||
msgstr "Kommentar"
|
||||
|
||||
#: models.py:79
|
||||
msgid "Has been Confirmed"
|
||||
msgstr "Wurde bestätigt"
|
||||
|
||||
#: models.py:81
|
||||
msgid "Only valid and confirmed Hanchans will be counted in the rating."
|
||||
msgstr "Nur gültige und bestätigte Hanchans kommen in die Wertung."
|
||||
|
||||
#: models.py:87 templates/mahjong_ranking/hanchan_form.html:19
|
||||
#: templates/mahjong_ranking/player_dan_score.html:17
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:15
|
||||
#: templates/mahjong_ranking/player_kyu_score.html:18
|
||||
#: templates/mahjong_ranking/player_ladder_score.html:17
|
||||
msgid "Players"
|
||||
msgstr "Spieler"
|
||||
|
||||
#: models.py:91 templates/mahjong_ranking/ladderranking_list.html:8
|
||||
#: templates/mahjong_ranking/player_dan_score.html:15
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:14
|
||||
#: templates/mahjong_ranking/player_kyu_score.html:16
|
||||
#: models.py:79 templates/mahjong_ranking/player_dan_score.html:15
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:13
|
||||
#: templates/mahjong_ranking/player_kyu_score.html:15
|
||||
#: templates/mahjong_ranking/player_ladder_score.html:15
|
||||
#: templates/mahjong_ranking/seasonranking_list.html:9
|
||||
msgid "Start"
|
||||
msgstr "Beginn"
|
||||
|
||||
#: models.py:92
|
||||
#: models.py:80
|
||||
msgid "This is crucial to get the right Hanchans that scores"
|
||||
msgstr "Wichtig damit die richtigen Hanchans in die Wertung kommen."
|
||||
|
||||
#: models.py:93
|
||||
msgid "Is Valid"
|
||||
msgstr "Ist gültig"
|
||||
#: models.py:83
|
||||
msgid "Player 1"
|
||||
msgstr "Spieler 1"
|
||||
|
||||
#: models.py:97
|
||||
#: models.py:84 models.py:85 models.py:93 models.py:94 models.py:102
|
||||
#: models.py:103 models.py:111 models.py:112
|
||||
#: templates/mahjong_ranking/eventhanchan_list.html:18
|
||||
#: templates/mahjong_ranking/eventranking_list.html:22
|
||||
#: templates/mahjong_ranking/hanchan_confirm_delete.html:15
|
||||
#: templates/mahjong_ranking/hanchan_form.html:43
|
||||
#: templates/mahjong_ranking/kyudanranking_list.html:30
|
||||
#: templates/mahjong_ranking/seasonranking_list.html:30
|
||||
msgid "Score"
|
||||
msgstr "Punkte"
|
||||
|
||||
#: models.py:90 models.py:99 models.py:108 models.py:117 models.py:119
|
||||
#: templates/mahjong_ranking/player_dan_score.html:19
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:17
|
||||
msgid "Comment"
|
||||
msgstr "Kommentar"
|
||||
|
||||
#: models.py:92
|
||||
msgid "Player 2"
|
||||
msgstr "Spieler 2"
|
||||
|
||||
#: models.py:101
|
||||
msgid "Player 3"
|
||||
msgstr "Spieler 3"
|
||||
|
||||
#: models.py:110
|
||||
msgid "Player 4"
|
||||
msgstr "Spieler 4"
|
||||
|
||||
#: models.py:120
|
||||
msgid "Has been Confirmed"
|
||||
msgstr "Wurde bestätigt"
|
||||
|
||||
#: models.py:121
|
||||
msgid "Only valid and confirmed Hanchans will be counted in the rating."
|
||||
msgstr "Nur gültige und bestätigte Hanchans kommen in die Wertung."
|
||||
|
||||
#: models.py:124 models.py:480
|
||||
#: templates/mahjong_ranking/player_ladder_score.html:63
|
||||
#: templates/mahjong_ranking/seasonranking_list.html:77
|
||||
msgid "Season"
|
||||
msgstr "Saison"
|
||||
|
||||
#: models.py:129
|
||||
msgid "Hanchan"
|
||||
msgstr "Hanchan"
|
||||
|
||||
#: models.py:98 templates/mahjong_ranking/eventranking_list.html:18
|
||||
#: models.py:130 templates/mahjong_ranking/eventranking_list.html:18
|
||||
msgid "Hanchans"
|
||||
msgstr "Hanchans"
|
||||
|
||||
#: models.py:119
|
||||
msgid "For a Hanchan exactly 4 players are needed."
|
||||
msgstr "Eine Hanchan benötigt genau 4 Spieler."
|
||||
#: models.py:133
|
||||
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}"
|
||||
|
||||
#: models.py:160
|
||||
msgid "Hanchan has no event"
|
||||
msgstr "Hanchan gehört zu keiner Veranstaltung."
|
||||
#: models.py:158
|
||||
#, python-format
|
||||
msgid "%s can't attend the same game multiple times"
|
||||
msgstr "%s kann an einem Spiel nicht mehrfach teilnehmen."
|
||||
|
||||
#: models.py:162
|
||||
#, fuzzy
|
||||
#| msgid "Hanchan has no event"
|
||||
msgid "Hanchan has no start time set"
|
||||
msgstr "Hanchan gehört zu keiner Veranstaltung."
|
||||
#: models.py:165
|
||||
msgid "Games in the future may not be added, Dr. Brown"
|
||||
msgstr "Spiele aus der Zukunft dürfen noch nicht erfasst werden. Dr. Brown."
|
||||
|
||||
#: models.py:166
|
||||
#: models.py:167
|
||||
msgid "Only games during the event are allowed"
|
||||
msgstr "Nur Spiele während der Veranstaltung zählen."
|
||||
|
||||
#: models.py:236
|
||||
#: models.py:169
|
||||
msgid "Gamescore is lower then 100.000 Pt."
|
||||
msgstr "Spielstand ist weniger als 100.000 Punkte"
|
||||
|
||||
#: models.py:171
|
||||
msgid "Gamescore is over 100.000 Pt."
|
||||
msgstr "Spielstand ist über 100.000 Punkte."
|
||||
|
||||
#: models.py:285
|
||||
msgid "Kyū/Dan Ranking"
|
||||
msgstr "Kyū/Dan Wertung"
|
||||
|
||||
#: models.py:237
|
||||
#: models.py:286
|
||||
msgid "Kyū/Dan Rankings"
|
||||
msgstr "Kyū/Dan Wertungen"
|
||||
|
||||
#: models.py:505
|
||||
msgid "Ladder Season"
|
||||
msgstr "Saison"
|
||||
|
||||
#: models.py:506
|
||||
msgid "Ladder Seasons"
|
||||
msgstr "Saisons"
|
||||
|
||||
#: templates/mahjong_ranking/eventhanchan_list.html:7
|
||||
msgid "Played Hanchans"
|
||||
msgstr "Gespielte Hanchans"
|
||||
|
||||
#: templates/mahjong_ranking/eventhanchan_list.html:15
|
||||
#: templates/mahjong_ranking/eventhanchan_list.html:17
|
||||
#: templates/mahjong_ranking/hanchan_confirm_delete.html:14
|
||||
msgid "Place"
|
||||
msgstr "Platz"
|
||||
|
||||
#: templates/mahjong_ranking/eventhanchan_list.html:16
|
||||
#: templates/mahjong_ranking/eventranking_list.html:22
|
||||
#: templates/mahjong_ranking/hanchan_form.html:24
|
||||
#: templates/mahjong_ranking/kyudanranking_list.html:30
|
||||
#: templates/mahjong_ranking/ladderranking_list.html:29
|
||||
msgid "Score"
|
||||
msgstr "Punkte"
|
||||
|
||||
#: templates/mahjong_ranking/eventhanchan_list.html:18
|
||||
#: templates/mahjong_ranking/eventhanchan_list.html:20
|
||||
#: templates/mahjong_ranking/hanchan_confirm_delete.html:17
|
||||
#: templates/mahjong_ranking/player_dan_score.html:18
|
||||
msgid "Dan Points"
|
||||
msgstr "Dan Punkte"
|
||||
|
||||
#: templates/mahjong_ranking/eventhanchan_list.html:20
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:17
|
||||
#: templates/mahjong_ranking/player_kyu_score.html:19
|
||||
#: templates/mahjong_ranking/eventhanchan_list.html:22
|
||||
#: templates/mahjong_ranking/hanchan_confirm_delete.html:19
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:16
|
||||
#: templates/mahjong_ranking/player_kyu_score.html:18
|
||||
msgid "Kyu Points"
|
||||
msgstr "Kyu Punkte"
|
||||
|
||||
#: templates/mahjong_ranking/eventhanchan_list.html:34
|
||||
#: templates/mahjong_ranking/hanchan_confirm_delete.html:4
|
||||
#: templates/mahjong_ranking/hanchan_confirm_delete.html:10
|
||||
#: templates/mahjong_ranking/hanchan_confirm_delete.html:32
|
||||
#: templates/mahjong_ranking/player_dan_score.html:45
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:34
|
||||
#: templates/mahjong_ranking/player_kyu_score.html:42
|
||||
#: templates/mahjong_ranking/player_ladder_score.html:39
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:33
|
||||
#: templates/mahjong_ranking/player_kyu_score.html:41
|
||||
#: templates/mahjong_ranking/player_ladder_score.html:52
|
||||
msgid "Delete Hanchan"
|
||||
msgstr "Hanchan löschen"
|
||||
|
||||
#: templates/mahjong_ranking/eventhanchan_list.html:37
|
||||
#: templates/mahjong_ranking/hanchan_form.html:4
|
||||
#: templates/mahjong_ranking/hanchan_form.html:16
|
||||
#: templates/mahjong_ranking/hanchan_form.html:38
|
||||
#: templates/mahjong_ranking/player_dan_score.html:48
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:37
|
||||
#: templates/mahjong_ranking/player_kyu_score.html:45
|
||||
#: templates/mahjong_ranking/player_ladder_score.html:42
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:36
|
||||
#: templates/mahjong_ranking/player_kyu_score.html:44
|
||||
#: templates/mahjong_ranking/player_ladder_score.html:55
|
||||
msgid "Edit Hanchan"
|
||||
msgstr "Hanchan bearbeiten"
|
||||
|
||||
@@ -183,7 +189,7 @@ msgstr "Veranstaltung bearbeiten"
|
||||
#: templates/mahjong_ranking/eventhanchan_list.html:48
|
||||
#: templates/mahjong_ranking/eventranking_list.html:56
|
||||
#: templates/mahjong_ranking/hanchan_form.html:4
|
||||
#: templates/mahjong_ranking/hanchan_form.html:16
|
||||
#: templates/mahjong_ranking/hanchan_form.html:38
|
||||
msgid "Add Hanchan"
|
||||
msgstr "Hanchan hinzufügen"
|
||||
|
||||
@@ -194,77 +200,77 @@ msgstr "Turnierwertung"
|
||||
|
||||
#: templates/mahjong_ranking/eventranking_list.html:13
|
||||
#: templates/mahjong_ranking/kyudanranking_list.html:25
|
||||
#: templates/mahjong_ranking/ladderranking_list.html:20
|
||||
#: templates/mahjong_ranking/seasonranking_list.html:21
|
||||
msgid "Rank"
|
||||
msgstr "Rang"
|
||||
|
||||
#: templates/mahjong_ranking/eventranking_list.html:14
|
||||
#: templates/mahjong_ranking/kyudanranking_list.html:12
|
||||
#: templates/mahjong_ranking/ladderranking_list.html:21
|
||||
#: templates/mahjong_ranking/seasonranking_list.html:22
|
||||
msgid "Avatar"
|
||||
msgstr "Avatar"
|
||||
|
||||
#: templates/mahjong_ranking/eventranking_list.html:15
|
||||
#: templates/mahjong_ranking/kyudanranking_list.html:15
|
||||
#: templates/mahjong_ranking/ladderranking_list.html:22
|
||||
#: templates/mahjong_ranking/seasonranking_list.html:23
|
||||
msgid "Nickname"
|
||||
msgstr "Spitzname"
|
||||
|
||||
#: templates/mahjong_ranking/eventranking_list.html:16
|
||||
#: templates/mahjong_ranking/ladderranking_list.html:23
|
||||
#: templates/mahjong_ranking/seasonranking_list.html:24
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
#: templates/mahjong_ranking/eventranking_list.html:17
|
||||
#: templates/mahjong_ranking/ladderranking_list.html:24
|
||||
#: templates/mahjong_ranking/seasonranking_list.html:25
|
||||
msgid "Average"
|
||||
msgstr "Durchschnitt"
|
||||
|
||||
#: templates/mahjong_ranking/eventranking_list.html:21
|
||||
#: templates/mahjong_ranking/ladderranking_list.html:28
|
||||
#: templates/mahjong_ranking/player_dan_score.html:16
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:16
|
||||
#: templates/mahjong_ranking/player_kyu_score.html:17
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:15
|
||||
#: templates/mahjong_ranking/player_kyu_score.html:16
|
||||
#: templates/mahjong_ranking/player_ladder_score.html:16
|
||||
#: templates/mahjong_ranking/seasonranking_list.html:29
|
||||
msgid "Placement"
|
||||
msgstr "Platzierung"
|
||||
|
||||
#: templates/mahjong_ranking/eventranking_list.html:23
|
||||
#: templates/mahjong_ranking/ladderranking_list.html:30
|
||||
#: templates/mahjong_ranking/seasonranking_list.html:31
|
||||
msgid "count"
|
||||
msgstr "Anzahl"
|
||||
|
||||
#: templates/mahjong_ranking/eventranking_list.html:24
|
||||
#: templates/mahjong_ranking/ladderranking_list.html:31
|
||||
#: templates/mahjong_ranking/seasonranking_list.html:32
|
||||
msgid "good"
|
||||
msgstr "gut"
|
||||
|
||||
#: templates/mahjong_ranking/eventranking_list.html:25
|
||||
#: templates/mahjong_ranking/ladderranking_list.html:32
|
||||
#: templates/mahjong_ranking/seasonranking_list.html:33
|
||||
msgid "won"
|
||||
msgstr "gewonnen"
|
||||
|
||||
#: templates/mahjong_ranking/hanchan_confirm_delete.html:16
|
||||
#: templates/mahjong_ranking/hanchan_confirm_delete.html:38
|
||||
msgid "Cancel"
|
||||
msgstr "Abbruch"
|
||||
|
||||
#: templates/mahjong_ranking/hanchan_confirm_delete.html:17
|
||||
#: templates/mahjong_ranking/hanchan_confirm_delete.html:39
|
||||
msgid "Delete"
|
||||
msgstr "Löschen"
|
||||
|
||||
#: templates/mahjong_ranking/hanchan_form.html:23
|
||||
msgid "User"
|
||||
msgstr "Benutzer"
|
||||
#: templates/mahjong_ranking/hanchan_form.html:42
|
||||
msgid "Player"
|
||||
msgstr "Spieler"
|
||||
|
||||
#: templates/mahjong_ranking/hanchan_form.html:25
|
||||
msgid "Bonus"
|
||||
msgstr "Bonus"
|
||||
#: templates/mahjong_ranking/hanchan_form.html:65
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
#: templates/mahjong_ranking/hanchan_form.html:46
|
||||
#: templates/mahjong_ranking/hanchan_form.html:77
|
||||
msgid "back"
|
||||
msgstr "Zurück"
|
||||
|
||||
#: templates/mahjong_ranking/hanchan_form.html:47
|
||||
#: templates/mahjong_ranking/hanchan_form.html:78
|
||||
msgid "save"
|
||||
msgstr "Speichern"
|
||||
|
||||
@@ -281,39 +287,6 @@ msgstr "Voller Name"
|
||||
msgid "Games Total"
|
||||
msgstr "Spiele total"
|
||||
|
||||
#: templates/mahjong_ranking/ladderranking_list.html:9
|
||||
msgid "End"
|
||||
msgstr "Ende"
|
||||
|
||||
#: templates/mahjong_ranking/ladderranking_list.html:10
|
||||
msgid "Participants"
|
||||
msgstr "Teilnehmer"
|
||||
|
||||
#: templates/mahjong_ranking/ladderranking_list.html:59
|
||||
msgid "Latest Hanchans"
|
||||
msgstr "Letzten Hanchans"
|
||||
|
||||
#: templates/mahjong_ranking/ladderranking_list.html:68
|
||||
msgid "Latest Events"
|
||||
msgstr "Letzte Veranstaltungen"
|
||||
|
||||
#: templates/mahjong_ranking/ladderranking_list.html:74
|
||||
msgid "Ladder Archive"
|
||||
msgstr "Ladder Archiv"
|
||||
|
||||
#: templates/mahjong_ranking/ladderranking_list.html:76
|
||||
#: templates/mahjong_ranking/player_ladder_score.html:51
|
||||
msgid "Season"
|
||||
msgstr "Saison"
|
||||
|
||||
#: templates/mahjong_ranking/page.html:5
|
||||
msgid "Archive"
|
||||
msgstr "Archiv"
|
||||
|
||||
#: templates/mahjong_ranking/page.html:7
|
||||
msgid "Add Event"
|
||||
msgstr "Veranstaltung hinzufügen"
|
||||
|
||||
#: templates/mahjong_ranking/player_dan_score.html:4
|
||||
#: templates/mahjong_ranking/player_dan_score.html:5
|
||||
msgid "Dan Score for"
|
||||
@@ -324,30 +297,40 @@ msgid "Hanchans that apply to the Dan Score"
|
||||
msgstr "Hanchans welche zur Dan Wertung zählen"
|
||||
|
||||
#: templates/mahjong_ranking/player_dan_score.html:13
|
||||
#: templates/mahjong_ranking/player_kyu_score.html:14
|
||||
#: templates/mahjong_ranking/player_kyu_score.html:13
|
||||
#: templates/mahjong_ranking/player_ladder_score.html:13
|
||||
msgid "Date"
|
||||
msgstr "Datum"
|
||||
|
||||
#: templates/mahjong_ranking/player_dan_score.html:14
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:13
|
||||
#: templates/mahjong_ranking/player_kyu_score.html:15
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:12
|
||||
#: templates/mahjong_ranking/player_kyu_score.html:14
|
||||
#: templates/mahjong_ranking/player_ladder_score.html:14
|
||||
msgid "Event"
|
||||
msgstr "Veranstaltung"
|
||||
|
||||
#: templates/mahjong_ranking/player_dan_score.html:17
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:14
|
||||
#: templates/mahjong_ranking/player_kyu_score.html:17
|
||||
#: templates/mahjong_ranking/player_ladder_score.html:17
|
||||
msgid "Players"
|
||||
msgstr "Spieler"
|
||||
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:4
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:6
|
||||
msgid "Unconfirmed Hanchans from"
|
||||
msgstr "Nicht bestätigte Hanchans von"
|
||||
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:9
|
||||
msgid "Invalid hanchans with"
|
||||
msgstr "Ungültige Hanchans mit"
|
||||
|
||||
#: templates/mahjong_ranking/player_kyu_score.html:4
|
||||
#: templates/mahjong_ranking/player_kyu_score.html:6
|
||||
msgid "Kyu Score for"
|
||||
msgstr "Kyu Wertung für"
|
||||
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:6
|
||||
#: templates/mahjong_ranking/player_invalid_score.html:10
|
||||
msgid "Invalid hanchans with"
|
||||
msgstr "Ungültige Hanchans mit"
|
||||
|
||||
#: templates/mahjong_ranking/player_kyu_score.html:10
|
||||
#: templates/mahjong_ranking/player_kyu_score.html:9
|
||||
msgid "Hanchans that apply to the Kyu Score"
|
||||
msgstr "Hanchans welche zur Kyu Wertung zählen"
|
||||
|
||||
@@ -356,27 +339,94 @@ msgstr "Hanchans welche zur Kyu Wertung zählen"
|
||||
msgid "Ladder Score for"
|
||||
msgstr "Ladder Wertung für"
|
||||
|
||||
#: templates/mahjong_ranking/player_ladder_score.html:9
|
||||
#: templates/mahjong_ranking/player_ladder_score.html:8
|
||||
msgid "Hanchans that apply to the Ladder Score"
|
||||
msgstr "Hanchans welche in der Ladder zählen"
|
||||
|
||||
#: templates/mahjong_ranking/player_ladder_score.html:51
|
||||
#: templates/mahjong_ranking/player_ladder_score.html:69
|
||||
msgid "Go"
|
||||
msgstr "Los"
|
||||
|
||||
#: views.py:149 views.py:166
|
||||
#: templates/mahjong_ranking/seasonranking_list.html:10
|
||||
msgid "End"
|
||||
msgstr "Ende"
|
||||
|
||||
#: templates/mahjong_ranking/seasonranking_list.html:11
|
||||
msgid "Participants"
|
||||
msgstr "Teilnehmer"
|
||||
|
||||
#: templates/mahjong_ranking/seasonranking_list.html:60
|
||||
msgid "Latest Hanchans"
|
||||
msgstr "Letzten Hanchans"
|
||||
|
||||
#: templates/mahjong_ranking/seasonranking_list.html:69
|
||||
msgid "Latest Events"
|
||||
msgstr "Letzte Veranstaltungen"
|
||||
|
||||
#: templates/mahjong_ranking/seasonranking_list.html:75
|
||||
msgid "Ladder Archive"
|
||||
msgstr "Ladder Archiv"
|
||||
|
||||
#: views.py:94
|
||||
#, python-format
|
||||
msgid "%s has been updated successfully."
|
||||
msgstr "%s wurde erfolgreich aktualisiert."
|
||||
|
||||
#: views.py:96
|
||||
#, 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 anlagen."
|
||||
|
||||
#: views.py:113 views.py:129
|
||||
msgid "Event does not exist"
|
||||
msgstr "Veranstaltung existiert nicht"
|
||||
|
||||
#: views.py:207
|
||||
msgid "Season does not exist"
|
||||
msgstr "Saison existiert nicht"
|
||||
|
||||
#: views.py:300
|
||||
#, python-format
|
||||
msgid "No user found matching the name %s"
|
||||
#: views.py:181
|
||||
msgid "No user found matching the name {}"
|
||||
msgstr "Kein Benutzer mit dem Namen %s gefunden"
|
||||
|
||||
#~ msgid "It's not allowed to enter future games."
|
||||
#~ msgstr "Spiele in der Zukunft sind nicht erlaubt."
|
||||
|
||||
#~ msgid "Only games running during this event are allowed."
|
||||
#~ msgstr "Nur Spiele während der Veranstaltung sind erlaubt."
|
||||
|
||||
#~ msgid "A valid Hanchan needs 4 players"
|
||||
#~ msgstr "Eine gültige Hanchan braucht 4 Spieler"
|
||||
|
||||
#~ msgid "Is Valid"
|
||||
#~ msgstr "Ist gültig"
|
||||
|
||||
#~ msgid "For a Hanchan exactly 4 players are needed."
|
||||
#~ msgstr "Eine Hanchan benötigt genau 4 Spieler."
|
||||
|
||||
#~ msgid "Hanchan has no event"
|
||||
#~ msgstr "Hanchan gehört zu keiner Veranstaltung."
|
||||
|
||||
#~ msgid "Hanchan has no start time set"
|
||||
#~ msgstr "Hanchan gehört zu keiner Veranstaltung."
|
||||
|
||||
#~ msgid "Ladder Season"
|
||||
#~ msgstr "Saison"
|
||||
|
||||
#~ msgid "Ladder Seasons"
|
||||
#~ msgstr "Saisons"
|
||||
|
||||
#~ msgid "User"
|
||||
#~ msgstr "Benutzer"
|
||||
|
||||
#~ msgid "Bonus"
|
||||
#~ msgstr "Bonus"
|
||||
|
||||
#~ msgid "Archive"
|
||||
#~ msgstr "Archiv"
|
||||
|
||||
#~ msgid "Add Event"
|
||||
#~ msgstr "Veranstaltung hinzufügen"
|
||||
|
||||
#~ msgid "Season does not exist"
|
||||
#~ msgstr "Saison existiert nicht"
|
||||
|
||||
#~ msgid "Edit"
|
||||
#~ msgstr "Bearbeiten"
|
||||
|
||||
|
||||
79
src/mahjong_ranking/managers.py
Normal file
79
src/mahjong_ranking/managers.py
Normal file
@@ -0,0 +1,79 @@
|
||||
__author__ = 'christian'
|
||||
|
||||
from datetime import date
|
||||
|
||||
from django.db import models
|
||||
|
||||
|
||||
class HanchanManager(models.Manager):
|
||||
use_for_related_fields = True
|
||||
|
||||
def confirmed_hanchans(self, user=None, **kwargs):
|
||||
if user:
|
||||
return self.user_hanchans(user, confirmed=True, **kwargs)
|
||||
else:
|
||||
return self.filter(confirmed=True, **kwargs)
|
||||
|
||||
def dan_hanchans(self, user, **kwargs):
|
||||
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)
|
||||
queryset = queryset.select_related().order_by('-start')
|
||||
for hanchan in queryset:
|
||||
hanchan.get_playerdata(user)
|
||||
return queryset
|
||||
|
||||
def kyu_hanchans(self, user, **kwargs):
|
||||
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)
|
||||
queryset = queryset.select_related().order_by('-start')
|
||||
for hanchan in queryset:
|
||||
hanchan.get_playerdata(user)
|
||||
return queryset
|
||||
|
||||
def season_hanchans(self, user=None, season=None):
|
||||
season = season or date.today().year
|
||||
return self.confirmed_hanchans(user=user, season=season)
|
||||
|
||||
def user_hanchans(self, user, **kwargs):
|
||||
queryset = self.filter(
|
||||
models.Q(player1=user) | models.Q(player2=user) |
|
||||
models.Q(player3=user) | models.Q(player4=user)
|
||||
)
|
||||
queryset = queryset.select_related().order_by('-start')
|
||||
if kwargs:
|
||||
queryset = queryset.filter(**kwargs)
|
||||
for hanchan in queryset:
|
||||
hanchan.get_playerdata(user)
|
||||
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, **kwargs):
|
||||
if user:
|
||||
return self.user_hanchans(user, confirmed=False, **kwargs)
|
||||
else:
|
||||
return self.filter(confirmed=False, **kwargs)
|
||||
|
||||
|
||||
class SeasonRankingManager(models.Manager):
|
||||
|
||||
def current_rankings(self):
|
||||
current_season = date.today().year
|
||||
return self.filter(season=current_season)
|
||||
|
||||
@property
|
||||
def season_list(self):
|
||||
values_list = self.model.objects.values_list('season', flat=True)
|
||||
return values_list.order_by('season').distinct()
|
||||
@@ -5,10 +5,8 @@ Created on 23.05.2011
|
||||
@author: christian
|
||||
"""
|
||||
from django.core.cache import cache
|
||||
from django.db import transaction
|
||||
|
||||
from mahjong_ranking import models
|
||||
from . import logger
|
||||
from . import logger, MIN_HANCHANS_FOR_LADDER
|
||||
|
||||
|
||||
class DenormalizationUpdateMiddleware(object):
|
||||
@@ -16,62 +14,74 @@ class DenormalizationUpdateMiddleware(object):
|
||||
This Class deferres the recalculation for the Otaku XP at the end of a
|
||||
response.
|
||||
"""
|
||||
event_queue = set()
|
||||
season_queue = set()
|
||||
|
||||
@transaction.atomic
|
||||
def process_response(self, request, response):
|
||||
if request.method != 'POST':
|
||||
# We only do this in POST request, to speedup the responsetime.
|
||||
return response
|
||||
# We only do this in POST request, to speedup the responsetime.
|
||||
if request.method == 'POST':
|
||||
queue = cache.get('event_ranking_queue', set())
|
||||
if len(queue) > 0:
|
||||
self.recalculate_event_rankings(queue)
|
||||
cache.set('event_ranking_queue', set(), 360)
|
||||
|
||||
# recalculate tournament (event) rankings:
|
||||
event_ranking_queue = cache.get('event_ranking_queue', set())
|
||||
if len(event_ranking_queue) > 0:
|
||||
while len(event_ranking_queue) > 0:
|
||||
event_id, user_id = event_ranking_queue.pop()
|
||||
logger.info("recalculate %d tournament Ranking in %s", user_id,
|
||||
event_id)
|
||||
ranking = models.EventRanking.objects.get_or_create(
|
||||
event_id=event_id, user_id=user_id)[0]
|
||||
ranking.recalculate()
|
||||
self.recalculate_event_placement(event_id)
|
||||
cache.set('event_ranking_queue', event_ranking_queue, 360)
|
||||
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)
|
||||
|
||||
queue = cache.get('ladder_ranking_queue', set())
|
||||
if len(queue) > 0:
|
||||
self.recalculate_ladder_ranking(queue)
|
||||
cache.set('ladder_ranking_queue', set(), 360)
|
||||
|
||||
for event_id in self.event_queue:
|
||||
self.update_event_placement()
|
||||
for season in self.season_queue:
|
||||
self.update_season_placements()
|
||||
|
||||
# recalculate dirty kyu/dan rankings:
|
||||
kyu_dan_ranking_queue = cache.get('kyu_dan_ranking_queue', set())
|
||||
while len(kyu_dan_ranking_queue) > 0:
|
||||
user_id = kyu_dan_ranking_queue.pop()
|
||||
ranking = models.KyuDanRanking.objects.get_or_create(
|
||||
user_id=user_id)[0]
|
||||
ranking.recalculate()
|
||||
cache.set('kyu_dan_ranking_queue', kyu_dan_ranking_queue, 360)
|
||||
|
||||
# Recaluclate Dirty LadderRankings:
|
||||
ladder_ranking_queue = cache.get('ladder_ranking_queue', set())
|
||||
while len(ladder_ranking_queue) > 0:
|
||||
season_id, user_id = ladder_ranking_queue.pop()
|
||||
if season_id and user_id:
|
||||
ranking = models.LadderRanking.objects.get_or_create(
|
||||
user_id=user_id, season_id=season_id)[0]
|
||||
ranking.recalculate()
|
||||
else:
|
||||
logger.error('Season: %i; Benutzer Nr. %i - existiert nicht!',
|
||||
season_id, user_id)
|
||||
cache.set('ladder_ranking_queue', ladder_ranking_queue, 360)
|
||||
|
||||
# Recaluclate Dirty Season Placements:
|
||||
ladder_season_queue = cache.get('ladder_season_queue', set())
|
||||
for season_id in ladder_season_queue:
|
||||
season = models.LadderSeason.objects.get_or_create(pk=season_id)[0]
|
||||
season.recalculate()
|
||||
cache.set('ladder_season_queue', ladder_season_queue, 360)
|
||||
return response
|
||||
|
||||
def recalculate_event_placement(self, event_id):
|
||||
eventranking_set = models.EventRanking.objects.filter(
|
||||
event_id=event_id).order_by('avg_placement', '-avg_score')
|
||||
eventranking_set.update(placement=None)
|
||||
placement = 1
|
||||
for ranking in eventranking_set:
|
||||
ranking.placement = placement
|
||||
placement += 1
|
||||
ranking.save(force_update=True)
|
||||
def recalculate_event_rankings(self, queue):
|
||||
# recalculate tournament (event) rankings:
|
||||
for event_id, user_id in queue:
|
||||
logger.info("recalculate %d tournament Ranking in %s", user_id, event_id)
|
||||
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
|
||||
|
||||
def recalulate_kyu_dan_ranking(self, queue):
|
||||
for user_id in queue:
|
||||
ranking = models.KyuDanRanking.objects.get_or_create(user_id=user_id)[0]
|
||||
ranking.recalculate()
|
||||
return queue
|
||||
|
||||
def recalculate_ladder_ranking(self, queue):
|
||||
for season, user_id in queue:
|
||||
ladder = models.SeasonRanking.objects.get_or_create(user_id=user_id, season=season)[0]
|
||||
ladder.recalculate()
|
||||
self.season_queue.add(season)
|
||||
|
||||
def update_event_placements(self):
|
||||
for event_id in self.event_queue:
|
||||
eventranking_set = models.EventRanking.objects.filter(event_id=event_id).order_by('avg_placement', '-avg_score')
|
||||
placement = 1
|
||||
for ranking in eventranking_set:
|
||||
ranking.placement = placement
|
||||
placement += 1
|
||||
ranking.save(force_update=True)
|
||||
|
||||
def update_season_placements(self):
|
||||
for season in self.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
|
||||
).order_by('avg_placement', '-avg_score')
|
||||
placement = 1
|
||||
for ranking in season_rankings:
|
||||
ranking.placement = placement
|
||||
ranking.save(force_update=True)
|
||||
placement += 1
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
|
||||
# TODO: Rankings archiv Flag erstellen, womit sie nicht mehr neuberechnet werden dürfen.
|
||||
|
||||
from datetime import date, timedelta
|
||||
from datetime import date
|
||||
|
||||
from django.conf import settings
|
||||
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, logger, set_dirty
|
||||
from . import KYU_RANKS, DAN_RANKS, DAN_RANKS_DICT, logger, set_dirty, MIN_HANCHANS_FOR_LADDER
|
||||
from . import managers
|
||||
|
||||
kyu_dan_rankings = set()
|
||||
ladder_rankings = set()
|
||||
@@ -52,8 +52,8 @@ class EventRanking(models.Model):
|
||||
"""
|
||||
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
|
||||
event_hanchans = Player.objects.confirmed_hanchans(user=self.user_id,
|
||||
event=self.event_id) # @IgnorePep8
|
||||
aggregator = event_hanchans.aggregate(
|
||||
models.Avg('placement'),
|
||||
models.Avg('game_score'),
|
||||
@@ -75,18 +75,54 @@ class Hanchan(models.Model):
|
||||
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)
|
||||
start = models.DateTimeField(_('Start'),
|
||||
help_text=_('This is crucial to get the right Hanchans that scores')
|
||||
)
|
||||
|
||||
player1 = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='user_hanchan+', verbose_name=_('Player 1'))
|
||||
player1_input_score = models.IntegerField(_('Score'))
|
||||
player1_game_score = models.PositiveIntegerField(_('Score'), default=0, editable=False)
|
||||
player1_placement = models.PositiveSmallIntegerField(default=0, editable=False)
|
||||
player1_kyu_points = models.SmallIntegerField(blank=True, null=True, editable=False)
|
||||
player1_dan_points = models.SmallIntegerField(blank=True, null=True, editable=False)
|
||||
player1_bonus_points = models.SmallIntegerField(blank=True, null=True, editable=False)
|
||||
player1_comment = models.CharField(_('Comment'), blank=True, max_length=255, editable=False)
|
||||
|
||||
player2 = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='user_hanchan+', verbose_name=_('Player 2'))
|
||||
player2_input_score = models.IntegerField(_('Score'))
|
||||
player2_game_score = models.PositiveIntegerField(_('Score'), default=0, editable=False)
|
||||
player2_placement = models.PositiveSmallIntegerField(default=0, editable=False)
|
||||
player2_kyu_points = models.SmallIntegerField(blank=True, null=True, editable=False)
|
||||
player2_dan_points = models.SmallIntegerField(blank=True, null=True, editable=False)
|
||||
player2_bonus_points = models.SmallIntegerField(blank=True, null=True, editable=False)
|
||||
player2_comment = models.CharField(_('Comment'), blank=True, max_length=255, editable=False)
|
||||
|
||||
player3 = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='user_hanchan+', verbose_name=_('Player 3'))
|
||||
player3_input_score = models.IntegerField(_('Score'))
|
||||
player3_game_score = models.PositiveIntegerField(_('Score'), default=0, editable=False)
|
||||
player3_placement = models.PositiveSmallIntegerField(default=0, editable=False)
|
||||
player3_kyu_points = models.SmallIntegerField(blank=True, null=True, editable=False)
|
||||
player3_dan_points = models.SmallIntegerField(blank=True, null=True, editable=False)
|
||||
player3_bonus_points = models.SmallIntegerField(blank=True, null=True, editable=False)
|
||||
player3_comment = models.CharField(_('Comment'), blank=True, max_length=255, editable=False)
|
||||
|
||||
player4 = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='user_hanchan+', verbose_name=_('Player 4'))
|
||||
player4_input_score = models.IntegerField(_('Score'))
|
||||
player4_game_score = models.PositiveIntegerField(_('Score'), default=0, editable=False)
|
||||
player4_placement = models.PositiveSmallIntegerField(default=0, editable=False)
|
||||
player4_kyu_points = models.SmallIntegerField(blank=True, null=True, editable=False)
|
||||
player4_dan_points = models.SmallIntegerField(blank=True, null=True, editable=False)
|
||||
player4_bonus_points = models.SmallIntegerField(blank=True, null=True, editable=False)
|
||||
player4_comment = models.CharField(_('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.')
|
||||
)
|
||||
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)
|
||||
start = models.DateTimeField(_('Start'), help_text=_('This is crucial to get the right Hanchans that scores'))
|
||||
valid = models.BooleanField(_('Is Valid'), default=False)
|
||||
season = models.PositiveSmallIntegerField(_('Season'), editable=False, db_index=True)
|
||||
objects = managers.HanchanManager()
|
||||
|
||||
class Meta(object):
|
||||
ordering = ('-start',)
|
||||
@@ -94,77 +130,48 @@ class Hanchan(models.Model):
|
||||
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('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:
|
||||
self.valid = False
|
||||
return 'Endpunktestand weniger als 100.000 Punkte.'
|
||||
elif score_sum > 100000:
|
||||
self.valid = False
|
||||
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...'
|
||||
return _("Hanchan from {0:%Y-%m-%d} at {0:%H:%M} with {1}").format(
|
||||
self.start, self.player_names
|
||||
)
|
||||
|
||||
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.
|
||||
Check if 4 different Players are attending the Game,
|
||||
if the Game between the opening hours and the Hanchan has a total of 100.000 Points.
|
||||
"""
|
||||
super(Hanchan, self).clean()
|
||||
errors = {}
|
||||
score_sum = 0
|
||||
player_set = set()
|
||||
|
||||
# if self.pk and self.player_set.distinct().count() != 4:
|
||||
# raise ValidationError(
|
||||
# _('For a Hanchan exactly 4 players are needed.'))
|
||||
if not self.event_id:
|
||||
raise ValidationError(_("Hanchan has no event"))
|
||||
elif not self.start:
|
||||
raise ValidationError(_("Hanchan has no start time set"))
|
||||
elif self.start > timezone.now():
|
||||
raise ValidationError(_("It's not allowed to enter future games."))
|
||||
try:
|
||||
self.clean_fields()
|
||||
except ValidationError as e:
|
||||
print e.update_error_dict(errors)
|
||||
return
|
||||
|
||||
for i in xrange(1, 5):
|
||||
player = getattr(self, 'player%d' % i)
|
||||
input_score = getattr(self, 'player%d_input_score' % i)
|
||||
score_sum += input_score
|
||||
game_score = input_score if input_score > 0 else 0
|
||||
if player in player_set:
|
||||
errors['player%d' % i] = _("%s can't attend the same game multiple times") % player
|
||||
else:
|
||||
player_set.add(player)
|
||||
setattr(self, 'player%d_game_score' % i, game_score)
|
||||
|
||||
# Check if the game was played during the event
|
||||
if self.start > timezone.now():
|
||||
errors['start'] = _("Games in the future may not be added, Dr. Brown")
|
||||
elif not (self.event.start <= self.start <= self.event.end):
|
||||
raise ValidationError(_("Only games during the event are allowed"))
|
||||
return self
|
||||
errors['start'] = _("Only games during the event are allowed")
|
||||
elif score_sum < 100000:
|
||||
errors['player4_input_score'] = _('Gamescore is lower then 100.000 Pt.')
|
||||
elif score_sum > 100000:
|
||||
errors['player4_input_score'] = _('Gamescore is over 100.000 Pt.')
|
||||
|
||||
if errors:
|
||||
raise ValidationError(errors)
|
||||
|
||||
def compute_player_placements(self):
|
||||
u"""
|
||||
@@ -172,42 +179,85 @@ class Hanchan(models.Model):
|
||||
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('-game_score')
|
||||
player_names = []
|
||||
other_player_placement = 0
|
||||
other_player_score = 0
|
||||
other_player_game_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.game_score <= 0:
|
||||
player.placement = 4
|
||||
elif player.game_score == other_player_score:
|
||||
player.placement = other_player_placement
|
||||
player_nr = 1
|
||||
for player in self.player_list:
|
||||
if player['game_score'] == other_player_game_score:
|
||||
player['placement'] = other_player_placement
|
||||
else:
|
||||
player.placement = placement
|
||||
player['placement'] = placement
|
||||
setattr(self, "player%d" % player_nr, player['user'])
|
||||
setattr(self, "player%d_input_score" % player_nr, player['input_score'])
|
||||
setattr(self, "player%d_game_score" % player_nr, player['game_score'])
|
||||
setattr(self, "player%d_placement" % player_nr, player['placement'])
|
||||
setattr(self, "player%d_kyu_points" % player_nr, player['kyu_points'])
|
||||
setattr(self, "player%d_dan_points" % player_nr, player['dan_points'])
|
||||
setattr(self, "player%d_bonus_points" % player_nr, player['bonus_points'])
|
||||
setattr(self, "player%d_comment" % player_nr, player['comment'])
|
||||
other_player_placement = player['placement']
|
||||
other_player_game_score = player['game_score']
|
||||
player_names.append(player['user'].username)
|
||||
player_nr += 1
|
||||
placement += 1
|
||||
other_player_placement = player.placement
|
||||
other_player_score = player.game_score
|
||||
player.save(season_id=self.season_id, mark_dirty=True)
|
||||
self.player_names = ', '.join(player_names)
|
||||
|
||||
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}
|
||||
return "{url:s}#{id:d}".format(
|
||||
url=reverse('event-hanchan-list', kwargs={'event':self.event_id}),
|
||||
id= self.pk
|
||||
)
|
||||
|
||||
def get_playerdata(self, user):
|
||||
"""i small workaround to access score, placement and points of a
|
||||
specific user prominent from a in the user templates"""
|
||||
for player in ('player1', 'player2', 'player3', 'player4'):
|
||||
if getattr(self, player) == user:
|
||||
self.user = user
|
||||
self.input_score = getattr(self, '%s_input_score' % player)
|
||||
self.game_score = getattr(self, '%s_game_score' % player)
|
||||
self.placement = getattr(self, '%s_placement' % player)
|
||||
self.kyu_points = getattr(self, '%s_kyu_points' % player)
|
||||
self.dan_points = getattr(self, '%s_dan_points' % player)
|
||||
self.bonus_points = getattr(self, '%s_bonus_points' % player)
|
||||
self.player_comment = getattr(self, '%s_comment' % player)
|
||||
|
||||
def update_playerdata(self, user, **kwargs):
|
||||
"""i small workaround to access score, placement of a specific user
|
||||
prominent from a in the user templates"""
|
||||
for player in ('player1', 'player2', 'player3', 'player4'):
|
||||
if getattr(self, player) == user:
|
||||
setattr(self, '%s_input_score' % player, self.input_score)
|
||||
setattr(self, '%s_game_score' % player, self.game_score)
|
||||
setattr(self, '%s_placement' % player, self.placement)
|
||||
setattr(self, '%s_kyu_points' % player, self.kyu_points)
|
||||
setattr(self, '%s_dan_points' % player, self.dan_points)
|
||||
setattr(self, '%s_bonus_points' % player, self.bonus_points)
|
||||
setattr(self, '%s_comment' % player, self.player_comment)
|
||||
|
||||
@property
|
||||
def player_list(self):
|
||||
player_list = []
|
||||
for i in xrange(1, 5):
|
||||
player_list.append({
|
||||
'user': getattr(self, 'player%d' % i),
|
||||
'input_score': getattr(self, 'player%d_input_score' % i),
|
||||
'game_score': getattr(self, 'player%d_game_score' % i),
|
||||
'placement': getattr(self, 'player%d_placement' % i),
|
||||
'kyu_points': getattr(self, 'player%d_kyu_points' % i),
|
||||
'dan_points': getattr(self, 'player%d_dan_points' % i),
|
||||
'bonus_points': getattr(self, 'player%d_bonus_points' % i),
|
||||
'comment': getattr(self, 'player%d_comment' % i),
|
||||
})
|
||||
# sort player by Score:
|
||||
return sorted(player_list, key=lambda player: player['input_score'], reverse=True)
|
||||
|
||||
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('-input_score'))
|
||||
self.season = self.event.mahjong_season or date.today().year
|
||||
self.full_clean()
|
||||
self.compute_player_placements()
|
||||
return models.Model.save(self, **kwargs)
|
||||
|
||||
|
||||
@@ -225,10 +275,10 @@ class KyuDanRanking(models.Model):
|
||||
won_hanchans = models.PositiveIntegerField(default=0)
|
||||
good_hanchans = models.PositiveIntegerField(default=0)
|
||||
hanchan_count = models.PositiveIntegerField(default=0)
|
||||
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)
|
||||
wins_in_a_row = 0
|
||||
|
||||
class Meta(object):
|
||||
ordering = ('-dan_points', '-kyu_points',)
|
||||
@@ -243,19 +293,17 @@ class KyuDanRanking(models.Model):
|
||||
|
||||
def append_3_in_a_row_bonuspoints(self, hanchan):
|
||||
u"""
|
||||
Wenn der Spieler 3 Siege in folge hatte, bekommt er so viele Punkte
|
||||
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
|
||||
if self.dan and self.wins_in_a_row >= 3:
|
||||
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
|
||||
@@ -263,50 +311,46 @@ class KyuDanRanking(models.Model):
|
||||
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("min required points for the next dan: %d", new_dan_points)
|
||||
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)
|
||||
hanchan.player_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):
|
||||
# TODO: Komplett Überabreiten!
|
||||
def append_tournament_bonuspoints(self, hanchan):
|
||||
"""
|
||||
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
|
||||
:param hanchan: 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.
|
||||
hanchans_this_event = Hanchan.objects.user_hanchans(
|
||||
user=self.user, event=hanchan.event
|
||||
).order_by('-start')
|
||||
last_hanchan_this_event = hanchans_this_event[0]
|
||||
if hanchan != last_hanchan_this_event:
|
||||
return # Das braucht nur am Ende eines Turnieres gemacht werden.
|
||||
event_ranking = EventRanking.objects.get(
|
||||
user=self.user,
|
||||
event=current_event)
|
||||
event=hanchan.event)
|
||||
if event_ranking.placement == 1:
|
||||
bonus_points += 4
|
||||
player.comment += '+4 Punkte für den Sieg des Turnieres. '
|
||||
hanchan.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
|
||||
hanchan.comment += '+8 Pkt: alle Spiele des Turnieres gewonnen. '
|
||||
|
||||
if bonus_points and self.dan:
|
||||
player.dan_points += bonus_points
|
||||
hanchan.dan_points += bonus_points
|
||||
self.dan_points += bonus_points
|
||||
elif bonus_points:
|
||||
player.kyu_points += bonus_points
|
||||
hanchan.kyu_points += bonus_points
|
||||
self.kyu_points += bonus_points
|
||||
hanchan.bonus_points += bonus_points
|
||||
|
||||
def get_absolute_url(self):
|
||||
if self.dan or self.dan_points > 0:
|
||||
@@ -319,87 +363,91 @@ class KyuDanRanking(models.Model):
|
||||
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)
|
||||
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 = 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)
|
||||
self.kyu = None
|
||||
self.won_hanchans = 0
|
||||
self.good_hanchans = 0
|
||||
|
||||
for played_hanchan in valid_hanchans:
|
||||
self.update_hanchan_points(played_hanchan)
|
||||
self.append_tournament_bonuspoints(played_hanchan)
|
||||
logger.info("recalculating Kyu/Dan punkte for %s...", self.user)
|
||||
self.update_rank()
|
||||
valid_hanchans = Hanchan.objects.confirmed_hanchans(user=self.user)
|
||||
valid_hanchans = valid_hanchans.order_by('start')
|
||||
if self.legacy_date:
|
||||
valid_hanchans = valid_hanchans.filter(start__gt=self.legacy_date)
|
||||
|
||||
self.hanchan_count = valid_hanchans.count()
|
||||
for hanchan in valid_hanchans:
|
||||
hanchan.get_playerdata(self.user)
|
||||
hanchan.bonus_points=0
|
||||
hanchan.player_comment=""
|
||||
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(played_hanchan)
|
||||
self.append_3_in_a_row_bonuspoints(hanchan)
|
||||
self.update_rank()
|
||||
played_hanchan.save(force_update=True, mark_dirty=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(force_update=True)
|
||||
self.save(force_update=True)
|
||||
|
||||
def update_hanchan_points(self, played_hanchan):
|
||||
def update_hanchan_points(self, hanchan):
|
||||
"""
|
||||
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.
|
||||
:type hanchan: Hanchan
|
||||
:param hanchan: Das Player Objekt das neuberechnet werden soll.
|
||||
"""
|
||||
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
|
||||
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
|
||||
tourney_points = 4 - hanchan.placement
|
||||
if self.dan:
|
||||
played_hanchan.dan_points = tourney_points
|
||||
hanchan.dan_points = tourney_points
|
||||
else:
|
||||
played_hanchan.kyu_points = tourney_points
|
||||
hanchan.kyu_points = tourney_points
|
||||
elif self.dan:
|
||||
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
|
||||
if hanchan.game_score >= 60000:
|
||||
hanchan.dan_points = 3
|
||||
elif hanchan.game_score == 0:
|
||||
hanchan.dan_points = -3
|
||||
elif hanchan.placement == 1:
|
||||
hanchan.dan_points = 2
|
||||
elif hanchan.placement == 2:
|
||||
hanchan.dan_points = 1
|
||||
elif hanchan.placement == 3:
|
||||
hanchan.dan_points = -1
|
||||
elif hanchan.placement == 4:
|
||||
hanchan.dan_points = -2
|
||||
elif hanchan.game_score >= 60000:
|
||||
hanchan.kyu_points = 3
|
||||
elif hanchan.game_score >= 30000:
|
||||
hanchan.kyu_points = 1
|
||||
elif hanchan.input_score < 10000:
|
||||
hanchan.kyu_points = -1
|
||||
else:
|
||||
played_hanchan.kyu_points = 0
|
||||
hanchan.kyu_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:
|
||||
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)
|
||||
# Only substract so much points that player has 0 Points:
|
||||
if self.dan_points + hanchan.dan_points < 0:
|
||||
hanchan.dan_points -= (self.dan_points + hanchan.dan_points)
|
||||
self.dan_points += hanchan.dan_points
|
||||
else:
|
||||
if self.kyu_points + played_hanchan.kyu_points < 0:
|
||||
# Only substract so much points that player has 0 Points:
|
||||
played_hanchan.kyu_points -= (self.kyu_points + played_hanchan.kyu_points)
|
||||
self.kyu_points += played_hanchan.kyu_points
|
||||
# Only substract so much points that player has 0 Points:
|
||||
if self.kyu_points + hanchan.kyu_points < 0:
|
||||
hanchan.kyu_points -= (self.kyu_points + hanchan.kyu_points)
|
||||
self.kyu_points += hanchan.kyu_points
|
||||
|
||||
# TODO: Merkwürdige Methode die zwar funktioniert aber nicht sehr aussagekräfig ist. Überarbeiten?
|
||||
def update_rank(self):
|
||||
if self.dan and self.dan_points < 0:
|
||||
self.dan_points = 0
|
||||
@@ -427,15 +475,16 @@ class KyuDanRanking(models.Model):
|
||||
break
|
||||
|
||||
|
||||
class LadderRanking(models.Model):
|
||||
class SeasonRanking(models.Model):
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL)
|
||||
season = models.ForeignKey('LadderSeason')
|
||||
season = models.PositiveSmallIntegerField(_('Season'))
|
||||
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)
|
||||
objects = managers.SeasonRankingManager()
|
||||
|
||||
class Meta(object):
|
||||
ordering = ('placement', 'avg_placement', '-avg_score',)
|
||||
@@ -444,217 +493,40 @@ class LadderRanking(models.Model):
|
||||
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(
|
||||
avg_placement = models.Avg('placement'),
|
||||
avg_score = models.Avg('game_score'),
|
||||
hanchan_count = models.Count('pk')
|
||||
)
|
||||
season_hanchans = Hanchan.objects.season_hanchans(user=self.user, season=self.season)
|
||||
sum_placement = 0
|
||||
sum_score = 0
|
||||
self.placement = None
|
||||
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)
|
||||
self.hanchan_count = season_hanchans.count()
|
||||
self.good_hanchans = 0
|
||||
self.won_hanchans = 0
|
||||
|
||||
|
||||
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:
|
||||
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
|
||||
|
||||
def get_by_date(self, deadline):
|
||||
"""
|
||||
returns the season that where running on the given date.
|
||||
:param deadline: the date you're intrested in
|
||||
"""
|
||||
logger.info(u'Recalculate LadderRanking for Player %s in Season %s', self.user, self.season)
|
||||
for hanchan in season_hanchans:
|
||||
sum_placement += hanchan.placement
|
||||
sum_score += hanchan.game_score
|
||||
self.won_hanchans += 1 if hanchan.placement == 1 else 0
|
||||
self.good_hanchans += 1 if hanchan.placement == 2 else 0
|
||||
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.PositiveSmallIntegerField()
|
||||
start = models.DateField()
|
||||
end = models.DateField()
|
||||
objects = LadderSeasonManager()
|
||||
|
||||
class Meta(object):
|
||||
ordering = ('start',)
|
||||
verbose_name = _('Ladder Season')
|
||||
verbose_name_plural = _('Ladder Seasons')
|
||||
|
||||
def __unicode__(self):
|
||||
return str(self.name)
|
||||
|
||||
def get_absolute_url(self):
|
||||
"""
|
||||
URL zur Hanchanliste des Events wo diese Hanchan gelistet wurde.
|
||||
"""
|
||||
return reverse('mahjong-ladder', kwargs={'season': self.pk})
|
||||
|
||||
@property
|
||||
def participants(self):
|
||||
return self.ladderranking_set.filter(placement__isnull=False).count()
|
||||
|
||||
def recalculate(self):
|
||||
logger.info(u'Recalculate LadderSeason %s', self.name)
|
||||
self.ladderranking_set.update(placement=None)
|
||||
ladderrankings_for_placement = self.ladderranking_set.filter(
|
||||
hanchan_count__gt=MIN_HANCHANS_FOR_LADDER
|
||||
).order_by(
|
||||
'avg_placement', '-avg_score'
|
||||
)
|
||||
placement = 1
|
||||
for ranking in ladderrankings_for_placement:
|
||||
ranking.placement = placement
|
||||
ranking.save(force_update=True)
|
||||
placement += 1
|
||||
self.avg_placement = sum_placement / self.hanchan_count
|
||||
self.avg_score = sum_score / self.hanchan_count
|
||||
except ZeroDivisionError:
|
||||
self.avg_placement = None
|
||||
self.avg_score = None
|
||||
self.save(force_update=True)
|
||||
|
||||
|
||||
class PlayerManager(models.Manager):
|
||||
use_for_related_fields = True
|
||||
def update_ranking(sender, instance, **kwargs):
|
||||
for user in (instance.player1, instance.player2, instance.player3, instance.player4):
|
||||
logger.debug("Marking %s's kyu/dan for recalculation", user)
|
||||
set_dirty(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)
|
||||
if instance.event.mahjong_tournament:
|
||||
logger.debug("Marking tournament %s for recalculation.", instance.event)
|
||||
set_dirty(event=instance.event_id, user=user.id)
|
||||
set_dirty(season=instance.season)
|
||||
|
||||
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):
|
||||
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(settings.AUTH_USER_MODEL)
|
||||
input_score = models.IntegerField(default=0)
|
||||
game_score = models.PositiveIntegerField(default=0)
|
||||
placement = models.PositiveSmallIntegerField(
|
||||
blank=True,
|
||||
null=True,
|
||||
default=None
|
||||
)
|
||||
kyu_points = models.SmallIntegerField(
|
||||
blank=True,
|
||||
null=True,
|
||||
default=None
|
||||
)
|
||||
dan_points = models.SmallIntegerField(
|
||||
blank=True,
|
||||
null=True,
|
||||
default=None
|
||||
)
|
||||
bonus_points = models.PositiveSmallIntegerField(
|
||||
blank=True,
|
||||
null=True,
|
||||
default=0
|
||||
)
|
||||
comment = models.TextField(_('Comment'), blank=True)
|
||||
objects = PlayerManager()
|
||||
|
||||
class Meta(object):
|
||||
unique_together = ('hanchan', 'user')
|
||||
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)
|
||||
|
||||
# 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)
|
||||
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)
|
||||
models.signals.pre_delete.connect(update_ranking, sender=Hanchan)
|
||||
models.signals.post_save.connect(update_ranking, sender=Hanchan)
|
||||
|
||||
@@ -7,11 +7,13 @@
|
||||
<h2 class="grid_12">{% trans 'Played Hanchans' %}</h2>
|
||||
<p> </p>
|
||||
{% for hanchan in hanchan_list %}
|
||||
<div class="grid_12" id="{{ hanchan.pk }}"><h3>{{hanchan.start|time:'H:i'}}: {{ hanchan.player_names }}</h3></div>
|
||||
{% for player in hanchan.player_set.all %}
|
||||
<a class="grid_1" href="{% url 'player-ladder-score' player.user %}"><img src="{% if player.user.avatar %}{{player.user.thumbnail.url}}{% else %}{{STATIC_URL}}img/unknown_thumbnail.png{% endif %}" class="avatar" alt="{{ player.user }}" title="{{ player.user }}"/></a>
|
||||
<h3 class="grid_12" id="{{ hanchan.pk }}">{{hanchan.start|time:'H:i'}}: {{ hanchan.player_names }}</h3>
|
||||
{% for player in hanchan.player_list %}
|
||||
<a class="grid_1" href="{% url 'player-ladder-score' player.user %}"><img
|
||||
src="{% if player.user.avatar %}{{player.user.thumbnail.url}}{% else %}{{STATIC_URL}}img/unknown_thumbnail.png{% endif %}"
|
||||
class="avatar" alt="{{ player.user }}" title="{{ player.user }}"/></a>
|
||||
<div class="grid_2">
|
||||
<a class="player" href="{% url 'player-ladder-score' player.user %}" title="{{ player.comment }}">{{ player.user }}</a>
|
||||
<p><a class="player" href="{% url 'player-ladder-score' player.user %}" title="{{ player.comment }}">{{ player.user }}</a></p>
|
||||
<p><strong>{{ player.placement|ordinal }} {% trans 'Place' %}</strong><br />
|
||||
<strong>{% trans 'Score' %}:</strong> {{ player.game_score|intcomma }}<br />
|
||||
{% if player.dan_points != None %}
|
||||
@@ -20,11 +22,9 @@
|
||||
<strong>{% trans 'Kyu Points' %}:</strong> {{ player.kyu_points|default:0 }}
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% if not hanchan.valid %}
|
||||
<p class="grid_12 error"><strong>Ungültig:</strong> {{hanchan.check_validity}}</p>
|
||||
{% elif not hanchan.confirmed %}
|
||||
{% if not hanchan.confirmed %}
|
||||
<p class="grid_12 error">Diese Hanchan wurde nicht anerkannt und wird daher nicht gezählt.</p>
|
||||
{% elif hanchan.comment %}
|
||||
<p class="grid_12">{{ hanchan.comment }}</p>
|
||||
|
||||
@@ -1,23 +1,47 @@
|
||||
{% extends "events/event_detail.html" %}
|
||||
{% load i18n comments %}
|
||||
{% load i18n humanize %}
|
||||
|
||||
{% block meta_title %}{% trans 'Delete Hanchan' %}{% endblock %}
|
||||
|
||||
{% block maincontent %}
|
||||
<form method="post" class="grid_12">
|
||||
{% csrf_token %}
|
||||
<fieldset>
|
||||
<legend>{% trans "Delete Hanchan" %}</legend>
|
||||
{% include 'form.html' %}
|
||||
<div class="grid_2 fa fa-exclamation-triangle fa-5x" ></div>
|
||||
<p class="grid_6">Bist du sicher dass du diese Hanchan und alle Ergebnisse und Wertungen welche sich hierauf beziehen löschen möchtest?</p>
|
||||
<br class="clear">
|
||||
<p class="buttonbar">
|
||||
<button type="button" onclick="window.history.back()"><span class="fa fa-close"></span> {% trans 'Cancel' %}</button>
|
||||
<button type="submit"><span class="fa fa-trash"></span> {% trans 'Delete' %}</button>
|
||||
</p>
|
||||
</fieldset>
|
||||
<h3 class="grid_12" id="{{ hanchan.pk }}">{{hanchan.start|time:'H:i'}}: {{ hanchan.player_names }}</h3>
|
||||
{% for player in hanchan.player_list %}
|
||||
<a class="grid_1" href="{% url 'player-ladder-score' player.user %}"><img
|
||||
src="{% if player.user.avatar %}{{player.user.thumbnail.url}}{% else %}{{STATIC_URL}}img/unknown_thumbnail.png{% endif %}"
|
||||
class="avatar" alt="{{ player.user }}" title="{{ player.user }}"/></a>
|
||||
<div class="grid_2">
|
||||
<p><a class="player" href="{% url 'player-ladder-score' player.user %}" title="{{ player.comment }}">{{ player.user }}</a></p>
|
||||
<p><strong>{{ player.placement|ordinal }} {% trans 'Place' %}</strong><br />
|
||||
<strong>{% trans 'Score' %}:</strong> {{ player.game_score|intcomma }}<br />
|
||||
{% if player.dan_points != None %}
|
||||
<strong>{% trans 'Dan Points' %}:</strong> {{ player.dan_points }}
|
||||
{% else %}
|
||||
<strong>{% trans 'Kyu Points' %}:</strong> {{ player.kyu_points|default:0 }}
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% if not hanchan.confirmed %}
|
||||
<p class="grid_12 error">Diese Hanchan wurde nicht anerkannt und wird daher nicht gezählt.</p>
|
||||
{% elif hanchan.comment %}
|
||||
<p class="grid_12">{{ hanchan.comment }}</p>
|
||||
{% endif %}
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<fieldset class="grid_12">
|
||||
<legend>{% trans "Delete Hanchan" %}</legend>
|
||||
{% include 'form.html' %}
|
||||
<label class="field_name fa fa-exclamation-triangle fa-5x fa-pull-left" ></label>
|
||||
<p>Bist du sicher dass du diese Hanchan und alle Ergebnisse und Wertungen welche sich hierauf beziehen löschen möchtest?</p>
|
||||
<br class="clear">
|
||||
<p class="buttonbar">
|
||||
<button type="button" onclick="window.history.back()"><span class="fa fa-close"></span> {% trans 'Cancel' %}</button>
|
||||
<button type="submit"><span class="fa fa-trash"></span> {% trans 'Delete' %}</button>
|
||||
</p>
|
||||
</fieldset>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
{% block buttonbar %}{% endblock %}
|
||||
|
||||
{% block buttonbar %}{% endblock %}
|
||||
{% block comments %}{% endblock %}
|
||||
@@ -2,45 +2,76 @@
|
||||
{% load i18n comments fieldset_extras %}
|
||||
|
||||
{% block title %}{% if hanchan.id %}{% trans "Edit Hanchan" %}{% else %}{% trans "Add Hanchan" %}{% endif %}{% endblock %}
|
||||
|
||||
{% block extra_head %}
|
||||
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.min.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
<script class="javascript">
|
||||
{% block javascript %}
|
||||
function recalculate_values(element) {
|
||||
var total = 0
|
||||
$('input[name$="_input_score"]').each(function() {total += Number($(this).val());});
|
||||
$('#total').val(total)
|
||||
|
||||
var difference = 100000 - total
|
||||
if (difference > 0) {
|
||||
differnence_text = difference + ' offen'
|
||||
} else if (difference < 0) {
|
||||
differnence_text = (0 - difference) + ' zu viel'
|
||||
} else {
|
||||
differnence_text = 'Ok'
|
||||
}
|
||||
$('#difference').text(differnence_text)
|
||||
}
|
||||
$("#id_start_1").focus();
|
||||
$('input[name$="_input_score"]').keyup(function() {recalculate_values(this);});
|
||||
$('input[name$="_input_score"]').change(function() {recalculate_values(this);});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block maincontent %}
|
||||
{% get_fieldset "event, start" from form as event_formset %}
|
||||
{% if perms.mahjong_ranking.delete_hanchan %}
|
||||
{% for error in form.non_field_errors %}<p class="error">{{error}}</p>{% endfor %}
|
||||
<form class="grid_12" method="post"><fieldset>
|
||||
{% csrf_token %}
|
||||
<legend>{% if hanchan.id %}{% trans "Edit Hanchan" %}{% else %}{% trans "Add Hanchan" %}{% endif %}</legend>
|
||||
{% get_fieldset "start" from form as event_formset %}
|
||||
<table>
|
||||
<thead><tr>
|
||||
<th width="25%">{% trans 'Player' %}</th>
|
||||
<th width="75%">{% trans 'Score' %}</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
{% with event_formset as form %}{% include "form.html" %} {% endwith %}
|
||||
<tr>
|
||||
<td>{{ form.player1 }} {% if form.player1.errors %}{{ form.player1.errors }}{% endif %}</td>
|
||||
<td>{{ form.player1_input_score}} {% if form.player1_input_score.errors %}{{ form.player1_input_score.errors }}{% endif %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ form.player2 }} {% if form.player2.errors %}{{ form.player2.errors }}{% endif %}</td>
|
||||
<td>{{ form.player2_input_score}} {% if form.player2_input_score.errors %}{{ form.player2_input_score.errors }}{% endif %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ form.player3 }} {% if form.player3.errors %}{{ form.player3.errors }}{% endif %}</td>
|
||||
<td>{{ form.player3_input_score}} {% if form.player3_input_score.errors %}{{ form.player3_input_score.errors }}{% endif %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ form.player4 }} {% if form.player4.errors %}{{ form.player4.errors }}{% endif %}</td>
|
||||
<td>{{ form.player4_input_score}} {% if form.player4_input_score.errors %}{{ form.player4_input_score.errors }}{% endif %}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot><tr>
|
||||
<td>{% trans 'Total' %}</td>
|
||||
<td><input type="number" value="0" id="total" disabled> <span id="difference"></span></td>
|
||||
</tr></tfoot>
|
||||
</table>
|
||||
{% if perms.mahjong_ranking.change_hanchan %}
|
||||
{% get_fieldset "comment, confirmed" from form as hanchan_formset %}
|
||||
{% else %}
|
||||
{% get_fieldset "comment" from form as hanchan_formset %}
|
||||
{% endif %}
|
||||
{% for hidden in form.hidden_fields %}{% if hidden.errors %}{{ hidden.errors }}{% endif %}{% endfor %}
|
||||
|
||||
<form class="grid_12" method="post"><fieldset>
|
||||
{% csrf_token %}
|
||||
<legend>{% if hanchan.id %}{% trans "Edit Hanchan" %}{% else %} {% trans "Add Hanchan" %}{% endif %}</legend>
|
||||
{% with event_formset as form %}{% include "form.html" %}{% endwith %}
|
||||
<div>
|
||||
<label class="field_name required">{% trans 'Players' %}</label>
|
||||
{{ formset.management_form }}
|
||||
<table>
|
||||
<tr>
|
||||
<th>{% trans 'User' %}</th>
|
||||
<th>{% trans 'Score' %}</th>
|
||||
<th>{% trans 'Bonus' %}</th>
|
||||
<th width="75%">{% trans 'Comment' %}</th>
|
||||
</tr>
|
||||
{% for form in formset %}
|
||||
<tr>
|
||||
<td class="{{form.user.css_classes}}">{{form.id}} {{form.user}} {% for error in form.user.errors %}<br />{{error}}{% endfor %}</td>
|
||||
<td class="{{form.input_score.css_classes}}">{{form.input_score}} {% for error in form.input_score.errors %}<br />{{error}}{% endfor %}</td>
|
||||
<td class="{{form.bonus_points.css_classes}}">{{form.bonus_points}} {% for error in form.bonus_points.errors %}<br />{{error}}{% endfor %}</td>
|
||||
<td class="{{form.comment.css_classes}}" width="*">{{form.comment}} {% for error in form.comment.errors %}<br />{{error}}{% endfor %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{{ formset.non_form_errors }}
|
||||
{% if hanchan.pk and not hanchan.valid %}<p class="error">{{hanchan.check_validity}}</p>{% endif %}
|
||||
{% with hanchan_formset as form %}
|
||||
{% for error in form.non_field_errors %}<p class="error">{{error}}</p>{% endfor %}
|
||||
{% include "form.html" %}
|
||||
{% endwith %}
|
||||
{% with hanchan_formset as form %}{% include "form.html" %} {% endwith %}
|
||||
|
||||
<p class="buttonbar">
|
||||
<a href="{% url 'event-hanchan-list' event.pk %}" class="button"><span class="fa fa-undo"></span> {% trans 'back' %}</a>
|
||||
@@ -49,4 +80,5 @@
|
||||
</fieldset></form>
|
||||
{% endblock %}
|
||||
|
||||
{% block comments %}{% endblock %}
|
||||
{% block buttonbar %}{% endblock %}
|
||||
@@ -1,11 +1,10 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "mahjong_ranking/page.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans 'Player List' %}{% endblock %}
|
||||
{% block teaser %}<h2>{% trans 'Player List' %}</h2>{% endblock %}
|
||||
|
||||
{% block maincontent %}
|
||||
<main class="grid_12">
|
||||
{% block content %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -56,5 +55,4 @@
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</main>
|
||||
{% endblock %}
|
||||
@@ -1,9 +1,3 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block additional_nav_elements %}
|
||||
<a href="{% url 'season_ranking-archive' %}" class="{% if is_archive %}active{% endif %}">{% trans 'Archive' %}</a>
|
||||
{% if perms.events.add_event %}
|
||||
<a href="{% url 'event-form' %}" class="{% ifequal request.path '/events/add/' %}active{% endifequal %}">{% trans 'Add Event' %}</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block jumbotron_background %}{{STATIC_URL}}img/teaser/mahjong.jpg{% endblock %}
|
||||
@@ -27,25 +27,25 @@
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
{% for result in player_list %}
|
||||
{% for hanchan in hanchan_list %}
|
||||
<tr>
|
||||
<td>{{ result.hanchan.start|date:'SHORT_DATE_FORMAT' }}</td>
|
||||
<td><a href="{{ result.hanchan.get_absolute_url }}">{{ result.hanchan.event.name }}</a></td>
|
||||
<td>{{ result.hanchan.start|time:'H:i' }}</td>
|
||||
<td class="center">{{result.placement}}.</td>
|
||||
{% for player in result.hanchan.player_set.all %}
|
||||
<td>{{ hanchan.start|date:'SHORT_DATE_FORMAT' }}</td>
|
||||
<td><a href="{{ hanchan.get_absolute_url }}">{{ hanchan.event.name }}</a></td>
|
||||
<td>{{ hanchan.start|time:'TIME_FORMAT' }}</td>
|
||||
<td class="center">{{hanchan.placement}}.</td>
|
||||
{% for player in hanchan.player_list %}
|
||||
<td class="center"><a href="{% url 'player-dan-score' player.user.username %}">{{player.user}}</a><br/>
|
||||
{{player.game_score }}
|
||||
</td>
|
||||
{% endfor %}
|
||||
<td class="center">{{result.dan_points}}</td>
|
||||
<td>{{ result.comment }}</td>
|
||||
<td class="center">{{hanchan.dan_points}}</td>
|
||||
<td>{{ hanchan.comment }}</td>
|
||||
<td>
|
||||
{% if perms.mahjong_ranking.delete_hanchan %}
|
||||
<a href="{% url 'delete-hanchan' result.hanchan.pk %}"><span class="fa fa-trash" title="{% trans 'Delete Hanchan' %}"></span></a>
|
||||
<a href="{% url 'delete-hanchan' hanchan.pk %}"><span class="fa fa-trash" title="{% trans 'Delete Hanchan' %}"></span></a>
|
||||
{% endif %}
|
||||
{% if perms.mahjong_ranking.change_hanchan %}
|
||||
<a href="{% url 'edit-hanchan' result.hanchan.pk %}"><span class="fa fa-pencil" title="{% trans 'Edit Hanchan' %}"></span></a>
|
||||
<a href="{% url 'edit-hanchan' hanchan.pk %}"><span class="fa fa-pencil" title="{% trans 'Edit Hanchan' %}"></span></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
{% extends "membership/membership_detail.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %} {% trans 'Kyu Score for' %} {{player.username}} {% endblock %}
|
||||
{% block title %} {% trans 'Unconfirmed Hanchans from' %} {{player.username}} {% endblock %}
|
||||
|
||||
{% block teaser %}<h2>{% trans 'Invalid hanchans with' %} {{membership.username}}</h2>{% endblock %}
|
||||
{% block teaser %}<h2>{% trans 'Unconfirmed Hanchans from' %} {{membership.username}}</h2>{% endblock %}
|
||||
|
||||
{% block score_list %}
|
||||
<div class="grid_12">
|
||||
<h2>{% trans 'Invalid hanchans with' %} {{membership.username}}</h2>
|
||||
<table>
|
||||
<thead><tr>
|
||||
@@ -19,26 +18,25 @@
|
||||
<th colspan="2"></th>
|
||||
</tr></thead>
|
||||
|
||||
{% for result in player_list %}
|
||||
{% for hanchan in hanchan_list%}
|
||||
<tr>
|
||||
<td><a href="{{ result.hanchan.get_absolute_url }}">{{ result.hanchan.event.name }}</a></td>
|
||||
<td>{{ result.hanchan.start|time:'H:i' }}</td>
|
||||
{% for player in result.hanchan.player_set.all %}
|
||||
<td><a href="{{ hanchan.get_absolute_url }}">{{ hanchan.event.name }}</a></td>
|
||||
<td>{{ hanchan.start|time:'TIME_FORMAT' }}</td>
|
||||
{% for player in hanchan.player_list %}
|
||||
<td class="center"><a href="{% url 'player-invalid-score' player.user.username %}">{{player.user}}</a><br/> {{player.input_score }}</td>
|
||||
{% endfor %}
|
||||
<td class="center">{{result.placement}}</td>
|
||||
<td class="center">{{result.kyu_points}}</td>
|
||||
<td>{{ result.comment }}</td>
|
||||
<td class="center">{{hanchan.placement}}</td>
|
||||
<td class="center">{{hanchan.kyu_points}}</td>
|
||||
<td>{{ hanchan.comment }}</td>
|
||||
<td>
|
||||
{% if perms.mahjong_ranking.delete_hanchan %}
|
||||
<a href="{% url 'delete-hanchan' result.hanchan.pk %}"><span class="fa fa-trash" title="{% trans 'Delete Hanchan' %}"></span></a>
|
||||
<a href="{% url 'delete-hanchan' hanchan.pk %}"><span class="fa fa-trash" title="{% trans 'Delete Hanchan' %}"></span></a>
|
||||
{% endif %}
|
||||
{% if perms.mahjong_ranking.change_hanchan %}
|
||||
<a href="{% url 'edit-hanchan' result.hanchan.pk %}"><span class="fa fa-pencil" title="{% trans 'Edit Hanchan' %}"></span></a>
|
||||
<a href="{% url 'edit-hanchan' hanchan.pk %}"><span class="fa fa-pencil" title="{% trans 'Edit Hanchan' %}"></span></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -6,7 +6,6 @@
|
||||
{% block teaser %}<h2>{% trans 'Kyu Score for' %} {{membership.username}}</h2>{% endblock %}
|
||||
|
||||
{% block score_list %}
|
||||
<div class="grid_12">
|
||||
<h2>{% trans 'Hanchans that apply to the Kyu Score' %}</h2>
|
||||
<table>
|
||||
<thead><tr>
|
||||
@@ -27,26 +26,25 @@
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
{% for result in player_list %}
|
||||
{% for hanchan in hanchan_list%}
|
||||
<tr>
|
||||
<td>{{ result.hanchan.start|date:'SHORT_DATE_FORMAT' }}</td>
|
||||
<td><a href="{{ result.hanchan.get_absolute_url }}">{{ result.hanchan.event.name }}</a></td>
|
||||
<td>{{ result.hanchan.start|time:'H:i' }}</td>
|
||||
<td class="center">{{result.placement}}.</td>
|
||||
{% for player in result.hanchan.player_set.select_related %}
|
||||
<td>{{ hanchan.start|date:'SHORT_DATE_FORMAT' }}</td>
|
||||
<td><a href="{{ hanchan.get_absolute_url }}">{{ hanchan.event.name }}</a></td>
|
||||
<td>{{ hanchan.start|time:'TIME_FORMAT' }}</td>
|
||||
<td class="center">{{hanchan.placement}}.</td>
|
||||
{% for player in hanchan.player_list %}
|
||||
<td class="center"><a href="{% url 'player-kyu-score' player.user.username %}">{{player.user}}</a><br/> {{player.game_score }}</td>
|
||||
{% endfor %}
|
||||
<td class="center">{{result.kyu_points}}</td>
|
||||
<td class="center">{{hanchan.kyu_points}}</td>
|
||||
<td>
|
||||
{% if perms.mahjong_ranking.delete_hanchan %}
|
||||
<a href="{% url 'delete-hanchan' result.hanchan.pk %}"><span class="fa fa-trash" title="{% trans 'Delete Hanchan' %}"></span></a>
|
||||
<a href="{% url 'delete-hanchan' hanchan.pk %}"><span class="fa fa-trash" title="{% trans 'Delete Hanchan' %}"></span></a>
|
||||
{% endif %}
|
||||
{% if perms.mahjong_ranking.change_hanchan %}
|
||||
<a href="{% url 'edit-hanchan' result.hanchan.pk %}"><span class="fa fa-pencil" title="{% trans 'Edit Hanchan' %}"></span></a>
|
||||
<a href="{% url 'edit-hanchan' hanchan.pk %}"><span class="fa fa-pencil" title="{% trans 'Edit Hanchan' %}"></span></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,57 +1,74 @@
|
||||
{% extends "membership/membership_detail.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %} {% trans 'Ladder Score for' %} {{membership.username}} {% endblock %}
|
||||
{% block teaser %}<h2>{% trans 'Ladder Score for' %} {{membership.username}}</h2>{% endblock %}
|
||||
{% block title %} {% trans 'Ladder Score for' %} {{membership.username}} / {{ season }}{% endblock %}
|
||||
{% block teaser %}<h2>{% trans 'Ladder Score for' %} {{membership.username}} / {{ season }}</h2>{% endblock %}
|
||||
|
||||
{% block score_list %}
|
||||
<div class="grid_12">
|
||||
<h2>{% trans 'Hanchans that apply to the Ladder Score' %} - {{season.name}}</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowspan="2">{% trans 'Date' %}</th>
|
||||
<th rowspan="2">{% trans 'Event' %}</th>
|
||||
<th rowspan="2">{% trans 'Start' %}</th>
|
||||
<th rowspan="2">{% trans 'Placement' %}</th>
|
||||
<th colspan="4">{% trans 'Players' %}</th>
|
||||
<th rowspan="2"></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>1.</th>
|
||||
<th>2.</th>
|
||||
<th>3.</th>
|
||||
<th>4.</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for result in player_list %}
|
||||
<tr>
|
||||
<td>{{ result.hanchan.start|date:'SHORT_DATE_FORMAT' }}</td>
|
||||
<td><a href="{{ result.hanchan.get_absolute_url }}">{{ result.hanchan.event.name }}</a></td>
|
||||
<td>{{ result.hanchan.start|time:'H:i' }}</td>
|
||||
<td class="center">{{result.placement}}.</td>
|
||||
{% for player in result.hanchan.player_set.select_related %}
|
||||
<td class="center"><a href="{% url 'player-ladder-score' player.user.username %}?season={{season.id}}">{{player.user}}</a><br/> {{player.game_score}}</td>
|
||||
{% endfor %}
|
||||
<td>
|
||||
{% if perms.mahjong_ranking.delete_hanchan %}
|
||||
<a href="{% url 'delete-hanchan' result.hanchan.pk %}"><span class="fa fa-trash" title="{% trans 'Delete Hanchan' %}"></span></a>
|
||||
{% endif %}
|
||||
{% if perms.mahjong_ranking.change_hanchan %}
|
||||
<a href="{% url 'edit-hanchan' result.hanchan.pk %}"><span class="fa fa-pencil" title="{% trans 'Edit Hanchan' %}"></span></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfooter>
|
||||
<tr><td colspan="9" class="right">
|
||||
<form action="{% url 'player-ladder-score' membership.username %}">
|
||||
<label for="id_season">{% trans 'Season' %}:</label> {{seasons_select_form.season}} <button type="submit">{% trans 'Go' %}</button>
|
||||
</form>
|
||||
</td></tr>
|
||||
</tfooter>
|
||||
<h2>{% trans 'Hanchans that apply to the Ladder Score' %} - {{ season }}</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowspan="2">{% trans 'Date' %}</th>
|
||||
<th rowspan="2">{% trans 'Event' %}</th>
|
||||
<th rowspan="2">{% trans 'Start' %}</th>
|
||||
<th rowspan="2">{% trans 'Placement' %}</th>
|
||||
<th colspan="4">{% trans 'Players' %}</th>
|
||||
<th rowspan="2"></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>1.</th>
|
||||
<th>2.</th>
|
||||
<th>3.</th>
|
||||
<th>4.</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for hanchan in hanchan_list %}
|
||||
<tr>
|
||||
<td>{{ hanchan.start|date:'SHORT_DATE_FORMAT' }}</td>
|
||||
<td><a href="{{ hanchan.get_absolute_url }}">{{ hanchan.event.name }}</a></td>
|
||||
<td>{{ hanchan.start|time:'TIME_FORMAT' }}</td>
|
||||
<td class="center">{{ hanchan.placement }}.</td>
|
||||
<td class="center">
|
||||
<a href="{% url 'player-ladder-score' hanchan.player1 %}?season={{season}}">{{hanchan.player1}}</a><br>
|
||||
{{hanchan.player1_game_score}}
|
||||
</td>
|
||||
<td class="center">
|
||||
<a href="{% url 'player-ladder-score' hanchan.player2 %}?season={{season}}">{{hanchan.player2}}</a><br>
|
||||
{{hanchan.player2_game_score}}
|
||||
</td>
|
||||
<td class="center">
|
||||
<a href="{% url 'player-ladder-score' hanchan.player3 %}?season={{season}}">{{hanchan.player3}}</a><br>
|
||||
{{hanchan.player3_game_score}}
|
||||
</td>
|
||||
<td class="center">
|
||||
<a href="{% url 'player-ladder-score' hanchan.player4 %}?season={{season}}">{{hanchan.player4}}</a><br>
|
||||
{{hanchan.player4_game_score}}
|
||||
</td>
|
||||
<td class="center">
|
||||
{% if perms.mahjong_ranking.delete_hanchan %}
|
||||
<a href="{% url 'delete-hanchan' hanchan.pk %}"><span class="fa fa-trash" title="{% trans 'Delete Hanchan' %}"></span></a>
|
||||
{% endif %}
|
||||
{% if perms.mahjong_ranking.change_hanchan %}
|
||||
<a href="{% url 'edit-hanchan' hanchan.pk %}"><span class="fa fa-pencil" title="{% trans 'Edit Hanchan' %}"></span></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot><tr><td colspan="9" class="right">
|
||||
<form action="{% url 'player-ladder-score' membership.username %}" method="get">
|
||||
<label for="season">{% trans 'Season' %}:</label>
|
||||
<select id="season" name="season" size="1">
|
||||
{% for season_link in season_list%}
|
||||
<option value="{{ season_link }}" {% ifequal season season_link %}selected="selected"{% endifequal %}>{{ season_link }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<button type="submit">{% trans 'Go' %}</button>
|
||||
</form>
|
||||
</td></tr></tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,19 +1,19 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "mahjong_ranking/page.html" %}
|
||||
{% load i18n comments%}
|
||||
|
||||
|
||||
{% block teaser %}
|
||||
<h2>Mahjong Ranking - {{season.name}}</h2>
|
||||
<h2>Mahjong Ladder - {{ season }}</h2>
|
||||
<div id="teaser_text">
|
||||
<ul class="info">
|
||||
<li><span class="fa fa-calendar-o"></span> {% trans 'Start' %}: {{season.start}}</li>
|
||||
<li><span class="fa fa-calendar-o"></span> {% trans 'End' %}: {{season.end}}</li>
|
||||
<li><span class="fa fa-users"></span> {% trans 'Participants' %}: {{season.participants}}</li>
|
||||
<li><span class="fa fa-calendar-o"></span> {% trans 'Start' %}: {{ season_start|date:'SHORT_DATE_FORMAT' }}</li>
|
||||
<li><span class="fa fa-calendar-o"></span> {% trans 'End' %}: {{ season_end|date:'SHORT_DATE_FORMAT' }}</li>
|
||||
<li><span class="fa fa-users"></span> {% trans 'Participants' %}: {{ seasonranking_list.count }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block maincontent %}
|
||||
<main class="grid_12">
|
||||
{% block content %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -32,11 +32,11 @@
|
||||
<th>{% trans "won" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for player in ladderranking_list %}
|
||||
{% for player in seasonranking_list %}
|
||||
<tr>
|
||||
<td class="center">{{player.placement}}.</td>
|
||||
<td><a href="{{ player.get_absolute_url }}?season={{season.id}}"><img src="{% if player.user.avatar %}{{player.user.thumbnail.url}}{% else %}{{STATIC_URL}}img/unknown_thumbnail.png{% endif %}" class="avatar" alt=""/></a></td>
|
||||
<td><a href="{{ player.get_absolute_url }}?season={{season.id}}">{{player.user}}</a></td>
|
||||
<td><a href="{{ player.get_absolute_url }}?season={{season}}"><img src="{% if player.user.avatar %}{{player.user.thumbnail.url}}{% else %}{{STATIC_URL}}img/unknown_thumbnail.png{% endif %}" class="avatar" alt=""/></a></td>
|
||||
<td><a href="{{ player.get_absolute_url }}?season={{season}}">{{player.user}}</a></td>
|
||||
<td>{% if user.is_authenticated %}{{player.user.last_name}} {{player.user.first_name}}{% else %}---{% endif %}</td>
|
||||
<td class="center">{{player.avg_placement|floatformat:2 }}</td>
|
||||
<td class="right">{{player.avg_score|floatformat:0 }}</td>
|
||||
@@ -52,7 +52,6 @@
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
||||
{% block redbox %}
|
||||
@@ -76,7 +75,7 @@
|
||||
<label for="season">{% trans 'Season' %}</label>
|
||||
<select id="season" name="season" size="1" onChange="window.location.href = document.season_select.season.options[document.season_select.season.selectedIndex].value;">
|
||||
{% for season_link in season_list%}
|
||||
<option value="{{ season_link.get_absolute_url }}" {% ifequal season.id season_link.id %}selected="selected"{% endifequal %}>{{ season_link.name }}</option>
|
||||
<option value="{% url 'mahjong-ladder' season_link %}" {% ifequal season season_link %}selected="selected"{% endifequal %}>{{ season_link }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</form>
|
||||
@@ -14,14 +14,14 @@ import views
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
# url('^$', RedirectView.as_view(pattern_name='mahjong-ladder', permanent=True)),
|
||||
url('^$',views.LadderRankingList.as_view()),
|
||||
url('^$',views.SeasonRankingList.as_view()),
|
||||
url(r'^event/(?P<event>[\d]+)/mahjong/$', views.EventHanchanList.as_view(), name="event-hanchan-list"),
|
||||
url(r'^event/(?P<event>[\d]+)/add-hanchan/$', views.HanchanForm.as_view(), name="add-hanchan-form"),
|
||||
url(r'^event/(?P<event>[\d]+)/mahjong-ranking/$', views.EventRankingList.as_view(), name="event-ranking"),
|
||||
url(r'^hanchan/(?P<hanchan>[\d]+)/edit/$', views.HanchanForm.as_view(), name="edit-hanchan"),
|
||||
url(r'^hanchan/(?P<hanchan>[\d]+)/delete/$', views.DeleteHanchan.as_view(), name="delete-hanchan"),
|
||||
url(r'^mahjong-ladder/$', views.LadderRankingList.as_view(), name="mahjong-ladder"),
|
||||
url(r'^mahjong-ladder/(?P<season>[\d]+)/$', views.LadderRankingList.as_view(), name="mahjong-ladder"),
|
||||
url(r'^mahjong-ladder/$', views.SeasonRankingList.as_view(), name="mahjong-ladder"),
|
||||
url(r'^mahjong-ladder/(?P<season>[\d]+)/$', views.SeasonRankingList.as_view(), name="mahjong-ladder"),
|
||||
url(r'^player/(?P<username>[\-\.\d\w]+)/dan/$', views.PlayerDanScore.as_view(), name="player-dan-score"),
|
||||
url(r'^player/(?P<username>[\-\.\d\w]+)/invalid/$', views.PlayerInvalidScore.as_view(), name="player-invalid-score"),
|
||||
url(r'^player/(?P<username>[\-\.\d\w]+)/kyu/$', views.PlayerKyuScore.as_view(), name="player-kyu-score"),
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
from datetime import date
|
||||
|
||||
import urllib
|
||||
|
||||
from django.contrib import auth, messages
|
||||
from django.contrib import auth
|
||||
from django.core.urlresolvers import reverse
|
||||
import django.forms
|
||||
import django.http
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views import generic
|
||||
import xlwt
|
||||
|
||||
from events.models import Event
|
||||
from events.views import EventDetailMixin
|
||||
from . import forms, models
|
||||
from membership.models import Membership
|
||||
from utils.mixins import LoginRequiredMixin, PermissionRequiredMixin
|
||||
|
||||
|
||||
kyu_dan_order = {
|
||||
'+full_name': ('user__last_name', 'user__first_name'),
|
||||
'-full_name': ('-user__last_name', '-user__first_name'),
|
||||
@@ -46,49 +43,32 @@ class DeleteHanchan(EventDetailMixin, PermissionRequiredMixin, generic.DeleteVie
|
||||
kwargs={'event': self.object.event.pk})
|
||||
|
||||
|
||||
class HanchanForm(EventDetailMixin, PermissionRequiredMixin, generic.UpdateView):
|
||||
class HanchanForm(SuccessMessageMixin, EventDetailMixin, PermissionRequiredMixin, generic.UpdateView):
|
||||
"""
|
||||
Ein Formular um eine neue Hanchan anzulegen, bzw. eine bestehende zu
|
||||
Ein Formular um neue Hanchans anzulegen, bzw. eine bestehende zu
|
||||
bearbeitsen
|
||||
"""
|
||||
form_class = forms.HanchanForm
|
||||
model = models.Hanchan
|
||||
permission_required = 'mahjong_ranking.add_hanchan'
|
||||
|
||||
def form_valid(self, form, formset):
|
||||
if not self.object.pk:
|
||||
self.object = form.save()
|
||||
formset.save()
|
||||
self.object.save()
|
||||
else:
|
||||
formset.save()
|
||||
self.object = form.save()
|
||||
return django.http.HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
def form_invalid(self, form, formset):
|
||||
return self.render_to_response(self.get_context_data(
|
||||
form=form,
|
||||
formset=formset,
|
||||
object=self.object,
|
||||
))
|
||||
def get_context_data(self, **kwargs):
|
||||
context = generic.UpdateView.get_context_data(self, **kwargs)
|
||||
context['event'] = self.event
|
||||
return context
|
||||
|
||||
def get_form_class(self):
|
||||
"""
|
||||
Returns the form class to use in this view
|
||||
Users with edit Persmission will see the AdminForm to confirm
|
||||
unconfirmed Hanchans.
|
||||
"""
|
||||
if self.request.user.has_perm('mahjong_ranking.delete_hanchan'):
|
||||
if self.request.user.has_perm('mahjong_ranking.change_hanchan'):
|
||||
return forms.HanchanAdminForm
|
||||
else:
|
||||
return forms.HanchanForm
|
||||
|
||||
def get_formset(self):
|
||||
return forms.PlayerFormSet(
|
||||
data=self.request.POST or None,
|
||||
instance=self.object
|
||||
)
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
if self.kwargs.get('hanchan'):
|
||||
if self.kwargs.get('hanchan') and self.request.user.has_perm('mahjong_ranking.change_hanchan'):
|
||||
hanchan = models.Hanchan.objects.get(id=self.kwargs['hanchan'])
|
||||
self.event = hanchan.event
|
||||
elif self.kwargs.get('event'):
|
||||
@@ -97,38 +77,23 @@ class HanchanForm(EventDetailMixin, PermissionRequiredMixin, generic.UpdateView)
|
||||
event=self.event,
|
||||
start=self.event.start
|
||||
)
|
||||
else:
|
||||
hanchan = self.model()
|
||||
if hanchan.id and not hanchan.valid:
|
||||
messages.warning(self.request, hanchan.check_validity())
|
||||
return hanchan
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = generic.UpdateView.get_context_data(self, **kwargs)
|
||||
context['event'] = self.event
|
||||
return context
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
def get_success_url(self):
|
||||
"""
|
||||
|
||||
:param request:
|
||||
Adding a new Hanchan redirect to create_hanchan form.
|
||||
Editing an exiting one: redierect to the Hanchanevent.
|
||||
"""
|
||||
self.object = self.get_object()
|
||||
form = self.get_form(self.get_form_class())
|
||||
formset = self.get_formset()
|
||||
return self.render_to_response(
|
||||
self.get_context_data(form=form, formset=formset)
|
||||
)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
form_class = self.get_form_class()
|
||||
form = self.get_form(form_class)
|
||||
formset = self.get_formset()
|
||||
if form.is_valid() and formset.is_valid():
|
||||
return self.form_valid(form, formset)
|
||||
if self.kwargs.get('hanchan'):
|
||||
return self.object.get_absolute_url()
|
||||
else:
|
||||
return self.form_invalid(form, formset)
|
||||
return reverse('add-hanchan-form', kwargs={'event': self.event.pk})
|
||||
|
||||
def get_success_message(self, cleaned_data):
|
||||
if self.kwargs.get('hanchan'):
|
||||
return _('%s has been updated successfully.') % self.object
|
||||
else:
|
||||
return _('%s has been added successfully. You can now add a new one.') % self.object
|
||||
|
||||
|
||||
class EventHanchanList(EventDetailMixin, generic.ListView):
|
||||
@@ -142,7 +107,6 @@ class EventHanchanList(EventDetailMixin, generic.ListView):
|
||||
try:
|
||||
self.event = models.Event.objects.get(pk=self.kwargs['event'])
|
||||
queryset = models.Hanchan.objects.filter(event=self.event)
|
||||
queryset = queryset.prefetch_related('player_set__user')
|
||||
queryset = queryset.order_by('start')
|
||||
return queryset
|
||||
except models.Event.DoesNotExist:
|
||||
@@ -160,8 +124,7 @@ class EventRankingList(EventDetailMixin, generic.ListView):
|
||||
try:
|
||||
self.event = models.Event.objects.get(pk=self.kwargs['event'])
|
||||
queryset = models.EventRanking.objects.filter(event=self.event)
|
||||
queryset = queryset.prefetch_related()
|
||||
return queryset
|
||||
return queryset.prefetch_related()
|
||||
except models.Event.DoesNotExist:
|
||||
raise django.http.Http404(_('Event does not exist'))
|
||||
|
||||
@@ -176,130 +139,47 @@ class KyuDanRankingList(generic.ListView):
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.order_by = kyu_dan_order[
|
||||
kwargs.get('order_by', self.default_order)] # @IgnorePep8
|
||||
kwargs.get('order_by', self.default_order)
|
||||
]
|
||||
return generic.ListView.dispatch(self, request, *args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = models.KyuDanRanking.objects.all()
|
||||
queryset = queryset.select_related('user__membership')
|
||||
queryset = queryset.order_by(*self.order_by)
|
||||
return queryset
|
||||
queryset = models.KyuDanRanking.objects.all().order_by(*self.order_by)
|
||||
return queryset.select_related('user__membership')
|
||||
|
||||
|
||||
class LadderRankingList(generic.ListView):
|
||||
model = models.LadderRanking
|
||||
class SeasonRankingList(generic.ListView):
|
||||
model = models.SeasonRanking
|
||||
paginate_by = 25
|
||||
season = None
|
||||
is_archive = False
|
||||
|
||||
def get_queryset(self):
|
||||
try:
|
||||
if self.kwargs.get('season'):
|
||||
self.season = models.LadderSeason.objects.get(
|
||||
pk=self.kwargs['season'])
|
||||
self.is_archive = True
|
||||
elif self.kwargs.get('is_archive'):
|
||||
self.season = models.LadderSeason.objects.order_by('-pk')[1]
|
||||
self.is_archive = True
|
||||
else:
|
||||
self.season = models.LadderSeason.objects.current()
|
||||
except models.LadderSeason.DoesNotExist:
|
||||
raise django.http.Http404(_('Season does not exist'))
|
||||
queryset = models.LadderRanking.objects.filter(season=self.season,
|
||||
placement__isnull=False).select_related()
|
||||
return queryset
|
||||
self.season = int(self.kwargs.get('season', date.today().year))
|
||||
queryset = self.model.objects.filter(season=self.season,
|
||||
placement__isnull=False)
|
||||
return queryset.select_related()
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = generic.ListView.get_context_data(self, **kwargs)
|
||||
context['is_archive'] = self.is_archive
|
||||
context = super(SeasonRankingList, self).get_context_data(**kwargs)
|
||||
context['season'] = self.season
|
||||
context['season_list'] = models.LadderSeason.objects.all()
|
||||
context['latest_hanchan_list'] = models.Hanchan.objects.filter(
|
||||
valid=True)[:3]
|
||||
context['season_start'] = date(year=self.season, month=1, day=1)
|
||||
context['season_end'] = date(year=self.season, month=12, day=31)
|
||||
context['season_list'] = models.SeasonRanking.objects.season_list
|
||||
context['latest_hanchan_list'] = models.Hanchan.objects.confirmed_hanchans()[:3]
|
||||
context['latest_event_list'] = Event.objects.archive()[:3]
|
||||
return context
|
||||
|
||||
|
||||
class LadderRankingExcel(generic.View):
|
||||
center = xlwt.easyxf('align: horiz centre')
|
||||
# Format für Datumswerte
|
||||
date = xlwt.easyxf('align: horiz right', num_format_str='d.MM.YYYY')
|
||||
# Format für Überschriften
|
||||
header = xlwt.easyxf('font: bold on; align: wrap on, vert centre, \
|
||||
horiz center; pattern: back_color grey25;')
|
||||
filename = u"ladderranking.xls"
|
||||
|
||||
def get(self, request, **kwargs):
|
||||
self.queryset = self.team.members.all()
|
||||
response = django.http.HttpResponse(mimetype=u'application/msexcel')
|
||||
|
||||
filename = urllib.quote(self.filename.encode('utf-8'))
|
||||
response[
|
||||
'Content-Disposition'] = "attachment; filename*=UTF-8''%s" % filename
|
||||
|
||||
field_names = [u"Login", u"Name", u"E-Mail", u"Telefon", u"Handy",
|
||||
u"Geburtstag",
|
||||
u"T-Shirt", u"Teams", u"Gr.", u"Kg", u"Notfall Adresse",
|
||||
u"Notfall Nummer", u"Notizen", ]
|
||||
|
||||
|
||||
# Erstelle ein Workbook (Das ist eine Excel Datei)
|
||||
workbook = xlwt.Workbook(encoding='utf-8')
|
||||
|
||||
# Das Sheet ist eine Tabelle in der Excel Datei.
|
||||
# Dort wird die erste Spalte fixiert und mit den Zeilenüberschriften befüllt.
|
||||
sheet = workbook.add_sheet('Mitglieder', cell_overwrite_ok=False)
|
||||
sheet.set_panes_frozen(True)
|
||||
sheet.set_horz_split_pos(1) # in general, freeze after last heading row
|
||||
sheet.set_remove_splits(
|
||||
True) # if user does unfreeze, don't leave a split there
|
||||
for column in range(0, len(field_names)):
|
||||
sheet.write(0, column, field_names[column], style=header)
|
||||
|
||||
# Dann fangen wir an ab der 2. Zeile die einzelnen Datensätze einzuspielen.
|
||||
current_row = 2
|
||||
for user in self.queryset:
|
||||
self.set_col(sheet, current_row, 0, user.username)
|
||||
self.set_col(sheet, current_row, 1, user.get_full_name())
|
||||
self.set_col(sheet, current_row, 2, user.email)
|
||||
try:
|
||||
profile = user.get_profile()
|
||||
self.set_col(sheet, current_row, 3, profile.telephone or None)
|
||||
self.set_col(sheet, current_row, 4, profile.mobilephone or None)
|
||||
self.set_col(sheet, current_row, 5, profile.birthday or None,
|
||||
style=self.date)
|
||||
self.set_col(sheet, current_row, 6, profile.shirt_size or None,
|
||||
style=self.center)
|
||||
self.set_col(sheet, current_row, 7, self.get_other_teams(user))
|
||||
self.set_col(sheet, current_row, 8, profile.height or None)
|
||||
self.set_col(sheet, current_row, 9, profile.weight or None)
|
||||
self.set_col(sheet, current_row, 10,
|
||||
profile.emergency_contact or None)
|
||||
self.set_col(sheet, current_row, 11,
|
||||
profile.emergency_phone or None)
|
||||
except Membership.DoesNotExist:
|
||||
pass
|
||||
current_row += 1
|
||||
|
||||
for column in range(0, 13):
|
||||
sheet.col(column).width = (self.max_colwidth[column] + 1) * 256
|
||||
|
||||
workbook.save(response)
|
||||
return response
|
||||
|
||||
|
||||
class PlayerScore(LoginRequiredMixin, generic.ListView):
|
||||
paginate_by = 25
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
user_model = auth.get_user_model()
|
||||
try:
|
||||
user_model = auth.get_user_model()
|
||||
self.user = user_model.objects.get(username=self.kwargs.get('username'))
|
||||
except user_model.DoesNotExist:
|
||||
raise django.http.Http404(
|
||||
_("No user found matching the name %s") % self.kwargs.get(
|
||||
'username'))
|
||||
return generic.ListView.get(self, request, *args, **kwargs)
|
||||
raise django.http.Http404(_("No user found matching the name {}").format(self.kwargs.get('username')))
|
||||
return super(PlayerScore, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = generic.ListView.get_context_data(self, **kwargs)
|
||||
@@ -310,11 +190,11 @@ class PlayerScore(LoginRequiredMixin, generic.ListView):
|
||||
except models.KyuDanRanking.DoesNotExist:
|
||||
context['ranking'] = None
|
||||
try:
|
||||
context['ladder_ranking'] = models.LadderRanking.objects.get(
|
||||
context['ladder_ranking'] = models.SeasonRanking.objects.get(
|
||||
user=self.user,
|
||||
season=models.LadderSeason.objects.current())
|
||||
except models.LadderRanking.DoesNotExist:
|
||||
context['ladder_ranking'] = models.LadderRanking(user=self.user)
|
||||
season=date.today().year)
|
||||
except models.SeasonRanking.DoesNotExist:
|
||||
context['ladder_ranking'] = models.SeasonRanking(user=self.user)
|
||||
return context
|
||||
|
||||
|
||||
@@ -322,42 +202,39 @@ class PlayerDanScore(PlayerScore):
|
||||
template_name = 'mahjong_ranking/player_dan_score.html'
|
||||
|
||||
def get_queryset(self):
|
||||
return models.Player.objects.dan_hanchans(self.user)
|
||||
return models.Hanchan.objects.dan_hanchans(user=self.user)
|
||||
|
||||
|
||||
class PlayerInvalidScore(PlayerScore):
|
||||
template_name = 'mahjong_ranking/player_invalid_score.html'
|
||||
|
||||
def get_queryset(self):
|
||||
return models.Player.objects.non_counting_hanchans(self.user)
|
||||
return models.Hanchan.objects.unconfirmed_hanchans(user=self.user)
|
||||
|
||||
|
||||
class PlayerKyuScore(PlayerScore):
|
||||
template_name = 'mahjong_ranking/player_kyu_score.html'
|
||||
|
||||
def get_queryset(self):
|
||||
return models.Player.objects.kyu_hanchans(self.user)
|
||||
return models.Hanchan.objects.kyu_hanchans(self.user)
|
||||
|
||||
|
||||
class PlayerLadderScore(PlayerScore):
|
||||
template_name = 'mahjong_ranking/player_ladder_score.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = PlayerScore.get_context_data(self, **kwargs)
|
||||
season_list = models.LadderRanking.objects.filter(
|
||||
user=self.user).select_related('user')
|
||||
season_list = season_list.values_list('id', 'season__name')
|
||||
context = super(PlayerLadderScore, self).get_context_data(**kwargs)
|
||||
context['season'] = self.season
|
||||
context['seasons_select_form'] = forms.SeasonSelectForm(user=self.user)
|
||||
context['seasons_select_field'] = django.forms.ChoiceField(
|
||||
choices=season_list)
|
||||
context['season_start'] = date(year=self.season, month=1, day=1)
|
||||
context['season_end'] = date(year=self.season, month=12, day=31)
|
||||
context['season_list'] = models.SeasonRanking.objects.season_list
|
||||
return context
|
||||
|
||||
def get_queryset(self, **kwargs):
|
||||
if self.request.GET.get('season'):
|
||||
self.season = models.LadderSeason.objects.get(
|
||||
pk=self.request.GET['season'])
|
||||
else:
|
||||
self.season = models.LadderSeason.objects.current()
|
||||
return models.Player.objects.ladder_hanchans(user=self.user,
|
||||
season=self.season)
|
||||
try:
|
||||
self.season = int(self.request.GET.get('season'))
|
||||
except:
|
||||
self.season = date.today().year
|
||||
hanchan_list = models.Hanchan.objects.season_hanchans(user=self.user, season=self.season)
|
||||
print hanchan_list
|
||||
return hanchan_list
|
||||
|
||||
Reference in New Issue
Block a user