added new fields to KyuDanRanking that allow to pick up the calculation from the last state of the KyuDanRanking.
last_hanchan_date: it contains the start of the latest hanchan content for this players ranking. wins_in_row: to save the currents wins in a row Added option to calcuclate rankings until a given datetime.
This commit is contained in:
@@ -1,12 +1,11 @@
|
||||
"""Export Mahjong Rankings as excel files."""
|
||||
|
||||
from datetime import date
|
||||
from operator import itemgetter
|
||||
|
||||
import openpyxl
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.utils.dateparse import parse_date
|
||||
from openpyxl.styles import Border
|
||||
|
||||
from datetime import date, time, datetime
|
||||
from django.utils import timezone
|
||||
from mahjong_ranking.models import SeasonRanking, KyuDanRanking
|
||||
|
||||
THIN_BORDER = openpyxl.styles.Side(style='thin', color="d3d7cf")
|
||||
@@ -34,6 +33,11 @@ FLOAT_STYLE.font = DEFAULT_STYLE.font
|
||||
FLOAT_STYLE.border = DEFAULT_STYLE.border
|
||||
FLOAT_STYLE.number_format = '#,##0.00'
|
||||
|
||||
DATE_STYLE = openpyxl.styles.NamedStyle(name='date')
|
||||
DATE_STYLE.font = DEFAULT_STYLE.font
|
||||
DATE_STYLE.border = DEFAULT_STYLE.border
|
||||
DATE_STYLE.number_format = 'dd.mm.yyyy'
|
||||
|
||||
|
||||
def geneate_excel():
|
||||
"""Generate an excel .xlsx spreadsheet from json data of the kyu/dan
|
||||
@@ -45,8 +49,9 @@ def geneate_excel():
|
||||
workbook.add_named_style(DEFAULT_STYLE)
|
||||
workbook.add_named_style(INT_STYLE)
|
||||
workbook.add_named_style(FLOAT_STYLE)
|
||||
workbook.add_named_style(DATE_STYLE)
|
||||
|
||||
for sheet in workbook.worksheets:
|
||||
print(sheet)
|
||||
workbook.remove(sheet)
|
||||
return workbook
|
||||
|
||||
@@ -55,6 +60,8 @@ def generate_sheet(workbook, title, columns_settings, json_data):
|
||||
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
|
||||
@@ -90,10 +97,10 @@ def generate_sheet(workbook, title, columns_settings, json_data):
|
||||
ws.column_dimensions[settings['col']].width = settings['width']
|
||||
|
||||
|
||||
def export_season_rankings(workbook):
|
||||
json_data = sorted(SeasonRanking.objects.json_data(),
|
||||
key=itemgetter('placement'))
|
||||
title = "Mahjong Ladder - {}".format(date.today().year)
|
||||
def export_season_rankings(workbook, until):
|
||||
SeasonRanking.objects.update(until=until)
|
||||
json_data = SeasonRanking.objects.json_data()
|
||||
title = "Mahjong Ladder - {}".format(until.year)
|
||||
columns_settings = (
|
||||
{'col': 'A', 'title': 'Rang', 'attr': 'placement', 'style': 'int',
|
||||
'width': 8},
|
||||
@@ -111,7 +118,6 @@ def export_season_rankings(workbook):
|
||||
{'col': 'G', 'title': 'Gewonnen', 'attr': 'won_hanchans',
|
||||
'style': 'int', 'width': 10},
|
||||
)
|
||||
|
||||
generate_sheet(
|
||||
workbook=workbook,
|
||||
title=title,
|
||||
@@ -119,7 +125,8 @@ def export_season_rankings(workbook):
|
||||
json_data=json_data)
|
||||
|
||||
|
||||
def export_kyu_dan_rankings(workbook):
|
||||
def export_kyu_dan_rankings(workbook, until):
|
||||
KyuDanRanking.objects.update(until=until)
|
||||
json_data = KyuDanRanking.objects.json_data()
|
||||
title = "Kyū & Dan Rankings"
|
||||
columns_settings = (
|
||||
@@ -136,7 +143,9 @@ def export_kyu_dan_rankings(workbook):
|
||||
{'col': 'F', 'title': 'Gut', 'attr': 'good_hanchans',
|
||||
'style': 'int', 'width': 5},
|
||||
{'col': 'G', 'title': 'Gewonnen', 'attr': 'won_hanchans',
|
||||
'style': 'int', 'width': 10},
|
||||
'style': 'int', 'width': 8},
|
||||
{'col': 'H', 'title': 'letzte Hanchan', 'attr': 'last_hanchan_date',
|
||||
'style': 'date', 'width': 16},
|
||||
)
|
||||
generate_sheet(
|
||||
workbook=workbook,
|
||||
@@ -148,11 +157,16 @@ def export_kyu_dan_rankings(workbook):
|
||||
class Command(BaseCommand):
|
||||
"""Exports the SeasonRankings"""
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('--until', nargs='?', type=parse_date)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
"""Exports the current ladder ranking in a spreadsheet.
|
||||
This is useful as a backup in form of a hardcopy."""
|
||||
until = timezone.make_aware(
|
||||
datetime.combine(options['until'] or date.today(),
|
||||
time(23, 59, 59)))
|
||||
workbook = geneate_excel()
|
||||
export_season_rankings(workbook)
|
||||
export_kyu_dan_rankings(workbook)
|
||||
workbook.save('sample.x')
|
||||
workbook.save('mahjong_rankings_{}.xlsx'.format(str(date.today())))
|
||||
export_season_rankings(workbook, until=until)
|
||||
export_kyu_dan_rankings(workbook, until=until)
|
||||
workbook.save('mahjong_rankings_{:%Y-%m-%d}.xlsx'.format(until))
|
||||
|
||||
@@ -5,27 +5,29 @@ Recalculate Mahjong Rankings...
|
||||
"""
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from mahjong_ranking import LOGGER
|
||||
from datetime import date, datetime, time
|
||||
from mahjong_ranking import models
|
||||
|
||||
from django.utils.dateparse import parse_date
|
||||
from django.utils import timezone
|
||||
|
||||
class Command(BaseCommand):
|
||||
""" Recalculate all Kyu/Dan Rankings """
|
||||
|
||||
help = "Recalculate all Kyu/Dan Rankings"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('--since', nargs='?', type=parse_date)
|
||||
parser.add_argument('--until', nargs='?', type=parse_date)
|
||||
parser.add_argument('--forcerecalc', action='store_true')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
old_attr = {'dan': None, 'dan_points': None,
|
||||
'kyu': None, 'kyu_points': None, 'won_hanchans': None,
|
||||
'good_hanchans': None, 'hanchan_count': None}
|
||||
for ranking in models.KyuDanRanking.objects.all():
|
||||
old_attr = {attr: getattr(ranking, attr) for attr in old_attr.keys()}
|
||||
ranking.recalculate()
|
||||
for attr, old_value in old_attr.items():
|
||||
if getattr(ranking, attr) != old_value:
|
||||
LOGGER.warning(
|
||||
"%(user)s recalc shows differences in %(attr)s! old: %(old)d, new: %(new)d",
|
||||
{'user': ranking.user, 'attr': attr,
|
||||
'old': old_value, 'new': getattr(ranking, attr)}
|
||||
)
|
||||
since = options.get('since', None)
|
||||
until = options.get('until', None)
|
||||
force_recalc = options.get('forecerecalc', False)
|
||||
if isinstance(since, date):
|
||||
since = datetime.combine(since, time(0, 0, 0))
|
||||
since = timezone.make_aware(since)
|
||||
if isinstance(until, date):
|
||||
until = datetime.combine(until, time(23, 59, 59))
|
||||
until = timezone.make_aware(until)
|
||||
models.KyuDanRanking.objects.update(since=since, until=until, force_recalc=force_recalc)
|
||||
@@ -1,7 +1,8 @@
|
||||
"""ObjectManagers for the Django Models used in the Mahjong-Ranking."""
|
||||
from datetime import date
|
||||
|
||||
from . import LOGGER
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class HanchanManager(models.Manager):
|
||||
@@ -12,7 +13,7 @@ class HanchanManager(models.Manager):
|
||||
"""
|
||||
use_for_related_fields = True
|
||||
|
||||
def confirmed_hanchans(self, user=None, **filter_args):
|
||||
def confirmed_hanchans(self, user=None, until=None, **filter_args):
|
||||
""" Return all valid and confirmed Hanchans.
|
||||
|
||||
:param user: Only return Hanchans where this user participated.
|
||||
@@ -20,9 +21,12 @@ class HanchanManager(models.Manager):
|
||||
:return: QuerySet Object
|
||||
"""
|
||||
if user:
|
||||
return self.user_hanchans(user, confirmed=True, **filter_args)
|
||||
else:
|
||||
return self.filter(confirmed=True, **filter_args)
|
||||
return self.user_hanchans(user, confirmed=True, until=until,
|
||||
**filter_args)
|
||||
hanchans = self.filter(confirmed=True, **filter_args)
|
||||
if until:
|
||||
hanchans = hanchans.filter(start__lte=until)
|
||||
return hanchans
|
||||
|
||||
def dan_hanchans(self, user, **filter_args):
|
||||
""" Return all Hanchans where a specific user has participated and had
|
||||
@@ -39,7 +43,7 @@ class HanchanManager(models.Manager):
|
||||
models.Q(player4=user, player4_dan_points__isnull=False)
|
||||
).filter(confirmed=True, **filter_args)
|
||||
queryset = queryset.select_related().order_by('-start')
|
||||
[ hanchan.get_playerdata(user) for hanchan in queryset ]
|
||||
[hanchan.get_playerdata(user) for hanchan in queryset]
|
||||
return queryset
|
||||
|
||||
def kyu_hanchans(self, user, **filter_args):
|
||||
@@ -57,20 +61,23 @@ class HanchanManager(models.Manager):
|
||||
models.Q(player4=user, player4_kyu_points__isnull=False)
|
||||
).filter(confirmed=True, **filter_args)
|
||||
queryset = queryset.select_related().order_by('-start')
|
||||
[ hanchan.get_playerdata(user) for hanchan in queryset ]
|
||||
[hanchan.get_playerdata(user) for hanchan in queryset]
|
||||
return queryset
|
||||
|
||||
def season_hanchans(self, user=None, season=None):
|
||||
def season_hanchans(self, user=None, season=None, until=None):
|
||||
"""Return all Hanchans that belong to a given or the current season.
|
||||
|
||||
:param user: Only return Hanchans where this user participated.
|
||||
:param season: the year of the wanted season, current year if None.
|
||||
:return: QuerySet Object
|
||||
"""
|
||||
season = season or date.today().year
|
||||
return self.confirmed_hanchans(user=user, season=season)
|
||||
try:
|
||||
season = season or until.year
|
||||
except AttributeError:
|
||||
season = date.today().year
|
||||
return self.confirmed_hanchans(user=user, season=season, until=until)
|
||||
|
||||
def user_hanchans(self, user, since=None, **filter_args):
|
||||
def user_hanchans(self, user, since=None, until=None, **filter_args):
|
||||
"""Return all Hanchans where a specific user has participated.
|
||||
|
||||
:param user: Return Hanchans where this user participated.
|
||||
@@ -82,12 +89,13 @@ class HanchanManager(models.Manager):
|
||||
models.Q(player1=user) | models.Q(player2=user) |
|
||||
models.Q(player3=user) | models.Q(player4=user)
|
||||
)
|
||||
if since:
|
||||
queryset = queryset.filter(start__gte=since, **filter_args)
|
||||
else:
|
||||
queryset = queryset.filter(**filter_args)
|
||||
if since:
|
||||
queryset = queryset.filter(start__gte=since)
|
||||
if until:
|
||||
queryset = queryset.filter(start__lte=until)
|
||||
queryset = queryset.select_related().order_by('-start')
|
||||
[ hanchan.get_playerdata(user) for hanchan in queryset ]
|
||||
[hanchan.get_playerdata(user) for hanchan in queryset]
|
||||
return queryset
|
||||
|
||||
def unconfirmed_hanchans(self, user=None, **filter_args):
|
||||
@@ -158,8 +166,27 @@ class SeasonRankingManager(models.Manager):
|
||||
})
|
||||
return json_data
|
||||
|
||||
class KyuDanRankingManager(models.Manager):
|
||||
def update(self, season=None, until=None, force_recalc=False):
|
||||
try:
|
||||
season = season or until.year
|
||||
except AttributeError:
|
||||
season = date.today().year
|
||||
if until or force_recalc:
|
||||
for ranking in self.filter(season=season):
|
||||
ranking.recalculate(until=until)
|
||||
for placement, ranking in enumerate(self.season_rankings(season)):
|
||||
ranking.placement = placement
|
||||
ranking.save(force_update=True, update_fields=['placement'])
|
||||
|
||||
def season_rankings(self, season=None):
|
||||
season = season or date.today().year
|
||||
rankings = self.filter(
|
||||
season=season,
|
||||
hanchan_count__gt=settings.MIN_HANCHANS_FOR_LADDER)
|
||||
return rankings.order_by('avg_placement', '-avg_score')
|
||||
|
||||
|
||||
class KyuDanRankingManager(models.Manager):
|
||||
def json_data(self):
|
||||
""" Get all Rankings for a given Season and return them as a list of
|
||||
dict objects, suitable for JSON exports and other processings.
|
||||
@@ -172,9 +199,12 @@ class KyuDanRankingManager(models.Manager):
|
||||
values = values.values('user_id', 'user__username',
|
||||
'user__first_name', 'user__last_name',
|
||||
'dan', 'dan_points', 'kyu', 'kyu_points',
|
||||
'hanchan_count', 'won_hanchans', 'good_hanchans')
|
||||
'hanchan_count', 'won_hanchans', 'good_hanchans',
|
||||
'last_hanchan_date')
|
||||
for user in values:
|
||||
if user['dan']:
|
||||
if user['hanchan_count'] == 0:
|
||||
continue
|
||||
elif user['dan']:
|
||||
rank = '{}. Dan'.format(user['dan'])
|
||||
points = user['dan_points']
|
||||
else:
|
||||
@@ -183,11 +213,31 @@ class KyuDanRankingManager(models.Manager):
|
||||
json_data.append({
|
||||
'user_id': user['user_id'],
|
||||
'username': user['user__username'],
|
||||
'full_name': " ".join([user['user__last_name'], user['user__first_name']]),
|
||||
'full_name': " ".join(
|
||||
[user['user__last_name'], user['user__first_name']]),
|
||||
'rank': rank,
|
||||
'points': points,
|
||||
'hanchan_count': user['hanchan_count'],
|
||||
'good_hanchans': user['good_hanchans'],
|
||||
'won_hanchans': user['won_hanchans']
|
||||
'won_hanchans': user['won_hanchans'],
|
||||
'last_hanchan_date': user['last_hanchan_date']
|
||||
|
||||
})
|
||||
return json_data
|
||||
|
||||
def update(self, since=None, until=None, force_recalc=False):
|
||||
old_attr = {'dan': None, 'dan_points': None,
|
||||
'kyu': None, 'kyu_points': None, 'won_hanchans': None,
|
||||
'good_hanchans': None, 'hanchan_count': None}
|
||||
for ranking in self.all():
|
||||
old_attr = {attr: getattr(ranking, attr) for attr in
|
||||
old_attr.keys()}
|
||||
ranking.calculate(since=since, until=until,
|
||||
force_recalc=force_recalc)
|
||||
for attr, old_value in old_attr.items():
|
||||
if getattr(ranking, attr) != old_value:
|
||||
LOGGER.warning(
|
||||
"%(user)s recalc shows differences in %(attr)s! old: %(old)d, new: %(new)d",
|
||||
{'user': ranking.user, 'attr': attr,
|
||||
'old': old_value, 'new': getattr(ranking, attr)}
|
||||
)
|
||||
|
||||
@@ -35,7 +35,7 @@ class DenormalizationUpdateMiddleware(object): # Ignore PyLintBear (R0903)
|
||||
user_id, hanchan_start = kyu_dan_ranking_queue.pop()
|
||||
kyu_dan_ranking = models.KyuDanRanking.objects.get_or_create(
|
||||
user_id=user_id)[0]
|
||||
kyu_dan_ranking.recalculate(hanchan_start)
|
||||
kyu_dan_ranking.calculate(since=hanchan_start)
|
||||
cache.set('kyu_dan_ranking_queue', set(), 360)
|
||||
|
||||
ladder_ranking_queue = cache.get('ladder_ranking_queue', set())
|
||||
@@ -58,12 +58,5 @@ class DenormalizationUpdateMiddleware(object): # Ignore PyLintBear (R0903)
|
||||
|
||||
for season in season_queue:
|
||||
LOGGER.info(u'Recalculate placements for Season %d', season)
|
||||
season_rankings = models.SeasonRanking.objects.filter(
|
||||
season=season, hanchan_count__gt=MIN_HANCHANS_FOR_LADDER
|
||||
).order_by('avg_placement', '-avg_score')
|
||||
placement = 1
|
||||
for ranking in season_rankings:
|
||||
ranking.placement = placement
|
||||
ranking.save(force_update=True)
|
||||
placement += 1
|
||||
models.SeasonRanking.objects.update(season=season)
|
||||
return response
|
||||
|
||||
29
src/mahjong_ranking/migrations/0005_auto_20171115_0653.py
Normal file
29
src/mahjong_ranking/migrations/0005_auto_20171115_0653.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.7 on 2017-11-15 05:53
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mahjong_ranking', '0004_auto_20170218_1947'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='kyudanranking',
|
||||
options={'ordering': ('-dan_points', 'dan', '-kyu_points'), 'verbose_name': 'Kyū/Dan Wertung', 'verbose_name_plural': 'Kyū/Dan Wertungen'},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='kyudanranking',
|
||||
name='last_hanchan_date',
|
||||
field=models.DateTimeField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='kyudanranking',
|
||||
name='wins_in_a_row',
|
||||
field=models.PositiveIntegerField(default=0),
|
||||
),
|
||||
]
|
||||
@@ -4,7 +4,7 @@
|
||||
# werden dürfen.
|
||||
|
||||
from __future__ import division
|
||||
|
||||
from datetime import datetime, time
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse
|
||||
@@ -345,10 +345,10 @@ class KyuDanRanking(models.Model):
|
||||
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
|
||||
wins_in_a_row = models.PositiveIntegerField(default=0)
|
||||
last_hanchan_date = models.DateTimeField(blank=True, null=True)
|
||||
objects = managers.KyuDanRankingManager()
|
||||
|
||||
|
||||
class Meta(object):
|
||||
ordering = ('-dan_points', 'dan', '-kyu_points')
|
||||
verbose_name = _(u'Kyū/Dan Ranking')
|
||||
@@ -436,11 +436,19 @@ class KyuDanRanking(models.Model):
|
||||
else:
|
||||
return reverse('player-kyu-score', args=[self.user.username])
|
||||
|
||||
def recalculate(self, hanchan_start=None):
|
||||
def calculate(self, since=None, until=None, force_recalc=False):
|
||||
"""
|
||||
Fetches all valid Hanchans from this Player and recalculates his
|
||||
Kyu/Dan Ranking.
|
||||
"""
|
||||
valid_hanchans = Hanchan.objects.confirmed_hanchans(user=self.user)
|
||||
valid_hanchans = valid_hanchans.order_by('start')
|
||||
if since and self.last_hanchan_date and since < self.last_hanchan_date:
|
||||
force_recalc = True
|
||||
if until and self.last_hanchan_date and until < self.last_hanchan_date:
|
||||
force_recalc = True
|
||||
if force_recalc:
|
||||
# Setze alles auf die legacy Werte und berechne alles von neuem.
|
||||
self.dan = None
|
||||
self.dan_points = self.legacy_dan_points or 0
|
||||
self.kyu = None
|
||||
@@ -449,15 +457,22 @@ class KyuDanRanking(models.Model):
|
||||
self.good_hanchans = 0
|
||||
self.won_hanchans = 0
|
||||
self.update_rank()
|
||||
|
||||
LOGGER.info(
|
||||
"recalculating Kyu/Dan points for %s since %s...",
|
||||
self.user, str(hanchan_start)
|
||||
)
|
||||
valid_hanchans = Hanchan.objects.confirmed_hanchans(
|
||||
user=self.user).order_by('start')
|
||||
self.last_hanchan_date = None
|
||||
if self.legacy_date:
|
||||
valid_hanchans = valid_hanchans.filter(start__gt=self.legacy_date)
|
||||
since = timezone.make_aware(
|
||||
datetime.combine(self.legacy_date, time(0, 0, 0)))
|
||||
else:
|
||||
since = None
|
||||
else:
|
||||
since = self.last_hanchan_date or self.legacy_date
|
||||
LOGGER.info(
|
||||
"recalculating Kyu/Dan points for %(user)s since %(since)s...",
|
||||
{'user': self.user, 'since': str(since)}
|
||||
)
|
||||
if since:
|
||||
valid_hanchans = valid_hanchans.filter(start__gt=since)
|
||||
if until:
|
||||
valid_hanchans = valid_hanchans.filter(start__lte=until)
|
||||
|
||||
""" TODO: Hanchan Punkte nur neu berechnen wenn sie nach hachan_start
|
||||
lagen. Es müssen aber alle durch die Schleife rennen, damit die Punkte
|
||||
@@ -465,7 +480,7 @@ class KyuDanRanking(models.Model):
|
||||
self.hanchan_count += valid_hanchans.count()
|
||||
for hanchan in valid_hanchans:
|
||||
hanchan.get_playerdata(self.user)
|
||||
if hanchan_start and hanchan_start < hanchan.start:
|
||||
if since and since < hanchan.start:
|
||||
self.dan_points += hanchan.dan_points or 0
|
||||
self.kyu_points += hanchan.kyu_points or 0
|
||||
self.update_rank()
|
||||
@@ -482,6 +497,7 @@ class KyuDanRanking(models.Model):
|
||||
hanchan.save(recalculate=False)
|
||||
self.won_hanchans += 1 if hanchan.placement == 1 else 0
|
||||
self.good_hanchans += 1 if hanchan.placement == 2 else 0
|
||||
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: '
|
||||
@@ -593,9 +609,9 @@ class SeasonRanking(models.Model):
|
||||
def get_absolute_url(self):
|
||||
return reverse('player-ladder-score', args=[self.user.username])
|
||||
|
||||
def recalculate(self):
|
||||
def recalculate(self, until=None):
|
||||
season_hanchans = Hanchan.objects.season_hanchans(
|
||||
user=self.user, season=self.season)
|
||||
user=self.user, season=self.season, until=until)
|
||||
sum_placement = 0
|
||||
sum_score = 0
|
||||
self.placement = None
|
||||
|
||||
@@ -34,7 +34,7 @@ class KyuDanTest(TestCase):
|
||||
|
||||
for ranking in KyuDanRanking.objects.all():
|
||||
original = {a: getattr(ranking, a) for a in self.equal_attrs}
|
||||
ranking.recalculate()
|
||||
ranking.calculate()
|
||||
for attr in self.equal_attrs:
|
||||
self.assertEqual(
|
||||
original[attr],
|
||||
@@ -62,7 +62,7 @@ class KyuDanTest(TestCase):
|
||||
continue
|
||||
rnd = random.randrange(confirmed_hanchans.count())
|
||||
since = confirmed_hanchans[rnd].start
|
||||
ranking.recalculate(hanchan_start=since)
|
||||
ranking.calculate(since=since)
|
||||
for attr in self.equal_attrs:
|
||||
self.assertEqual(
|
||||
original[attr],
|
||||
|
||||
Reference in New Issue
Block a user