From d5b67ffe0cac3a8327c0a33a8b7afaecd859a6be Mon Sep 17 00:00:00 2001 From: Xeniac Date: Fri, 3 Nov 2017 07:15:28 +0100 Subject: [PATCH] export_ranking now exports KyuDanRankings and SeasonRankings. --- .../management/commands/export_ranking.py | 156 +++++++++++++++--- src/mahjong_ranking/managers.py | 34 ++++ src/mahjong_ranking/models.py | 4 +- src/mahjong_ranking/sitemaps.py | 53 ++++++ 4 files changed, 226 insertions(+), 21 deletions(-) create mode 100644 src/mahjong_ranking/sitemaps.py diff --git a/src/mahjong_ranking/management/commands/export_ranking.py b/src/mahjong_ranking/management/commands/export_ranking.py index 737180b..164ced4 100644 --- a/src/mahjong_ranking/management/commands/export_ranking.py +++ b/src/mahjong_ranking/management/commands/export_ranking.py @@ -1,36 +1,148 @@ """Export Mahjong Rankings as excel files.""" +from datetime import date from operator import itemgetter +import openpyxl 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 rankings. :param json_data: The ladder ranking as JSON export.""" - workbook = Workbook() - worksheet = workbook.active + 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) + 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')) - for row in json_data: - worksheet.append([ - row['placement'], row['username'], - row['avg_placement'], row['avg_score'], - row['hanchan_count'], - row['good_hanchans'], row['won_hanchans'] - ]) - workbook.save("sample.xlsx") +def generate_sheet(workbook, title, columns_settings, json_data): + row = 1 + ws = workbook.create_sheet() + ws.title = title + + # 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): + 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): @@ -39,4 +151,8 @@ class Command(BaseCommand): def handle(self, *args, **options): """Exports the current ladder ranking in a spreadsheet. 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()))) diff --git a/src/mahjong_ranking/managers.py b/src/mahjong_ranking/managers.py index fe77795..aa0421a 100644 --- a/src/mahjong_ranking/managers.py +++ b/src/mahjong_ranking/managers.py @@ -157,3 +157,37 @@ class SeasonRankingManager(models.Manager): 'won_hanchans': user['won_hanchans'] }) 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 diff --git a/src/mahjong_ranking/models.py b/src/mahjong_ranking/models.py index eb452a6..6a7d589 100644 --- a/src/mahjong_ranking/models.py +++ b/src/mahjong_ranking/models.py @@ -346,9 +346,11 @@ class KyuDanRanking(models.Model): legacy_dan_points = models.PositiveIntegerField(default=0) legacy_kyu_points = models.PositiveIntegerField(default=0) wins_in_a_row = 0 + objects = managers.KyuDanRankingManager() + class Meta(object): - ordering = ('-dan', '-dan_points', '-kyu_points',) + ordering = ('-dan_points', 'dan', '-kyu_points') verbose_name = _(u'Kyū/Dan Ranking') verbose_name_plural = _(u'Kyū/Dan Rankings') diff --git a/src/mahjong_ranking/sitemaps.py b/src/mahjong_ranking/sitemaps.py new file mode 100644 index 0000000..ccd216d --- /dev/null +++ b/src/mahjong_ranking/sitemaps.py @@ -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'