Fehler bei Vergabe von Bonuspunkte korrigiert.

Kommentare für Bonuspunkte werden jetzt als Kommentar beim Spieler hinterlassen, nicht als Kommentar in der Hanchan.
FIXED: 3_in_a_row counter wurde nicht zurückgesetzt wenn Bonuspunkte vergeben wurden.
FIXED: Durchschnittliche Platzierung während eines Events wurde nur als Ganzzahl berechnet. Wird nun als Fießkomma berechnet und gesichert.
This commit is contained in:
Christian Berg
2016-01-09 22:55:26 +01:00
parent 088efe2f39
commit b1586efbab
155 changed files with 2571 additions and 2835 deletions

View File

@@ -24,7 +24,8 @@ def recalculate(modeladmin, request, queryset):
set_dirty(user=kyu_dan_ranking.user_id)
elif isinstance(modeladmin, SeasonRankingAdmin):
for ladder_ranking in queryset:
set_dirty(user=ladder_ranking.user_id, season=ladder_ranking.season)
set_dirty(user=ladder_ranking.user_id,
season=ladder_ranking.season)
recalculate.short_description = _("Recalculate")
@@ -41,6 +42,7 @@ def unconfirm(modeladmin, request, queryset):
hanchan.save()
unconfirm.short_description = _('Set unconfirmed')
class EventRankingAdmin(admin.ModelAdmin):
list_filter = ['event']
list_display = ('placement', 'user', 'event', 'avg_placement', 'avg_score',
@@ -61,7 +63,7 @@ class HanchanAdmin(admin.ModelAdmin):
('Hanchan', {
'fields': (
('event', 'season'),
'start',
'start',
('player1', 'player1_input_score', 'player1_comment'),
('player2', 'player2_input_score', 'player2_comment'),
('player3', 'player3_input_score', 'player3_comment'),
@@ -84,7 +86,7 @@ class KyuDanAdmin(admin.ModelAdmin):
'classes': ('grp-collapse grp-open',),
}),
('Frühere Aufzeichnungen', {
'fields': ('legacy_date', ('legacy_kyu_points', 'legacy_dan_points')),
'fields': ('legacy_date', 'legacy_hanchan_count', ('legacy_kyu_points', 'legacy_dan_points')),
'classes': ('grp-collapse grp-closed',),
}),
)

View File

@@ -23,45 +23,33 @@ class HanchanForm(forms.ModelForm):
class Meta(object):
model = models.Hanchan
fields = ( 'start',
'player1', 'player1_input_score',
'player2', 'player2_input_score',
'player3', 'player3_input_score',
'player4', 'player4_input_score',
fields = ('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')
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'
player_input_score = 'player%d_input_score' % i
self.fields[player_input_score].widget.attrs['size'] = 6
self.fields[player_input_score].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."""
start = self.cleaned_data['start']
event = self.cleaned_data['event']
if start > timezone.now():
raise django.forms.ValidationError(
_("It's not allowed to enter future games."))
if not event.start <= start <= event.end:
raise django.forms.ValidationError(
_("Only games running during this event are allowed."))
return start
'''
class HanchanAdminForm(HanchanForm):
class Meta(object):
model = models.Hanchan
fields = HanchanForm.Meta.fields + ('confirmed',)
class SeasonSelectForm(django.forms.Form):
season = django.forms.ChoiceField(label='', choices=('a', 'b', 'c'))

View File

@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: kasu.mahjong_ranking\n"
"Report-Msgid-Bugs-To: \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"
"PO-Revision-Date: 2015-09-06 00:13+0200\n"
"Last-Translator: Christian Berg <xeniac.at@gmail.com>\n"
"Language-Team: Kasu <verein@kasu.at>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
@@ -375,7 +375,7 @@ 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."
msgstr "%s wurde erfolgreich hinzugefügt. Du kannst eine neue eintragen."
#: views.py:113 views.py:129
msgid "Event does not exist"

View File

@@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
"""
Export Mahjong Rankings...
"""
from operator import itemgetter
from django.core.management.base import BaseCommand
from mahjong_ranking.models import SeasonRanking
from openpyxl import Workbook
class Command(BaseCommand):
help = "Exports the SeasonRankings"
def geneate_seasonexcel(self, json_data):
wb = Workbook()
worksheet = wb.active
worksheet.append([
'Rang', 'Spitzname',
'⌀ Platz', '⌀ Punkte',
'Hanchans', 'Gut', 'Gewonnen'
])
json_data = json_data.values()
json_data = sorted(json_data, key=itemgetter('placement'))
for row in json_data:
print row
worksheet.append([
row['placement'], row['username'],
row['avg_placement'], row['avg_score'],
row['hanchan_count'],
row['good_hanchans'], row['won_hanchans']
])
wb.save("sample.xlsx")
def handle(self, *args, **options):
season_json = SeasonRanking.objects.json_data()
self.geneate_seasonexcel(season_json)

View File

@@ -85,7 +85,6 @@ class Command(BaseCommand):
self.add_players(hanchan)
hanchan.save()
def handle(self, *args, **options):
num_hanchans = int(options.get('hanchans', 4))
self.user_list = list(auth.get_user_model().objects.all())
@@ -93,5 +92,3 @@ class Command(BaseCommand):
for event in Event.objects.all():
for i in range(random.randrange(2, 8)):
self.create_hanchan(event)

View File

@@ -1,11 +1,11 @@
__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):
@@ -77,3 +77,25 @@ class SeasonRankingManager(models.Manager):
def season_list(self):
values_list = self.model.objects.values_list('season', flat=True)
return values_list.order_by('season').distinct()
def json_data(self, season=None):
season = season or date.today().year
json_data = {}
values = self.filter(season=season, placement__isnull=False)
values = values.values('placement', 'user_id', 'user__username',
'user__first_name', 'user__last_name', 'avg_placement', 'avg_score',
'hanchan_count', 'good_hanchans', 'won_hanchans')
for user in values:
json_data[user['user_id']] = {
'placement': user['placement'],
'user_id': user['user_id'],
'username': user['user__username'],
'first_name': user['user__first_name'],
'last_name': user['user__last_name'],
'avg_placement': user['avg_placement'],
'avg_score': user['avg_score'],
'hanchan_count': user['hanchan_count'],
'good_hanchans': user['good_hanchans'],
'won_hanchans': user['won_hanchans']
}
return json_data

View File

@@ -36,38 +36,41 @@ class DenormalizationUpdateMiddleware(object):
cache.set('ladder_ranking_queue', set(), 360)
for event_id in self.event_queue:
self.update_event_placement()
self.update_event_placements()
for season in self.season_queue:
self.update_season_placements()
return response
def recalculate_event_rankings(self, queue):
# recalculate tournament (event) rankings:
for event_id, user_id in queue:
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]
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 = 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 = 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')
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

View File

@@ -0,0 +1,157 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('events', '0005_auto_20150907_2021'),
]
operations = [
migrations.CreateModel(
name='EventRanking',
fields=[
('id', models.AutoField(verbose_name='ID',
serialize=False, auto_created=True, primary_key=True)),
('placement', models.PositiveIntegerField(null=True, blank=True)),
('avg_placement', models.FloatField(default=4)),
('avg_score', models.FloatField(default=0)),
('hanchan_count', models.PositiveIntegerField(default=0)),
('good_hanchans', models.PositiveIntegerField(default=0)),
('won_hanchans', models.PositiveIntegerField(default=0)),
('event', models.ForeignKey(to='events.Event')),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ('placement', 'avg_placement', '-avg_score'),
},
),
migrations.CreateModel(
name='Hanchan',
fields=[
('id', models.AutoField(verbose_name='ID',
serialize=False, auto_created=True, primary_key=True)),
('start', models.DateTimeField(
help_text='Wichtig damit die richtigen Hanchans in die Wertung kommen.', verbose_name='Beginn')),
('player1_input_score', models.IntegerField(verbose_name='Punkte')),
('player1_game_score', models.PositiveIntegerField(
default=0, verbose_name='Punkte', editable=False)),
('player1_placement', models.PositiveSmallIntegerField(
default=0, editable=False)),
('player1_kyu_points', models.SmallIntegerField(
null=True, editable=False, blank=True)),
('player1_dan_points', models.SmallIntegerField(
null=True, editable=False, blank=True)),
('player1_bonus_points', models.SmallIntegerField(
null=True, editable=False, blank=True)),
('player1_comment', models.CharField(verbose_name='Anmerkung',
max_length=255, editable=False, blank=True)),
('player2_input_score', models.IntegerField(verbose_name='Punkte')),
('player2_game_score', models.PositiveIntegerField(
default=0, verbose_name='Punkte', editable=False)),
('player2_placement', models.PositiveSmallIntegerField(
default=0, editable=False)),
('player2_kyu_points', models.SmallIntegerField(
null=True, editable=False, blank=True)),
('player2_dan_points', models.SmallIntegerField(
null=True, editable=False, blank=True)),
('player2_bonus_points', models.SmallIntegerField(
null=True, editable=False, blank=True)),
('player2_comment', models.CharField(verbose_name='Anmerkung',
max_length=255, editable=False, blank=True)),
('player3_input_score', models.IntegerField(verbose_name='Punkte')),
('player3_game_score', models.PositiveIntegerField(
default=0, verbose_name='Punkte', editable=False)),
('player3_placement', models.PositiveSmallIntegerField(
default=0, editable=False)),
('player3_kyu_points', models.SmallIntegerField(
null=True, editable=False, blank=True)),
('player3_dan_points', models.SmallIntegerField(
null=True, editable=False, blank=True)),
('player3_bonus_points', models.SmallIntegerField(
null=True, editable=False, blank=True)),
('player3_comment', models.CharField(verbose_name='Anmerkung',
max_length=255, editable=False, blank=True)),
('player4_input_score', models.IntegerField(verbose_name='Punkte')),
('player4_game_score', models.PositiveIntegerField(
default=0, verbose_name='Punkte', editable=False)),
('player4_placement', models.PositiveSmallIntegerField(
default=0, editable=False)),
('player4_kyu_points', models.SmallIntegerField(
null=True, editable=False, blank=True)),
('player4_dan_points', models.SmallIntegerField(
null=True, editable=False, blank=True)),
('player4_bonus_points', models.SmallIntegerField(
null=True, editable=False, blank=True)),
('player4_comment', models.CharField(verbose_name='Anmerkung',
max_length=255, editable=False, blank=True)),
('comment', models.TextField(verbose_name='Anmerkung', blank=True)),
('confirmed', models.BooleanField(
default=True, help_text='Nur g\xfcltige und best\xe4tigte Hanchans kommen in die Wertung.', verbose_name='Wurde best\xe4tigt')),
('player_names', models.CharField(max_length=255, editable=False)),
('season', models.PositiveSmallIntegerField(
verbose_name='Saison', editable=False, db_index=True)),
('event', models.ForeignKey(to='events.Event')),
('player1', models.ForeignKey(related_name='user_hanchan+',
verbose_name='Spieler 1', to=settings.AUTH_USER_MODEL)),
('player2', models.ForeignKey(related_name='user_hanchan+',
verbose_name='Spieler 2', to=settings.AUTH_USER_MODEL)),
('player3', models.ForeignKey(related_name='user_hanchan+',
verbose_name='Spieler 3', to=settings.AUTH_USER_MODEL)),
('player4', models.ForeignKey(related_name='user_hanchan+',
verbose_name='Spieler 4', to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ('-start',),
'verbose_name': 'Hanchan',
'verbose_name_plural': 'Hanchans',
},
),
migrations.CreateModel(
name='KyuDanRanking',
fields=[
('id', models.AutoField(verbose_name='ID',
serialize=False, auto_created=True, primary_key=True)),
('dan', models.PositiveSmallIntegerField(null=True, blank=True)),
('dan_points', models.PositiveIntegerField(default=0)),
('kyu', models.PositiveSmallIntegerField(
default=10, null=True, blank=True)),
('kyu_points', models.PositiveIntegerField(default=0)),
('won_hanchans', models.PositiveIntegerField(default=0)),
('good_hanchans', models.PositiveIntegerField(default=0)),
('hanchan_count', models.PositiveIntegerField(default=0)),
('legacy_date', models.DateField(null=True, blank=True)),
('legacy_dan_points', models.PositiveIntegerField(default=0)),
('legacy_kyu_points', models.PositiveIntegerField(default=0)),
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ('-dan', '-dan_points', '-kyu_points'),
'verbose_name': 'Ky\u016b/Dan Wertung',
'verbose_name_plural': 'Ky\u016b/Dan Wertungen',
},
),
migrations.CreateModel(
name='SeasonRanking',
fields=[
('id', models.AutoField(verbose_name='ID',
serialize=False, auto_created=True, primary_key=True)),
('season', models.PositiveSmallIntegerField(verbose_name='Saison')),
('placement', models.PositiveIntegerField(null=True, blank=True)),
('avg_placement', models.FloatField(null=True, blank=True)),
('avg_score', models.FloatField(null=True, blank=True)),
('hanchan_count', models.PositiveIntegerField(default=0)),
('good_hanchans', models.PositiveIntegerField(default=0)),
('won_hanchans', models.PositiveIntegerField(default=0)),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ('placement', 'avg_placement', '-avg_score'),
},
),
]

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('mahjong_ranking', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='kyudanranking',
name='legacy_hanchan_count',
field=models.PositiveIntegerField(default=0),
),
]

View File

@@ -1,7 +1,9 @@
# -*- encoding: utf-8 -*-
# TODO: Rankings archiv Flag erstellen, womit sie nicht mehr neuberechnet werden dürfen.
# TODO: Rankings archiv Flag erstellen, womit sie nicht mehr neuberechnet
# werden dürfen.
from __future__ import division
from datetime import date
from django.conf import settings
@@ -9,11 +11,10 @@ from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext as _
from events.models import Event
from . import KYU_RANKS, DAN_RANKS, DAN_RANKS_DICT, logger, set_dirty, MIN_HANCHANS_FOR_LADDER
from . import KYU_RANKS, DAN_RANKS, DAN_RANKS_DICT, logger, set_dirty
from . import managers
kyu_dan_rankings = set()
@@ -40,7 +41,7 @@ class EventRanking(models.Model):
ordering = ('placement', 'avg_placement', '-avg_score',)
def get_absolute_url(self):
return reverse('event-ranking', args=[self.tourney_id])
return reverse('event-ranking', args=[self.event_id])
def recalculate(self):
"""
@@ -50,23 +51,33 @@ class EventRanking(models.Model):
können zwar sehr leicht errechnet werden, es macht trotzdem Sinn
sie zwischen zu speichern.
"""
logger.info(u'Recalculate EventRanking for Player %s in %s', self.user,
self.event.name) # @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'),
models.Count('pk'))
self.avg_placement = aggregator['placement__avg']
self.avg_score = aggregator['score__avg']
self.hanchan_count = aggregator['pk__count']
self.good_hanchans = event_hanchans.filter(placement__lt=3).count()
self.won_hanchans = event_hanchans.filter(placement=1).count()
logger.info(
u'Recalculate EventRanking for Player %s in %s',
self.user, self.event.name
)
sum_placement = 0.0
sum_score = 0.0
event_hanchans = Hanchan.objects.confirmed_hanchans(
user=self.user_id,
event=self.event_id
)
self.hanchan_count = event_hanchans.count()
self.won_hanchans = 0
self.good_hanchans = 0
if self.hanchan_count <= 0:
self.delete()
else:
self.save()
for hanchan in event_hanchans:
hanchan.get_playerdata(user=self.user)
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
self.avg_placement = sum_placement / self.hanchan_count
self.avg_score = sum_score / self.hanchan_count
self.save(force_update=True)
class Hanchan(models.Model):
@@ -76,52 +87,88 @@ class Hanchan(models.Model):
Außerdem gehört jede Hanchan zu einer Veranstaltung.
"""
event = models.ForeignKey(Event)
start = models.DateTimeField(_('Start'),
help_text=_('This is crucial to get the right Hanchans that scores')
)
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 = 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)
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 = 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)
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 = 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)
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 = 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)
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.')
help_text=_(
'Only valid and confirmed Hanchans '
'will be counted in the rating.')
)
player_names = models.CharField(max_length=255, editable=False)
season = models.PositiveSmallIntegerField(_('Season'), editable=False, db_index=True)
season = models.PositiveSmallIntegerField(
_('Season'), editable=False, db_index=True)
objects = managers.HanchanManager()
class Meta(object):
@@ -137,7 +184,8 @@ class Hanchan(models.Model):
def clean(self):
"""
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.
if the Game between the opening hours and the Hanchan has a total of
100.000 Points.
"""
errors = {}
score_sum = 0
@@ -155,18 +203,21 @@ class Hanchan(models.Model):
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
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")
errors['start'] = _(
"Games in the future may not be added, Dr. Brown")
elif not (self.event.start <= self.start <= self.event.end):
errors['start'] = _("Only games during the event are allowed")
elif score_sum < 100000:
errors['player4_input_score'] = _('Gamescore is lower then 100.000 Pt.')
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.')
@@ -190,12 +241,18 @@ class Hanchan(models.Model):
else:
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_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']
@@ -206,13 +263,13 @@ class Hanchan(models.Model):
def get_absolute_url(self):
return "{url:s}#{id:d}".format(
url=reverse('event-hanchan-list', kwargs={'event':self.event_id}),
id= self.pk
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"""
"""small workaround to access score, placement and points of a
specific user prominent in the user templates"""
for player in ('player1', 'player2', 'player3', 'player4'):
if getattr(self, player) == user:
self.user = user
@@ -252,7 +309,8 @@ class Hanchan(models.Model):
'comment': getattr(self, 'player%d_comment' % i),
})
# sort player by Score:
return sorted(player_list, key=lambda player: player['input_score'], reverse=True)
return sorted(player_list, key=lambda player: player['input_score'],
reverse=True)
def save(self, **kwargs):
self.season = self.event.mahjong_season or date.today().year
@@ -276,12 +334,13 @@ class KyuDanRanking(models.Model):
good_hanchans = models.PositiveIntegerField(default=0)
hanchan_count = models.PositiveIntegerField(default=0)
legacy_date = models.DateField(blank=True, null=True)
legacy_hanchan_count = models.PositiveIntegerField(default=0)
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',)
ordering = ('-dan', '-dan_points', '-kyu_points',)
verbose_name = _(u'Kyū/Dan Ranking')
verbose_name_plural = _(u'Kyū/Dan Rankings')
@@ -316,15 +375,18 @@ class KyuDanRanking(models.Model):
hanchan.dan_points += bonus_points
hanchan.bonus_points += bonus_points
hanchan.player_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
self.wins_in_a_row = 0
# 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.
bei Bedarf Bonuspunkte vergeben, falls der Spieler das Turnier
gewonnen hat.
:param hanchan: Ein Player Objekt
"""
bonus_points = 0
@@ -333,24 +395,27 @@ class KyuDanRanking(models.Model):
).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=hanchan.event)
if event_ranking.placement == 1:
bonus_points += 4
hanchan.comment += '+4 Punkte für den Sieg des Turnieres. '
if event_ranking.avg_placement == 1:
bonus_points += 8
hanchan.comment += '+8 Pkt: alle Spiele des Turnieres gewonnen. '
return False # Das braucht nur am Ende eines Turnieres gemacht werden.
else:
event_ranking = EventRanking.objects.get(
user=self.user,
event=hanchan.event
)
if event_ranking.placement == 1:
bonus_points += 4
hanchan.player_comment += u'+4 Punkte Turnier gewonnen. '
if event_ranking.avg_placement == 1:
bonus_points += 8
hanchan.player_comment += u'+8 Pkt: alle Spiele des Turnieres gewonnen. '
if bonus_points and self.dan:
hanchan.dan_points += bonus_points
self.dan_points += bonus_points
elif bonus_points:
hanchan.kyu_points += bonus_points
self.kyu_points += bonus_points
hanchan.bonus_points += bonus_points
if bonus_points and self.dan:
hanchan.dan_points += bonus_points
self.dan_points += bonus_points
elif bonus_points:
hanchan.kyu_points += bonus_points
self.kyu_points += bonus_points
hanchan.bonus_points += bonus_points
return True
def get_absolute_url(self):
if self.dan or self.dan_points > 0:
@@ -363,32 +428,33 @@ class KyuDanRanking(models.Model):
Fetches all valid Hanchans from this Player and recalculates his
Kyu/Dan Ranking.
"""
self.kyu_points = self.legacy_kyu_points or 0
self.dan_points = self.legacy_dan_points or 0
self.dan = None
self.dan_points = self.legacy_dan_points or 0
self.kyu = None
self.won_hanchans = 0
self.good_hanchans = 0
self.kyu_points = self.legacy_kyu_points or 0
self.hanchan_count = self.legacy_hanchan_count or 0
logger.info("recalculating Kyu/Dan punkte for %s...", self.user)
self.good_hanchans = 0
self.won_hanchans = 0
logger.info("recalculating Kyu/Dan points 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()
self.hanchan_count += valid_hanchans.count()
for hanchan in valid_hanchans:
hanchan.get_playerdata(self.user)
hanchan.bonus_points=0
hanchan.player_comment=""
hanchan.bonus_points = 0
hanchan.player_comment = u""
self.update_hanchan_points(hanchan)
if hanchan.event.mahjong_tournament:
self.append_tournament_bonuspoints(hanchan)
self.update_rank()
self.append_3_in_a_row_bonuspoints(hanchan)
self.update_rank()
self.won_hanchans += 1 if hanchan.placement == 1 else 0
self.won_hanchans += 1 if hanchan.placement == 1 else 0
self.good_hanchans += 1 if hanchan.placement == 2 else 0
hanchan.update_playerdata(self.user)
hanchan.save(force_update=True)
@@ -447,7 +513,8 @@ class KyuDanRanking(models.Model):
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?
# 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
@@ -493,7 +560,8 @@ class SeasonRanking(models.Model):
return reverse('player-ladder-score', args=[self.user.username])
def recalculate(self):
season_hanchans = Hanchan.objects.season_hanchans(user=self.user, season=self.season)
season_hanchans = Hanchan.objects.season_hanchans(
user=self.user, season=self.season)
sum_placement = 0
sum_score = 0
self.placement = None
@@ -501,7 +569,9 @@ class SeasonRanking(models.Model):
self.good_hanchans = 0
self.won_hanchans = 0
logger.info(u'Recalculate LadderRanking for Player %s in Season %s', self.user, self.season)
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
@@ -517,16 +587,21 @@ class SeasonRanking(models.Model):
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)
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)
logger.debug(
"marking event %s for recalculation.", instance.event)
set_dirty(event=instance.event_id, user=user.id)
if instance.season:
logger.debug("Marking %s's ladder %i season for recalculation.", user, instance.season)
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)
models.signals.pre_delete.connect(update_ranking, sender=Hanchan)
models.signals.post_save.connect(update_ranking, sender=Hanchan)

View File

@@ -1,5 +1,5 @@
{% extends "events/event_detail.html" %}
{% load i18n humanize %}
{% load i18n humanize thumbnail %}
{% block title %}Hanchans: {{ event.name }}{% endblock %}
@@ -7,38 +7,45 @@
<h2 class="grid_12">{% trans 'Played Hanchans' %}</h2>
<p>&nbsp;</p>
{% for hanchan in hanchan_list %}
<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 %}
<p class="grid_12 more_link">
{% if perms.mahjong_ranking.delete_hanchan %}
<a href="{% url 'delete-hanchan' hanchan.pk %}" class="button"><span class="fa fa-trash"></span> {% trans 'Delete Hanchan' %}</a>
<h3 class="grid_12" id="{{ hanchan.pk }}">{{hanchan.start|time:'H:i'}}: {{ hanchan.player_names }}</h3>
{% for player in hanchan.player_list %}
<div class="grid_3">
<a href="{% url 'player-ladder-score' player.user %}" class="avatar"><img
src="{% thumbnail player.user.avatar|default:'unknown_profile.jpg' 'avatar' %}"
width="70" height="70" alt="" title="{{ player.user }}"/></a>
<a class="player" href="{% url 'player-ladder-score' player.user %}" title="{{ player.comment }}">{{ player.user }}</a>
<ul class="fa-ul">
<li><strong>{{ player.placement|ordinal }} {% trans 'Place' %}</strong></li>
<li><strong>{% trans 'Score' %}:</strong> {{ player.game_score|intcomma }}</li>
{% if player.dan_points != None %}
<li><strong>{% trans 'Dan Points' %}:</strong> {{ player.dan_points }}</li>
{% else %}
<li><strong>{% trans 'Kyu Points' %}:</strong> {{ player.kyu_points|default:0 }}</li>
{% endif %}
</ul>
</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 %}
{% if perms.mahjong_ranking.change_hanchan %}
<a href="{% url 'edit-hanchan' hanchan.pk %}" class="button"><span class="fa fa-pencil"></span> {% trans 'Edit Hanchan' %}</a>
{% endif %}
</p>
<p class="grid_12 more_link">
{% if perms.mahjong_ranking.delete_hanchan %}
<a href="{% url 'delete-hanchan' hanchan.pk %}" class="button">
<span class="fa fa-trash"></span>
{% trans 'Delete Hanchan' %}
</a>
{% endif %}
{% if perms.mahjong_ranking.change_hanchan %}
<a href="{% url 'edit-hanchan' hanchan.pk %}" class="button">
<span class="fa fa-pencil"></span>
{% trans 'Edit Hanchan' %}
</a>
{% endif %}
</p>
{% empty %}
<h3 class="grid_12">{% trans 'No Hanchan has been added to this event yet.'%}</h3>
<h3 class="grid_12">{% trans 'No Hanchan has been added to this event yet.'%}</h3>
{% endfor %}
{% endblock %}

View File

@@ -1,6 +1,5 @@
{% extends "events/event_detail.html" %}
{% load i18n comments%}
{% load i18n comments humanize thumbnail %}
{% block title %}{% trans "Tournament Ranking" %}: {{ event.name }}{% endblock %}
{% block teaser %}<h1>{% trans "Tournament Ranking" %}: {{ event.name }}</h1>{% endblock %}
@@ -28,17 +27,14 @@
{% for player in eventranking_list %}
<tr>
<td class="center">{{player.placement}}.</td>
<td class="avatar"><a href="{{ player.user.get_absolute_url }}">
{% if player.user.avatar %}
<img src="{{player.user.thumbnail.url}}" alt="" />
{% else %}
<img src="{{STATIC_URL}}img/unknown_thumbnail.png" alt=""/>
{% endif %}
</a></td>
<td><a href="{{ player.user.get_absolute_url }}">{{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>
<td><a href="{{ player.get_absolute_url }}?season={{season}}"><img
src="{% thumbnail player.user.avatar|default:'unknown_profile.jpg' 'avatar' %}"
width="70" height="70" 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|intcomma }}</td>
<td class="right">{{player.hanchan_count}}</td>
<td class="right">{{player.good_hanchans}}</td>
<td class="right">{{player.won_hanchans}}</td>

View File

@@ -1,5 +1,5 @@
{% extends "events/event_detail.html" %}
{% load i18n humanize %}
{% load i18n humanize thumbnail %}
{% block meta_title %}{% trans 'Delete Hanchan' %}{% endblock %}
@@ -7,7 +7,8 @@
<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 %}"
src="{% thumbnail user.avatar|default:'unknown_profile.jpg' 'avatar' %}"
width="70" height="70"
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>

View File

@@ -40,25 +40,42 @@ $('input[name$="_input_score"]').change(function() {recalculate_values(this);});
<table>
<thead><tr>
<th width="25%">{% trans 'Player' %}</th>
<th width="75%">{% trans 'Score' %}</th>
<th width="25%">{% trans 'Score' %}</th>
<th width="50%">{% trans 'Comment' %}</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>
<td>{{ form.player1_input_score}}</td>
<td>
{{ form.instance.player1_comment}}
{% 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>
<td>{{ form.player2_input_score}}</td>
<td>
{{ form.instance.player2_comment }}
{% 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>
<td>{{ form.player3_input_score}}</td>
<td>
{{ form.instance.player3_comment }}
{% 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>
<td>{{ form.player4_input_score}}</td>
<td>
{{ form.instance.player4_comment }}
{% if form.player4_input_score.errors %}{{ form.player4_input_score.errors }}{% endif %}
</td>
</tr>
</tbody>
<tfoot><tr>

View File

@@ -1,9 +1,11 @@
{% extends "mahjong_ranking/page.html" %}
{% load i18n %}
{% load i18n thumbnail %}
{% block title %}{% trans 'Player List' %}{% endblock %}
{% block teaser %}<h1>{% trans 'Player List' %}</h1>{% endblock %}
{% block redbox %}{% include 'mahjong_ranking/ladder_redbox.html' %}{% endblock %}
{% block content %}
<table>
<thead>
@@ -40,11 +42,9 @@
{% for ranking in kyudanranking_list %}
<tr>
<td>
{% if ranking.user.avatar %}
<a href="{{ ranking.get_absolute_url }}"><img src="{{ranking.user.thumbnail.url}}" alt="" /></a>
{% else %}
<a href="{{ ranking.get_absolute_url }}"><img src="{{STATIC_URL}}img/unknown_thumbnail.png" alt=""/></a>
{% endif %}
<a href="{{ ranking.get_absolute_url }}"><img
src="{% thumbnail ranking.user.avatar|default:'unknown_profile.jpg' 'avatar' %}"
width="70" height="70" alt="" /></a>
</td>
<td><a href="{{ ranking.get_absolute_url }}">{{ ranking.user }}</a></td>
<td>{% if user.is_authenticated %}{{ranking.user.last_name}} {{ranking.user.first_name}}{% else %}---{% endif %}</td>

View File

@@ -0,0 +1,36 @@
{% load i18n %}
<h2>{% trans 'Latest Hanchans' %}</h2>
<ul class="fa-ul">
{% for hanchan in latest_hanchan_list %}
<li><span class="fa-li fa fa-table"></span>
<a href="{% url 'event-hanchan-list' hanchan.event_id %}">
<time datetime="{{ hanchan.start|date:'c' }}">{{ hanchan.start|date:'D' }}
{{ hanchan.start|date:'SHORT_DATE_FORMAT' }} {{hanchan.start|time:'H:i'}}
</time></a>:<br />
<small>{{hanchan.player_names}}</small>
</li>
{% endfor %}
</ul>
<h3>{% trans 'Latest Events' %}</h3>
<ul class="fa-ul">
{% for event in latest_event_list %}
<li>
<span class="fa-li fa fa-calendar-o"></span>
<a href="{% url 'event-hanchan-list' event.pk %}">
<time datetime="{{event.start|date:'c'}}">{{ event.start|date:'D' }} {{ event.start|date:'SHORT_DATE_FORMAT' }}</time>:
{{event.name}}</a></li>
{% endfor %}
</ul>
{% if season_list %}
<h3>{% trans 'Ladder Archive' %}</h3>
<form name="season_select">
<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="{% url 'mahjong-ladder' season_link %}" {% ifequal season season_link %}selected="selected"{% endifequal %}>{{ season_link }}</option>
{% endfor %}
</select>
</form>
{% endif %}

View File

@@ -5,8 +5,7 @@
{% block teaser %}<h1>{% trans 'Dan Score for' %} {{membership.username}}</h1>{% endblock %}
{% block score_list %}
<div class="grid_12">
<h2>{% trans 'Hanchans that apply to the Dan Score' %}</h2>
<h2 class="grid_12">{% trans 'Hanchans that apply to the Dan Score' %}</h2>
<table>
<thead>
<tr>
@@ -39,7 +38,7 @@
</td>
{% endfor %}
<td class="center">{{hanchan.dan_points}}</td>
<td>{{ hanchan.comment }}</td>
<td>{{ hanchan.player_comment }}</td>
<td>
{% if perms.mahjong_ranking.delete_hanchan %}
<a href="{% url 'delete-hanchan' hanchan.pk %}"><span class="fa fa-trash" title="{% trans 'Delete Hanchan' %}"></span></a>
@@ -51,5 +50,4 @@
</tr>
{% endfor %}
</table>
</div>
{% endblock %}

View File

@@ -6,7 +6,7 @@
{% block teaser %}<h1>{% trans 'Unconfirmed Hanchans from' %} {{membership.username}}</h1>{% endblock %}
{% block score_list %}
<h2>{% trans 'Invalid hanchans with' %} {{membership.username}}</h2>
<h2 class="grid_12">{% trans 'Invalid hanchans with' %} {{membership.username}}</h2>
<table>
<thead><tr>
<th>{% trans 'Event' %}</th>

View File

@@ -6,7 +6,7 @@
{% block teaser %}<h1>{% trans 'Kyu Score for' %} {{membership.username}}</h1>{% endblock %}
{% block score_list %}
<h2>{% trans 'Hanchans that apply to the Kyu Score' %}</h2>
<h2 class="grid_12">{% trans 'Hanchans that apply to the Kyu Score' %}</h2>
<table>
<thead><tr>
<tr>

View File

@@ -5,7 +5,7 @@
{% block teaser %}<h1>{% trans 'Ladder Score for' %} {{membership.username}} / {{ season }}</h1>{% endblock %}
{% block score_list %}
<h2>{% trans 'Hanchans that apply to the Ladder Score' %} - {{ season }}</h2>
<h2 class="grid_12">{% trans 'Hanchans that apply to the Ladder Score' %} - {{ season }}</h2>
<table>
<thead>

View File

@@ -1,11 +1,12 @@
{% extends "mahjong_ranking/page.html" %}
{% load i18n comments%}
{% load i18n comments humanize thumbnail %}
{% block title %}Mahjong Ladder - {{ season }}{% endblock %}
{% block teaser %}
<h1>Mahjong Ladder - {{ season }}</h1>
<div id="teaser_text">
<ul class="info">
<ul class="info">
<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>
@@ -13,6 +14,8 @@
</div>
{% endblock %}
{% block redbox %}{% include 'mahjong_ranking/ladder_redbox.html' %}{% endblock %}
{% block content %}
<table>
<thead>
@@ -35,11 +38,13 @@
{% for player in seasonranking_list %}
<tr>
<td class="center">{{player.placement}}.</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}}"><img
src="{% thumbnail player.user.avatar|default:'unknown_profile.jpg' 'avatar' %}"
width="70" height="70" 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>
<td class="center">{{player.avg_placement|ordinal }}</td>
<td class="right">{{player.avg_score|floatformat:0|intcomma }}</td>
<td class="right">{{player.hanchan_count}}</td>
<td class="right">{{player.good_hanchans}}</td>
<td class="right">{{player.won_hanchans}}</td>
@@ -52,31 +57,4 @@
</tr>
{% endfor %}
</table>
{% endblock %}
{% block redbox %}
<h2>{% trans 'Latest Hanchans' %}</h2>
<ul class="fa-ul">
{% for hanchan in latest_hanchan_list %}
<li><span class="fa-li fa fa-table"></span> <a href="{% url 'event-hanchan-list' hanchan.event.pk %}">{{hanchan.event.name}}</a>
{{hanchan.start|time:'H:i'}}:
<small>{{hanchan.player_names}}</small>
</li>
{% endfor %}
</ul>
<h3>{% trans 'Latest Events' %}</h3>
<ul class="fa-ul">
{% for event in latest_event_list %}
<li><span class="fa-li fa fa-calendar-o"></span> <a href="{% url 'event-hanchan-list' event.pk %}">{{event.name}}</a></li>
{% endfor %}
</ul>
<h3>{% trans 'Ladder Archive' %}</h3>
<form name="season_select">
<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="{% url 'mahjong-ladder' season_link %}" {% ifequal season season_link %}selected="selected"{% endifequal %}>{{ season_link }}</option>
{% endfor %}
</select>
</form>
{% endblock %}
{% endblock %}

View File

@@ -9,6 +9,7 @@ from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.

View File

@@ -13,19 +13,33 @@ import views
urlpatterns = patterns(
'',
# url('^$', RedirectView.as_view(pattern_name='mahjong-ladder', permanent=True)),
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.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"),
url(r'^player/(?P<username>[\-\.\d\w]+)/ladder/$', views.PlayerLadderScore.as_view(), name="player-ladder-score"),
url(r'^mahjong/$', views.KyuDanRankingList.as_view(), name="kyudanranking-list"),
url(r'^mahjong/(?P<order_by>[\+\-\w]+)/$', views.KyuDanRankingList.as_view(), name="kyudanranking-list"),
url(r'^$', RedirectView.as_view(
url='/ranking/mahjong-ladder/', permanent=True)
),
url(r'^event/(?P<event>[\d]+)/mahjong/$',
views.EventHanchanList.as_view(), name="event-hanchan-list"),
url(r'^event/(?P<event>[\d]+)/add-hanchan/$',
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.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"),
url(r'^player/(?P<username>[\-\.\d\w]+)/ladder/$',
views.PlayerLadderScore.as_view(), name="player-ladder-score"),
url(r'^mahjong/$', views.KyuDanRankingList.as_view(),
name="kyudanranking-list"),
url(r'^mahjong/(?P<order_by>[\+\-\w]+)/$',
views.KyuDanRankingList.as_view(), name="kyudanranking-list"),
)

View File

@@ -6,7 +6,7 @@ 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.utils.translation import ugettext as _
from django.views import generic
from events.models import Event
@@ -21,14 +21,15 @@ kyu_dan_order = {
'-hanchan_count': ('-hanchan_count',),
'+rank': ('-kyu', 'dan'),
'-rank': ('-dan', 'kyu'),
'+score': ('dan_points', 'kyu_points'),
'-score': ('-dan_points', '-kyu_points'),
'+score': ('dan_points', '-dan', 'kyu_points'),
'-score': ('-dan_points', 'dan', '-kyu_points'),
'+username': ('user__username',),
'-username': ('-user__username',)
}
class DeleteHanchan(EventDetailMixin, PermissionRequiredMixin, generic.DeleteView):
class DeleteHanchan(EventDetailMixin, PermissionRequiredMixin,
generic.DeleteView):
"""
Fragt zuerst nach, ob die Hanchan wirklich gelöscht werden soll.
Wir die Frage mit "Ja" beantwortet, wird die die Hanchan gelöscht.
@@ -43,7 +44,8 @@ class DeleteHanchan(EventDetailMixin, PermissionRequiredMixin, generic.DeleteVie
kwargs={'event': self.object.event.pk})
class HanchanForm(SuccessMessageMixin, EventDetailMixin, PermissionRequiredMixin, generic.UpdateView):
class HanchanForm(SuccessMessageMixin, EventDetailMixin,
PermissionRequiredMixin, generic.UpdateView):
"""
Ein Formular um neue Hanchans anzulegen, bzw. eine bestehende zu
bearbeitsen
@@ -68,7 +70,8 @@ class HanchanForm(SuccessMessageMixin, EventDetailMixin, PermissionRequiredMixin
return forms.HanchanForm
def get_object(self, queryset=None):
if self.kwargs.get('hanchan') and self.request.user.has_perm('mahjong_ranking.change_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'):
@@ -93,7 +96,9 @@ class HanchanForm(SuccessMessageMixin, EventDetailMixin, PermissionRequiredMixin
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
return _(
'%s has been added successfully. You can now add a new '
'one.') % self.object
class EventHanchanList(EventDetailMixin, generic.ListView):
@@ -129,7 +134,26 @@ class EventRankingList(EventDetailMixin, generic.ListView):
raise django.http.Http404(_('Event does not exist'))
class KyuDanRankingList(generic.ListView):
class MahjongMixin(object):
def get_context_data(self, **kwargs):
context = super(MahjongMixin, self).get_context_data(**kwargs)
try:
context['season'] = self.season
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
except AttributeError:
pass
context[
'latest_hanchan_list'] = \
models.Hanchan.objects.confirmed_hanchans()[
:3]
context['latest_event_list'] = Event.objects.latest_events(num=3)
return context
class KyuDanRankingList(MahjongMixin, generic.ListView):
"""
Anzeige aller Spiele mit ihrem Kyu bzw Dan Grad.
"""
@@ -145,30 +169,22 @@ class KyuDanRankingList(generic.ListView):
def get_queryset(self):
queryset = models.KyuDanRanking.objects.all().order_by(*self.order_by)
return queryset.select_related('user__membership')
return queryset.select_related()
class SeasonRankingList(generic.ListView):
class SeasonRankingList(MahjongMixin, generic.ListView):
model = models.SeasonRanking
paginate_by = 25
season = None
def get_queryset(self):
self.season = int(self.kwargs.get('season', date.today().year))
queryset = self.model.objects.filter(season=self.season,
placement__isnull=False)
queryset = self.model.objects.filter(
season=self.season,
placement__isnull=False
)
return queryset.select_related()
def get_context_data(self, **kwargs):
context = super(SeasonRankingList, self).get_context_data(**kwargs)
context['season'] = self.season
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 PlayerScore(LoginRequiredMixin, generic.ListView):
paginate_by = 25
@@ -176,9 +192,12 @@ class PlayerScore(LoginRequiredMixin, generic.ListView):
def get(self, request, *args, **kwargs):
user_model = auth.get_user_model()
try:
self.user = user_model.objects.get(username=self.kwargs.get('username'))
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 {}").format(self.kwargs.get('username')))
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):
@@ -235,6 +254,7 @@ class PlayerLadderScore(PlayerScore):
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)
hanchan_list = models.Hanchan.objects.season_hanchans(
user=self.user, season=self.season)
print hanchan_list
return hanchan_list