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:
Christian Berg
2015-08-23 16:37:39 +02:00
parent 8981d4b261
commit a7bfd2157d
279 changed files with 14708 additions and 2429 deletions

View File

@@ -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)

View File

@@ -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
)

View File

@@ -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"

View 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()

View File

@@ -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

View File

@@ -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)

View File

@@ -7,11 +7,13 @@
<h2 class="grid_12">{% trans 'Played Hanchans' %}</h2>
<p>&nbsp;</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>

View File

@@ -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 %}

View File

@@ -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 %}

View File

@@ -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 %}

View File

@@ -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 %}

View File

@@ -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>

View File

@@ -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 %}

View File

@@ -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 %}

View File

@@ -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 %}

View File

@@ -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>

View File

@@ -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"),

View File

@@ -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