Stylesheets werden nur mit Less erzeugt. Damit können sie gleich

optimiert werden.
jQuery wurde entfernt.
Übersetzungen wurden korrigiert.
Neue sync.sh Datei, welche änderungen mit rsync auf den Server spielt
und das neuladen erzwingt.
This commit is contained in:
Christian Berg
2014-12-14 23:57:40 +01:00
parent 711c303f9a
commit 9c2413a794
76 changed files with 2514 additions and 1884 deletions

View File

64
maistar_ranking/admin.py Normal file
View File

@@ -0,0 +1,64 @@
# -*- encoding: utf-8 -*-
"""
Created on 19.09.2011
@author: christian
"""
from django.contrib import admin
from django.utils.translation import ugettext as _
from . import forms, models
def recalculate(modeladmin, request, queryset): # @UnusedVariable
if isinstance(modeladmin, RankingAdmin):
seasons = set()
for ranking in queryset:
ranking.recalculate()
seasons.add(ranking.season)
for season in seasons:
models.Ranking.objects.calculate_rankings(season)
recalculate.short_description = _("Recalculate")
class RankingAdmin(admin.ModelAdmin):
actions = [recalculate]
list_display = (
'season', 'placement', 'user', 'avg_placement', 'avg_score',
'games_count', 'games_good', 'games_won'
)
list_display_links = ('user',)
list_filter = ['season']
class GameAdmin(admin.ModelAdmin):
actions = [recalculate]
# date_hierarchy = 'event__start'
fields = (
'event',
('player1', 'player1_score', 'player1_placement'),
('player2', 'player2_score', 'player2_placement'),
('player3', 'player3_score', 'player3_placement'),
('player4', 'player4_score', 'player4_placement'),
('player5', 'player5_score', 'player5_placement'),
('player6', 'player6_score', 'player6_placement'),
'comment',
'confirmed'
)
form = forms.GameForm
list_display = ('event', 'season', 'player_names', 'comment', 'confirmed')
readonly_fields = (
'player1_placement', 'player2_placement', 'player3_placement',
'player4_placement', 'player5_placement', 'player6_placement'
)
search_fields = [
'player1__username', 'player2__username', 'player3__username',
'player4__username', 'player5__username', 'player6__username'
]
admin.site.register(models.Game, GameAdmin)
admin.site.register(models.Ranking, RankingAdmin)

37
maistar_ranking/forms.py Normal file
View File

@@ -0,0 +1,37 @@
# -*- encoding: utf-8 -*-
__author__ = 'christian'
from django import forms
from django.utils.translation import ugettext as _
from . import models
class GameForm(forms.ModelForm):
error_css_class = 'error'
required_css_class = 'required'
class Meta(object):
fields = [
'player1', 'player1_score',
'player2', 'player2_score',
'player3', 'player3_score',
'player4', 'player4_score',
'player5', 'player5_score',
'player6', 'player6_score',
'comment'
]
model = models.Game
def clean(self):
cleaned_data = super(GameForm, self).clean()
players_in_game = set()
for player_nr in (
'player1', 'player2', 'player3', 'player4', 'player5', 'player6'):
current_player = cleaned_data.get(player_nr)
if current_player and current_player in players_in_game:
msg = _("%s may only participate once." % current_player)
self._errors[player_nr] = self.error_class([msg])
del cleaned_data[player_nr]
players_in_game.add(current_player)
return cleaned_data

Binary file not shown.

View File

@@ -0,0 +1,272 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: kasu.mahjong_ranking\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-12-14 01:12+0100\n"
"PO-Revision-Date: 2014-12-11 22:47+0100\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"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 1.6.11\n"
"X-Translated-Using: django-rosetta 0.7.2\n"
#: admin.py:24
msgid "Recalculate"
msgstr "Neuberechnen"
#: forms.py:33
#, python-format
msgid "%s may only participate once."
msgstr "%s darf nur einmal teilnehmen."
#: models.py:20
msgid "Comment"
msgstr "Kommentar"
#: models.py:22
msgid "Player 1"
msgstr "Spieler 1"
#: models.py:24 models.py:30 models.py:36 models.py:42 models.py:48
#: models.py:54 templates/maistar_ranking/ranking_list.html:20
msgid "Score"
msgstr "Punkte"
#: models.py:28
msgid "Player 2"
msgstr "Spieler 2"
#: models.py:34
msgid "Player 3"
msgstr "Spieler 3"
#: models.py:40
msgid "Player 4"
msgstr "Spieler 4"
#: models.py:46
msgid "Player 5"
msgstr "Spieler 5"
#: models.py:52
msgid "Player 6"
msgstr "Spieler 6"
#: models.py:58
msgid "Has been confirmed"
msgstr "Wurde bestätigt"
#: models.py:60
msgid "the game only counts whe it has been confirmed"
msgstr "das Spiel zählt nur wenn es bestätigt wurde"
#: models.py:63 models.py:151
#: templates/maistar_ranking/player_game_list.html:6
#: templates/maistar_ranking/ranking_list.html:4
#: templates/maistar_ranking/ranking_list.html:71
msgid "Season"
msgstr "Saison"
#: models.py:69
msgid "Mai-Star Game with {0} from {1:%Y-%m-%d}"
msgstr "Mai-Star Spiel mit {0} vom {1:%Y-%m-%d}"
#: templates/maistar_ranking/game_form.html:5
#: templates/maistar_ranking/game_form.html:16
#: templates/maistar_ranking/game_list.html:27
#: templates/maistar_ranking/player_game_list.html:46
msgid "Edit Game"
msgstr "Spiel bearbeiten"
#: templates/maistar_ranking/game_form.html:5
#: templates/maistar_ranking/game_form.html:16
#: templates/maistar_ranking/game_list.html:42
msgid "Add Game"
msgstr "Spiel hinzufügen"
#: templates/maistar_ranking/game_form.html:78
msgid "Back"
msgstr "Zurück"
#: templates/maistar_ranking/game_form.html:80
msgid "Save"
msgstr "Speichern"
#: templates/maistar_ranking/game_list.html:4
#: templates/maistar_ranking/player_game_list.html:6
msgid "Mai-Star Games"
msgstr "Mai-Star Spiele"
#: templates/maistar_ranking/game_list.html:7
msgid "Played Mai-Star Games"
msgstr "Gespielte Mai-Star Spiele"
#: templates/maistar_ranking/game_list.html:12
msgid "Game"
msgstr "Spiel"
#: templates/maistar_ranking/game_list.html:15
msgid "Place"
msgstr "Platz"
#: templates/maistar_ranking/game_list.html:19
#: templates/maistar_ranking/player_game_list.html:38
msgid "Points"
msgstr "Punkte"
#: templates/maistar_ranking/game_list.html:24
#: templates/maistar_ranking/hanchan_confirm_delete.html:18
#: templates/maistar_ranking/hanchan_confirm_delete.html:19
#: templates/maistar_ranking/player_game_list.html:43
msgid "Delete"
msgstr "Löschen"
#: templates/maistar_ranking/game_list.html:24
#: templates/maistar_ranking/player_game_list.html:43
msgid "Delete Game"
msgstr "Spiel löschen"
#: templates/maistar_ranking/game_list.html:27
#: templates/maistar_ranking/player_game_list.html:46
msgid "Edit"
msgstr "Bearbeiten"
#: templates/maistar_ranking/game_list.html:32
msgid "No Mai-Star games have been added to this event yet."
msgstr "Für diese Veranstaltung wurden noch keine Mai-Star Spiele erfasst."
#: templates/maistar_ranking/game_list.html:40
#: templates/maistar_ranking/game_list.html:42
msgid "Add"
msgstr "Hinzufügen"
#: templates/maistar_ranking/game_list.html:40
msgid "Edit Event"
msgstr "Veranstaltung bearbeiten"
#: templates/maistar_ranking/hanchan_confirm_delete.html:4
#: templates/maistar_ranking/hanchan_confirm_delete.html:10
msgid "Delete game"
msgstr "Spiel löschen"
#: templates/maistar_ranking/hanchan_confirm_delete.html:14
#: templates/maistar_ranking/hanchan_confirm_delete.html:15
msgid "Cancel"
msgstr "Abbrechen"
#: templates/maistar_ranking/page.html:5
msgid "Archive"
msgstr "Archiv"
#: templates/maistar_ranking/page.html:7
msgid "Add Event"
msgstr "Veranstaltung hinzufügen"
#: templates/maistar_ranking/player_game_list.html:4
msgid "Ladder Score for"
msgstr "Ladder Wertung für"
#: templates/maistar_ranking/player_game_list.html:10
msgid "Mai-Star Games with"
msgstr "Mai-Star Spiele mit"
#: templates/maistar_ranking/player_game_list.html:15
msgid "Date"
msgstr "Datum"
#: templates/maistar_ranking/player_game_list.html:16
msgid "Event"
msgstr "Veranstaltung"
#: templates/maistar_ranking/player_game_list.html:17
msgid "Players"
msgstr "Spieler"
#: templates/maistar_ranking/ranking_list.html:4
msgid "Mai-Star Ranking"
msgstr "Mai-Star Platzierung"
#: templates/maistar_ranking/ranking_list.html:11
#: templates/maistar_ranking/ranking_list.html:19
msgid "Placement"
msgstr "Platzierung"
#: templates/maistar_ranking/ranking_list.html:12
msgid "Avatar"
msgstr "Avatar"
#: templates/maistar_ranking/ranking_list.html:13
msgid "Nickname"
msgstr "Spitzname"
#: templates/maistar_ranking/ranking_list.html:14
msgid "Name"
msgstr "Name"
#: templates/maistar_ranking/ranking_list.html:15
msgid "Average"
msgstr "Durchschnitt"
#: templates/maistar_ranking/ranking_list.html:16
msgid "Games"
msgstr "Spiele"
#: templates/maistar_ranking/ranking_list.html:21
msgid "count"
msgstr "Anzahl"
#: templates/maistar_ranking/ranking_list.html:22
msgid "good"
msgstr "Gut"
#: templates/maistar_ranking/ranking_list.html:23
msgid "won"
msgstr "Gewonnen"
#: templates/maistar_ranking/ranking_list.html:42
msgid ""
"Unfortunately, nobody has it been done in the ranking.\n"
" A player must have 6 games done, to be added to the ranking."
msgstr ""
"Leider hat es noch niemand in die Wertung geschafft. Spieler, die weniger "
"als 6 Spiele innerhalb einer Saison absolviert haben, werden für das "
"Endergebnis nicht gewertet."
#: templates/maistar_ranking/ranking_list.html:52
msgid "Latest Games"
msgstr "Letzten Spiele"
#: templates/maistar_ranking/ranking_list.html:62
msgid "Latest Events"
msgstr "Letzten Veranstaltungen"
#: templates/maistar_ranking/ranking_list.html:69
msgid "Ladder Archive"
msgstr "Archiv"
#~ msgid "Player List"
#~ msgstr "Spieler Liste"
#~ msgid "Full Name"
#~ msgstr "Voller Name"
#~ msgid "Rank"
#~ msgstr "Rang"
#~ msgid "Games Total"
#~ msgstr "Spiele Gesamt"
#~ msgid "Event does not exist"
#~ msgstr "Termin existiert nicht."
#~ msgid "No user found matching the name %s"
#~ msgstr "Kein Benutzer mit dem Namen %s gefunden."

View File

@@ -0,0 +1,60 @@
# -*- encoding: utf-8 -*-
"""
Created on 22.11.2014
@author: christian
"""
from django.db import models
from . import settings
class GameManager(models.Manager):
use_for_related_fields = True
def player_games(self, player, season=None, **kwargs):
queryset = self.filter(
models.Q(player1=player) | models.Q(player2=player) |
models.Q(player3=player) | models.Q(player4=player) |
models.Q(player5=player) | models.Q(player6=player)
)
if season:
queryset = queryset.filter(season=season)
if kwargs:
queryset = queryset.filter(**kwargs)
return queryset
class LadderManager(models.Manager):
use_for_related_fields = True
def calculate_rankings(self, season):
"""
Berechnet die Jahresrankings für die Spieler neu.
@param season: Das Jahr das neu berechnet werden soll
@type season: Integer
"""
rankings = self.filter(season=season)
rankings.update(placement=None)
rankings = rankings.filter(
games_count__gte=settings.MAISTAR_GAMES_FOR_LADDERRANKING) # @IgnorePep8
rankings = rankings.order_by('avg_placement', '-avg_score')
placement = 1
previous_avg_placement = None
previous_avg_score = None
previous_placement = None
for ranking in rankings:
if ranking.avg_placement == previous_avg_placement \
and ranking.avg_score == previous_avg_score:
ranking.placement = previous_placement
else:
ranking.placement = placement
ranking.save(force_update=True)
previous_avg_placement = ranking.avg_placement
previous_avg_score = ranking.avg_score
previous_placement = ranking.placement
placement += 1
def get_seasons(self):
return self.values_list('season', flat=True).order_by(
'season').distinct()

221
maistar_ranking/models.py Normal file
View File

@@ -0,0 +1,221 @@
# -*- encoding: utf-8 -*-
import logging
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.translation import ugettext as _
from events.models import Event
from membership.models import Membership
from . import settings, managers
class Game(models.Model):
"""
Ein kompettes Spiel Mai-Star bestehend aus 6 Spielern
"""
event = models.ForeignKey(Event, related_name='maistargame_set')
comment = models.TextField(_('Comment'), blank=True)
player1 = models.ForeignKey(
settings.AUTH_USER_MODEL, verbose_name=_("Player 1"), related_name='+'
)
player1_score = models.SmallIntegerField(_("Score"))
player1_placement = models.PositiveSmallIntegerField(editable=False)
player2 = models.ForeignKey(
settings.AUTH_USER_MODEL, verbose_name=_("Player 2"), related_name='+'
)
player2_score = models.SmallIntegerField(_("Score"))
player2_placement = models.PositiveSmallIntegerField(editable=False)
player3 = models.ForeignKey(
settings.AUTH_USER_MODEL, verbose_name=_("Player 3"), related_name='+'
)
player3_score = models.SmallIntegerField(_("Score"))
player3_placement = models.PositiveSmallIntegerField(editable=False)
player4 = models.ForeignKey(
settings.AUTH_USER_MODEL, verbose_name=_("Player 4"), related_name='+'
)
player4_score = models.SmallIntegerField(_("Score"))
player4_placement = models.PositiveSmallIntegerField(editable=False)
player5 = models.ForeignKey(
settings.AUTH_USER_MODEL, verbose_name=_("Player 5"), related_name='+'
)
player5_score = models.SmallIntegerField(_("Score"))
player5_placement = models.PositiveSmallIntegerField(editable=False)
player6 = models.ForeignKey(
settings.AUTH_USER_MODEL, verbose_name=_("Player 6"), related_name='+'
)
player6_score = models.SmallIntegerField(_("Score"))
player6_placement = models.PositiveSmallIntegerField(editable=False)
confirmed = models.BooleanField(
_('Has been confirmed'),
default=True,
help_text=_('the game only counts whe it has been confirmed')
)
player_names = models.CharField(max_length=255, editable=False)
season = models.PositiveSmallIntegerField(_('Season'), editable=False,
db_index=True)
objects = managers.GameManager()
def __str__(self):
return _("Mai-Star Game with {0} from {1:%Y-%m-%d}").format(
self.player_names, self.event.start
)
def get_absolute_url(self):
"""
URL zur Hanchanliste des Events wo diese Hanchan gelistet wurde.
"""
url = reverse('maistar-game-list',
kwargs={'event': self.event.pk})
return u'%(url)s#%(pk)d' % {'url': url, 'pk': self.pk}
@property
def player_list(self):
try:
return self._player_list
except AttributeError:
self._player_list = list()
for nr in range(1, 7):
self._player_list.append({
'user': getattr(self, 'player%d' % nr),
'membership': Membership.objects.get(
user=getattr(self, 'player%d' % nr)
),
'score': getattr(self, 'player%d_score' % nr),
'placement': getattr(self, 'player%d_placement' % nr),
})
return self._player_list
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
u"""
Bestimmt die Platzierung eines jeden Spielers noch vor dem speichern.
Außerdem wird eine Kommasperierte Liste der Spiele mitgespeichert.
"""
logging.debug("Berechne die Platzierungen neu...")
game_date = self.event.start.date()
player_tuples = [
(self.player1.id, self.player1.username, self.player1_score),
(self.player2.id, self.player2.username, self.player2_score),
(self.player3.id, self.player3.username, self.player3_score),
(self.player4.id, self.player4.username, self.player4_score),
(self.player5.id, self.player5.username, self.player5_score),
(self.player6.id, self.player6.username, self.player6_score),
]
season_start = settings.MAISTAR_SEASON_START.replace(
year=game_date.year)
# sort player by Score:
player_tuples = sorted(player_tuples, key=lambda player: player[2],
reverse=True)
logging.debug(player_tuples)
other_player_ranking = 1
other_player_score = 0
player_names = []
ranking = 1
player_nr = 1
for player_id, player_name, player_score in player_tuples:
if player_score == other_player_score:
player_ranking = other_player_ranking
else:
player_ranking = ranking
setattr(self, "player%d_id" % player_nr, player_id)
setattr(self, "player%d_score" % player_nr, player_score)
setattr(self, "player%d_placement" % player_nr, player_ranking)
other_player_ranking = player_ranking
other_player_score = player_score
player_names.append(player_name)
player_nr += 1
ranking += 1
self.player_names = ', '.join(player_names)
if game_date >= season_start:
self.season = season_start.year
else:
self.season = season_start.year - 1
super(Game, self).save(force_insert=force_insert,
force_update=force_update, using=using,
update_fields=update_fields)
class Ranking(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
season = models.PositiveSmallIntegerField(_("Season"))
placement = models.PositiveIntegerField(blank=True, null=True)
avg_placement = models.PositiveSmallIntegerField(blank=True, null=True)
avg_score = models.SmallIntegerField(blank=True, null=True)
games_count = models.PositiveSmallIntegerField(default=0)
games_good = models.PositiveSmallIntegerField(default=0)
games_won = models.PositiveSmallIntegerField(default=0)
objects = managers.LadderManager()
class Meta(object):
ordering = ('-season', 'placement', 'avg_placement', '-avg_score',)
def __str__(self):
return "Mai-Star Ranking: %s, Season: %d" % (self.user, self.season)
def get_absolute_url(self):
return reverse('maistar-player-games', kwargs={
'username': self.user.username,
'season': self.season
})
def recalculate(self):
logging.info(
u'Recalculate Mai-Star Ranking for Player %s in Season %s',
self.user, self.season
)
self.placement = None
self.avg_placement = None
self.avg_score = None
self.games_count = 0
self.games_good = 0
self.games_won = 0
player_score = 0
player_placement = 0
for game in Game.objects.player_games(self.user, self.season):
placement = 0
score = 0
for player in ('player1', 'player2', 'player3', 'player4',
'player5', 'player6'):
if getattr(game, player) == self.user:
placement = getattr(game, "%s_placement" % player)
score = getattr(game, "%s_score" % player)
player_placement += placement
player_score += score
self.games_count += 1
self.games_good += 1 if placement <= 3 else 0
self.games_won += 1 if placement == 1 else 0
if self.games_count > 0:
self.avg_placement = round(player_placement / self.games_count)
self.avg_score = round(player_score / self.games_count)
self.save()
def update_maistar_ranking(sender, instance, **kwargs):
for player in instance.player_list:
ranking, created = Ranking.objects.get_or_create(
user=player['user'],
season=instance.season
)
if created:
logging.debug('Created ranking for %s in Season %d',
player['user'].username, instance.season)
else:
logging.debug('Updating ranking for %s in Season %d',
player['user'].username, instance.season)
ranking.recalculate()
Ranking.objects.calculate_rankings(instance.season)
models.signals.post_delete.connect(update_maistar_ranking, sender=Game)
models.signals.post_save.connect(update_maistar_ranking, sender=Game)

View File

@@ -0,0 +1,24 @@
"""
Global Settings for the Maistar Ranking System
Created on 22.11.2014
@author: christian
"""
from datetime import date
from django.conf import settings
AUTH_USER_MODEL = settings.AUTH_USER_MODEL
MAISTAR_GAMES_FOR_LADDERRANKING = getattr(
settings, 'MAISTAR_GAMES_FOR_LADDERRANKING', 6
)
MAISTAR_SEASON_START = getattr(
settings, 'MAISTAR_SEASON_START', date(year=1, month=8, day=1)
)

View File

@@ -0,0 +1,87 @@
{% extends "events/event_detail.html" %}
{% load i18n comments fieldset_extras %}
{% block title %}
{% if game.id %}{% trans "Edit Game" %}{% else %}{% trans "Add Game" %}{% endif %}
{% endblock %}
{% block maincontent %}
{% for error in form.errors %}<p class="error">{{error}}</p>{% endfor %}
{% for error in form.non_field_errors %}<p class="error">{{error}}</p>{% endfor %}
<form class="grid_12" method="post">
{% csrf_token %}
<fieldset>
<legend>{% if game.id %}{% trans "Edit Game" %}{% else %} {% trans "Add Game" %}{% endif %}</legend>
<p>
<label for="id_player1" class="field_name {{ form.player1.css_classes }}">{{ form.player1.label }}</label>
{{ form.player1 }}
<label for="id_player1_score" class="{{ form.player1_score.css_classes }}">{{ form.player1_score.label }}</label>
{{ form.player1_score}}
</p>
{% if form.player1.errors %}{{ form.player1.errors }}{% endif %}
{% if form.player1_score.errors %}{{ form.player1_score.errors }}{% endif %}
<p>
<label for="id_player2" class="field_name {{ form.player2.css_classes }}">{{ form.player2.label }}</label>
{{ form.player2 }}
<label for="id_player2_score" class="{{ form.player2_score.css_classes }}">{{ form.player2_score.label }}</label>
{{ form.player2_score}}
</p>
{% if form.player2.errors %}{{ form.player2.errors }}{% endif %}
{% if form.player2_score.errors %}{{ form.player2_score.errors }}{% endif %}
<p>
<label for="id_player3" class="field_name {{ form.player3.css_classes }}">{{ form.player3.label }}</label>
{{ form.player3 }}
<label for="id_player3_score" class="{{ form.player3_score.css_classes }}">{{ form.player3_score.label }}</label>
{{ form.player3_score}}
</p>
{% if form.player3.errors %}{{ form.player3.errors }}{% endif %}
{% if form.player3_score.errors %}{{ form.player3_score.errors }}{% endif %}
<p>
<label for="id_player4" class="field_name {{ form.player4.css_classes }}">{{ form.player4.label }}</label>
{{ form.player4 }}
<label for="id_player4_score" class="{{ form.player4_score.css_classes }}">{{ form.player4_score.label }}</label>
{{ form.player4_score}}
</p>
{% if form.player4.errors %}{{ form.player4.errors }}{% endif %}
{% if form.player4_score.errors %}{{ form.player4_score.errors }}{% endif %}
<p>
<label for="id_player5" class="field_name {{ form.player5.css_classes }}">{{ form.player5.label }}</label>
{{ form.player5 }}
<label for="id_player5_score" class="{{ form.player5_score.css_classes }}">{{ form.player5_score.label }}</label>
{{ form.player5_score}}
</p>
{% if form.player5.errors %}{{ form.player5.errors }}{% endif %}
{% if form.player5_score.errors %}{{ form.player5_score.errors }}{% endif %}
<p>
<label for="id_player6" class="field_name {{ form.player6.css_classes }}">{{ form.player6.label }}</label>
{{ form.player6 }}
<label for="id_player6_score" class="{{ form.player6_score.css_classes }}">{{ form.player6_score.label }}</label>
{{ form.player6_score}}
</p>
{% if form.player6.errors %}{{ form.player6.errors }}{% endif %}
{% if form.player6_score.errors %}{{ form.player6_score.errors }}{% endif %}
<p>
<label for="id_comment" class="field_name {{ form.comment.css_classes }}">{{ form.comment.label }}</label>
{{ form.comment }}
</p>
<p class="buttonbar">
<a href="{{game.get_absolute_url}}" class="button"><img
src="{{STATIC_URL}}icons/arrow_undo.png"
alt="{% trans 'Back' %}"/> {% trans 'Back' %}</a>
<button type="submit"><img src="{{STATIC_URL}}icons/table_save.png"
alt="{% trans 'Save' %}"/> {% trans 'Save' %}
</button>
</p>
</fieldset>
</form>
{% endblock %}
{% block buttonbar %}{% endblock %}

View File

@@ -0,0 +1,44 @@
{% extends "events/event_detail.html" %}
{% load i18n humanize %}
{% block title %}{% trans 'Mai-Star Games' %} - {{ event.name }}{% endblock %}
{% block maincontent %}
<h2 class="grid_12">{% trans 'Played Mai-Star Games' %}</h2>
<p>&nbsp;</p>
{% for game in game_list %}
<article id="{{ game.pk }}">
<h3 class="grid_12">{{ forloop.counter|ordinal }} {% trans 'Game' %}</h3>
{% for player in game.player_list %}
<div class="grid_2 center">
<h4>{{ player.placement|ordinal }} {% trans 'Place' %}</h4>
<a href="{% url 'maistar-player-games' username=player.user.username season=game.season %}"><img
src="{% if player.membership.thumbnail %}{{player.membership.thumbnail.url}}{% else %}{{STATIC_URL}}img/unknown_thumbnail.png{% endif %}" alt=""/></a>
<p><a href="{% url 'maistar-player-games' username=player.user.username season=game.season %}">{{player.user.username}}</a><br/>
{{player.score}} {% trans 'Points' %}</p>
</div>
{% endfor %}
<div class="grid_12 buttonbar">
{% if perms.maistar_ranking.delete_dame %}
<a class="button" href="{% url 'maistar-delete-game' game.pk %}"><img src="{{STATIC_URL}}icons/table_delete.png" alt="{% trans 'Delete' %}"/> {% trans 'Delete Game' %}</a>
{% endif %}
{% if perms.maistar_ranking.change_game %}
<a class="button" href="{% url 'maistar-edit-game' game.pk %}"><img src="{{STATIC_URL}}icons/table_edit.png" alt="{% trans 'Edit' %}" /> {% trans 'Edit Game' %}</a>
{% endif %}
</div>
</article>
{% empty %}
<p class="grid_12">{% trans 'No Mai-Star games have been added to this event yet.'%}</p>
{% endfor %}
<p>&nbsp;</p>
{% endblock %}
{% block buttonbar %}
{% if perms.maistar_ranking.add_game %}
<a class="button" href="{{event.get_edit_url}}"><img src="{{STATIC_URL}}icons/calendar_edit.png"
alt="{% trans 'Add' %}"/> {% trans 'Edit Event' %}</a>
<a class="button" href="{% url 'maistar-add-game' event.id %}"><img src="{{STATIC_URL}}icons/add.png"
alt="{% trans 'Add' %}"/> {% trans 'Add Game' %}</a>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,26 @@
{% extends "base.html" %}
{% load i18n comments %}
{% block meta_title %}{% trans 'Delete game' %}{% endblock %}
{% block content %}
<form method="post">
{% csrf_token %}
<fieldset>
<legend>{% trans "Delete game" %}</legend>
{% include 'form.html' %}
<p class="buttonbar">
<button type="button" onclick="window.history.back()">
<img src="{{STATIC_URL}}icons/cancel.png" alt="{% trans 'Cancel' %}"/>
{% trans 'Cancel' %}
</button>
<button type="submit">
<img src="{{STATIC_URL}}icons/table_delete.png" alt="{% trans 'Delete' %}"/>
{% trans 'Delete' %}
</button>
</p>
</fieldset>
</form>
{% endblock %}
{% block buttonbar %}{% endblock %}

View File

@@ -0,0 +1,9 @@
{% 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 %}

View File

@@ -0,0 +1,55 @@
{% extends "membership/membership_detail.html" %}
{% load i18n humanize %}
{% block title %} {% trans 'Ladder Score for' %} {{membership.user.username}} {% endblock %}
{% block teaser %} <h2>{% trans 'Mai-Star Games' %}: {{membership.user.username}} <br/> {% trans 'Season' %} {{season}}</h2> {% endblock %}
{% block score_list %}
<div class="grid_12">
<h2>{% trans 'Mai-Star Games with' %} {{ membership.user.username }} - {{season}}</h2>
<table>
<thead>
<tr>
<th rowspan="2">{% trans 'Date' %}</th>
<th rowspan="2">{% trans 'Event' %}</th>
<th colspan="6">{% trans 'Players' %}</th>
<th rowspan="2"></th>
</tr>
<tr>
<th>{{ '1'|ordinal }}</th>
<th>{{ '2'|ordinal }}</th>
<th>{{ '3'|ordinal }}</th>
<th>{{ '4'|ordinal }}</th>
<th>{{ '5'|ordinal }}</th>
<th>{{ '6'|ordinal }}</th>
</tr>
</thead>
<tbody>
{% for game in game_list %}
<tr>
<td class="center">{{ game.event.start|date:'SHORT_DATE_FORMAT' }}</td>
<td><a href="{{ game.get_absolute_url }}">{{ game.event.name }}</a></td>
</td>
{% for player in game.player_list %}
<td class="center">
<a href="{% url 'maistar-player-games' username=player.user.username season=season %}">{{ player.user.username }}</a>
<br/>{{player.score}} {% trans 'Points' %}
</td>
{% endfor %}
<td>
{% if perms.maistar_ranking.delete_game %}
<a href="{% url 'maistar-delete-game' game.pk %}"><img src="{{STATIC_URL}}icons/table_delete.png" alt="{% trans 'Delete' %}" title="{% trans 'Delete Game' %}"/></a>
{% endif %}
{% if perms.maistar_ranking.change_game %}
<a href="{% url 'maistar-edit-game' game.pk %}"><img src="{{STATIC_URL}}icons/table_edit.png" alt="{% trans 'Edit' %}" title="{% trans 'Edit Game' %}"/></a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

View File

@@ -0,0 +1,79 @@
{% extends "base.html" %}
{% load i18n comments humanize %}
{% block teaser %} <h2>{% trans 'Mai-Star Ranking' %} - {% trans 'Season' %} {{season}}</h2> {% endblock %}
{% block maincontent %}
<main class="grid_12">
<table>
<thead>
<tr>
<th rowspan="2">{% trans "Placement" %}</th>
<th rowspan="2">{% trans "Avatar" %}</th>
<th rowspan="2">{% trans "Nickname" %}</th>
<th rowspan="2">{% trans "Name" %}</th>
<th colspan="2">{% trans 'Average' %}</th>
<th colspan="3">{% trans 'Games' %}</th>
</tr>
<tr>
<th>{% trans 'Placement' %}</th>
<th>{% trans "Score" %}</th>
<th>{% trans "count" %}</th>
<th>{% trans "good" %}</th>
<th>{% trans "won" %}</th>
</tr>
</thead>
{% for player in ranking_list %}
{% with player.user.get_profile as profile %}
<tr>
<td class="center">{{ player.placement|ordinal }}</td>
<td><a href="{{ player.get_absolute_url }}"><img src="{% if profile.thumbnail %}{{profile.thumbnail.url}}{% else %}{{STATIC_URL}}img/unknown_thumbnail.png{% endif %}" class="avatar" alt="{{ player.user }}"/></a></td>
<td><a href="{{ player.get_absolute_url }}">{{player.user}}</a></td>
<td>{% if user.is_authenticated %}{{profile.last_name}} {{profile.first_name}}{% else %} ---{% endif %}</td>
<td class="right">{{player.avg_placement|floatformat:0 }}</td>
<td class="right">{{player.avg_score|intcomma }}</td>
<td class="right">{{player.games_count}}</td>
<td class="right">{{player.games_good}}</td>
<td class="right">{{player.games_won}}</td>
</tr>
{% endwith %}
{% empty %}
<tr>
<td colspan="8">{% blocktrans %}Unfortunately, nobody has it been done in the ranking.
A player must have 6 games done, to be added to the ranking.{% endblocktrans %}
</td>
</tr>
{% endfor %}
</table>
</main>
{% endblock %}
{% block redbox %}
<h2>{% trans 'Latest Games' %}</h2>
<ul class="list">
{% for game in latest_games %}
<li class="hanchan">
<a href="{% url 'maistar-game-list' game.event.pk %}">{{game.event.start|date:'SHORT_DATE_FORMAT'}}:</a>
<small>{{game.player_names}}</small>
</li>
{% endfor %}
</ul>
<h3>{% trans 'Latest Events' %}</h3>
<ul class="list">
{% for event in latest_events %}
<li class="event"><a href="{% url 'maistar-game-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 'maistar-ranking' season=season_link %}" {% ifequal season season_link %}selected="selected"{% endifequal %}>{{ season_link }}</option>
{% endfor %}
</select>
</form>
{% endblock %}

32
maistar_ranking/urls.py Normal file
View File

@@ -0,0 +1,32 @@
# -*- encoding: utf-8 -*-
"""
Created on 03.10.2011
@author: christian
"""
from django.conf.urls import * # @UnusedWildImport
from django.views.generic import RedirectView
from .views import DeleteGame, ListGames, ListPlayerGames, \
ListRankings, GameForm
urlpatterns = patterns(
'maistar_ranking.views',
url('^$', RedirectView.as_view(pattern_name='maistar-ranking')),
url(r'ranking/$', ListRankings.as_view(), name="maistar-ranking"),
url(r'ranking/(?P<season>[\d]+)/$', ListRankings.as_view(),
name="maistar-ranking"), # @IgnorePep8
url(r'event/(?P<event>[\d]+)/$', ListGames.as_view(),
name="maistar-game-list"), # @IgnorePep8
url(r'event/(?P<event>[\d]+)/add/$', GameForm.as_view(),
name="maistar-add-game"), # @IgnorePep8
url(r'game/(?P<game>[\d]+)/edit/$', GameForm.as_view(),
name="maistar-edit-game"), # @IgnorePep8
url(r'game/(?P<game>[\d]+)/delete/$', DeleteGame.as_view(),
name="maistar-delete-game"), # @IgnorePep8
url(r'player/(?P<username>[\-\.\d\w]+)/$', ListPlayerGames.as_view(),
name="maistar-player-games"), # @IgnorePep8
url(r'player/(?P<username>[\-\.\d\w]+)/(?P<season>[\d]+)/$',
ListPlayerGames.as_view(), name="maistar-player-games"), # @IgnorePep8
)

121
maistar_ranking/views.py Normal file
View File

@@ -0,0 +1,121 @@
# -*- encoding: utf-8 -*-
from datetime import date
from django.contrib import auth
from django.core.urlresolvers import reverse
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext as _
from django.views import generic
from events.models import Event
from events.views import EventDetailMixin
from membership.models import Membership
from utils.mixins import LoginRequiredMixin, PermissionRequiredMixin
from mahjong_ranking.views import PlayerScore
from . import forms, models
class GameForm(EventDetailMixin, PermissionRequiredMixin, generic.UpdateView):
model = models.Game
permission_required = 'maistar_ranking.add_game'
form_class = forms.GameForm
def get_object(self, queryset=None):
if 'game' in self.kwargs:
game = get_object_or_404(models.Game, pk=self.kwargs['game'])
elif 'event' in self.kwargs:
self.event = get_object_or_404(Event, pk=self.kwargs['event'])
game = models.Game(event=self.event)
return game
class DeleteGame(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.
"""
model = models.Game
permission_required = 'maistar_ranking.delete_game'
pk_url_kwarg = 'hanchan'
def get_success_url(self):
return reverse(
'maistar_ranking-game-list',
kwargs={'event': self.object.event.pk}
)
class UpdateGame(EventDetailMixin, PermissionRequiredMixin, generic.UpdateView):
"""
Ein Formular um eine neues Spiel anzulegen, bzw. eine bestehendes zu
bearbeiten.
"""
model = models.Game
permission_required = 'maistar_ranking.update_game'
def get_object(self, queryset=None): # @UnusedVariable
game = models.Game.objects.get(id=self.kwargs['game'])
self.event = game.event
return game
def get_context_data(self, **kwargs):
context = generic.UpdateView.get_context_data(self, **kwargs)
context['event'] = self.event
return context
class ListGames(EventDetailMixin, generic.ListView):
"""
Auflistung aller Spiele welche während einer Veranstaltung gespielt wurden.
"""
model = models.Game
def get_queryset(self):
self.event = get_object_or_404(models.Event, pk=self.kwargs['event'])
return models.Game.objects.filter(event=self.event).select_related()
class ListPlayerGames(PlayerScore):
template_name = 'maistar_ranking/player_game_list.html'
paginate_by = 25
context_object_name = 'game_list'
def get_context_data(self, **kwargs):
context = super(ListPlayerGames, self).get_context_data()
context['membership'] = Membership.objects.get(user=self.user)
context['season_list'] = models.Ranking.objects.filter(user=self.user).values_list('season', flat=True)
context['season'] = self.season
return context
def get_queryset(self, **kwargs):
self.user = get_object_or_404(auth.get_user_model(),
username=self.kwargs.get('username'))
self.season = self.kwargs.get('season', date.today().year)
return models.Game.objects.player_games(player=self.user,
season=self.season)
class ListRankings(generic.ListView):
model = models.Ranking
paginate_by = 50
season = None
is_archive = False
def get_queryset(self):
self.season = int(self.kwargs.get('season', date.today().year))
queryset = models.Ranking.objects.filter(
season=self.season,
placement__isnull=False
)
queryset = queryset.select_related()
return queryset
def get_context_data(self, **kwargs):
context = generic.ListView.get_context_data(self, **kwargs)
context['is_archive'] = self.is_archive
context['season'] = self.season
context['season_list'] = models.Ranking.objects.get_seasons()
context['latest_games'] = models.Game.objects.filter(confirmed=True)[:3]
context['latest_events'] = Event.objects.archive()[:3]
return context