XLSX Export vereinheitlicht.

Spieler Hanchanlisten können nun als XLSX exportiert werden.
Anpassungen in den Einstellungen für die parametisierten Kyu/Dan Berechnung.
This commit is contained in:
2017-12-26 21:45:39 +01:00
parent 9f6fffa4f4
commit fdbf819092
22 changed files with 589 additions and 320 deletions

View File

@@ -1,214 +0,0 @@
"""Export Mahjong Rankings as excel files."""
import os
from datetime import date, time, datetime
import openpyxl
from django.conf import settings
from django.core.management.base import BaseCommand
from django.utils import timezone
from django.utils.dateparse import parse_date
from django.core.mail import EmailMessage
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'
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'
MAIL_BODY = """
Hallo! Ich bin's dein Server.
Ich habe gerade die Mahjong Rankings als Excel exportiert und dachte mir das
ich sie dir am besten gleich schicke.
Bitte versuche nicht auf diese E-Mail zu antworten.
Ich bin nur ein dummes Programm.
mit lieben Grüßen
Der Kasu Server
"""
def geneate_excel():
"""Generate an excel .xlsx spreadsheet from json data of the kyu/dan
rankings.
:param json_data: The ladder ranking as JSON export."""
workbook = openpyxl.Workbook()
workbook.add_named_style(HEADING_STYLE)
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:
workbook.remove(sheet)
return workbook
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
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 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, 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},
{'col': 'B', 'title': 'Spitzname', 'attr': 'username',
'style': 'content',
'width': 25},
{'col': 'C', 'title': '⌀ Platz', 'attr': 'avg_placement',
'style': 'float', '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, until):
KyuDanRanking.objects.update(until=until)
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': 8},
{'col': 'H', 'title': 'letzte Hanchan', 'attr': 'last_hanchan_date',
'style': 'date', 'width': 16},
)
generate_sheet(
workbook=workbook,
title=title,
columns_settings=columns_settings,
json_data=json_data)
class Command(BaseCommand):
"""Exports the SeasonRankings"""
filename = str()
until = datetime
def add_arguments(self, parser):
parser.add_argument(
'--until', nargs='?', type=parse_date,
default=date.today(), metavar='YYYY-MM-DD',
help='Calculate and export rankings until the given date.')
parser.add_argument(
'--mail', nargs='*', type=str, metavar='user@example.com',
help='Send the spreadsheet via eMail to the given recipient.')
def handle(self, *args, **options):
"""Exports the current ladder ranking in a spreadsheet.
This is useful as a backup in form of a hardcopy."""
self.until = timezone.make_aware(datetime.combine(
options['until'], time(23, 59, 59)
))
self.filename = os.path.join(
settings.RANKING_EXPORT_PATH,
'mahjong_rankings_{:%Y-%m-%d}.xlsx'.format(self.until)
)
workbook = geneate_excel()
export_season_rankings(workbook, until=self.until)
export_kyu_dan_rankings(workbook, until=self.until)
os.makedirs(settings.RANKING_EXPORT_PATH, exist_ok=True)
workbook.save(self.filename)
if options['mail']:
self.send_mail(options['mail'])
def send_mail(self, recipients):
mail = EmailMessage(
subject='Mahjong Rankings vom {:%d.%m.%Y}'.format(self.until),
body=MAIL_BODY,
from_email=settings.DEFAULT_FROM_EMAIL,
to=recipients)
mail.attach_file(self.filename)
mail.send()

View File

@@ -0,0 +1,126 @@
"""Export Mahjong Rankings as excel files."""
import os
from datetime import date, time, datetime
from django.conf import settings
from django.core.mail import EmailMessage
from django.core.management.base import BaseCommand
from django.utils import timezone
from django.utils.dateparse import parse_date
from kasu import xlsx
from mahjong_ranking.models import SeasonRanking, KyuDanRanking
MAIL_BODY = """
Hallo! Ich bin's dein Server.
Ich habe gerade die Mahjong Rankings als Excel exportiert und dachte mir das
ich sie dir am besten gleich schicke.
Bitte versuche nicht auf diese E-Mail zu antworten.
Ich bin nur ein dummes Programm.
mit lieben Grüßen
Der Kasu Server
"""
def export_season_rankings(workbook, until):
SeasonRanking.objects.update(until=until)
season = until.year if until else date.today().year
object_list = SeasonRanking.objects.season_rankings()
title = "Mahjong Ladder - {}".format(season)
columns_settings = (
{'col': 'A', 'title': 'Rang', 'attr': 'placement', 'style': 'Integer',
'width': 8},
{'col': 'B', 'title': 'Spitzname', 'attr': 'user.username',
'style': 'Content',
'width': 25},
{'col': 'C', 'title': '⌀ Platz', 'attr': 'avg_placement',
'style': 'Float', 'width': 8},
{'col': 'D', 'title': '⌀ Punkte', 'attr': 'avg_score',
'style': 'Float', 'width': 12},
{'col': 'E', 'title': 'Hanchans', 'attr': 'hanchan_count',
'style': 'Integer', 'width': 10},
{'col': 'F', 'title': 'Gut', 'attr': 'good_hanchans',
'style': 'Integer', 'width': 5},
{'col': 'G', 'title': 'Gewonnen', 'attr': 'won_hanchans',
'style': 'Integer', 'width': 10},
)
workbook.generate_sheet(
title=title,
columns_settings=columns_settings,
object_list=object_list)
def export_kyu_dan_rankings(workbook, until):
KyuDanRanking.objects.update(until=until)
object_list = KyuDanRanking.objects.all()
title = "Kyū & Dan Rankings"
columns_settings = (
{'col': 'A', 'title': 'Spitzname', 'attr': 'user.username',
'style': 'Content', 'width': 14},
{'col': 'B', 'title': 'Voller Name', 'attr': 'user.full_name',
'style': 'Content', 'width': 20},
{'col': 'C', 'title': 'Rang', 'attr': 'rank',
'style': 'Content', 'width': 8},
{'col': 'D', 'title': 'Punkte', 'attr': 'points',
'style': 'Integer', 'width': 8},
{'col': 'E', 'title': 'Hanchans', 'attr': 'hanchan_count',
'style': 'Integer', 'width': 10},
{'col': 'F', 'title': 'Gut', 'attr': 'good_hanchans',
'style': 'Integer', 'width': 5},
{'col': 'G', 'title': 'Gewonnen', 'attr': 'won_hanchans',
'style': 'Integer', 'width': 10},
{'col': 'H', 'title': 'letzte Hanchan', 'attr': 'last_hanchan_date',
'style': 'Date', 'width': 16},
)
workbook.generate_sheet(
title=title,
columns_settings=columns_settings,
object_list=object_list)
class Command(BaseCommand):
"""Exports the SeasonRankings"""
filename = str()
until = datetime
def add_arguments(self, parser):
parser.add_argument(
'--until', nargs='?', type=parse_date,
default=date.today(), metavar='YYYY-MM-DD',
help='Calculate and export rankings until the given date.')
parser.add_argument(
'--mail', nargs='*', type=str, metavar='user@example.com',
help='Send the spreadsheet via eMail to the given recipient.')
def handle(self, *args, **options):
"""Exports the current ladder ranking in a spreadsheet.
This is useful as a backup in form of a hardcopy."""
self.until = timezone.make_aware(datetime.combine(
options['until'], time(23, 59, 59)
))
self.filename = os.path.join(
settings.RANKING_EXPORT_PATH,
'mahjong_rankings_{:%Y-%m-%d}.xlsx'.format(self.until)
)
workbook = xlsx.Workbook()
export_season_rankings(workbook, until=self.until)
export_kyu_dan_rankings(workbook, until=self.until)
os.makedirs(settings.RANKING_EXPORT_PATH, exist_ok=True)
workbook.save(self.filename)
if options['mail']:
self.send_mail(options['mail'])
def send_mail(self, recipients):
mail = EmailMessage(
subject='Mahjong Rankings vom {:%d.%m.%Y}'.format(self.until),
body=MAIL_BODY,
from_email=settings.DEFAULT_FROM_EMAIL,
to=recipients)
mail.attach_file(self.filename)
mail.send()

View File

@@ -4,30 +4,39 @@
Recalculate Mahjong Rankings...
"""
from django.core.management.base import BaseCommand
from datetime import date, datetime, time
from mahjong_ranking import models
from django.utils.dateparse import parse_date
from django.core.management.base import BaseCommand
from django.utils import timezone
from django.utils.dateparse import parse_date
from mahjong_ranking import models
class Command(BaseCommand):
""" Recalculate all Kyu/Dan Rankings """
help = "Recalculate all Kyu/Dan Rankings"
help = "Recalculate the 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')
parser.add_argument('-s', '--since', nargs='?', type=parse_date,
metavar='YYYY-MM-DD',
help='Use all Hanchans since the given date.')
parser.add_argument('-u', '--until', nargs='?', type=parse_date,
metavar='YYYY-MM-DD',
help='Only use Hanchans until the given date.')
parser.add_argument('-f', '--force', action='store_true',
help="Force the recalculation of all Hanchans.")
def handle(self, *args, **options):
since = options.get('since', None)
until = options.get('until', None)
force_recalc = options.get('forcerecalc')
force_recalc = options.get('force')
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)
models.KyuDanRanking.objects.update(since=since, until=until,
force_recalc=force_recalc)