export_ranking now exports KyuDanRankings and SeasonRankings.
This commit is contained in:
@@ -1,36 +1,148 @@
|
|||||||
"""Export Mahjong Rankings as excel files."""
|
"""Export Mahjong Rankings as excel files."""
|
||||||
|
|
||||||
|
from datetime import date
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
|
||||||
|
import openpyxl
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from openpyxl import Workbook
|
from openpyxl.styles import Border
|
||||||
|
|
||||||
from mahjong_ranking.models import SeasonRanking
|
from mahjong_ranking.models import SeasonRanking, KyuDanRanking
|
||||||
|
|
||||||
|
THIN_BORDER = openpyxl.styles.Side(style='thin', color="d3d7cf")
|
||||||
|
|
||||||
|
HEADING_STYLE = openpyxl.styles.NamedStyle(name="heading")
|
||||||
|
HEADING_STYLE.font = openpyxl.styles.Font(name='Philosopher', size=11,
|
||||||
|
bold=True, color='ffffff')
|
||||||
|
HEADING_STYLE.fill = openpyxl.styles.PatternFill(fill_type='solid',
|
||||||
|
start_color='a40000',
|
||||||
|
end_color='a40000')
|
||||||
|
|
||||||
|
DEFAULT_STYLE = openpyxl.styles.NamedStyle(name='content')
|
||||||
|
DEFAULT_STYLE.font = openpyxl.styles.Font(name='Philosopher', size=10,
|
||||||
|
bold=False, color='000000')
|
||||||
|
DEFAULT_STYLE.border = openpyxl.styles.Border(bottom=THIN_BORDER,
|
||||||
|
top=THIN_BORDER)
|
||||||
|
|
||||||
|
INT_STYLE = openpyxl.styles.NamedStyle(name='int')
|
||||||
|
INT_STYLE.font = DEFAULT_STYLE.font
|
||||||
|
INT_STYLE.border = DEFAULT_STYLE.border
|
||||||
|
INT_STYLE.number_format = '#,##0'
|
||||||
|
|
||||||
|
FLOAT_STYLE = openpyxl.styles.NamedStyle(name='float')
|
||||||
|
FLOAT_STYLE.font = DEFAULT_STYLE.font
|
||||||
|
FLOAT_STYLE.border = DEFAULT_STYLE.border
|
||||||
|
FLOAT_STYLE.number_format = '#,##0.00'
|
||||||
|
|
||||||
|
|
||||||
def geneate_seasonexcel(json_data):
|
def geneate_excel():
|
||||||
"""Generate an excel .xlsx spreadsheet from json data of the kyu/dan
|
"""Generate an excel .xlsx spreadsheet from json data of the kyu/dan
|
||||||
rankings.
|
rankings.
|
||||||
|
|
||||||
:param json_data: The ladder ranking as JSON export."""
|
:param json_data: The ladder ranking as JSON export."""
|
||||||
workbook = Workbook()
|
workbook = openpyxl.Workbook()
|
||||||
worksheet = workbook.active
|
workbook.add_named_style(HEADING_STYLE)
|
||||||
|
workbook.add_named_style(DEFAULT_STYLE)
|
||||||
|
workbook.add_named_style(INT_STYLE)
|
||||||
|
workbook.add_named_style(FLOAT_STYLE)
|
||||||
|
for sheet in workbook.worksheets:
|
||||||
|
print(sheet)
|
||||||
|
workbook.remove(sheet)
|
||||||
|
return workbook
|
||||||
|
|
||||||
worksheet.append([
|
|
||||||
'Rang', 'Spitzname',
|
|
||||||
'⌀ Platz', '⌀ Punkte',
|
|
||||||
'Hanchans', 'Gut', 'Gewonnen'
|
|
||||||
])
|
|
||||||
|
|
||||||
json_data = sorted(json_data, key=itemgetter('placement'))
|
def generate_sheet(workbook, title, columns_settings, json_data):
|
||||||
for row in json_data:
|
row = 1
|
||||||
worksheet.append([
|
ws = workbook.create_sheet()
|
||||||
row['placement'], row['username'],
|
ws.title = title
|
||||||
row['avg_placement'], row['avg_score'],
|
|
||||||
row['hanchan_count'],
|
# setup print orientation
|
||||||
row['good_hanchans'], row['won_hanchans']
|
ws.page_setup.orientation = ws.ORIENTATION_PORTRAIT
|
||||||
])
|
ws.page_setup.paperSize = ws.PAPERSIZE_A4
|
||||||
workbook.save("sample.xlsx")
|
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 json_data:
|
||||||
|
row += 1
|
||||||
|
for column, settings in enumerate(columns_settings, 1):
|
||||||
|
cell = ws.cell(column=column, row=row, value=line[settings['attr']])
|
||||||
|
cell.style = settings['style']
|
||||||
|
|
||||||
|
# set column widths
|
||||||
|
for settings in columns_settings:
|
||||||
|
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)
|
||||||
|
columns_settings = (
|
||||||
|
{'col': 'A', 'title': 'Rang', 'attr': 'placement', 'style': 'int',
|
||||||
|
'width': 8},
|
||||||
|
{'col': 'B', 'title': 'Spitzname', 'attr': 'username',
|
||||||
|
'style': 'content',
|
||||||
|
'width': 25},
|
||||||
|
{'col': 'C', 'title': '⌀ Platz', 'attr': 'avg_placement',
|
||||||
|
'style': 'int', 'width': 8},
|
||||||
|
{'col': 'D', 'title': '⌀ Punkte', 'attr': 'avg_score',
|
||||||
|
'style': 'float', 'width': 12},
|
||||||
|
{'col': 'E', 'title': 'Hanchans', 'attr': 'hanchan_count',
|
||||||
|
'style': 'int', 'width': 10},
|
||||||
|
{'col': 'F', 'title': 'Gut', 'attr': 'good_hanchans',
|
||||||
|
'style': 'int', 'width': 5},
|
||||||
|
{'col': 'G', 'title': 'Gewonnen', 'attr': 'won_hanchans',
|
||||||
|
'style': 'int', 'width': 10},
|
||||||
|
)
|
||||||
|
|
||||||
|
generate_sheet(
|
||||||
|
workbook=workbook,
|
||||||
|
title=title,
|
||||||
|
columns_settings=columns_settings,
|
||||||
|
json_data=json_data)
|
||||||
|
|
||||||
|
|
||||||
|
def export_kyu_dan_rankings(workbook):
|
||||||
|
json_data = KyuDanRanking.objects.json_data()
|
||||||
|
title = "Kyū & Dan Rankings"
|
||||||
|
columns_settings = (
|
||||||
|
{'col': 'A', 'title': 'Spitzname', 'attr': 'username',
|
||||||
|
'style': 'content', 'width': 14},
|
||||||
|
{'col': 'B', 'title': 'Voller Name', 'attr': 'full_name',
|
||||||
|
'style': 'content', 'width': 20},
|
||||||
|
{'col': 'C', 'title': 'Rang', 'attr': 'rank',
|
||||||
|
'style': 'content', 'width': 8},
|
||||||
|
{'col': 'D', 'title': 'Punkte', 'attr': 'points',
|
||||||
|
'style': 'int', 'width': 8},
|
||||||
|
{'col': 'E', 'title': 'Hanchans', 'attr': 'hanchan_count',
|
||||||
|
'style': 'int', 'width': 10},
|
||||||
|
{'col': 'F', 'title': 'Gut', 'attr': 'good_hanchans',
|
||||||
|
'style': 'int', 'width': 5},
|
||||||
|
{'col': 'G', 'title': 'Gewonnen', 'attr': 'won_hanchans',
|
||||||
|
'style': 'int', 'width': 10},
|
||||||
|
)
|
||||||
|
generate_sheet(
|
||||||
|
workbook=workbook,
|
||||||
|
title=title,
|
||||||
|
columns_settings=columns_settings,
|
||||||
|
json_data=json_data)
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
@@ -39,4 +151,8 @@ class Command(BaseCommand):
|
|||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
"""Exports the current ladder ranking in a spreadsheet.
|
"""Exports the current ladder ranking in a spreadsheet.
|
||||||
This is useful as a backup in form of a hardcopy."""
|
This is useful as a backup in form of a hardcopy."""
|
||||||
geneate_seasonexcel(SeasonRanking.objects.json_data())
|
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())))
|
||||||
|
|||||||
@@ -157,3 +157,37 @@ class SeasonRankingManager(models.Manager):
|
|||||||
'won_hanchans': user['won_hanchans']
|
'won_hanchans': user['won_hanchans']
|
||||||
})
|
})
|
||||||
return json_data
|
return json_data
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
:param season: Season that should be exported, current season if empty
|
||||||
|
:return: a list() of dict() objects suiteable for JSON export.
|
||||||
|
"""
|
||||||
|
json_data = list()
|
||||||
|
values = self.all()
|
||||||
|
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')
|
||||||
|
for user in values:
|
||||||
|
if user['dan']:
|
||||||
|
rank = '{}. Dan'.format(user['dan'])
|
||||||
|
points = user['dan_points']
|
||||||
|
else:
|
||||||
|
rank = '{}. Kyū'.format(user['kyu'])
|
||||||
|
points = user['kyu_points']
|
||||||
|
json_data.append({
|
||||||
|
'user_id': user['user_id'],
|
||||||
|
'username': user['user__username'],
|
||||||
|
'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']
|
||||||
|
})
|
||||||
|
return json_data
|
||||||
|
|||||||
@@ -346,9 +346,11 @@ class KyuDanRanking(models.Model):
|
|||||||
legacy_dan_points = models.PositiveIntegerField(default=0)
|
legacy_dan_points = models.PositiveIntegerField(default=0)
|
||||||
legacy_kyu_points = models.PositiveIntegerField(default=0)
|
legacy_kyu_points = models.PositiveIntegerField(default=0)
|
||||||
wins_in_a_row = 0
|
wins_in_a_row = 0
|
||||||
|
objects = managers.KyuDanRankingManager()
|
||||||
|
|
||||||
|
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
ordering = ('-dan', '-dan_points', '-kyu_points',)
|
ordering = ('-dan_points', 'dan', '-kyu_points')
|
||||||
verbose_name = _(u'Kyū/Dan Ranking')
|
verbose_name = _(u'Kyū/Dan Ranking')
|
||||||
verbose_name_plural = _(u'Kyū/Dan Rankings')
|
verbose_name_plural = _(u'Kyū/Dan Rankings')
|
||||||
|
|
||||||
|
|||||||
53
src/mahjong_ranking/sitemaps.py
Normal file
53
src/mahjong_ranking/sitemaps.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
"""To geneate a Sitemap with all events."""
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
|
from django.contrib.sitemaps import Sitemap
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from events.models import Event
|
||||||
|
from kasu.sitemaps import GenericSitemap
|
||||||
|
from .models import SeasonRanking
|
||||||
|
|
||||||
|
|
||||||
|
class EventRankingSitemap(GenericSitemap):
|
||||||
|
@staticmethod
|
||||||
|
def items():
|
||||||
|
"""add all upcoming and archived events to the sitemap."""
|
||||||
|
return Event.objects.all().exclude(eventranking=None)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def location(event):
|
||||||
|
return reverse('event-ranking', kwargs={'event': event.id})
|
||||||
|
|
||||||
|
|
||||||
|
class EventHanchanSitemap(GenericSitemap):
|
||||||
|
@staticmethod
|
||||||
|
def items():
|
||||||
|
"""add all upcoming and archived events to the sitemap."""
|
||||||
|
return Event.objects.all().exclude(eventranking=None)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def location(event):
|
||||||
|
return reverse('event-hanchan-list', kwargs={'event': event.id})
|
||||||
|
|
||||||
|
|
||||||
|
class MajongSeasonSitemap(Sitemap):
|
||||||
|
priority = 0.5
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def items():
|
||||||
|
seasons = SeasonRanking.objects.all().distinct('season').order_by(
|
||||||
|
'season')
|
||||||
|
seasons = seasons.values_list('season', flat=True)
|
||||||
|
return seasons
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def location(season):
|
||||||
|
return reverse('mahjong-ladder', kwargs={'season': season})
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def changefreq(season):
|
||||||
|
if season == date.today().year:
|
||||||
|
return 'weekly'
|
||||||
|
else:
|
||||||
|
return 'never'
|
||||||
Reference in New Issue
Block a user