Diverse Umbauarbeiten für das neue Ranking.

This commit is contained in:
2017-12-22 10:51:20 +01:00
parent b20b988e5d
commit 1fdf88c6d2
6 changed files with 243 additions and 62 deletions

View File

@@ -17,16 +17,22 @@ class Command(BaseCommand):
parser.add_argument('reset_date', type=parse_date) parser.add_argument('reset_date', type=parse_date)
def handle(self, *args, **options): def handle(self, *args, **options):
reset_date = timezone.make_aware(datetime.combine(options.get('reset_date'), time(23, 59, 59))) legacy_attrs = [ key for key in models.KyuDanRanking.__dict__.keys()
# models.KyuDanRanking.objects.update(until=reset_date, force_recalc=True) if key.startswith('legacy') ]
dan_rankigns = models.KyuDanRanking.objects.filter(dan__isnull=False) legacy_attrs.remove('legacy_date')
for ranking in dan_rankigns: reset_date = timezone.make_aware(datetime.combine(
options.get('reset_date'), time(23, 59, 59)))
models.KyuDanRanking.objects.update(until=reset_date, force_recalc=True)
for ranking in models.KyuDanRanking.objects.filter(dan__gt=0):
print(ranking)
ranking.dan = 1 ranking.dan = 1
ranking.dan_points = 0 ranking.dan_points = 0
ranking.kyu = None
ranking.kyu_points = 0
ranking.wins_in_a_row = 0
ranking.legacy_date = reset_date.date() ranking.legacy_date = reset_date.date()
ranking.legacy_hanchan_count = ranking.hanchan_count for legacy_attr in legacy_attrs:
ranking.legacy_dan_points = ranking.dan_points attr = legacy_attr.split("_", maxsplit=1)[1]
ranking.legacy_kyu_points = ranking.kyu_points print(ranking, legacy_attr, attr, getattr(ranking, attr))
setattr(ranking, legacy_attr, getattr(ranking, attr))
ranking.save() ranking.save()

View File

@@ -23,11 +23,11 @@ class Command(BaseCommand):
def handle(self, *args, **options): def handle(self, *args, **options):
since = options.get('since', None) since = options.get('since', None)
until = options.get('until', None) until = options.get('until', None)
force_recalc = options.get('forecerecalc', False) force_recalc = options.get('forcerecalc')
if isinstance(since, date): if isinstance(since, date):
since = datetime.combine(since, time(0, 0, 0)) since = datetime.combine(since, time(0, 0, 0))
since = timezone.make_aware(since) since = timezone.make_aware(since)
if isinstance(until, date): if isinstance(until, date):
until = datetime.combine(until, time(23, 59, 59)) until = datetime.combine(until, time(23, 59, 59))
until = timezone.make_aware(until) until = timezone.make_aware(until)
models.KyuDanRanking.objects.update(since=since, until=until, force_recalc=force_recalc) models.KyuDanRanking.objects.update(since=since, until=until, force_recalc=force_recalc)

View File

@@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.8 on 2017-12-14 12:18
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('mahjong_ranking', '0005_auto_20171115_0653'),
]
operations = [
migrations.AddField(
model_name='kyudanranking',
name='legacy_dan',
field=models.PositiveSmallIntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='kyudanranking',
name='legacy_good_hanchans',
field=models.PositiveIntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='kyudanranking',
name='legacy_kyu',
field=models.PositiveSmallIntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='kyudanranking',
name='legacy_won_hanchans',
field=models.PositiveIntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='kyudanranking',
name='max_dan_points',
field=models.PositiveIntegerField(default=0),
),
migrations.AlterField(
model_name='eventranking',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='hanchan',
name='player1',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='user_hanchan+', to=settings.AUTH_USER_MODEL, verbose_name='Spieler 1'),
),
migrations.AlterField(
model_name='hanchan',
name='player2',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='user_hanchan+', to=settings.AUTH_USER_MODEL, verbose_name='Spieler 2'),
),
migrations.AlterField(
model_name='hanchan',
name='player3',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='user_hanchan+', to=settings.AUTH_USER_MODEL, verbose_name='Spieler 3'),
),
migrations.AlterField(
model_name='hanchan',
name='player4',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='user_hanchan+', to=settings.AUTH_USER_MODEL, verbose_name='Spieler 4'),
),
migrations.AlterField(
model_name='kyudanranking',
name='legacy_dan_points',
field=models.PositiveIntegerField(blank=True, null=True),
),
migrations.AlterField(
model_name='kyudanranking',
name='legacy_hanchan_count',
field=models.PositiveIntegerField(blank=True, null=True),
),
migrations.AlterField(
model_name='kyudanranking',
name='legacy_kyu_points',
field=models.PositiveIntegerField(blank=True, null=True),
),
migrations.AlterField(
model_name='seasonranking',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
),
]

View File

@@ -347,9 +347,12 @@ class KyuDanRanking(models.Model):
hanchan_count = models.PositiveIntegerField(default=0) hanchan_count = models.PositiveIntegerField(default=0)
legacy_date = models.DateField(blank=True, null=True) legacy_date = models.DateField(blank=True, null=True)
legacy_dan = models.PositiveSmallIntegerField(blank=True, null=True) legacy_dan = models.PositiveSmallIntegerField(blank=True, null=True)
legacy_dan_points = models.PositiveIntegerField(default=0) legacy_dan_points = models.PositiveIntegerField(blank=True, null=True)
legacy_kyu_points = models.PositiveIntegerField(default=0) legacy_kyu = models.PositiveSmallIntegerField(blank=True, null=True)
legacy_hanchan_count = models.PositiveIntegerField(default=0) legacy_kyu_points = models.PositiveIntegerField(blank=True, null=True)
legacy_hanchan_count = models.PositiveIntegerField(blank=True, null=True)
legacy_good_hanchans = models.PositiveIntegerField(blank=True, null=True)
legacy_won_hanchans = models.PositiveIntegerField(blank=True, null=True)
wins_in_a_row = models.PositiveIntegerField(default=0) wins_in_a_row = models.PositiveIntegerField(default=0)
last_hanchan_date = models.DateTimeField(blank=True, null=True) last_hanchan_date = models.DateTimeField(blank=True, null=True)
objects = managers.KyuDanRankingManager() objects = managers.KyuDanRankingManager()
@@ -371,12 +374,15 @@ class KyuDanRanking(models.Model):
das er einen Dan Rang aufsteigt. Dies wird als Kommentar abgespeichert, das er einen Dan Rang aufsteigt. Dies wird als Kommentar abgespeichert,
um es besser nachvollziehen zu können. um es besser nachvollziehen zu können.
""" """
if self.dan and hanchan.placement == 1: if not self.dan or not settings.DAN_3_WINS_IN_A_ROW:
return
if hanchan.placement == 1:
self.wins_in_a_row += 1 self.wins_in_a_row += 1
else: else:
self.wins_in_a_row = 0 self.wins_in_a_row = 0
return
if self.wins_in_a_row >= 3 and self.dan < 9:
if self.dan and self.wins_in_a_row >= 3 and self.dan < 9:
LOGGER.info( LOGGER.info(
'adding bonuspoints for 3 wins in a row for %s', self.user) 'adding bonuspoints for 3 wins in a row for %s', self.user)
new_dan_rank = self.dan + 1 new_dan_rank = self.dan + 1
@@ -397,6 +403,7 @@ class KyuDanRanking(models.Model):
bonus_points, new_dan_rank) bonus_points, new_dan_rank)
self.dan_points += bonus_points self.dan_points += bonus_points
self.wins_in_a_row = 0 self.wins_in_a_row = 0
self.update_rank()
def append_tournament_bonuspoints(self, hanchan): def append_tournament_bonuspoints(self, hanchan):
""" """
@@ -455,18 +462,17 @@ class KyuDanRanking(models.Model):
# Setze alles auf die legacy Werte und berechne alles von neuem. # Setze alles auf die legacy Werte und berechne alles von neuem.
self.dan = self.legacy_dan self.dan = self.legacy_dan
self.dan_points = self.legacy_dan_points or 0 self.dan_points = self.legacy_dan_points or 0
self.kyu = None self.max_dan_points = self.dan_points
self.kyu = self.legacy_kyu
self.kyu_points = self.legacy_kyu_points or 0 self.kyu_points = self.legacy_kyu_points or 0
self.hanchan_count = self.legacy_hanchan_count or 0 self.hanchan_count = self.legacy_hanchan_count or 0
self.good_hanchans = 0 self.good_hanchans = self.legacy_good_hanchans or 0
self.won_hanchans = 0 self.won_hanchans = self.legacy_won_hanchans or 0
self.update_rank()
self.last_hanchan_date = None self.last_hanchan_date = None
if self.legacy_date: self.update_rank()
since = timezone.make_aware( since = timezone.make_aware(datetime.combine(
datetime.combine(self.legacy_date, time(0, 0, 0))) self.legacy_date,
else: time(23, 59, 59))) if self.legacy_date else None
since = None
elif self.last_hanchan_date: elif self.last_hanchan_date:
since = self.last_hanchan_date since = self.last_hanchan_date
elif self.legacy_date: elif self.legacy_date:
@@ -481,40 +487,27 @@ class KyuDanRanking(models.Model):
valid_hanchans = valid_hanchans.filter(start__gt=since) valid_hanchans = valid_hanchans.filter(start__gt=since)
if until: if until:
valid_hanchans = valid_hanchans.filter(start__lte=until) valid_hanchans = valid_hanchans.filter(start__lte=until)
self.hanchan_count += valid_hanchans.count()
for hanchan in valid_hanchans: for hanchan in valid_hanchans:
self.hanchan_count += 1
hanchan.get_playerdata(self.user) hanchan.get_playerdata(self.user)
if since and hanchan.start < since: if since and hanchan.start < since:
print(hanchan, "<", since, "no recalc") LOGGER.debug(hanchan, "<", since, "no recalc")
self.dan_points += hanchan.dan_points or 0 self.dan_points += hanchan.dan_points or 0
self.kyu_points += hanchan.kyu_points or 0 self.kyu_points += hanchan.kyu_points or 0
self.update_rank() self.update_rank()
else: else:
hanchan.bonus_points = 0 hanchan.bonus_points = 0
hanchan.player_comment = u"" hanchan.player_comment = ""
self.update_hanchan_points(hanchan) self.update_hanchan_points(hanchan)
if hanchan.event.mahjong_tournament: if hanchan.event.mahjong_tournament:
self.append_tournament_bonuspoints(hanchan) self.append_tournament_bonuspoints(hanchan)
self.update_rank() self.update_rank()
self.append_3_in_a_row_bonuspoints(hanchan) self.append_3_in_a_row_bonuspoints(hanchan)
self.update_rank()
hanchan.update_playerdata(self.user) hanchan.update_playerdata(self.user)
hanchan.save(recalculate=False) hanchan.save(recalculate=False)
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 self.good_hanchans += 1 if hanchan.placement == 2 else 0
self.last_hanchan_date = hanchan.start self.last_hanchan_date = hanchan.start
LOGGER.debug(
'id: %(id)d, start: %(start)s, placement: %(placement)d, '
'score: %(score)d, kyu points: %(kyu_points)d, dan points: '
'%(dan_points)d, bonus points: %(bonus_points)d',
{'id': hanchan.pk, 'start': hanchan.start,
'placement': hanchan.placement,
'score': hanchan.game_score,
'kyu_points': hanchan.kyu_points or 0,
'dan_points': hanchan.dan_points or 0,
'bonus_points': hanchan.bonus_points or 0}
)
self.save(force_update=True) self.save(force_update=True)
def update_hanchan_points(self, hanchan): def update_hanchan_points(self, hanchan):
@@ -572,25 +565,27 @@ class KyuDanRanking(models.Model):
self.kyu_points += hanchan.kyu_points self.kyu_points += hanchan.kyu_points
def update_rank(self): def update_rank(self):
print(self.user, self.dan, self.kyu) # Update Dan ranking:
old_dan = self.dan if self.dan or self.dan_points > 0:
if settings.DAN_ALLOW_DROP_DOWN and (self.dan or self.dan_points > 0): if settings.DAN_ALLOW_DROP_DOWN:
self.dan = max((dan for min_points, dan in settings.DAN_RANKS if self.dan = max((dan for min_points, dan in settings.DAN_RANKS
self.dan_points > min_points)) if self.dan_points > min_points))
elif self.dan or self.dan_points > 0: else:
self.dan = max((dan for min_points, dan in settings.DAN_RANKS if self.max_dan_points = max(self.max_dan_points, self.dan_points)
self.max_dan_points > min_points)) self.dan = max((dan for min_points, dan in settings.DAN_RANKS
if self.max_dan_points > min_points))
# jump from Kyu to Dan
elif self.kyu_points > 50: elif self.kyu_points > 50:
self.dan = 1 self.dan = 1
self.dan_points = 0 self.dan_points = 0
self.kyu = None self.kyu = None
self.kyu_points = 0 self.kyu_points = 0
self.wins_in_a_row = 0 self.wins_in_a_row = 0
# update Kyu ranking_
else: else:
print(self, self.kyu_points) self.kyu = min((kyu for min_points, kyu in settings.KYU_RANKS
self.kyu = max((kyu for min_points, kyu in settings.KYU_RANKS if if self.kyu_points > min_points))
self.kyu_points > min_points))
self.wins_in_a_row = 0 if self.dan > old_dan else self.wins_in_a_row
class SeasonRanking(models.Model): class SeasonRanking(models.Model):

View File

@@ -50,4 +50,8 @@
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
{% endblock %} {% endblock %}
{% block buttonbar %}
<a href="?download=xlsx" class="button"><span class="fa fa-table"></span> Download</a>
{% endblock %}

View File

@@ -28,6 +28,56 @@ KYU_DAN_ORDER = { # map sort URL args to Django ORM order_by args
'-username': ('-user__username',) '-username': ('-user__username',)
} }
def getattr_recursive(obj, attr_string):
attr_list = attr_string.split('.')
return_value=None
for attr in attr_list:
print("Obj:", obj,'Attr:', attr)
return_value = getattr(obj, attr)
obj = return_value
return return_value
def generate_sheet(workbook, title, columns_settings, object_list):
row = 1
ws = workbook.create_sheet()
ws.title = title
ws.syncHorizontal = True
ws.filterMode = True
# setup print orientation
ws.page_setup.orientation = ws.ORIENTATION_PORTRAIT
ws.page_setup.paperSize = ws.PAPERSIZE_A4
ws.page_setup.fitToWidth = True
ws.print_options.horizontalCentered = True
# setup page header
ws.oddHeader.left.text = title
ws.oddHeader.left.size = 14
ws.oddHeader.left.font = "Amerika Sans"
ws.oddHeader.left.color = "000000"
ws.oddHeader.right.text = str(date.today())
ws.oddHeader.right.size = 14
ws.oddHeader.right.font = "Amerika Sans"
ws.oddHeader.right.color = "000000"
# write table header
for column, data in enumerate(columns_settings, 1):
cell = ws.cell(column=column, row=row, value=data['title'])
cell.style = 'heading'
# write the table content
for line in object_list:
row += 1
for column, settings in enumerate(columns_settings, 1):
cell = ws.cell(column=column, row=row, value=getattr_recursive(line, settings['attr']))
cell.style = settings['style']
# set column widths
for settings in columns_settings:
ws.column_dimensions[settings['col']].width = settings['width']
class DeleteHanchan(EventDetailMixin, PermissionRequiredMixin, class DeleteHanchan(EventDetailMixin, PermissionRequiredMixin,
generic.DeleteView): generic.DeleteView):
@@ -117,9 +167,6 @@ class EventRankingList(EventDetailMixin, generic.ListView):
model = models.EventRanking model = models.EventRanking
class KyuDanRankingList(MahjongMixin, generic.ListView): class KyuDanRankingList(MahjongMixin, generic.ListView):
"""List all Players with an Kyu or Dan score. """ """List all Players with an Kyu or Dan score. """
default_order = '-score' default_order = '-score'
@@ -133,7 +180,6 @@ class KyuDanRankingList(MahjongMixin, generic.ListView):
] ]
return super(KyuDanRankingList, self).dispatch(request, *args, **kwargs) return super(KyuDanRankingList, self).dispatch(request, *args, **kwargs)
def get_queryset(self): def get_queryset(self):
queryset = models.KyuDanRanking.objects.filter( queryset = models.KyuDanRanking.objects.filter(
hanchan_count__gt=0).order_by(*self.order_by) hanchan_count__gt=0).order_by(*self.order_by)
@@ -160,7 +206,6 @@ class PlayerScore(LoginRequiredMixin, generic.ListView):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
user_model = auth.get_user_model() user_model = auth.get_user_model()
username = kwargs.get('username')
try: try:
self.user = user_model.objects.get( self.user = user_model.objects.get(
username=self.kwargs.get('username')) username=self.kwargs.get('username'))
@@ -168,6 +213,9 @@ class PlayerScore(LoginRequiredMixin, generic.ListView):
raise django.http.Http404( raise django.http.Http404(
_("No user found matching the name {}").format( _("No user found matching the name {}").format(
self.kwargs.get('username'))) self.kwargs.get('username')))
print(request.GET)
if request.GET.get('download') == 'xlsx':
return self.get_xlsx(request, *args, **kwargs)
return super(PlayerScore, self).get(request, *args, **kwargs) return super(PlayerScore, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@@ -186,12 +234,47 @@ class PlayerScore(LoginRequiredMixin, generic.ListView):
context['ladder_ranking'] = models.SeasonRanking(user=self.user) context['ladder_ranking'] = models.SeasonRanking(user=self.user)
return context return context
def get_xlsx(self, request, *args, **kwargs):
from management.commands.export_ranking import geneate_excel
self.object_list = self.get_queryset()
allow_empty = self.get_allow_empty()
response = django.http.HttpResponse(
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
response[
'Content-Disposition'] = 'attachment; filename="{xlsx_filename}"'.format(xlsx_filename=self.xlsx_filename)
xlxs_workbook = geneate_excel()
print(self.xlsx_columns)
generate_sheet(xlxs_workbook,
title=self.xlsx_filename,
columns_settings=self.xlsx_columns,
object_list=self.object_list
)
xlxs_workbook.create_sheet()
xlxs_workbook.save(response)
return response
class PlayerDanScore(PlayerScore): class PlayerDanScore(PlayerScore):
template_name = 'mahjong_ranking/player_dan_score.html' template_name = 'mahjong_ranking/player_dan_score.html'
xlsx_columns = (
{'col': 'A', 'title': 'Beginn', 'attr': 'start', 'style': 'date', 'width': 14},
{'col': 'B', 'title': 'Platzierung', 'attr': 'placement', 'style': 'int', 'width': 8},
{'col': 'C', 'title': 'Spieler 1', 'attr': 'player1.username', 'style': 'content', 'width': 8},
{'col': 'D', 'title': 'Punkte', 'attr': 'player1_game_score', 'style': 'int', 'width': 8},
{'col': 'E', 'title': 'Spieler 2', 'attr': 'player2.username', 'style': 'content', 'width': 8},
{'col': 'F', 'title': 'Punkte', 'attr': 'player2_game_score', 'style': 'int', 'width': 8},
{'col': 'G', 'title': 'Spieler 3', 'attr': 'player3.username', 'style': 'content', 'width': 8},
{'col': 'H', 'title': 'Punkte', 'attr': 'player3_game_score', 'style': 'int', 'width': 8},
{'col': 'I', 'title': 'Spieler 4', 'attr': 'player4.username', 'style': 'content', 'width': 8},
{'col': 'J', 'title': 'Punkte', 'attr': 'player4_game_score', 'style': 'int', 'width': 8},
{'col': 'K', 'title': 'Dan Punkte', 'attr': 'dan_points', 'style': 'int', 'width': 8},
{'col': 'L', 'title': 'Anmerkung', 'attr': 'comment', 'style': 'content', 'width': 8},
)
def get_queryset(self): def get_queryset(self):
kyu_dan_ranking = models.KyuDanRanking.objects.get(user=self.user) kyu_dan_ranking = models.KyuDanRanking.objects.get(user=self.user)
self.xlsx_filename = "{username}_dan_score.xlsx".format(
username=self.user.username)
return models.Hanchan.objects.dan_hanchans(user=self.user, return models.Hanchan.objects.dan_hanchans(user=self.user,
since=kyu_dan_ranking.legacy_date) since=kyu_dan_ranking.legacy_date)
@@ -200,6 +283,8 @@ class PlayerInvalidScore(PlayerScore):
template_name = 'mahjong_ranking/player_invalid_score.html' template_name = 'mahjong_ranking/player_invalid_score.html'
def get_queryset(self): def get_queryset(self):
self.xlsx_filename = "{username}_invalid_score.xlsx".format(
username=self.user.username)
return models.Hanchan.objects.unconfirmed(user=self.user) return models.Hanchan.objects.unconfirmed(user=self.user)
@@ -207,6 +292,8 @@ class PlayerKyuScore(PlayerScore):
template_name = 'mahjong_ranking/player_kyu_score.html' template_name = 'mahjong_ranking/player_kyu_score.html'
def get_queryset(self): def get_queryset(self):
self.xlsx_filename = "{username}_kyu_score.xlsx".format(
username=self.user.username)
return models.Hanchan.objects.kyu_hanchans(self.user) return models.Hanchan.objects.kyu_hanchans(self.user)
@@ -224,6 +311,8 @@ class PlayerLadderScore(PlayerScore):
def get_queryset(self, **kwargs): def get_queryset(self, **kwargs):
self.season = int(self.request.GET.get('season', date.today().year)) self.season = int(self.request.GET.get('season', date.today().year))
self.xlsx_filename = "{username}_ladder ({season}).xlsx".format(
username=self.user.username, season=self.season)
hanchan_list = models.Hanchan.objects.season_hanchans( hanchan_list = models.Hanchan.objects.season_hanchans(
user=self.user, user=self.user,
season=self.season season=self.season