126 Commits

Author SHA1 Message Date
06785a3e49 Code Cleanup with ruff. 2023-08-13 15:37:48 +02:00
4c00211432 Small fixes for Warnings found by PyCharm, mostly spellingerrors. 2023-08-01 15:20:11 +02:00
c62d2ee010 Small fixes for Warnings found by PyCharm, mostly spellingerrors. 2023-08-01 14:38:32 +02:00
ef00fc25f7 Fixed: ugettext has been deprecated
Fixed: Deprecatios for Django 4.0
Changed: path instead of url_path
Added: fiaxateranking command
2023-07-20 22:56:13 +02:00
fbfabb982f Update to use django 4.x as foundation, makefile needed some changes. 2023-06-11 10:21:44 +02:00
3efc4335bb Fixed: ugettext has been deprecated
Fixed: Multiple Upload for Eventpics
2023-06-11 10:10:29 +02:00
Xeniac
e3daccafb1 FIXED: Small Typo in Makefile 2021-08-12 18:26:41 +02:00
Xeniac
1f4b978fd2 FIXED: ArticleDetail expected a unique slug, but slug is set to unique for month. 2021-05-25 20:44:35 +02:00
18d7f8a926 fixed player avatar display 2020-09-22 19:40:10 +02:00
2885ef9d5c fixed get_context_data() would never pull event from an Hanchan Model. 2020-09-22 19:35:09 +02:00
3f403b3f52 Cought unexpected DoesNotExist exception in EventSeriesForm 2020-02-08 14:40:26 +01:00
0d3c47d8b3 Code cleanup using panda-bears 2020-01-16 20:24:29 +01:00
95f5e0b920 small fixes for django 2.0 2019-03-14 18:41:11 +01:00
b12203911e Moved shellscript functionality to an Makefile 2019-03-14 18:38:24 +01:00
a733130b65 Changed Middleware definition to new Django style 2019-01-31 20:36:47 +01:00
483e6f215d Fixed: Middleware process_response got not executed. 2019-01-12 16:26:13 +01:00
2bdf8c9692 Workaround: get easy-thumbnails from git for django 2.1 compatibility. 2019-01-06 11:41:08 +01:00
36e24b7da8 Update dependencies to Django2
PostgreSQL psycopg2 dependecy changed to wheel distribution.
2019-01-02 12:07:52 +01:00
79a5cdb868 removed compiled translations 2019-01-02 11:55:11 +01:00
5ed8976074 fixed add_page and edit_page URL definitions to work with "/" path. 2019-01-01 18:30:52 +01:00
12930c2ec6 fixed format of <date datetime""> output. 2018-12-30 11:22:17 +01:00
e792da7459 Don't try to show the next event if we can't find one. 2018-12-30 11:20:41 +01:00
b01dc33cb4 fixed path to changed virtenv 2018-12-30 11:20:00 +01:00
1bdce47375 Default to new and empty Page Elements instead of None.
Fixes an issue when we try to acces Page.title in the template.
2018-12-29 18:11:11 +01:00
bb7d4c8a08 fix for django-extra-views 0.12.0 Updated class EventSeriesForm to use factory_kwargs. 2018-12-19 18:06:15 +01:00
a3ac722acf * Bugfix import the _() translation in mixins. 2018-06-08 09:05:45 +02:00
1313d96e8f * fixed 404 errors in the gallery. 2018-06-07 14:18:18 +02:00
f2836baae5 * removed gender, address and phone number from the forms.
* birthday, first and last name fields will be set to None if you uncheck membership.
2018-05-16 12:40:34 +02:00
d67719cc17 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	.gitignore
#	src/membership/forms.py
#	src/membership/templates/membership/membership_form.html
#	src/membership/templates/membership/register_form.html
2018-05-16 12:03:10 +02:00
12773463a6 * removed gender, address and phone number from the forms.
* birthday, first and last name fields will be set to None if you uncheck membership.
2018-05-16 11:59:08 +02:00
ba4620705f Catch an KyuDanRanking.DoesNotExist exception if the user has never played a game of mahjongg by now. 2018-05-08 12:31:46 +02:00
40d2120d7e Merge branch 'dsgvo' 2018-05-08 12:15:12 +02:00
f7e9585932 removed unneeded import 2018-05-08 12:12:15 +02:00
99ceb54db1 User must confirm that he have read and understood the privacy plicity. This policity and the stautes are now linked in the help_text. 2018-05-08 12:09:56 +02:00
f841c1736e User must confirm that he have read and understood the privacy plicity. This policity and the stautes are now linked in the help_text. 2018-05-08 12:07:39 +02:00
436a77d036 removed social auth methods for login 2018-05-07 23:37:48 +02:00
a10b2706c3 removed <p> Tags to guarantee vaild HTML output. 2018-05-04 12:59:38 +02:00
afc471d27f Added a themed an customizeable logged out message page. 2018-05-04 12:53:23 +02:00
1d18356c8c fixed: load all toplevel pages from the db, but only show active pages in the top menu. 2018-05-04 11:57:19 +02:00
5a32dbf0af Changed membership forms, text can now be changed via admin tool. 2018-05-04 11:34:57 +02:00
65b8208987 Better age check 2018-05-03 16:35:40 +02:00
4f61b1e73b Dirty check club member must be at least 16 years old. 2018-05-03 16:23:39 +02:00
df4d8c83a6 First draft of a disclaimer (german) 2018-05-03 10:32:27 +02:00
f2533273e9 You can only fill in your personal data if you apply for a full membership. 2018-05-03 10:12:23 +02:00
595341a53b Added a admin action to cleanup personal data from inactive members. 2018-05-03 09:45:06 +02:00
af46768e7d Don't display full name anywhere. 2018-05-01 20:27:57 +02:00
10bb990539 Don't display members real name anywhere! 2018-05-01 20:27:22 +02:00
90cefd8739 Track without cookies. 2018-04-30 11:37:32 +02:00
a4c4f96c06 Personal data is only needed for a club membership. 2018-04-30 11:12:20 +02:00
19bbb5a226 All personal data can only seen by yourself. 2018-04-30 10:16:04 +02:00
3a3c8ec0c5 Merge branch 'master' into css3_redesign
# Conflicts:
#	requirements/base.txt
#	src/content/locale/de/LC_MESSAGES/django.mo
#	src/content/locale/de/LC_MESSAGES/django.po
#	src/events/locale/de/LC_MESSAGES/django.mo
#	src/events/locale/de/LC_MESSAGES/django.po
#	src/kasu/locale/de/LC_MESSAGES/django.po
#	src/mahjong_ranking/locale/de/LC_MESSAGES/django.mo
#	src/mahjong_ranking/locale/de/LC_MESSAGES/django.po
#	src/maistar_ranking/locale/de/LC_MESSAGES/django.po
#	src/membership/locale/de/LC_MESSAGES/django.mo
#	src/membership/locale/de/LC_MESSAGES/django.po
#	src/utils/locale/de/LC_MESSAGES/django.po
2018-04-30 08:29:14 +02:00
4aab611026 requirements changed to django<2.0 for compatibility reasons. 2018-04-28 11:11:12 +02:00
d5995bc612 Updated all translations 2018-04-27 10:34:37 +02:00
3ef947f128 Event gallery shows most recent albums first.
Doubled the shown events in the gallery to 24 per page.
2018-04-27 10:20:29 +02:00
28f7292c9d Fixed typo Player List (Spieler Liste) is now
Players list (Spielerliste)
2018-04-27 10:12:39 +02:00
7b631230da Added the full address to the next/upcoming event in the redbox. 2018-04-27 10:11:19 +02:00
6804319c28 adapted cleanup script to new file locations and parameters. 2018-04-27 10:10:02 +02:00
a2df81d62e Fixed: Changed the environ to ORIGINAL_RECIPIENT 2018-01-17 16:19:48 +01:00
afd163298c * Throw 404 instead of a 503 in Event Mixins if the related event does
not exist.
* Changes in the KyuDanRanking View to be more stable if we get bogus
kwargs.
2018-01-10 02:02:35 +01:00
cce6ac6014 Merge remote-tracking branch 'origin/master' into riichi_2018
# Conflicts:
#	src/events/mixins.py
#	src/kasu/settings.py
#	src/mahjong_ranking/management/commands/export_ranking.py
#	src/mahjong_ranking/management/commands/resetdanranking.py
#	src/mahjong_ranking/management/commands/update_ranking.py
#	src/mahjong_ranking/managers.py
#	src/mahjong_ranking/models.py
#	src/mahjong_ranking/views.py
2018-01-04 08:39:49 +01:00
192721452e * Kommentare wenn Dan/Kyu Punktabzüge verringert werden um nicht unter
0 zu fallen.
* Neue Middleware die REMOTE_IP aus dem X-Forward-For Header setzt.
  Damit funktioniert das Kommentarsystem nun auch hinter nginx.
2017-12-29 10:03:08 +01:00
10c27784ee XLSX Export vereinheitlicht.
Spieler Hanchanlisten können nun als XLSX exportiert werden.
Anpassungen in den Einstellungen für die parametisierten Kyu/Dan Berechnung.
2017-12-26 21:45:39 +01:00
8ab99ec039 Noch mehr Einstellungen für Kasu Ranking, um es an das neue Dan System anpassen zu können. 2017-12-22 10:54:11 +01:00
1fdf88c6d2 Diverse Umbauarbeiten für das neue Ranking. 2017-12-22 10:51:20 +01:00
b20b988e5d Pinned Django on < 2.0 for better compatibility.
Mainlined traslation code for better DRY workflow.
Fixed the EventDetail Mixin.
2017-12-07 22:54:18 +01:00
cf0e5e778c added on_delete in models an migrations for django 2.0
compatibility.
2017-12-07 22:08:47 +01:00
c7b714c41b Added a setting where the exported excel files should be stored.
Added a option to send the exported excel as mail attachment.
2017-12-07 09:40:35 +01:00
f51155cfac Fixed queryset so that EventDetailView will work too. 2017-11-24 19:48:21 +01:00
1315bc4225 Fixed Hanchan count and filter from last merge. 2017-11-24 19:47:28 +01:00
97749bfd2e Squashed commit of the following:
commit bb5081a78b
Author: Xeniac <xeniac@posteo.at>
Date:   Thu Nov 23 22:02:40 2017 +0100

    Added a setting where the exported excel files should be stored.
    Added a option to send the exported excel as mail attachment.

commit 854fd38740
Author: Xeniac <xeniac@posteo.at>
Date:   Thu Nov 23 22:01:38 2017 +0100

    Fixed: enumerate the Seasonrankings starting with 1
    Fixed: Logging error when a value changed from/to None

commit 6de1ecb102
Author: Christian Berg <xeniac@posteo.at>
Date:   Thu Nov 23 14:15:36 2017 +0100

    add a latest method to query the latest x events

commit bf12060c3b
Author: Christian Berg <xeniac@posteo.at>
Date:   Thu Nov 23 14:15:12 2017 +0100

    add a latest method to query the latest x events

commit 5ad628f33a
Author: Christian Berg <xeniac@posteo.at>
Date:   Mon Nov 20 07:47:47 2017 +0100

    Changed PlayerDanScore to only list non-legacy hanchans

commit 36272c60d6
Author: Christian Berg <xeniac@posteo.at>
Date:   Mon Nov 20 07:42:44 2017 +0100

    fixed import of MIN_HANCHANS_FOR_LADDER that moved to settings

commit c428f6ed1f
Author: Christian Berg <xeniac@posteo.at>
Date:   Mon Nov 20 07:41:04 2017 +0100

    Updated docstrings for new since and until kwargs

commit 9276e97c36
Author: Christian Berg <xeniac@posteo.at>
Date:   Mon Nov 20 07:33:54 2017 +0100

    added a since parameter to the hanchan queries to return only hanchans since the give date and time

commit fd244f10e8
Author: Christian Berg <xeniac@posteo.at>
Date:   Sun Nov 19 16:55:10 2017 +0100

    new command: resetdanranking YYYY-MM-DD, sets every dan player to 1st dan with zero dan_points at the given date.

commit 0a45cf1fd8
Author: Christian Berg <xeniac@posteo.at>
Date:   Sun Nov 19 16:14:59 2017 +0100

    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.
2017-11-23 22:26:22 +01:00
84880281c6 Added a setting where the exported excel files should be stored.
Added a option to send the exported excel as mail attachment.
2017-11-23 22:02:40 +01:00
cffbd30d7e Fixed: enumerate the Seasonrankings starting with 1
Fixed: Logging error when a value changed from/to None
2017-11-23 22:01:38 +01:00
3a611ca9da add a latest method to query the latest x events 2017-11-23 14:15:36 +01:00
68c484afc9 add a latest method to query the latest x events 2017-11-23 14:15:12 +01:00
92470514c4 Changed PlayerDanScore to only list non-legacy hanchans 2017-11-20 07:47:47 +01:00
d33e5fc8c6 fixed import of MIN_HANCHANS_FOR_LADDER that moved to settings 2017-11-20 07:42:44 +01:00
bb110da5a2 Updated docstrings for new since and until kwargs 2017-11-20 07:41:04 +01:00
35a51091bf added a since parameter to the hanchan queries to return only hanchans since the give date and time 2017-11-20 07:33:54 +01:00
c0c48f950a new command: resetdanranking YYYY-MM-DD, sets every dan player to 1st dan with zero dan_points at the given date. 2017-11-19 16:55:10 +01:00
d9e0d5596c 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.
2017-11-19 16:14:59 +01:00
a3c02ae73a FIXED: DateTimeField Hanchan.start received a naive datetime 2017-11-13 22:59:13 +01:00
e6f2528a0e Moved configuration to settings 2017-11-13 21:02:12 +01:00
638ec96c25 Code cleanup some small changes in the formatting. 2017-11-07 06:52:55 +01:00
34d327f183 Code cleanup some small changes in the formatting. 2017-11-07 06:48:14 +01:00
002eb40ea5 Fixed: SplitDateTime Widgets always complains about the date/time format. 2017-11-05 21:58:12 +01:00
d97c9db539 Fixed Typer Error "can't compare datetime.datetime to datetime.date".
Add a datetime to the hanchan queue instead of an date.
2017-11-05 10:22:39 +01:00
7ef69849a4 Sitemaps for page that should be public viewable 2017-11-03 07:16:47 +01:00
8719c2377a export_ranking now exports KyuDanRankings and SeasonRankings. 2017-11-03 07:15:28 +01:00
abeb86d48f recalculating only the new hanchans shoud do the trick now.
the racalc cronjob reports erronous partly recalculations now.
A lot of code cleanups
2017-11-01 09:53:52 +01:00
42a6ebedf9 TODO ergänzt mit meldungen von coala.io
scripte nach bin/ verschoben
2017-09-08 07:51:07 +02:00
b3ab9798b5 Another Step in the Quest to clean up the code base. 2017-09-08 07:19:50 +02:00
ce218080b2 Removed manual references on the CKEditor, they will be injected by
django-ckeditor
2017-06-26 17:12:57 +02:00
63e099b7c5 Coala .coafile hinzugefügt für einfache überwachung der Codestandards. 2017-06-09 17:19:44 +02:00
a26a91c360 Eine Menge Aufräumarbeiten.
* Eine Testsuite um Mahrjong Ranking Berechnungen zu testen
* Erste Arbeiten um die Workarounds aus dem "utils" Paket los zu werden.
* Vieles am Code umformatiert für PEP8 conformität
2017-06-07 13:25:30 +02:00
cf0bbb4c8f Abhängikeiten aktualisiert.
Social Auth Module haben den Eigentümer verändert,die Paketnamen wurden angepasst
Abhängigkeiten für Python 3 kompatible Module aktualisiert
Versionseinschränkungen für Django Update entfernt.
2017-05-10 10:21:38 +02:00
321531c160 Anpassungen des Codes an Django 1.11 mit Python 3
Grapelli wurde entfernt, das neue Django Admin ist hübsch genug.
2017-05-10 10:15:39 +02:00
5437b7b8de Neues Kommando: update-ranking
Aktualisiert alle Kyu-/Dan Rankings mit einem einfachen Befehl in der Shell.

random-ranking wurde gelöscht, es wird nicht mehr verwendet.
2017-05-10 10:13:23 +02:00
10bdaaa98c Adapted Code for Django 1.11 and Python 3,
updated the CKEditor
2017-05-10 10:10:11 +02:00
9b4ab374c6 Auswahl wird auf aktivierte Accounts mit aktiver Mitgliedschaft beschränkt. 2017-03-13 09:33:27 +01:00
ae87414584 Migration auf dne neuen Social-Auth Code. 2017-02-18 19:59:27 +01:00
120b4ea17f requirements in des Repository aufgenommen, auch diese sollter versioniert sein. 2017-02-18 19:41:27 +01:00
fcb15c7e7e * Aktelle Vorlagen in den src Ordner verschoben, damit sie in der Versionierung enthalten sind.
* INSTALLED_APPS auf PROJECT_APPS + PREREQ_APPS damit Vorlagen aus den kasu/templates Ordner bevorzugt werden. So lassen sich Vorlagen von 3rd Party Apps überschreiben, wie z.b. django-contrib-comments.

* Javascript Code für Google Maps entfernt, dies funktioniert mit Content Security Policy nicht mehr so gut und wird eh nicht gebraucht.

* Javascript für das Hanchan Formular vom header an des Ende des body gesetzt, der Code darf erst ausgeführt werden, wenn das Formular aufgebaut wurde.
2017-02-18 16:15:35 +01:00
299418ee62 Abhängikieten aktualisiert python-social-auth 0.3 und aufwärts haben fehlerhafte Abhängikeiten 2017-01-12 20:14:15 +01:00
e5f0d7f6fe renamed club.jpg to about.jpg, that's the new slug for the about page on kasu.at 2016-10-14 21:35:44 +02:00
53974dcd48 Add /rankings/ to the disallow list.
It's content that don't belong to an searchindex.
2016-10-14 21:34:39 +02:00
222dd060b1 Added Datefields for creation and last modied metadata. It's currently used for the sitemap.xml. 2016-10-14 21:27:58 +02:00
bbe16b2d13 added localized deschription fields osed for the meta description tag on the page for SEO. 2016-10-14 21:25:23 +02:00
ba44e97e9a Django Sitemap Framework integriert 2016-10-11 23:54:18 +02:00
8595959872 Added "Host: Directive" for Yandex. This line is useless, but prevents their "webmaster central" to send me unneccesary "warnings". "Host: kasu.at" literaly says that this site is available under kasu.at, not www.kasu.at 2016-09-17 10:39:01 +02:00
9b0967adc8 Some changes that the settings to reduce the logic in the local_settings.py 2016-09-16 19:37:37 +02:00
ac78dc3d75 Helptext now appears beside a Checkbox/Circle, not as a prargraph below anymore. 2016-09-16 19:36:13 +02:00
57b7d5a84b Added database migrations for the previous changes. 2016-09-16 19:34:24 +02:00
6b37aa84b0 Updated german translation 2016-09-16 19:30:09 +02:00
Christian Berg
0c7bd466c7 Kleinigkeiten und Tippfehler in den Templates für die Registrierung geändert. 2016-09-16 19:17:39 +02:00
0bdd409dc2 User Registration now uses all possibilities of the Membership Model. So you could apply for a membership with one step.
Fixed an error in the cleanup-registration command.
Also more stuff has been translated
2016-09-16 18:57:28 +02:00
Christian Berg
9c4ec20394 adding GPLv3 License to the Project 2016-09-16 18:01:58 +02:00
Christian Berg
6f1512906d Änderungen am layout wie die config Dateien abgelegt werden, um passwörter leichter aus dem öffentlichen GIT repository verschwinden zu lassen 2016-09-14 00:57:57 +02:00
Christian Berg
12c9a6e0e9 Aktuelle Fassung der Kasu.at Homepage. Kleine kosmetische Optimierungen. 2016-09-13 23:11:00 +02:00
Christian Berg
8cac20dd2b FIXED: 9th Dan is the highest possible Dan, so you can't advanced higher with 3 wins in an row. 2016-04-09 23:58:52 +02:00
Christian Berg
b1586efbab Fehler bei Vergabe von Bonuspunkte korrigiert.
Kommentare für Bonuspunkte werden jetzt als Kommentar beim Spieler hinterlassen, nicht als Kommentar in der Hanchan.
FIXED: 3_in_a_row counter wurde nicht zurückgesetzt wenn Bonuspunkte vergeben wurden.
FIXED: Durchschnittliche Platzierung während eines Events wurde nur als Ganzzahl berechnet. Wird nun als Fießkomma berechnet und gesichert.
2016-01-09 22:55:26 +01:00
Christian Berg
088efe2f39 Abhänigikeiten richtig gestellt.
Django benötigt flup. Kasu verwendet jetzt PostgreSQL, MySQL Abhängigkeit entfernt.
2016-01-09 22:48:48 +01:00
Christian Berg
b9ec418e5e 2 Fehler behoben:
1.  Internet Explorer unterstützung für <main> Tag ist fehlerhaft, wurde per CSS Definituon behoben.
2.  Templates für registrierung versehntlich gelöscht, wurden im membership Modul wieder hergestellt.
2015-08-23 21:04:20 +02:00
Christian Berg
836eee983d Milestone 08-14
* Mahjong Ranking wurde stark vereinfacht um Fehler besser vorzubeugen.
* Online WYSIWYG Editor auf CKEditor umgeändert, damit online bearbeiten für unbedarfte besser funktioniert.
* Viele kleine Optimierungen am CSS für bessere Performance.
* CSS wird jetzt aus LESS Code generiert
* Für dise Arbeit wird jetzt grunt und node package management lokal verwendet.
2015-08-23 16:37:39 +02:00
Christian Berg
79eaeb34ad Fehlerbereinigung.
Mahjong Ranking berechnet nun die richtigen Ränge zu den legendären Kyu/Dan Punkten.

Stablie Version bevor die Datenstruktur des Mahjongrankings vereinfacht wird.
2015-08-18 20:25:37 +02:00
Christian Berg
bafbf38612 Vereinfachung Membership ist nun ein custom login model, keine Abhängikeiten mehr zu auth.User.
absofort können alle Benutzer ins Ranking eingetragen werden und nicht mehr nur "zahlende Mitglieder".
2015-08-10 20:49:07 +02:00
Christian Berg
b96b485b61 Anpassungen für das Hosting bei Djangoeurope und damit verbundenen Versionen Django 1.8 und Python 2.7 2015-08-05 18:55:53 +02:00
135 changed files with 2730 additions and 3208 deletions

View File

@@ -5,4 +5,4 @@ language = python
bears = PEP8Bear,PyLintBear,PyUnusedCodeBear bears = PEP8Bear,PyLintBear,PyUnusedCodeBear
use_spaces = True use_spaces = True
pylint_cli_options = --load-plugins pylint_django pylint_cli_options = --load-plugins pylint_django
pylint_disable = E1101,R0201,R0901,R0903 #pylint_disable = E1101,R0201,R0901,R0903

1
.gitignore vendored
View File

@@ -71,3 +71,4 @@ backup/
.[a-zA-Z]* .[a-zA-Z]*
local_settings.py local_settings.py
sample.xlsx sample.xlsx
venv/

80
Makefile Normal file
View File

@@ -0,0 +1,80 @@
ASSETS=requirements static
DEV_REQUIREMENTS=requirements/development.txt
DJANGO_SETTINGS_MODULE="kasu.settings"
EXCLUDE_FILES=*.pyc
PROJECT_PATH=$(CURDIR)
PYTHON=${VENV_PATH}/bin/python3
REQUIREMENTS=requirements/base.txt
SRC_PATH=${PROJECT_PATH}/src
MANAGE_PY=${PYTHON} -Wa ${SRC_PATH}/manage.py
SSH_LOGIN=kasu@kasu.at
VENV_PATH=$(PROJECT_PATH)/venv
.PHONY: clean venv
all: cleanup migrate testserver
venv: $(VENV_PATH)/bin/activate
$(VENV_PATH)/bin/activate:
@test -d $(VENV_PATH) || python3 -m venv --clear --system-site-packages $(VENV_PATH)
dev-requirements: venv ${DEV_REQUIREMENTS}
${PYTHON} -m pip install -qU pip
${PYTHON} -m pip install -qUr ${DEV_REQUIREMENTS}
requirements: venv ${REQUIREMENTS}
${PYTHON} -m pip install -qU pip
${PYTHON} -m pip install -qUr ${REQUIREMENTS}
grunt:
-grunt
requirements_remote:
@echo "Installing requirements"
ssh ${SSH_LOGIN} "virtualenv/bin/pip install -qUr ${REQUIREMENTS}"
sync_assets:
@echo "Syncing project assets ..."
rsync -qr --copy-links --delete ${ASSETS} ${SSH_LOGIN}:~/
sync_src:
@echo "Syncing Sourcecode ..."
find . -name EXCLUDE_FILES -exec rm -rf {} \;
rsync -qr --copy-links --delete ${SRC_PATH} ${SSH_LOGIN}:~/ --exclude 'src/kasu/local_settings.py'
restart_remote:
@echo "Rebuild and reload django..."
ssh ${SSH_LOGIN} "~/virtualenv/bin/python ~/src/manage.py collectstatic -l --noinput -v0"
ssh ${SSH_LOGIN} "~/init/kasu restart"
sync: sync_assets requirements_remote sync_src restart_remote
testserver: venv
${MANAGE_PY} runserver 127.0.0.1:8000
messages: venv
@echo "aktualisiere Übersetzungen..."
@(for d in ${SRC_PATH}/*; do \
test -d $$d/locale && cd $$d && ${VENV_PATH}/bin/django-admin makemessages -l de;\
done)
${MANAGE_PY} compilemessages -v0
cleanup-pyc:
@echo "lösche den Python Compiler Cache..."
find ${SRC_PATH} -name ${EXCLUDE_FILES} -exec rm -rf {} \;
collectstatic: venv
@echo "aktualisiere Statics"
${MANAGE_PY} collectstatic --noinput -c -v0
thumbnails: venv
@echo "Erstelle Vorschaubilder"
${MANAGE_PY} thumbnail_cleanup
cleanup: requirements messages cleanup-pyc collectstatic thumbnails
migrate: venv
${MANAGE_PY} migrate
migrations: venv
${MANAGE_PY} makemigrations

1072
TODO

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +0,0 @@
#!/bin/bash
source .virtualenv/bin/activate
echo "aktualisiere Übersetzungen..."
cd src
unset DJANGO_SETTINGS_MODULE
for dir in *
do
if [ -d ${dir}/locale ]
then
echo -n "$dir: "
cd ${dir}
django-admin.py makemessages -l de
cd ..
fi
done
sleep 5s
export DJANGO_SETTINGS_MODULE="kasu.settings"
./manage.py compilemessages
echo "lösche den Python Compiler Cache..."
find . -name "*.pyc" -exec rm -rf {} \;
echo "Aktualisiere Statics"
./manage.py collectstatic --noinput -c
echo "Erstelle Vorschaubilder"
./manage.py thumbnail_cleanup
touch kasu/wsgi.py

View File

@@ -1,23 +0,0 @@
#!/bin/bash
SSH_LOGIN="kasu@s21.wservices.ch"
SYNC_ASSESTS="requirements static"
SYNC_SOURCECODE="src"
EXCLUDE_FILES="*.pyc"
grunt
echo "Syncing project assets ..."
rsync -r --copy-links --delete ${SYNC_ASSESTS} ${SSH_LOGIN}:~/
echo "Installing dependecies"
ssh ${SSH_LOGIN} "virtualenv/bin/pip install --upgrade -r requirements/base.txt"
echo "Syncing Sourcecode ..."
find . -name "*.pyc" -exec rm -rf {} \;
rsync -r --copy-links --delete ${SYNC_SOURCECODE} ${SSH_LOGIN}:~/ --exclude 'src/kasu/local_settings.py'
echo "Rebuild and reload django..."
ssh ${SSH_LOGIN} "rm src/kasu/settings/development.*"
ssh ${SSH_LOGIN} "~/virtualenv/bin/python ~/src/manage.py collectstatic -l --noinput -v1"
ssh ${SSH_LOGIN} "~/init/kasu restart"

View File

@@ -1,6 +0,0 @@
#!/bin/sh
export DJANGO_SETTINGS_MODULE=kasu.settings
source .virtualenv/bin/activate
./manage.py runserver 0.0.0.0:8000

View File

@@ -1,5 +1,5 @@
beautifulsoup4 beautifulsoup4
django < 2.0 django < 5.0
django-appconf django-appconf
django-ckeditor django-ckeditor
django-contrib-comments django-contrib-comments
@@ -8,11 +8,12 @@ django-compressor
django-extra-views django-extra-views
django-markdown django-markdown
django-recaptcha django-recaptcha
easy-thumbnails easy_thumbnails[svg]
icalendar icalendar
openpyxl
markdown markdown
pillow pillow
psycopg2 psycopg2-binary
PyJWT PyJWT
pytz pytz
requests requests

View File

@@ -4,5 +4,5 @@ django-rosetta
sqlparse sqlparse
# Code Linting # Code Linting
coala-bears pylint>=2.0
pylint-django pylint-django

View File

@@ -5,6 +5,7 @@ from django.core.cache import cache
from . import models from . import models
from utils import STATUS_PUBLISHED from utils import STATUS_PUBLISHED
def content_menus(request): def content_menus(request):
""" Generate the menu tree and add these info to the template context. """ Generate the menu tree and add these info to the template context.
@@ -18,9 +19,9 @@ def content_menus(request):
:param request: a Django request object :param request: a Django request object
:return: a dict with the template variables mentioned above :return: a dict with the template variables mentioned above
""" """
current_page = None current_page: models.Page = models.Page.objects.get(slug='index')
current_top_page = None current_top_page: models.Page = models.Page.objects.get(slug='index')
current_path = request.path_info[1:request.path_info.rfind('.')] current_path: str = request.path_info[1:request.path_info.rfind('.')]
# erzeuge das Top-Level Menü # erzeuge das Top-Level Menü
top_level_pages = cache.get('top_level_pages') top_level_pages = cache.get('top_level_pages')

View File

@@ -3,7 +3,7 @@ import django_comments as comments
from django.conf import settings from django.conf import settings
from django.contrib.syndication.views import Feed from django.contrib.syndication.views import Feed
from django.utils.feedgenerator import Rss201rev2Feed from django.utils.feedgenerator import Rss201rev2Feed
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
from content.models import Article from content.models import Article
@@ -11,15 +11,15 @@ MAX_ARTICLE_ITEMS = 10 # Maximum count of articles in the news RSS feed.
MAX_COMMENT_ITEMS = 40 # Maximum count of comments in the comments RSS feed. MAX_COMMENT_ITEMS = 40 # Maximum count of comments in the comments RSS feed.
# Start ignoring PyLintBear (R0201) # noinspection PyMethodMayBeStatic
class LatestNews(Feed): class LatestNews(Feed):
""" An Feed with the latest news from this site """ """ An Feed with the latest news from this site """
link = "http://www.kasu.at/"
description = _("Current news from Kasu") description = _("Current news from Kasu")
title = "Kasu - traditonelle asiatische Spielkultur" link = "http://www.kasu.at/"
title = _("Current news from Kasu")
feed_type = Rss201rev2Feed feed_type = Rss201rev2Feed
def items(self): def items(self) -> object:
"""get the newest published news articles for the feed.""" """get the newest published news articles for the feed."""
return Article.objects.published()[:MAX_ARTICLE_ITEMS] return Article.objects.published()[:MAX_ARTICLE_ITEMS]
@@ -78,5 +78,3 @@ class LatestComments(Feed):
'user_name': item.user_name, 'user_name': item.user_name,
'content_object': item.content_object 'content_object': item.content_object
} }
# Stop ignoring

View File

@@ -5,7 +5,7 @@ Created on 04.10.2011
""" """
from django import forms from django import forms
from django.template.defaultfilters import slugify from django.template.defaultfilters import slugify
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
from . import models from . import models

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: kasu.content\n" "Project-Id-Version: kasu.content\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-08 00:17+0200\n" "POT-Creation-Date: 2023-08-07 20:38+0200\n"
"PO-Revision-Date: 2018-01-12 15:25+0105\n" "PO-Revision-Date: 2018-01-12 15:25+0105\n"
"Last-Translator: b'Christian Berg <kasu@xendynastie.at>'\n" "Last-Translator: b'Christian Berg <kasu@xendynastie.at>'\n"
"Language-Team: Deutsch <>\n" "Language-Team: Deutsch <>\n"
@@ -20,252 +20,247 @@ msgstr ""
"X-Generator: Poedit 1.8.9\n" "X-Generator: Poedit 1.8.9\n"
"X-Translated-Using: django-rosetta 0.7.14\n" "X-Translated-Using: django-rosetta 0.7.14\n"
#: .\content\feeds.py:18 #: feeds.py:17 feeds.py:19
msgid "Current news from Kasu" msgid "Current news from Kasu"
msgstr "Aktuelle Nachrichten von Kasu" msgstr "Aktuelle Nachrichten von Kasu"
#: .\content\feeds.py:51 #: feeds.py:51
msgid "Latest comments on kasu.at" msgid "Latest comments on kasu.at"
msgstr "Neueste Kommentare auf Kasu.at " msgstr "Neueste Kommentare auf Kasu.at "
#: .\content\feeds.py:52 #: feeds.py:52
msgid "Kasu - latest comments" msgid "Kasu - latest comments"
msgstr "Kasu - neue Kommentare" msgstr "Kasu - neue Kommentare"
#: .\content\forms.py:57 .\content\models.py:318 #: forms.py:57 models.py:320
msgid "Please upload a PDF-File to this PDF-Page." msgid "Please upload a PDF-File to this PDF-Page."
msgstr "Bitte eine PDF Datei für diese PDF Seite hochladen." msgstr "Bitte eine PDF Datei für diese PDF Seite hochladen."
#: .\content\models.py:76 #: models.py:76
msgid "Headline" msgid "Headline"
msgstr "Schlagzeile" msgstr "Schlagzeile"
#: .\content\models.py:78 #: models.py:78
msgid "Content" msgid "Content"
msgstr "Inhalt" msgstr "Inhalt"
#: .\content\models.py:82 .\content\models.py:150 #: models.py:82 models.py:151 templates/content/article_detail.html:28
#: .\content\templates\content\article_detail.html:25
msgid "Category" msgid "Category"
msgstr "Kategorie" msgstr "Kategorie"
#: .\content\models.py:83 .\content\models.py:143 #: models.py:83 models.py:144
msgid "Image" msgid "Image"
msgstr "Bild" msgstr "Bild"
#: .\content\models.py:85 .\content\models.py:145 #: models.py:85 models.py:146
msgid "Slug" msgid "Slug"
msgstr "Slug" msgstr "Slug"
#: .\content\models.py:88 .\content\templates\content\article_detail.html:23 #: models.py:88 templates/content/article_detail.html:21
msgid "Author" msgid "Author"
msgstr "Autor" msgstr "Autor"
#: .\content\models.py:89 #: models.py:89
msgid "Status" msgid "Status"
msgstr "Status" msgstr "Status"
#: .\content\models.py:91 #: models.py:91
msgid "Created" msgid "Created"
msgstr "Erstellt" msgstr "Erstellt"
#: .\content\models.py:92 #: models.py:92
msgid "Modified" msgid "Modified"
msgstr "Bearbeitet" msgstr "Bearbeitet"
#: .\content\models.py:97 #: models.py:97
msgid "Article" msgid "Article"
msgstr "Artikel" msgstr "Artikel"
#: .\content\models.py:98 #: models.py:98
msgid "Articles" msgid "Articles"
msgstr "Artikel" msgstr "Artikel"
#: .\content\models.py:139 .\content\models.py:140 #: models.py:140 models.py:141
msgid "Name" msgid "Name"
msgstr "Name" msgstr "Name"
#: .\content\models.py:141 .\content\models.py:142 #: models.py:142 models.py:143
msgid "Description" msgid "Description"
msgstr "Beschreibung" msgstr "Beschreibung"
#: .\content\models.py:151 #: models.py:152
msgid "Categories" msgid "Categories"
msgstr "Kategorien" msgstr "Kategorien"
#: .\content\models.py:182 .\content\models.py:188 #: models.py:184 models.py:190
msgid "The short name for the menu-entry of this page" msgid "The short name for the menu-entry of this page"
msgstr "Ein kurzer Name für den Menüeintrag" msgstr "Ein kurzer Name für den Menüeintrag"
#: .\content\models.py:193 .\content\models.py:198 #: models.py:195 models.py:200
msgid "The page title as you'd like it to be seen by the public" msgid "The page title as you'd like it to be seen by the public"
msgstr "Der Seitentitel der öffentlich angezeigt werden soll" msgstr "Der Seitentitel der öffentlich angezeigt werden soll"
#: .\content\models.py:200 #: models.py:202
msgid "slug" msgid "slug"
msgstr "Slug" msgstr "Slug"
#: .\content\models.py:203 #: models.py:205
msgid "" msgid ""
"The name of the page as it will appear in URLs e.g http://domain.com/blog/" "The name of the page as it will appear in URLs e.g http://domain.com/blog/"
"[my-slug]/" "[my-slug]/"
msgstr "Wie die Seite in der URL aufscheint also http://domain.com/blog/[slug]" msgstr "Wie die Seite in der URL aufscheint also http://domain.com/blog/[slug]"
#: .\content\models.py:212 #: models.py:214
msgid "Path" msgid "Path"
msgstr "Pfad" msgstr "Pfad"
#: .\content\models.py:224 #: models.py:226
msgid "Position" msgid "Position"
msgstr "Position" msgstr "Position"
#: .\content\models.py:229 #: models.py:231
msgid "status" msgid "status"
msgstr "Status" msgstr "Status"
#: .\content\models.py:232 .\content\models.py:234 #: models.py:234 models.py:236
msgid "search description" msgid "search description"
msgstr "Beschreibung für Suchfunktion" msgstr "Beschreibung für Suchfunktion"
#: .\content\models.py:237 #: models.py:239
msgid "content type" msgid "content type"
msgstr "Inhaltstyp" msgstr "Inhaltstyp"
#: .\content\models.py:242 #: models.py:244
msgid "enable comments" msgid "enable comments"
msgstr "Kommentare möglich" msgstr "Kommentare möglich"
#: .\content\models.py:247 #: models.py:249
msgid "Template" msgid "Template"
msgstr "Vorlage" msgstr "Vorlage"
#: .\content\models.py:255 #: models.py:257
msgid "first created at" msgid "first created at"
msgstr "erstellt am" msgstr "erstellt am"
#: .\content\models.py:260 #: models.py:262
msgid "latest updated at" msgid "latest updated at"
msgstr "letzte Aktualisierung am" msgstr "letzte Aktualisierung am"
#: .\content\models.py:331 #: models.py:333
msgid "Page" msgid "Page"
msgstr "Seite" msgstr "Seite"
#: .\content\models.py:332 #: models.py:334
msgid "Pages" msgid "Pages"
msgstr "Seiten" msgstr "Seiten"
#: .\content\templates\content\article_archive.html:5 #: templates/content/article_archive.html:5
#: .\content\templates\content\article_archive.html:20 #: templates/content/article_archive.html:20
msgid "Article Archive" msgid "Article Archive"
msgstr "Nachrichtenarchiv" msgstr "Nachrichtenarchiv"
#: .\content\templates\content\article_archive.html:35 #: templates/content/article_archive.html:35
#: .\content\templates\content\article_archive_month.html:5 #: templates/content/article_archive_month.html:5
#: .\content\templates\content\article_archive_year.html:7 #: templates/content/article_archive_year.html:7
msgid "Archive" msgid "Archive"
msgstr "Archiv" msgstr "Archiv"
#: .\content\templates\content\article_archive.html:56 #: templates/content/article_archive.html:56
msgid "All Categories" msgid "All Categories"
msgstr "Alle Kategorien" msgstr "Alle Kategorien"
#: .\content\templates\content\article_archive.html:71 #: templates/content/article_archive.html:71
msgid "created on" msgid "created on"
msgstr "erstellt am" msgstr "erstellt am"
#: .\content\templates\content\article_archive.html:73 #: templates/content/article_archive.html:73
msgid "by" msgid "by"
msgstr "von" msgstr "von"
#: .\content\templates\content\article_archive.html:74 #: templates/content/article_archive.html:74
#: .\content\templates\content\article_archive.html:75 #: templates/content/article_archive.html:75
msgid "comments" msgid "comments"
msgstr "Kommentare" msgstr "Kommentare"
#: .\content\templates\content\article_archive.html:81 #: templates/content/article_archive.html:81
msgid "Read More" msgid "Read More"
msgstr "Mehr lesen" msgstr "Mehr lesen"
#: .\content\templates\content\article_archive.html:86 #: templates/content/article_archive.html:86
msgid "We're sorry. Your search yielded no results." msgid "We're sorry. Your search yielded no results."
msgstr "Es tut uns leid. Deine Suche ergab keine Treffer." msgstr "Es tut uns leid. Deine Suche ergab keine Treffer."
#: .\content\templates\content\article_archive.html:104 #: templates/content/article_archive.html:104
msgid "Add Article" msgid "Add Article"
msgstr "neuer Artikel " msgstr "neuer Artikel "
#: .\content\templates\content\article_archive_month.html:7 #: templates/content/article_archive_month.html:7
msgid "back" msgid "back"
msgstr "Zurück" msgstr "Zurück"
#: .\content\templates\content\article_detail.html:24 #: templates/content/article_detail.html:25
msgid "Created on" msgid "Created on"
msgstr "Erstellt am" msgstr "Erstellt am"
#: .\content\templates\content\article_detail.html:36 #: templates/content/article_detail.html:39
msgid "share on" msgid "share on"
msgstr "Teile auf" msgstr "Teile auf"
#: .\content\templates\content\article_detail.html:51 .\content\views.py:156 #: templates/content/article_detail.html:48 views.py:160
msgid "Edit Article" msgid "Edit Article"
msgstr "Artikel bearbeiten" msgstr "Artikel bearbeiten"
#: .\content\templates\content\article_form.html:32 #: templates/content/article_form.html:24
#: .\content\templates\content\page_form.html:42 #: templates/content/article_form.html:31 templates/content/page_form.html:42
#: .\content\templates\content\page_form.html:49 #: templates/content/page_form.html:49
msgid "German" msgid "German"
msgstr "Deutsch" msgstr "Deutsch"
#: .\content\templates\content\article_form.html:33 #: templates/content/article_form.html:25
#: .\content\templates\content\page_form.html:43 #: templates/content/article_form.html:39 templates/content/page_form.html:43
#: .\content\templates\content\page_form.html:57 #: templates/content/page_form.html:57
msgid "English" msgid "English"
msgstr "Englisch" msgstr "Englisch"
#: .\content\templates\content\article_form.html:59 #: templates/content/article_form.html:47 templates/content/page_form.html:66
#: .\content\templates\content\page_form.html:66
msgid "reset" msgid "reset"
msgstr "Zurücksetzen" msgstr "Zurücksetzen"
#: .\content\templates\content\article_form.html:60 #: templates/content/article_form.html:48 templates/content/page_form.html:67
#: .\content\templates\content\page_form.html:67
msgid "save" msgid "save"
msgstr "Speichern" msgstr "Speichern"
#: .\content\templates\content\page_form.html:5 #: templates/content/page_form.html:5 templates/content/page_form.html:35
#: .\content\templates\content\page_form.html:35
msgid "Edit Page" msgid "Edit Page"
msgstr "Seite bearbeiten" msgstr "Seite bearbeiten"
#: .\content\templates\content\page_form.html:5 #: templates/content/page_form.html:5 templates/content/page_form.html:19
#: .\content\templates\content\page_form.html:19 #: templates/content/page_form.html:35
#: .\content\templates\content\page_form.html:35
msgid "Add Page" msgid "Add Page"
msgstr "Seite hinzufügen" msgstr "Seite hinzufügen"
#: .\content\templates\content\page_form.html:18 #: templates/content/page_form.html:18
msgid "Edit" msgid "Edit"
msgstr "Bearbeiten" msgstr "Bearbeiten"
#: .\content\templates\content\page_form.html:37 #: templates/content/page_form.html:37
msgid "HTML Specific" msgid "HTML Specific"
msgstr "HTML spezifisch" msgstr "HTML spezifisch"
#: .\content\views.py:53 #: views.py:53
msgid "This Category does not exist." msgid "This Category does not exist."
msgstr "Diese Kategorie existiert nicht." msgstr "Diese Kategorie existiert nicht."
#: .\content\views.py:157 #: views.py:161
msgid "Create Article" msgid "Create Article"
msgstr "Artikel erstellen" msgstr "Artikel erstellen"
#: .\content\views.py:233 #: views.py:241
#, python-format #, python-format
msgid "No Page found matching the Path %s" msgid "No Page found matching the Path %s"
msgstr "Keine Seite unter dem Pfad %s gefunden" msgstr "Keine Seite unter dem Pfad %s gefunden"
#: .\content\views.py:262 #: views.py:270
#, python-format #, python-format
msgid "No PDF Document found matching the Path %s" msgid "No PDF Document found matching the Path %s"
msgstr "Kein PDF Dokument unter dem Pfad %s gefunden." msgstr "Kein PDF Dokument unter dem Pfad %s gefunden."

View File

@@ -2,7 +2,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import migrations from django.db import migrations
from django.db import models
import ckeditor.fields import ckeditor.fields

View File

@@ -7,7 +7,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('content', '0005_auto_20161012_2236'), ('content', '0005_auto_20161012_2236'),
] ]
@@ -16,52 +15,68 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='article', model_name='article',
name='content_de', name='content_de',
field=ckeditor_uploader.fields.RichTextUploadingField(verbose_name='Inhalt'), field=ckeditor_uploader.fields.RichTextUploadingField(
verbose_name='Inhalt'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='article', model_name='article',
name='content_en', name='content_en',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, verbose_name='Content'), field=ckeditor_uploader.fields.RichTextUploadingField(
blank=True, verbose_name='Content'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='article', model_name='article',
name='headline_en', name='headline_en',
field=models.CharField(blank=True, max_length=255, verbose_name='Headline'), field=models.CharField(
blank=True, max_length=255, verbose_name='Headline'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='article', model_name='article',
name='image', name='image',
field=models.ImageField(blank=True, null=True, upload_to='news/', verbose_name='Bild'), field=models.ImageField(blank=True, null=True, upload_to='news/',
verbose_name='Bild'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='article', model_name='article',
name='slug', name='slug',
field=models.SlugField(unique_for_month='date_created', verbose_name='Slug'), field=models.SlugField(unique_for_month='date_created',
verbose_name='Slug'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='category', model_name='category',
name='image', name='image',
field=models.ImageField(blank=True, null=True, upload_to='news/categories/', verbose_name='Bild'), field=models.ImageField(blank=True, null=True,
upload_to='news/categories/',
verbose_name='Bild'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='page', model_name='page',
name='content_de', name='content_de',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, verbose_name='Inhalt'), field=ckeditor_uploader.fields.RichTextUploadingField(
blank=True,
verbose_name='Inhalt'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='page', model_name='page',
name='content_en', name='content_en',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, verbose_name='Content'), field=ckeditor_uploader.fields.RichTextUploadingField(
blank=True,
verbose_name='Content'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='page', model_name='page',
name='menu_name_de', name='menu_name_de',
field=models.CharField(help_text='Ein kurzer Name für den Menüeintrag', max_length=255, verbose_name='Menü Name'), field=models.CharField(
help_text='Ein kurzer Name für den Menüeintrag',
max_length=255, verbose_name='Menü Name'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='page', model_name='page',
name='menu_name_en', name='menu_name_en',
field=models.CharField(blank=True, help_text='Ein kurzer Name für den Menüeintrag', max_length=255, verbose_name='Menu Name'), field=models.CharField(
blank=True,
help_text='Ein kurzer Name für den Menüeintrag',
max_length=255, verbose_name='Menu Name'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='page', model_name='page',
@@ -76,16 +91,22 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='page', model_name='page',
name='template', name='template',
field=models.CharField(default='content/page.html', max_length=255, verbose_name='Vorlage'), field=models.CharField(default='content/page.html', max_length=255,
verbose_name='Vorlage'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='page', model_name='page',
name='title_de', name='title_de',
field=models.CharField(help_text="The page title as you'd like it to be seen by the public", max_length=255, verbose_name='Titel'), field=models.CharField(
help_text="The page title as you'd like it to be seen by the public",
max_length=255, verbose_name='Titel'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='page', model_name='page',
name='title_en', name='title_en',
field=models.CharField(blank=True, help_text="The page title as you'd like it to be seen by the public", max_length=255, verbose_name='Title'), field=models.CharField(
blank=True,
help_text="The page title as you'd like it to be seen by the public",
max_length=255, verbose_name='Title'),
), ),
] ]

View File

@@ -8,7 +8,6 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('content', '0006_auto_20171115_0653'), ('content', '0006_auto_20171115_0653'),
] ]
@@ -17,11 +16,15 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='article', model_name='article',
name='author', name='author',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='Autor'), field=models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
to=settings.AUTH_USER_MODEL, verbose_name='Autor'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='article', model_name='article',
name='category', name='category',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='content.Category', verbose_name='Kategorie'), field=models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
to='content.Category', verbose_name='Kategorie'),
), ),
] ]

View File

@@ -0,0 +1,74 @@
# Generated by Django 2.1.5 on 2019-01-06 18:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('content', '0007_auto_20171214_1215'),
]
operations = [
migrations.AlterField(
model_name='article',
name='status',
field=models.SmallIntegerField(
choices=[(-1, 'Rejected'), (0, 'Waiting...'), (1, 'Published')],
default=1, verbose_name='Status'),
),
migrations.AlterField(
model_name='page',
name='date_created',
field=models.DateTimeField(
auto_now_add=True, db_index=True,
verbose_name='erstellt am'),
),
migrations.AlterField(
model_name='page',
name='date_modified',
field=models.DateTimeField(auto_now=True,
verbose_name='letzte Aktualisierung am'),
),
migrations.AlterField(
model_name='page',
name='description_de',
field=models.TextField(blank=True,
verbose_name='Beschreibung für Suchfunktion'),
),
migrations.AlterField(
model_name='page',
name='description_en',
field=models.TextField(blank=True,
verbose_name='Beschreibung für Suchfunktion'),
),
migrations.AlterField(
model_name='page',
name='slug',
field=models.SlugField(
help_text='Wie die Seite in der URL aufscheint also ' +
'http://domain.com/blog/[slug]',
max_length=100, verbose_name='Slug'),
),
migrations.AlterField(
model_name='page',
name='status',
field=models.SmallIntegerField(
choices=[(-1, 'Rejected'), (0, 'Waiting...'), (1, 'Published')],
default=0, verbose_name='Status'),
),
migrations.AlterField(
model_name='page',
name='title_de',
field=models.CharField(
help_text='Der Seitentitel der öffentlich angezeigt werden soll',
max_length=255, verbose_name='Titel'),
),
migrations.AlterField(
model_name='page',
name='title_en',
field=models.CharField(
blank=True,
help_text='Der Seitentitel der öffentlich angezeigt werden soll',
max_length=255, verbose_name='Title'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-11 09:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('content', '0008_auto_20190106_1954'),
]
operations = [
migrations.AlterField(
model_name='page',
name='id',
field=models.AutoField(primary_key=True, serialize=False),
),
]

View File

@@ -0,0 +1,20 @@
# Generated by Django 4.2.2 on 2023-06-11 13:41
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('content', '0009_alter_page_id'),
]
operations = [
migrations.AlterField(
model_name='page',
name='parent',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.CASCADE,
related_name='subpages', to='content.page'),
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 4.2.2 on 2023-07-19 18:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('content', '0010_alter_page_parent'),
]
operations = [
migrations.AlterField(
model_name='page',
name='id',
field=models.AutoField(
auto_created=True, primary_key=True, serialize=False,
verbose_name='ID'),
),
]

View File

@@ -8,7 +8,7 @@ from django.template.defaultfilters import slugify
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import get_language, ugettext as _ from django.utils.translation import get_language, gettext as _
from utils import STATUS_CHOICES, STATUS_WAITING, STATUS_PUBLISHED, CLEANER from utils import STATUS_CHOICES, STATUS_WAITING, STATUS_PUBLISHED, CLEANER
@@ -111,6 +111,7 @@ class Article(models.Model):
"""Returns the headline of this article.""" """Returns the headline of this article."""
return self.headline return self.headline
# noinspection PyUnresolvedReferences
@property @property
def get_image(self): def get_image(self):
"""Return the article image, or the category image if unset.""" """Return the article image, or the category image if unset."""
@@ -169,6 +170,7 @@ class Category(models.Model):
return self.name return self.name
# noinspection PyUnresolvedReferences
class Page(models.Model): class Page(models.Model):
"""A page on this homepage. It can have a "static" HTML page, the URL of a """A page on this homepage. It can have a "static" HTML page, the URL of a
dynamic Django view, or a PDF document. dynamic Django view, or a PDF document.
@@ -216,7 +218,7 @@ class Page(models.Model):
blank=True, blank=True,
null=True, null=True,
related_name='subpages', related_name='subpages',
on_delete=models.SET_NULL on_delete=models.CASCADE
) )
position = models.PositiveSmallIntegerField( position = models.PositiveSmallIntegerField(
blank=True, blank=True,
@@ -317,12 +319,12 @@ class Page(models.Model):
raise ValidationError( raise ValidationError(
_(u'Please upload a PDF-File to this PDF-Page.')) _(u'Please upload a PDF-File to this PDF-Page.'))
def get_absolute_url(self): def get_absolute_url(self) -> str:
"""Return the path with an extension that matches the content type. """Return the path with an extension that matches the content type.
It's useful for an user to match the URL to the contenttype. It's useful for a user to match the URL to the contenttype.
:return: sting with the absolute URL of this page.""" :return: string with the absolute URL of this page."""
return '/' + self.path + CONTENT_TYPE_EXTENSIONS[self.content_type] return '/' + str(self.path) + CONTENT_TYPE_EXTENSIONS[self.content_type]
class Meta(object): class Meta(object):
"""Set default ordering and an unique priamry key to avoid dupes.""" """Set default ordering and an unique priamry key to avoid dupes."""

View File

@@ -4,26 +4,22 @@ Created on 03.10.2011
@author: christian @author: christian
""" """
from django.conf.urls import url from django.urls import path
from . import views
from .views import ArticleArchiveIndex, ArticleForm, ArticleYearArchive, \
ArticleMonthArchive, ArticleDetail
urlpatterns = [ urlpatterns = [
url(r'^$', ArticleArchiveIndex.as_view(), name='article-archive'), path("", views.ArticleArchiveIndex.as_view(), name='article-archive'),
url(r'^add/$', ArticleForm.as_view(), name='add-article'), path('add/', views.ArticleForm.as_view(), name='add-article'),
url(r'^edit/(?P<pk>[\d]+)/$', ArticleForm.as_view(), name='edit-article'), path('edit/<int:pk>/', views.ArticleForm.as_view(), name='edit-article'),
url(r'^(?P<year>[\d]{4})/$', ArticleYearArchive.as_view(), path('<int:year>/', views.ArticleYearArchive.as_view(), name='article-archive'),
path('<int:year>/<int:month>/', views.ArticleMonthArchive.as_view(),
name='article-archive'), name='article-archive'),
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/$', ArticleMonthArchive.as_view(), path('<int:year>/<int:month>/<slug:slug>/', views.ArticleDetail.as_view(),
name='show-article'),
path('<slug:category>/', views.ArticleArchiveIndex.as_view(),
name='article-archive'), name='article-archive'),
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/(?P<slug>[\-\d\w]+)/$', path('<slug:category>/<int:year>/', views.ArticleYearArchive.as_view(),
ArticleDetail.as_view(), name='show-article'), name='article-archive'),
url(r'^(?P<category>[\-\d\w]+)/$', ArticleArchiveIndex.as_view(), path('<slug:category>/<int:year>/<int:month>/', views.ArticleMonthArchive.as_view(),
name='article-archive'), name='article-archive'),
url(r'^(?P<category>[\-\d\w]+)/(?P<year>[\d]{4})/$',
ArticleYearArchive.as_view(), name='article-archive'),
url(r'^(?P<category>[\-\d\w]+)/(?P<year>[\d]{4})/(?P<month>[\d]+)/$',
ArticleMonthArchive.as_view(), name='article-archive'),
] ]

View File

@@ -8,7 +8,7 @@ class ArticleSitemap(GenericSitemap):
min_priority = 0.25 min_priority = 0.25
@staticmethod @staticmethod
def items(): def items(**kwargs):
"""only add published articles to the sitemap""" """only add published articles to the sitemap"""
return Article.objects.published() return Article.objects.published()
@@ -18,6 +18,6 @@ class PageSitemap(GenericSitemap):
min_priority = 0.5 min_priority = 0.5
@staticmethod @staticmethod
def items(): def items(**kwargs):
"""add all pages to the sitemap.""" """add all pages to the sitemap."""
return Page.objects.all() return Page.objects.all()

View File

@@ -53,10 +53,10 @@
{% block navigation %} {% block navigation %}
<ul id="navigation"> <ul id="navigation">
<li><a href="{{current_top_page.get_absolute_url}}" <li><a href="{{current_top_page.get_absolute_url}}"
{% if not active_category %} class="active" {% endif %}>{% trans 'All Categories' %}</a></li> class="active">{% trans 'All Categories' %}</a></li>
{% for category in categories %} {% for category in categories %}
<li><a href="{% url 'article-archive' category=category.slug %}" <li><a href="{% url 'article-archive' category=category.slug %}"
{% ifequal category.slug active_category.slug %} class="active"{% endifequal %}>{{ category.name }}</a></li> class="active">{{ category.name }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endblock %} {% endblock %}
@@ -69,7 +69,7 @@
<h3><a href="{{article.get_absolute_url}}">{{article.headline}}</a></h3> <h3><a href="{{article.get_absolute_url}}">{{article.headline}}</a></h3>
<ul class="info"> <ul class="info">
<li><span class="fa fa-calendar-o" title="{% trans 'created on' %}"></span> <time <li><span class="fa fa-calendar-o" title="{% trans 'created on' %}"></span> <time
datetime="{{article.date_created|date:'c'}}">{{ article.date_created|date }}</time></li> datetime="{{article.date_created|date:'Y-m-d\TH:i:sO'}}">{{ article.date_created|date }}</time></li>
<li><span class="fa fa-user" title="{% trans 'by' %}"></span> {{ article.author }}</li> <li><span class="fa fa-user" title="{% trans 'by' %}"></span> {{ article.author }}</li>
<li><span class="fa fa-comments" title="{% trans 'comments' %}"></span> <a <li><span class="fa fa-comments" title="{% trans 'comments' %}"></span> <a
href="{{article.get_absolute_url}}#comments" >{{comment_count}} {% trans "comments" %}</a></li> href="{{article.get_absolute_url}}#comments" >{{comment_count}} {% trans "comments" %}</a></li>

View File

@@ -1,5 +1,4 @@
{% extends "base.html" %} {% extends "base.html" %}{% load i18n comments thumbnail %}
{% load i18n comments thumbnail %}
{% block title %}{{ article.headline }}{% endblock %} {% block title %}{{ article.headline }}{% endblock %}
@@ -11,8 +10,7 @@
<meta property="og:url" content="http://www.kasu.at{{ article.get_absolute_url }}"/> <meta property="og:url" content="http://www.kasu.at{{ article.get_absolute_url }}"/>
<meta property="og:image" content="http://www.kasu.at{{article.get_image|thumbnail_url:'article'}}"/> <meta property="og:image" content="http://www.kasu.at{{article.get_image|thumbnail_url:'article'}}"/>
<meta property="og:description" content="{{article.content|striptags|truncatewords:25|force_escape}}"/> <meta property="og:description" content="{{article.content|striptags|truncatewords:25|force_escape}}"/>
<link rel="image_src" type="image/jpeg" href="{{article.get_image|thumbnail_url:'article'}}" /> <link rel="image_src" type="image/jpeg" href="{{article.get_image|thumbnail_url:'article'}}"/>{% endblock %}
{% endblock %}
{% block itemscope %}itemscope itemtype="http://schema.org/Article"{% endblock %} {% block itemscope %}itemscope itemtype="http://schema.org/Article"{% endblock %}
@@ -20,34 +18,31 @@
<h1 itemprop="name">{{article.headline}}</h1> <h1 itemprop="name">{{article.headline}}</h1>
<div id="teaser_text"> <div id="teaser_text">
<ul class="info"> <ul class="info">
<li><span class="fa fa-user"></span> <strong>{% trans 'Author' %}:</strong> <a href="{% url 'membership-details' article.author %}" itemprop="author">{{article.author}}</a></li> <li><span class="fa fa-user"></span> <strong>{% trans 'Author' %}:</strong>
<li><span class="fa fa-calendar-o"></span> <strong>{% trans 'Created on' %}: </strong><time datetime="{{article.date_created|date:'Y-m-d H:i'}}">{{ article.date_created|date:'DATE_FORMAT' }}</time></li> <a href="{% url 'membership-details' article.author %}" itemprop="author">{{article.author}}</a>
<li><span class="fa fa-tag"></span> <strong>{% trans "Category"%}: </strong><a href="{{ article.category.get_absolute_url }}" itemprop="articleSection">{{article.category.name}}</a></li> </li>
<li><span class="fa fa-calendar-o"></span>
<strong>{% trans 'Created on' %}:</strong>
<time datetime="{{article.date_created|date:'Y-m-d\TH:i:sO'}}">{{ article.date_created|date:'DATE_FORMAT' }}</time>
</li>
<li><span class="fa fa-tag"></span> <strong>{% trans "Category"%}:</strong>
<a href="{{ article.category.get_absolute_url }}" itemprop="articleSection">{{article.category.name}}</a>
</li>
</ul> </ul>
</div> </div>{% endblock %}
{% endblock %}
{% block maincontent %} {% block maincontent %}
<div itemprop="articleBody" class="grid_12"> <div itemprop="articleBody" class="grid_12">
<img alt="{{article.category.name}}" src="{{article.get_image|thumbnail_url:'article'}}" class="posting_image" itemprop="image"/> <img alt="{{article.category.name}}" src="{{article.get_image|thumbnail_url:'article'}}" class="posting_image" itemprop="image"/> {{ article.content }}
{{ article.content }}
</div> </div>
<p class="right"> <p class="right">
<strong>{% trans 'share on' %}:</strong> <strong>{% trans 'share on' %}:</strong>
<a class="button" href="https://plus.google.com/share?url=http%3A%2F%2Fwww.kasu.at{{article.get_absolute_url|urlencode}}" <a class="button" href="https://plus.google.com/share?url=http%3A%2F%2Fwww.kasu.at{{article.get_absolute_url|urlencode}}" onclick="javascript:window.open(this.href,'', 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=600,width=600');return false;"><span class="fa fa-google-plus"></span> Google+</a>
onclick="javascript:window.open(this.href,'', 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=600,width=600');return false;"><span class="fa fa-google-plus"></span> Google+</a>
<a class="button" href="https://twitter.com/share?url=http%3A%2F%2Fwww.kasu.at{{article.get_absolute_url|urlencode}}" target='_blank'><span class="fa fa-twitter"></span> Twitter</a> <a class="button" href="https://twitter.com/share?url=http%3A%2F%2Fwww.kasu.at{{article.get_absolute_url|urlencode}}" target='_blank'><span class="fa fa-twitter"></span> Twitter</a>
<a class="button" href="http://facebook.com/sharer.php?u=http%3A%2F%2Fwww.kasu.at{{article.get_absolute_url|urlencode}}?t={{article.headline|urlencode}}" target="_blank"><span class="fa fa-facebook"></span> Facebook</a> <a class="button" href="http://facebook.com/sharer.php?u=http%3A%2F%2Fwww.kasu.at{{article.get_absolute_url|urlencode}}?t={{article.headline|urlencode}}" target="_blank"><span class="fa fa-facebook"></span> Facebook</a>
</p> </p>{% endblock %}
{% endblock %}
{% block comments%} {% block comments%}{% render_comment_list for article %}{% render_comment_form for article %}{% endblock %}
{% render_comment_list for article %}
{% render_comment_form for article %}
{% endblock %}
{% block buttonbar %} {% block buttonbar %}{% if perms.content.change_article %}
{% if perms.content.change_article %} <a href="{% url 'edit-article' article.id %}" class="button"><span class="fa fa-pencil"></span> {% trans "Edit Article" %}</a>{% endif %}{% endblock %}
<a href="{% url 'edit-article' article.id %}" class="button"><span class="fa fa-pencil"></span> {% trans "Edit Article" %}</a>
{% endif %}
{% endblock %}

View File

@@ -9,51 +9,39 @@
{% endblock %} {% endblock %}
{% block maincontent %} {% block maincontent %}
{% get_fieldset "category, image" from form as fieldset_common %}
{% get_fieldset "headline_de" from form as fieldset_de %}
{% get_fieldset "headline_en" from form as fieldset_en %}
<form action="" method="post" enctype="multipart/form-data"> <form action="" method="post" enctype="multipart/form-data">
{{ form.media }} {% csrf_token %} {{ form.media }} {% csrf_token %}
<fieldset> <fieldset>
<div> {% with fieldset_common as form %}{% include "form.html" %}{% endwith %}
<label for="id_category" class="field_name {{ form.category.css_classes }}">{{ form.category.label}}</label>
{{ form.category }}
{% if form.category.help_text %}<p class="help_text">{{form.category.help_text}}</p>{% endif %}
{% if form.category.errors %}{{ form.category.errors }}{% endif %}
</div>
<div>
<label for="id_image" class="field_name {{ form.image.css_classes }}">{{ form.image.label}}</label>
{{ form.image }}
{% if form.image.help_text %}<p class="help_text">{{form.image.help_text}}</p>{% endif %}
{% if form.image.errors %}{{ form.image.errors }}{% endif %}
</div>
</fieldset> </fieldset>
<ul class="tabs grid_12"> <ul class="tabs">
<li><a href="#de">{% trans "German" %}</a></li> <li><a href="#de">{% trans "German" %}</a></li>
<li><a href="#en">{% trans "English" %}</a></li> <li><a href="#en">{% trans "English" %}</a></li>
</ul> </ul>
<label for="id_{{ form.headline_de.html_name }}" class="fieldname {{ form.headline_de.css_classes }}">
{{ form.headline_de.label }}</label>
<p class="grid_10">{{ form.headline_de }}</p>
<div class="tab_container"> <div class="tab_container">
<div id="de" class="tab_content"> <section id="de" class="tab_content">
<fieldset> <fieldset class="grid_12">
<legend>Deutsch</legend> <legend>{% trans "German" %}</legend>
<label for="id_{{ form.headline_de.html_name }}" class="fieldname {{ form.headline_de.css_classes }}"> {% with fieldset_de as form %}{% include "form.html" %}{% endwith %}
{{ form.headline_de.label }}</label>
<p class="grid_10">{{ form.headline_de }}</p>
</fieldset> </fieldset>
<br>
{{form.content_de}} {{form.content_de}}
</div> </section>
<div id="en" class="tab_content"> <section id="en" class="tab_content">
<h3 class="grid_12">English</h3> <fieldset class="grid_12">
<label for="id_{{ form.headline_en.html_name }}" class="grid_2 {{ form.headline_en.css_classes }}"> <legend>{% trans "English" %}</legend>
{{ form.headline_en.label }}</label> {% with fieldset_en as form %}{% include "form.html" %}{% endwith %}
<p class="grid_10">{{ form.headline_en }}</p> </fieldset>
<p>&nbsp;</p> <br>
{{form.content_en}} {{form.content_en}}
</div> </section>
</div> </div>
<p class="buttonbar"> <p class="buttonbar">
<button type="reset"><span class="fa fa-undo"></span> {% trans 'reset' %}</button> <button type="reset"><span class="fa fa-undo"></span> {% trans 'reset' %}</button>

View File

@@ -1,66 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>{% block title %}{{page.title}}{% endblock %}</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style>
@font-face {
font-family: 'Amerika Sans';
src: url('{{ STATIC_ROOT }}/fonts/amerika_sans.ttf');
}
@font-face {
font-family: 'Philosopher';
src: url('{{ STATIC_ROOT }}/fonts/philosopher-regular.ttf');
font-weight: normal;
font-style: normal;
}
h1,h2,h3,h4,h5,h6 {
font-family: 'Amerika Sans', Helvetica;
font-variant: small-caps;
font-weight: bold;
margin: 0.5em 0 0.2em 0;
}
body {
font-family: Philosopher;
font-size: 12pt;
}
#page_header {
text-align: right;
}
@page {
margin-right: 0;
margin-bottom: 0;
margin-top: 35mm;
margin-left: 2cm;
@frame header {
-pdf-frame-content : page_header;
top: 0;
margin: 5mm;
height: 4cm;
}
@frame footer {
-pdf-frame-content: page_footer;
bottom: 0cm;
height: 2cm;
margin: 5mm;
}
}
</style>
</head>
<body>
<h1>{{ page.title }}</h1>
{{ page.content }}
<div id="page_header">
<img src="{{STATIC_ROOT}}/img/logo.png" alt="Kasu">
</div>
<div id="page_footer">
{{ page.title }} Seite:
<pdf:pagenumber />
</div>
</body>
</html>

View File

@@ -5,7 +5,7 @@ from csp.decorators import csp_update
from django.conf import settings from django.conf import settings
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.http import HttpResponse, Http404 from django.http import HttpResponse, Http404
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
from django.views import generic from django.views import generic
from . import models, forms from . import models, forms
@@ -19,7 +19,7 @@ class WYSIWYGEditorMixin(PermissionRequiredMixin):
@csp_update(SCRIPT_SRC="'unsafe-eval'") @csp_update(SCRIPT_SRC="'unsafe-eval'")
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
"""Add "unsafe-eval" to the Content-Secuirty-Policy HTTP Headers for the """Add "unsafe-eval" to the Content-Security-Policy HTTP Headers for the
WYSIWYG Editor. WYSIWYG Editor.
:param request: the HTTP Request Object :param request: the HTTP Request Object
@@ -40,7 +40,7 @@ class ArticleArchiveMixin(object):
Filter the queryset by category if one has been specified in the URL Filter the queryset by category if one has been specified in the URL
:param queryset: an model.Article.objects Queryset :param queryset: an model.Article.objects Queryset
:return: an model.Article.objects Queryset filterd by category :return: an model.Article.objects Queryset filtered by category
""" """
category_slug = self.kwargs.get('category') category_slug = self.kwargs.get('category')
@@ -90,7 +90,7 @@ class ArticleArchiveIndex(ArticleArchiveMixin, generic.ArchiveIndexView):
class ArticleYearArchive(ArticleArchiveMixin, generic.YearArchiveView): class ArticleYearArchive(ArticleArchiveMixin, generic.YearArchiveView):
""" """
Displays the Articles filterd by a specific year Displays the Articles filtered by a specific year
""" """
queryset = models.Article.objects.filter(status=models.STATUS_PUBLISHED) queryset = models.Article.objects.filter(status=models.STATUS_PUBLISHED)
@@ -113,7 +113,7 @@ class ArticleYearArchive(ArticleArchiveMixin, generic.YearArchiveView):
class ArticleMonthArchive(ArticleArchiveMixin, generic.MonthArchiveView): class ArticleMonthArchive(ArticleArchiveMixin, generic.MonthArchiveView):
""" """
Displays the Articles filterd by a specific month Displays the Articles filtered by a specific month
""" """
queryset = models.Article.objects.filter(status=models.STATUS_PUBLISHED) queryset = models.Article.objects.filter(status=models.STATUS_PUBLISHED)
@@ -139,7 +139,11 @@ class ArticleDetail(generic.DetailView):
Render the news Article, but only if it got published. Render the news Article, but only if it got published.
""" """
def get_queryset(self):
queryset = models.Article.objects.filter(status=models.STATUS_PUBLISHED) queryset = models.Article.objects.filter(status=models.STATUS_PUBLISHED)
queryset = queryset.filter(date_created__year=self.kwargs['year'])
queryset = queryset.filter(date_created__month=self.kwargs['month'])
return queryset
class ArticleForm(WYSIWYGEditorMixin, generic.UpdateView): class ArticleForm(WYSIWYGEditorMixin, generic.UpdateView):
@@ -170,7 +174,7 @@ class ArticleForm(WYSIWYGEditorMixin, generic.UpdateView):
class PageAddForm(WYSIWYGEditorMixin, generic.CreateView): class PageAddForm(WYSIWYGEditorMixin, generic.CreateView):
""" Renders an Form to create a new page for users with conforming """ Renders a Form to create a new page for users with conforming
permissions.""" permissions."""
form_class = forms.PageForm form_class = forms.PageForm
@@ -179,6 +183,7 @@ class PageAddForm(WYSIWYGEditorMixin, generic.CreateView):
def get_initial(self): def get_initial(self):
"""Try to get the path of the parent page as initial data.""" """Try to get the path of the parent page as initial data."""
if self.kwargs['path']:
path = os.path.splitext(self.kwargs['path'])[0] path = os.path.splitext(self.kwargs['path'])[0]
if path.startswith('/'): if path.startswith('/'):
path = path[1:] path = path[1:]
@@ -186,10 +191,12 @@ class PageAddForm(WYSIWYGEditorMixin, generic.CreateView):
path = path[:-1] path = path[:-1]
parent = models.Page.objects.get(path=path) parent = models.Page.objects.get(path=path)
return {'parent': parent} return {'parent': parent}
else:
return None
class PageEditForm(WYSIWYGEditorMixin, generic.UpdateView): class PageEditForm(WYSIWYGEditorMixin, generic.UpdateView):
"""Renders an Form to edit a page for users with conforming permissions.""" """Renders a Form to edit a page for users with conforming permissions."""
form_class = forms.PageForm form_class = forms.PageForm
model = models.Page model = models.Page
@@ -198,13 +205,14 @@ class PageEditForm(WYSIWYGEditorMixin, generic.UpdateView):
def get_object(self, queryset=None): def get_object(self, queryset=None):
""" Get the path from the URL and fetch the corresponding page. """ Get the path from the URL and fetch the corresponding page.
First get the path wihout fileextentsion leading or trailing slashes, First get the path without file extension leading or trailing slashes,
then search in the database if such a page exists. then search in the database if such a page exists.
:param queryset: Get the single item from this filtered queryset. :param queryset: Get the single item from this filtered queryset.
:return: :return:
""" """
path = os.path.splitext(self.kwargs['path'])[0] path = self.kwargs['path'] or 'index.html'
path = os.path.splitext(path)[0]
queryset = queryset or self.get_queryset() queryset = queryset or self.get_queryset()
if path.startswith('/'): if path.startswith('/'):
path = path[1:] path = path[1:]
@@ -266,9 +274,9 @@ class PagePdf(generic.DeleteView):
def render_to_response(self, context, **response_kwargs): def render_to_response(self, context, **response_kwargs):
"""Stream the PDF File to the client and set the right content headers. """Stream the PDF File to the client and set the right content headers.
:param context: useless only for compatility :param context: useless only for compatibility
:param response_kwargs: will be added to the HttpResponse kwargs. :param response_kwargs: will be added to the HttpResponse kwargs.
:return: an HTTPResponse with PDF Content or an Http404 exception :return: an HTTPResponse with PDF Content or a Http404 exception
""" """
try: try:
with open(self.object.pdf_file.path, 'rb') as pdf_file: with open(self.object.pdf_file.path, 'rb') as pdf_file:
@@ -278,8 +286,8 @@ class PagePdf(generic.DeleteView):
**response_kwargs **response_kwargs
) )
return response return response
except: except FileExistsError:
raise Http404('File not Found %s.pdf' % self.kwargs['path']) raise Http404('File %s.pdf not found' % self.kwargs['path'])
class StartPage(generic.TemplateView): class StartPage(generic.TemplateView):
@@ -287,7 +295,7 @@ class StartPage(generic.TemplateView):
template_name = 'index.html' template_name = 'index.html'
def get_context_data(self): def get_context_data(self):
""" Adds recent ariticles and recent comments to the context. """ Adds recent articles and recent comments to the context.
:return: array() with the context data :return: array() with the context data
""" """

View File

@@ -1,6 +1,6 @@
"""Django admin interface for the event app. """Django admin interface for the event app.
It's the best way to add eventseries, or edit/delete events.""" It's the best way to add event-series, or edit/delete events."""
from django.contrib import admin from django.contrib import admin
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
@@ -8,7 +8,7 @@ from events.models import Event, Photo, Location
class EventInline(admin.TabularInline): class EventInline(admin.TabularInline):
"""To list events of an eventseries below the 'master event'""" """To list events of an event-series below the 'master event'"""
model = Event model = Event
fields = ('name', 'start', 'end') fields = ('name', 'start', 'end')
verbose_name_plural = _('Event Series') verbose_name_plural = _('Event Series')

View File

@@ -1,20 +1,20 @@
""" Content processor to display upcoming events on every page you want. """ """ Content processor to display upcoming events on every page you want. """
from django.core.cache import cache from django.core.cache import cache
from django.http import HttpRequest
from .models import Event from .models import Event
def events_overview(request): # Ignore PyLintBear (W0613) def events_overview(request: HttpRequest) -> dict[str, Event]:
""" """
Adds event information as variables to the template context on every page. Adds event information as variables to the template context on every page.
For speed reasons everything will be cached for an hour. the following For speed reasons everything will be cached for an hour. the following
variables will be added to the template context: variables will be added to the template context:
* current_event: If an event is running at this moment, the correspondi * current_event: object for the currently running event.
event object.
* next_event: the next event that is upcoming. * next_event: the next event that is upcoming.
* upcoming_events: the next 3 events that are upcoming. * upcoming_events: the next 3 events that are upcoming.
:param request: An Django HTTPRequest object :param request: a Django HTTPRequest object
:return: dict() with the new context variables :return: dict() with the new context variables
""" """
current_event = cache.get('current_event', False) current_event = cache.get('current_event', False)

View File

@@ -1,27 +1,42 @@
"""Django Forms to administrate the event content on the frontend.""" """Django Forms to administrate the event content on the frontend."""
from django import forms from django import forms
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
from . import models from . import models
class ClearableMultipleFileInput(forms.widgets.ClearableFileInput):
allow_multiple_selected = True
accept = "image/jpg"
class MultipleFileField(forms.FileField):
def __init__(self, *args, **kwargs):
kwargs.setdefault("widget", ClearableMultipleFileInput())
super().__init__(*args, **kwargs)
def clean(self, data, initial=None):
single_file_clean = super().clean
if isinstance(data, (list, tuple)):
result = [single_file_clean(d, initial) for d in data]
else:
result = single_file_clean(data, initial)
return result
class PhotoUploadForm(forms.Form): class PhotoUploadForm(forms.Form):
"""Form to upload multiple photos to a single event.""" """Form to upload multiple photos to a single event.
TODO: Check multiple upload
"""
error_css_class = 'error' error_css_class = 'error'
required_css_class = 'required' required_css_class = 'required'
photographer = forms.ModelChoiceField(get_user_model().objects.all(), photographer = forms.ModelChoiceField(get_user_model().objects.all(),
required=True, ) required=True, )
event = forms.ModelChoiceField(models.Event.objects.all(), required=True, ) event = forms.ModelChoiceField(models.Event.objects.all(), required=True, )
upload = forms.FileField( upload = MultipleFileField(
label=_('Images'), label=_('Images'),
required=True, required=True,
widget=forms.widgets.ClearableFileInput(
attrs={
'multiple': 'multiple',
'accept': "image/gif,image/png,image/jpeg"
}
)
) )

View File

@@ -1,18 +1,17 @@
""" urls for the event gallery part of the events app. """ from django.urls import path
from django.conf.urls import url
from . import views from . import views
urlpatterns = [ urlpatterns = [
url(r'^$', views.EventGallery.as_view(), name='event-gallery'), path("", views.EventGallery.as_view(), name='event-gallery'),
url(r'^(?P<event>[\d]+)/$', views.EventPhotoList.as_view(), path('<int:event>/', views.EventPhotoList.as_view(),
name='event-photo-list'), name='event-photo-list'),
url(r'^(?P<event>[\d]+)/upload/$', views.EventPhotoUpload.as_view(), path('<int:event>/upload/', views.EventPhotoUpload.as_view(),
name='event-photo-upload'), name='event-photo-upload'),
url(r'^(?P<event>[\d]+)/(?P<pk>[\d]+)/$', views.EventPhoto.as_view(), path('<int:event>/<int:pk>/', views.EventPhoto.as_view(),
name='event-photo'), name='event-photo'),
url(r'^delete/(?P<pk>[\d]+)/$', views.DeleteEventPhoto.as_view(), path('delete/<int:pk>/', views.DeleteEventPhoto.as_view(),
name='delete-event-photo'), name='delete-event-photo'),
url(r'^upload/$', views.EventPhotoUpload.as_view(), path('upload/', views.EventPhotoUpload.as_view(),
name='event-photo-upload'), name='event-photo-upload'),
] ]

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: kasu.events\n" "Project-Id-Version: kasu.events\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-08 00:17+0200\n" "POT-Creation-Date: 2023-08-07 20:38+0200\n"
"PO-Revision-Date: 2018-01-12 15:25+0105\n" "PO-Revision-Date: 2018-01-12 15:25+0105\n"
"Last-Translator: b'Christian Berg <kasu@xendynastie.at>'\n" "Last-Translator: b'Christian Berg <kasu@xendynastie.at>'\n"
"Language-Team: Kasu <verein@kasu.at>\n" "Language-Team: Kasu <verein@kasu.at>\n"
@@ -19,58 +19,58 @@ msgstr ""
"X-Generator: Poedit 1.8.9\n" "X-Generator: Poedit 1.8.9\n"
"X-Translated-Using: django-rosetta 0.7.14\n" "X-Translated-Using: django-rosetta 0.7.14\n"
#: .\events\admin.py:14 .\events\models.py:82 #: admin.py:14 models.py:83
msgid "Event Series" msgid "Event Series"
msgstr "Veranstaltungsreihen" msgstr "Veranstaltungsreihen"
#: .\events\forms.py:17 #: forms.py:38
msgid "Images" msgid "Images"
msgstr "Bilder" msgstr "Bilder"
#: .\events\forms.py:46 #: forms.py:61
msgid "start" msgid "start"
msgstr "Beginn" msgstr "Beginn"
#: .\events\forms.py:49 #: forms.py:64
msgid "end" msgid "end"
msgstr "Ende" msgstr "Ende"
#: .\events\models.py:52 .\events\models.py:176 .\events\models.py:217 #: mixins.py:86
msgid "Event does not exist"
msgstr "Veranstaltung gibt es nicht"
#: models.py:53 models.py:177 models.py:218
msgid "Name" msgid "Name"
msgstr "Name" msgstr "Name"
#: .\events\models.py:53 .\events\models.py:177 .\events\models.py:225 #: models.py:54 models.py:178 models.py:226
msgid "Description" msgid "Description"
msgstr "Beschreibung" msgstr "Beschreibung"
#: .\events\models.py:55 .\events\templates\events\event_detail.html:29 #: models.py:56 templates/events/event_detail.html:29
#: .\events\templates\events\event_detail.html:87 #: templates/events/event_detail.html:87 templates/events/event_list.html:28
#: .\events\templates\events\event_list.html:28
#: .\events\templates\events\photo_upload.html:13
msgid "Start" msgid "Start"
msgstr "Beginn" msgstr "Beginn"
#: .\events\models.py:56 .\events\templates\events\event_detail.html:30 #: models.py:57 templates/events/event_detail.html:30
#: .\events\templates\events\event_detail.html:89 #: templates/events/event_detail.html:89
msgid "End" msgid "End"
msgstr "Ende" msgstr "Ende"
#: .\events\models.py:57 .\events\models.py:185 #: models.py:58 models.py:186 templates/events/event_detail.html:34
#: .\events\templates\events\event_detail.html:34 #: templates/events/event_detail.html:80 templates/events/event_detail.html:92
#: .\events\templates\events\event_detail.html:80
#: .\events\templates\events\event_detail.html:92
msgid "Homepage" msgid "Homepage"
msgstr "Homepage" msgstr "Homepage"
#: .\events\models.py:59 .\events\models.py:179 .\events\models.py:219 #: models.py:60 models.py:180 models.py:220
msgid "Image" msgid "Image"
msgstr "Bild" msgstr "Bild"
#: .\events\models.py:66 #: models.py:67
msgid "Mahjong Tournament" msgid "Mahjong Tournament"
msgstr "Mahjong Turnier" msgstr "Mahjong Turnier"
#: .\events\models.py:68 #: models.py:69
msgid "" msgid ""
"This event is a tournament, different rules apply for the kyu " "This event is a tournament, different rules apply for the kyu "
"ranking." "ranking."
@@ -78,11 +78,11 @@ msgstr ""
"Diese Veranstaltung ist ein Turnier, es gelten andere Regeln für das Kyu " "Diese Veranstaltung ist ein Turnier, es gelten andere Regeln für das Kyu "
"Ranking." "Ranking."
#: .\events\models.py:72 #: models.py:73
msgid "Mahjong Season" msgid "Mahjong Season"
msgstr "Mahjong Saison" msgstr "Mahjong Saison"
#: .\events\models.py:83 #: models.py:84
msgid "" msgid ""
"Wenn dieser Event zu einer Veranstaltungsreihe gehört werden Ort, " "Wenn dieser Event zu einer Veranstaltungsreihe gehört werden Ort, "
"Beschreibung, Bild und Homepage von dem hier angegebenen Event " "Beschreibung, Bild und Homepage von dem hier angegebenen Event "
@@ -91,248 +91,223 @@ msgstr ""
"Wenn dieser Termin zu einer Veranstaltungsreihe gehört werden Ort, " "Wenn dieser Termin zu einer Veranstaltungsreihe gehört werden Ort, "
"Beschreibung, Bild und Homepage von dem hier angegebenen Event übernommen." "Beschreibung, Bild und Homepage von dem hier angegebenen Event übernommen."
#: .\events\models.py:92 .\events\models.py:195 .\events\models.py:248 #: models.py:93 models.py:196 models.py:249
msgid "first created at" msgid "first created at"
msgstr "erstellt am" msgstr "erstellt am"
#: .\events\models.py:97 .\events\models.py:200 .\events\models.py:253 #: models.py:98 models.py:201 models.py:254
msgid "latest updated at" msgid "latest updated at"
msgstr "letzte Aktualisierung am" msgstr "letzte Aktualisierung am"
#: .\events\models.py:103 #: models.py:104
msgid "Event" msgid "Event"
msgstr "Termin" msgstr "Termin"
#: .\events\models.py:104 #: models.py:105
msgid "Events" msgid "Events"
msgstr "Termine" msgstr "Termine"
#: .\events\models.py:117 #: models.py:118
msgid "A event can't end before it had started" msgid "A event can't end before it had started"
msgstr "Eine Veranstaltung kann nicht enden bevor sie begonnen hat" msgstr "Eine Veranstaltung kann nicht enden bevor sie begonnen hat"
#: .\events\models.py:186 #: models.py:187
msgid "Postal Code" msgid "Postal Code"
msgstr "Postleitzahl" msgstr "Postleitzahl"
#: .\events\models.py:187 #: models.py:188
msgid "Street Address" msgid "Street Address"
msgstr "Straße" msgstr "Straße"
#: .\events\models.py:188 #: models.py:189
msgid "Locality" msgid "Locality"
msgstr "Ort" msgstr "Ort"
#: .\events\models.py:189 #: models.py:190
msgid "Country" msgid "Country"
msgstr "Land" msgstr "Land"
#: .\events\models.py:204 #: models.py:205
msgid "Venue" msgid "Venue"
msgstr "Veranstaltungsort" msgstr "Veranstaltungsort"
#: .\events\models.py:205 #: models.py:206
msgid "Venues" msgid "Venues"
msgstr "Veranstaltungsorte" msgstr "Veranstaltungsorte"
#: .\events\models.py:232 #: models.py:233
msgid "Startpage" msgid "Startpage"
msgstr "Startseite" msgstr "Startseite"
#: .\events\models.py:235 #: models.py:236
msgid "Display this Photo on the Startpage Teaser" msgid "Display this Photo on the Startpage Teaser"
msgstr "Foto als Teaser auf der Startseite verwenden." msgstr "Foto als Teaser auf der Startseite verwenden."
#: .\events\models.py:237 #: models.py:238
msgid "Published on" msgid "Published on"
msgstr "Veröffentlicht am" msgstr "Veröffentlicht am"
#: .\events\models.py:239 #: models.py:240
msgid "Number of views" msgid "Number of views"
msgstr "Wie oft gesehen" msgstr "Wie oft gesehen"
#: .\events\models.py:263 .\events\templates\events\event_archive.html:38 #: models.py:264 templates/events/event_archive.html:38
#: .\events\templates\events\event_list.html:18 #: templates/events/event_list.html:18
msgid "Event Image" msgid "Event Image"
msgstr "Veranstaltungsbild" msgstr "Veranstaltungsbild"
#: .\events\models.py:264 #: models.py:265
msgid "Event Images" msgid "Event Images"
msgstr "Veranstaltungsbilder" msgstr "Veranstaltungsbilder"
#: .\events\templates\events\event_archive.html:5 #: templates/events/event_archive.html:5 templates/events/event_archive.html:9
#: .\events\templates\events\event_archive.html:9
msgid "Event Archive" msgid "Event Archive"
msgstr "Veranstaltungsarchiv" msgstr "Veranstaltungsarchiv"
#: .\events\templates\events\event_archive.html:42 #: templates/events/event_archive.html:42 templates/events/event_detail.html:85
#: .\events\templates\events\event_detail.html:85 #: templates/events/event_list.html:22 templates/events/photo_detail.html:53
#: .\events\templates\events\event_list.html:22
#: .\events\templates\events\photo_detail.html:53
msgid "Date" msgid "Date"
msgstr "Datum" msgstr "Datum"
#: .\events\templates\events\event_archive.html:47 #: templates/events/event_archive.html:47
msgid "Time" msgid "Time"
msgstr "Zeit" msgstr "Zeit"
#: .\events\templates\events\event_archive.html:49 #: templates/events/event_archive.html:49
#: .\events\templates\events\photo_upload.html:16
msgid "from" msgid "from"
msgstr "von" msgstr "von"
#: .\events\templates\events\event_archive.html:49 #: templates/events/event_archive.html:49
#: .\events\templates\events\photo_upload.html:16
msgid "to" msgid "to"
msgstr "bis" msgstr "bis"
#: .\events\templates\events\event_archive.html:57 #: templates/events/event_archive.html:57 templates/events/event_detail.html:31
#: .\events\templates\events\event_detail.html:31 #: templates/events/event_detail.html:72 templates/events/event_list.html:32
#: .\events\templates\events\event_detail.html:72
#: .\events\templates\events\event_list.html:32
#: .\events\templates\events\photo_upload.html:23
msgid "Location" msgid "Location"
msgstr "Ort" msgstr "Ort"
#: .\events\templates\events\event_archive.html:58 #: templates/events/event_archive.html:58 templates/events/event_list.html:35
#: .\events\templates\events\event_list.html:35
#: .\events\templates\events\photo_upload.html:25
#: .\events\templates\events\photo_upload.html:26
msgid "Comments" msgid "Comments"
msgstr "Kommentare" msgstr "Kommentare"
#: .\events\templates\events\event_archive.html:59 #: templates/events/event_archive.html:59 templates/events/event_detail.html:36
#: .\events\templates\events\event_detail.html:36 #: templates/events/event_detail.html:48 templates/events/photo_list.html:4
#: .\events\templates\events\event_detail.html:48
#: .\events\templates\events\photo_list.html:4
#: .\events\templates\events\photo_upload.html:28
#: .\events\templates\events\photo_upload.html:29
msgid "Photos" msgid "Photos"
msgstr "Fotos" msgstr "Fotos"
#: .\events\templates\events\event_archive.html:60 #: templates/events/event_archive.html:60
#: .\events\templates\events\event_archive.html:61 #: templates/events/event_archive.html:61 templates/events/event_detail.html:35
#: .\events\templates\events\event_detail.html:35 #: templates/events/event_detail.html:51
#: .\events\templates\events\event_detail.html:51
msgid "Hanchans" msgid "Hanchans"
msgstr "Hanchans" msgstr "Hanchans"
#: .\events\templates\events\event_detail.html:37 #: templates/events/event_detail.html:37
msgid "tourney" msgid "tourney"
msgstr "Turnier" msgstr "Turnier"
#: .\events\templates\events\event_detail.html:37 #: templates/events/event_detail.html:37
msgid "other rules apply here" msgid "other rules apply here"
msgstr "hier gelten andere Regeln" msgstr "hier gelten andere Regeln"
#: .\events\templates\events\event_detail.html:45 #: templates/events/event_detail.html:45
msgid "Info" msgid "Info"
msgstr "Info" msgstr "Info"
#: .\events\templates\events\event_detail.html:54 #: templates/events/event_detail.html:54
msgid "Mai-Star Games" msgid "Mai-Star Games"
msgstr "Mai-Star Spiele" msgstr "Mai-Star Spiele"
#: .\events\templates\events\event_detail.html:57 #: templates/events/event_detail.html:57
msgid "Event Ranking" msgid "Event Ranking"
msgstr "Veranstaltungs Wertung" msgstr "Veranstaltungs Wertung"
#: .\events\templates\events\event_detail.html:100 #: templates/events/event_detail.html:100
msgid "Share on Facebook" msgid "Share on Facebook"
msgstr "Auf Facebook teilen" msgstr "Auf Facebook teilen"
#: .\events\templates\events\event_detail.html:104 #: templates/events/event_detail.html:104
msgid "Share on Google+" msgid "Share on Google+"
msgstr "Auf Google+ teilen" msgstr "Auf Google+ teilen"
#: .\events\templates\events\event_detail.html:109 #: templates/events/event_detail.html:109
msgid "Share on Twitter" msgid "Share on Twitter"
msgstr "Auf Twitter teilen" msgstr "Auf Twitter teilen"
#: .\events\templates\events\event_detail.html:113 #: templates/events/event_detail.html:113
msgid "Show on Google Maps" msgid "Show on Google Maps"
msgstr "Auf Google Maps zeigen" msgstr "Auf Google Maps zeigen"
#: .\events\templates\events\event_detail.html:127 #: templates/events/event_detail.html:127 templates/events/event_form.html:9
#: .\events\templates\events\event_form.html:9 .\events\views.py:62 #: views.py:61
msgid "Edit Event" msgid "Edit Event"
msgstr "Termin bearbeiten" msgstr "Termin bearbeiten"
#: .\events\templates\events\event_detail.html:131 #: templates/events/event_detail.html:131
msgid "Add Dates" msgid "Add Dates"
msgstr "Termine hinzufügen" msgstr "Termine hinzufügen"
#: .\events\templates\events\event_form.html:9 #: templates/events/event_form.html:9 templates/events/page.html:9 views.py:61
#: .\events\templates\events\page.html:9 .\events\views.py:64
msgid "Add Event" msgid "Add Event"
msgstr "Neuer Termin" msgstr "Neuer Termin"
#: .\events\templates\events\event_form.html:18 #: templates/events/event_form.html:18 templates/events/photo_list.html:35
#: .\events\templates\events\photo_list.html:35
msgid "reset" msgid "reset"
msgstr "Zurücksetzen" msgstr "Zurücksetzen"
#: .\events\templates\events\event_form.html:19 #: templates/events/event_form.html:19
#: .\events\templates\events\eventseries_form.html:25 #: templates/events/eventseries_form.html:25
msgid "save" msgid "save"
msgstr "Speichern" msgstr "Speichern"
#: .\events\templates\events\event_list.html:4 #: templates/events/event_list.html:4 templates/events/event_list.html:5
#: .\events\templates\events\event_list.html:5
msgid "Upcoming Events" msgid "Upcoming Events"
msgstr "Bevorstehende Veranstaltungen" msgstr "Bevorstehende Veranstaltungen"
#: .\events\templates\events\eventseries_form.html:24 #: templates/events/eventseries_form.html:24
msgid "back" msgid "back"
msgstr "Zurück" msgstr "Zurück"
#: .\events\templates\events\photo_confirm_delete.html:17 #: templates/events/photo_confirm_delete.html:17
msgid "Cancel" msgid "Cancel"
msgstr "Abbrechen" msgstr "Abbrechen"
#: .\events\templates\events\photo_confirm_delete.html:21 #: templates/events/photo_confirm_delete.html:21
#: .\events\templates\events\photo_list.html:21 #: templates/events/photo_list.html:21
msgid "Delete" msgid "Delete"
msgstr "Löschen" msgstr "Löschen"
#: .\events\templates\events\photo_detail.html:44 #: templates/events/photo_detail.html:44
msgid "previous" msgid "previous"
msgstr "Zurück" msgstr "Zurück"
#: .\events\templates\events\photo_detail.html:52 #: templates/events/photo_detail.html:52
msgid "Photographer" msgid "Photographer"
msgstr "Fotograf" msgstr "Fotograf"
#: .\events\templates\events\photo_detail.html:58 #: templates/events/photo_detail.html:58
msgid "share on" msgid "share on"
msgstr "Teile auf" msgstr "Teile auf"
#: .\events\templates\events\photo_detail.html:81 #: templates/events/photo_detail.html:78
msgid "download" msgid "download"
msgstr "Herunterladen" msgstr "Herunterladen"
#: .\events\templates\events\photo_detail.html:82 #: templates/events/photo_detail.html:79
msgid "Rotate counter clockwise" msgid "Rotate counter clockwise"
msgstr "mit dem Uhrzeiger drehen" msgstr "mit dem Uhrzeiger drehen"
#: .\events\templates\events\photo_detail.html:83 #: templates/events/photo_detail.html:80
msgid "Rotate clockwise" msgid "Rotate clockwise"
msgstr "gegen den Uhrzeiger drehen" msgstr "gegen den Uhrzeiger drehen"
#: .\events\templates\events\photo_detail.html:84 #: templates/events/photo_detail.html:81
msgid "Save" msgid "Save"
msgstr "Speichern" msgstr "Speichern"
#: .\events\templates\events\photo_list.html:36 #: templates/events/photo_list.html:36 templates/events/photo_upload.html:13
#: .\events\templates\events\photo_upload.html:35
#: .\events\templates\events\photo_upload.html:49
msgid "Upload" msgid "Upload"
msgstr "Hochladen" msgstr "Hochladen"
#: .\events\views.py:150
msgid "Event does not exist"
msgstr "Veranstaltung gibt es nicht"
#~ msgid " Edit" #~ msgid " Edit"
#~ msgstr "Bearbeiten" #~ msgstr "Bearbeiten"

View File

@@ -29,16 +29,21 @@ class Migration(migrations.Migration):
('image', models.ImageField(storage=utils.OverwriteStorage( ('image', models.ImageField(storage=utils.OverwriteStorage(
), upload_to=events.models.get_upload_path, null=True, ), upload_to=events.models.get_upload_path, null=True,
verbose_name='Bild', blank=True)), verbose_name='Bild', blank=True)),
('is_tournament', models.BooleanField(default=False, ('is_tournament', models.BooleanField(
help_text='Diese Veranstaltung ist ein Turnier, es gelten andere Regeln f\xfcr das Kyu Ranking.', default=False,
help_text='Diese Veranstaltung ist ein Turnier, ' +
'es gelten andere Regeln f\xfcr das Kyu Ranking.',
verbose_name='Turnier')), verbose_name='Turnier')),
('photo_count', models.PositiveIntegerField( ('photo_count', models.PositiveIntegerField(
default=0, editable=False)), default=0, editable=False)),
('event_series', ('event_series',
models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, models.ForeignKey(
on_delete=django.db.models.deletion.SET_NULL,
editable=False, to='events.Event', editable=False, to='events.Event',
blank=True, blank=True,
help_text='Wenn dieser Termin zu einer Veranstaltungsreihe geh\xf6rt werden Ort, Beschreibung, Bild und Homepage von dem hier angegebenen Event \xfcbernommen.', help_text='Wenn dieser Termin zu einer Veranstaltungsreihe ' +
'geh\xf6rt werden Ort, Beschreibung, Bild und ' +
'Homepage von dem hier angegebenen Event \xfcbernommen.',
null=True, null=True,
verbose_name='Veranstaltungsreihen')), verbose_name='Veranstaltungsreihen')),
], ],
@@ -67,21 +72,19 @@ class Migration(migrations.Migration):
max_length=127, verbose_name='Stra\xdfe')), max_length=127, verbose_name='Stra\xdfe')),
('locality', ('locality',
models.CharField(max_length=127, verbose_name='Ort')), models.CharField(max_length=127, verbose_name='Ort')),
('country', models.CharField(max_length=2, verbose_name='Land', ('country', models.CharField(
choices=[(b'GB', max_length=2, verbose_name='Land',
'Vereinigtes K\xf6nigreich'), choices=[(b'GB', 'Vereinigtes K\xf6nigreich'),
(b'AF', 'Afghanistan'), (b'AF', 'Afghanistan'),
(b'AX', 'Aland Islands'), (b'AX', 'Aland Islands'),
(b'AL', 'Albanien'), (b'AL', 'Albanien'),
(b'DZ', 'Algerien'), ( (b'DZ', 'Algerien'),
b'AS', (b'AS', 'Amerikanisch-Samoa'),
'Amerikanisch-Samoa'),
(b'AD', 'Andorra'), (b'AD', 'Andorra'),
(b'AO', 'Angola'), (b'AO', 'Angola'),
(b'AI', 'Anguilla'), (b'AI', 'Anguilla'),
(b'AQ', 'Antarktika'), ( (b'AQ', 'Antarktika'),
b'AG', (b'AG', 'Antigua und Barbuda'),
'Antigua und Barbuda'),
(b'AR', 'Argentinien'), (b'AR', 'Argentinien'),
(b'AM', 'Armenien'), (b'AM', 'Armenien'),
(b'AW', 'Aruba'), (b'AW', 'Aruba'),
@@ -91,24 +94,20 @@ class Migration(migrations.Migration):
(b'BS', 'Bahamas'), (b'BS', 'Bahamas'),
(b'BH', 'Bahrein'), (b'BH', 'Bahrein'),
(b'BD', 'Bangladesch'), (b'BD', 'Bangladesch'),
(b'BB', 'Barbados'), ( (b'BB', 'Barbados'),
b'BY', (b'BY', 'Wei\xdfrussland'),
'Wei\xdfrussland'),
(b'BE', 'Belgien'), (b'BE', 'Belgien'),
(b'BZ', 'Belize'), (b'BZ', 'Belize'),
(b'BJ', 'Benin'), (b'BJ', 'Benin'),
(b'BM', 'Bermuda'), (b'BM', 'Bermuda'),
(b'BT', 'Bhutan'), (b'BT', 'Bhutan'),
(b'BO', 'Bolivien'), ( (b'BO', 'Bolivien'),
b'BA', (b'BA', 'Bosnien und Herzegowina'),
'Bosnien und Herzegowina'),
(b'BW', 'Botswana'), (b'BW', 'Botswana'),
(b'BV', 'Bouvet Island'), (b'BV', 'Bouvet Island'),
(b'BR', 'Brasilien'), ( (b'BR', 'Brasilien'),
b'IO', (b'IO', 'British Indian Ocean Territory'),
'British Indian Ocean Territory'), (b'BN', 'Brunei Darussalam'),
(b'BN',
'Brunei Darussalam'),
(b'BG', 'Bulgarien'), (b'BG', 'Bulgarien'),
(b'BF', 'Burkina Faso'), (b'BF', 'Burkina Faso'),
(b'BI', 'Burundi'), (b'BI', 'Burundi'),
@@ -117,51 +116,42 @@ class Migration(migrations.Migration):
(b'CA', 'Kanada'), (b'CA', 'Kanada'),
(b'CV', 'Cape Verde'), (b'CV', 'Cape Verde'),
(b'KY', 'Cayman Islands'), (b'KY', 'Cayman Islands'),
(b'CF', (b'CF', 'Zentralafrikanische Republik'),
'Zentralafrikanische Republik'),
(b'TD', 'Tschad'), (b'TD', 'Tschad'),
(b'CL', 'Chile'), (b'CL', 'Chile'),
(b'CN', 'China'), (b'CX', (b'CN', 'China'),
'Christmas Island'), (b'CX', 'Christmas Island'),
(b'CC', (b'CC', 'Cocos (Keeling) Islands'),
'Cocos (Keeling) Islands'),
(b'CO', 'Kolumbien'), (b'CO', 'Kolumbien'),
(b'KM', 'Komoren'), (b'KM', 'Komoren'),
(b'CG', 'Kongo'), (b'CD', (b'CG', 'Kongo'),
'Kongo, Demokratische Republik'), (b'CD', 'Kongo, Demokratische Republik'),
(b'CK', 'Cook-Inseln'), (b'CK', 'Cook-Inseln'),
(b'CR', 'Costa Rica'), (b'CR', 'Costa Rica'),
(b'CI', "Cote d'Ivoire"), (b'CI', "Cote d'Ivoire"),
(b'HR', 'Kroatien'), (b'HR', 'Kroatien'),
(b'CU', 'Kuba'), (b'CU', 'Kuba'),
(b'CY', 'Zypern'), (b'CZ', (b'CY', 'Zypern'),
'Tschechische Republik'), (b'CZ', 'Tschechische Republik'),
(b'DK', 'D\xe4nemark'), (b'DK', 'D\xe4nemark'),
(b'DJ', 'Dschibuti'), (b'DJ', 'Dschibuti'),
(b'DM', 'Dominica'), ( (b'DM', 'Dominica'),
b'DO', (b'DO', 'Dominikanische Republik'),
'Dominikanische Republik'),
(b'EC', 'Ecuador'), (b'EC', 'Ecuador'),
(b'EG', '\xc4gypten'), (b'EG', '\xc4gypten'),
(b'SV', 'El Salvador'), ( (b'SV', 'El Salvador'),
b'GQ', (b'GQ', '\xc4quatorial-Guinea'),
'\xc4quatorial-Guinea'),
(b'ER', 'Eritrea'), (b'ER', 'Eritrea'),
(b'EE', 'Estland'), (b'EE', 'Estland'),
(b'ET', '\xc4thiopien'), ( (b'ET', '\xc4thiopien'),
b'FK', (b'FK', 'Falklandinseln (Malvinas)'),
'Falklandinseln (Malvinas)'), (b'FO', 'F\xe4r\xf6er-Inseln'),
(b'FO',
'F\xe4r\xf6er-Inseln'),
(b'FJ', 'Fidschi'), (b'FJ', 'Fidschi'),
(b'FI', 'Finnland'), (b'FI', 'Finnland'),
(b'FR', 'Frankreich'), ( (b'FR', 'Frankreich'),
b'GF', (b'GF', 'Franz\xf6sisch-Guayana'),
'Franz\xf6sisch-Guayana'), (b'PF', 'Franz\xf6sisch-Polynesien'),
(b'PF', (b'TF', 'Franz\xf6sisch S\xfcdliche Territorien'),
'Franz\xf6sisch-Polynesien'),
(b'TF',
'Franz\xf6sisch S\xfcdliche Territorien'),
(b'GA', 'Gabun'), (b'GA', 'Gabun'),
(b'GM', 'Gambia'), (b'GM', 'Gambia'),
(b'GE', 'Georgia'), (b'GE', 'Georgia'),
@@ -178,18 +168,16 @@ class Migration(migrations.Migration):
(b'GN', 'Guinea'), (b'GN', 'Guinea'),
(b'GW', 'Guinea-Bissau'), (b'GW', 'Guinea-Bissau'),
(b'GY', 'Guyana'), (b'GY', 'Guyana'),
(b'HT', 'Haiti'), (b'HM', (b'HT', 'Haiti'),
'Heard und McDonald Inseln'), (b'HM', 'Heard und McDonald Inseln'),
(b'VA', (b'VA', 'Heiliger Stuhl (Vatikanstadt)'),
'Heiliger Stuhl (Vatikanstadt)'),
(b'HN', 'Honduras'), (b'HN', 'Honduras'),
(b'HK', 'Hongkong'), (b'HK', 'Hongkong'),
(b'HU', 'Ungarn'), (b'HU', 'Ungarn'),
(b'IS', 'Island'), (b'IS', 'Island'),
(b'IN', 'Indien'), (b'IN', 'Indien'),
(b'ID', 'Indonesien'), ( (b'ID', 'Indonesien'),
b'IR', (b'IR', 'Iran, Islamische Republik'),
'Iran, Islamische Republik'),
(b'IQ', 'Irak'), (b'IQ', 'Irak'),
(b'IE', 'Irland'), (b'IE', 'Irland'),
(b'IM', 'Isle of Man'), (b'IM', 'Isle of Man'),
@@ -201,39 +189,36 @@ class Migration(migrations.Migration):
(b'JO', 'Jordan'), (b'JO', 'Jordan'),
(b'KZ', 'Kasachstan'), (b'KZ', 'Kasachstan'),
(b'KE', 'Kenia'), (b'KE', 'Kenia'),
(b'KI', 'Kiribati'), ( (b'KI', 'Kiribati'),
b'KP', (b'KP', 'Korea, Demokratische Volksrepublik'),
'Korea, Demokratische Volksrepublik'), (b'KR', 'Korea, Republik'),
(
b'KR',
'Korea, Republik'),
(b'KW', 'Kuwait'), (b'KW', 'Kuwait'),
(b'KG', 'Kirgisistan'), ( (b'KG', 'Kirgisistan'),
b'LA', (b'LA', 'Lao Demokratischen Volksrepublik'),
'Lao Demokratischen Volksrepublik'),
(b'LV', 'Lettland'), (b'LV', 'Lettland'),
(b'LB', 'Libanon'), ( (b'LB', 'Libanon'),
b'LS', 'Lesotho'), (b'LS', 'Lesotho'),
(b'LR', 'Liberia'), (b'LR', 'Liberia'),
(b'LY', 'Libyen'), (b'LY', 'Libyen'),
(b'LI', 'Liechtenstein'), (b'LI', 'Liechtenstein'),
(b'LT', 'Litauen'), (b'LT', 'Litauen'),
(b'LU', 'Luxemburg'), (b'LU', 'Luxemburg'),
(b'MO', 'Macao'), (b'MK', (b'MO', 'Macao'),
(b'MK',
'Mazedonien, die ehemalige jugoslawische Republik'), 'Mazedonien, die ehemalige jugoslawische Republik'),
(b'MG', 'Madagaskar'), (b'MG', 'Madagaskar'),
(b'MW', 'Malawi'), (b'MW', 'Malawi'),
(b'MY', 'Malaysia'), (b'MY', 'Malaysia'),
(b'MV', 'Malediven'), (b'MV', 'Malediven'),
(b'ML', 'Mali'), (b'ML', 'Mali'),
(b'MT', 'Malta'), (b'MH', (b'MT', 'Malta'),
'Marshall Islands'), (b'MH', 'Marshall Islands'),
(b'MQ', 'Martinique'), (b'MQ', 'Martinique'),
(b'MR', 'Mauretanien'), (b'MR', 'Mauretanien'),
(b'MU', 'Mauritius'), (b'MU', 'Mauritius'),
(b'YT', 'Mayotte'), (b'YT', 'Mayotte'),
(b'MX', 'Mexiko'), (b'FM', (b'MX', 'Mexiko'),
'Mikronesien, F\xf6derierte Staaten von'), (b'FM', 'Mikronesien, F\xf6derierte Staaten von'),
(b'MD', 'Moldawien'), (b'MD', 'Moldawien'),
(b'MC', 'Monaco'), (b'MC', 'Monaco'),
(b'MN', 'Mongolei'), (b'MN', 'Mongolei'),
@@ -245,9 +230,8 @@ class Migration(migrations.Migration):
(b'NA', 'Namibia'), (b'NA', 'Namibia'),
(b'NR', 'Nauru'), (b'NR', 'Nauru'),
(b'NP', 'Nepal'), (b'NP', 'Nepal'),
(b'NL', 'Niederlande'), ( (b'NL', 'Niederlande'),
b'AN', (b'AN', 'Niederl\xe4ndische Antillen'),
'Niederl\xe4ndische Antillen'),
(b'NC', 'Neukaledonien'), (b'NC', 'Neukaledonien'),
(b'NZ', 'New Zealand'), (b'NZ', 'New Zealand'),
(b'NI', 'Nicaragua'), (b'NI', 'Nicaragua'),
@@ -260,11 +244,10 @@ class Migration(migrations.Migration):
(b'NO', 'Norwegen'), (b'NO', 'Norwegen'),
(b'OM', 'Oman'), (b'OM', 'Oman'),
(b'PK', 'Pakistan'), (b'PK', 'Pakistan'),
(b'PW', 'Palau'), (b'PS', (b'PW', 'Palau'),
'Pal\xe4stinensische Autonomiegebiete'), (b'PS', 'Pal\xe4stinensische Autonomiegebiete'),
(b'PA', 'Panama'), ( (b'PA', 'Panama'),
b'PG', (b'PG', 'Papua-Neuguinea'),
'Papua-Neuguinea'),
(b'PY', 'Paraguay'), (b'PY', 'Paraguay'),
(b'PE', 'Peru'), (b'PE', 'Peru'),
(b'PH', 'Philippinen'), (b'PH', 'Philippinen'),
@@ -272,26 +255,21 @@ class Migration(migrations.Migration):
(b'PL', 'Polen'), (b'PL', 'Polen'),
(b'PT', 'Portugal'), (b'PT', 'Portugal'),
(b'PR', 'Puerto Rico'), (b'PR', 'Puerto Rico'),
(b'QA', 'Katar'), (b'RE', (b'QA', 'Katar'),
'Wiedervereinigung'), (b'RE', 'Wiedervereinigung'),
(b'RO', 'Rum\xe4nien'), ( (b'RO', 'Rum\xe4nien'),
b'RU', (b'RU', 'Russischen F\xf6deration'),
'Russischen F\xf6deration'), (b'RW', 'Ruanda'),
(b'RW', 'Ruanda'), (b'BL', (b'BL', 'Saint Barthelemy'),
'Saint Barthelemy'), (b'SH', 'Saint Helena'),
(b'SH', 'Saint Helena'), ( (b'KN', 'Saint Kitts und Nevis'),
b'KN',
'Saint Kitts und Nevis'),
(b'LC', 'Santa Lucia'), (b'LC', 'Santa Lucia'),
(b'MF', 'Santa Martin'), ( (b'MF', 'Santa Martin'),
b'PM', (b'PM', 'Saint Pierre und Miquelon'),
'Saint Pierre und Miquelon'), (b'VC', 'Saint Vincent und die Grenadinen'),
(b'VC',
'Saint Vincent und die Grenadinen'),
(b'WS', 'Samoa'), (b'WS', 'Samoa'),
(b'SM', 'San Marino'), ( (b'SM', 'San Marino'),
b'ST', (b'ST', 'Sao Tome und Principe'),
'Sao Tome und Principe'),
(b'SA', 'Saudi-Arabien'), (b'SA', 'Saudi-Arabien'),
(b'SN', 'Senegal'), (b'SN', 'Senegal'),
(b'RS', 'Serbien'), (b'RS', 'Serbien'),
@@ -302,56 +280,46 @@ class Migration(migrations.Migration):
(b'SI', 'Slowenien'), (b'SI', 'Slowenien'),
(b'SB', 'Salomon-Inseln'), (b'SB', 'Salomon-Inseln'),
(b'SO', 'Somalia'), (b'SO', 'Somalia'),
(b'ZA', 'S\xfcdafrika'), ( (b'ZA', 'S\xfcdafrika'),
b'GS', (b'GS',
'S\xfcdgeorgien und die S\xfcdlichen Sandwichinseln'), 'S\xfcdgeorgien und die S\xfcdlichen Sandwichinseln'),
(b'ES', 'Spanien'), (b'ES', 'Spanien'),
(b'LK', 'Sri Lanka'), (b'LK', 'Sri Lanka'),
(b'SD', 'Sudan'), (b'SD', 'Sudan'),
(b'SR', 'Suriname'), ( (b'SR', 'Suriname'),
b'SJ', (b'SJ', 'Svalbard und Jan Mayen'),
'Svalbard und Jan Mayen'),
(b'SZ', 'Swaziland'), (b'SZ', 'Swaziland'),
(b'SE', 'Schweden'), (b'SE', 'Schweden'),
(b'CH', 'Schweiz'), ( (b'CH', 'Schweiz'),
b'SY', (b'SY', 'Arabische Republik Syrien'),
'Arabische Republik Syrien'),
(b'TW', (b'TW',
'Taiwan, Province of China'), 'Taiwan, Province of China'),
(b'TJ', 'Tadschikistan'), (b'TJ', 'Tadschikistan'),
(b'TZ', (b'TZ', 'Tansania, Vereinigte Republik'),
'Tansania, Vereinigte Republik'),
(b'TH', 'Thailand'), (b'TH', 'Thailand'),
(b'TL', 'Timor-Leste'), (b'TL', 'Timor-Leste'),
(b'TG', 'Togo'), (b'TG', 'Togo'),
(b'TK', 'Tokelau'), (b'TK', 'Tokelau'),
(b'TO', 'Tonga'), (b'TT', (b'TO', 'Tonga'),
'Trinidad und Tobago'), (b'TT', 'Trinidad und Tobago'),
(b'TN', 'Tunesien'), (b'TN', 'Tunesien'),
(b'TR', 'T\xfcrkei'), (b'TR', 'T\xfcrkei'),
(b'TM', 'Turkmenistan'), ( (b'TM', 'Turkmenistan'),
b'TC', (b'TC', 'Turks-und Caicosinseln'),
'Turks-und Caicosinseln'),
(b'TV', 'Tuvalu'), (b'TV', 'Tuvalu'),
(b'UG', 'Uganda'), (b'UG', 'Uganda'),
(b'UA', 'Ukraine'), ( (b'UA', 'Ukraine'),
b'AE', (b'AE', 'Vereinigte Arabische Emirate'),
'Vereinigte Arabische Emirate'), (b'US', 'Vereinigte Staaten'),
(b'US', (b'UM', 'United States Minor Outlying Islands'),
'Vereinigte Staaten'), (
b'UM',
'United States Minor Outlying Islands'),
(b'UY', 'Uruguay'), (b'UY', 'Uruguay'),
(b'UZ', 'Usbekistan'), (b'UZ', 'Usbekistan'),
(b'VU', 'Vanuatu'), (b'VU', 'Vanuatu'),
(b'VE', 'Venezuela'), (b'VE', 'Venezuela'),
(b'VN', 'Vietnam'), ( (b'VN', 'Vietnam'),
b'VG', (b'VG', 'Virgin Islands, British'),
'Virgin Islands, British'), (b'VI', 'Virgin Islands, US'),
(b'VI', (b'WF', 'Wallis und Futuna'),
'Virgin Islands, US'), (
b'WF',
'Wallis und Futuna'),
(b'EH', 'Westsahara'), (b'EH', 'Westsahara'),
(b'YE', 'Jemen'), (b'YE', 'Jemen'),
(b'ZM', 'Sambia'), (b'ZM', 'Sambia'),

View File

@@ -5,7 +5,6 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('events', '0001_initial'), ('events', '0001_initial'),
] ]
@@ -25,6 +24,9 @@ class Migration(migrations.Migration):
model_name='event', model_name='event',
name='mahjong_tournament', name='mahjong_tournament',
field=models.BooleanField( field=models.BooleanField(
default=False, help_text='Diese Veranstaltung ist ein Turnier, es gelten andere Regeln f\xfcr das Kyu Ranking.', verbose_name='Mahjong Tournament'), default=False,
help_text='Diese Veranstaltung ist ein Turnier, ' +
'es gelten andere Regeln f\xfcr das Kyu Ranking.',
verbose_name='Mahjong Tournament'),
), ),
] ]

View File

@@ -5,7 +5,6 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('events', '0002_auto_20150818_2139'), ('events', '0002_auto_20150818_2139'),
] ]
@@ -15,6 +14,9 @@ class Migration(migrations.Migration):
model_name='event', model_name='event',
name='mahjong_tournament', name='mahjong_tournament',
field=models.BooleanField( field=models.BooleanField(
default=False, help_text='Diese Veranstaltung ist ein Turnier, es gelten andere Regeln f\xfcr das Kyu Ranking.', verbose_name='Mahjong Turnier'), default=False,
help_text='Diese Veranstaltung ist ein Turnier, ' +
'es gelten andere Regeln f\xfcr das Kyu Ranking.',
verbose_name='Mahjong Turnier'),
), ),
] ]

View File

@@ -32,12 +32,14 @@ class Migration(migrations.Migration):
('description', models.TextField(max_length=300, ('description', models.TextField(max_length=300,
verbose_name='Beschreibung', verbose_name='Beschreibung',
blank=True)), blank=True)),
('on_startpage', models.BooleanField(default=False, ('on_startpage', models.BooleanField(
default=False,
help_text='Display this Photo on the Startpage Teaser', help_text='Display this Photo on the Startpage Teaser',
verbose_name='Startpage')), verbose_name='Startpage')),
('created_date', ('created_date',
models.DateTimeField(verbose_name='Published on')), models.DateTimeField(verbose_name='Published on')),
('views', models.PositiveIntegerField(default=0, ('views', models.PositiveIntegerField(
default=0,
verbose_name='Number of views', verbose_name='Number of views',
editable=False)), editable=False)),
], ],
@@ -67,7 +69,9 @@ class Migration(migrations.Migration):
field=models.ForeignKey( field=models.ForeignKey(
on_delete=django.db.models.deletion.SET_NULL, blank=True, on_delete=django.db.models.deletion.SET_NULL, blank=True,
to='events.Event', to='events.Event',
help_text='Wenn dieser Termin zu einer Veranstaltungsreihe geh\xf6rt werden Ort, Beschreibung, Bild und Homepage von dem hier angegebenen Event \xfcbernommen.', help_text='Wenn dieser Termin zu einer Veranstaltungsreihe geh\xf6rt ' +
'werden Ort, Beschreibung, Bild und Homepage von dem hier ' +
'angegebenen Event \xfcbernommen.',
null=True, verbose_name='Veranstaltungsreihen'), null=True, verbose_name='Veranstaltungsreihen'),
), ),
migrations.AlterField( migrations.AlterField(

View File

@@ -5,7 +5,6 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('events', '0004_auto_20150901_2204'), ('events', '0004_auto_20150901_2204'),
] ]
@@ -15,6 +14,7 @@ class Migration(migrations.Migration):
model_name='photo', model_name='photo',
name='on_startpage', name='on_startpage',
field=models.BooleanField( field=models.BooleanField(
default=False, help_text='Display this Photo on the Startpage Teaser', db_index=True, verbose_name='Startpage'), default=False, help_text='Display this Photo on the Startpage Teaser',
db_index=True, verbose_name='Startpage'),
), ),
] ]

View File

@@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('events', '0005_auto_20150907_2021'), ('events', '0005_auto_20150907_2021'),
] ]
@@ -14,7 +13,8 @@ class Migration(migrations.Migration):
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='photo', name='photo',
options={'ordering': ['created_date'], 'get_latest_by': 'created_date', options={'ordering': ['created_date'], 'get_latest_by': 'created_date',
'verbose_name': 'Veranstaltungsbild', 'verbose_name_plural': 'Veranstaltungsbilder'}, 'verbose_name': 'Veranstaltungsbild',
'verbose_name_plural': 'Veranstaltungsbilder'},
), ),
migrations.AlterField( migrations.AlterField(
model_name='event', model_name='event',
@@ -31,7 +31,9 @@ class Migration(migrations.Migration):
model_name='photo', model_name='photo',
name='on_startpage', name='on_startpage',
field=models.BooleanField( field=models.BooleanField(
default=False, help_text='Foto als Teaser auf der Startseite verwenden.', db_index=True, verbose_name='Startseite'), default=False,
help_text='Foto als Teaser auf der Startseite verwenden.',
db_index=True, verbose_name='Startseite'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='photo', model_name='photo',

View File

@@ -3,11 +3,10 @@ from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import datetime import datetime
from django.utils.timezone import utc from datetime import timezone
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('events', '0006_auto_20160916_1800'), ('events', '0006_auto_20160916_1800'),
] ]
@@ -17,39 +16,45 @@ class Migration(migrations.Migration):
model_name='event', model_name='event',
name='date_created', name='date_created',
field=models.DateTimeField( field=models.DateTimeField(
auto_now_add=True, null=True, verbose_name='first created at', db_index=True), auto_now_add=True, null=True, verbose_name='first created at',
db_index=True),
), ),
migrations.AddField( migrations.AddField(
model_name='event', model_name='event',
name='date_modified', name='date_modified',
field=models.DateTimeField(default=datetime.datetime( field=models.DateTimeField(default=datetime.datetime(
2016, 10, 12, 20, 24, 39, 910492, tzinfo=utc), verbose_name='latest updated at', auto_now=True), 2016, 10, 12, 20, 24, 39, 910492, tzinfo=timezone.utc),
verbose_name='latest updated at', auto_now=True),
preserve_default=False, preserve_default=False,
), ),
migrations.AddField( migrations.AddField(
model_name='location', model_name='location',
name='date_created', name='date_created',
field=models.DateTimeField( field=models.DateTimeField(
auto_now_add=True, null=True, verbose_name='first created at', db_index=True), auto_now_add=True, null=True, verbose_name='first created at',
db_index=True),
), ),
migrations.AddField( migrations.AddField(
model_name='location', model_name='location',
name='date_modified', name='date_modified',
field=models.DateTimeField(default=datetime.datetime( field=models.DateTimeField(default=datetime.datetime(
2016, 10, 12, 20, 24, 44, 566305, tzinfo=utc), verbose_name='latest updated at', auto_now=True), 2016, 10, 12, 20, 24, 44, 566305, tzinfo=timezone.utc),
verbose_name='latest updated at', auto_now=True),
preserve_default=False, preserve_default=False,
), ),
migrations.AddField( migrations.AddField(
model_name='photo', model_name='photo',
name='date_created', name='date_created',
field=models.DateTimeField( field=models.DateTimeField(
auto_now_add=True, null=True, verbose_name='first created at', db_index=True), auto_now_add=True, null=True, verbose_name='first created at',
db_index=True),
), ),
migrations.AddField( migrations.AddField(
model_name='photo', model_name='photo',
name='date_modified', name='date_modified',
field=models.DateTimeField(default=datetime.datetime( field=models.DateTimeField(default=datetime.datetime(
2016, 10, 12, 20, 24, 50, 509970, tzinfo=utc), verbose_name='latest updated at', auto_now=True), 2016, 10, 12, 20, 24, 50, 509970, tzinfo=timezone.utc),
verbose_name='latest updated at', auto_now=True),
preserve_default=False, preserve_default=False,
), ),
] ]

File diff suppressed because one or more lines are too long

View File

@@ -8,7 +8,6 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('events', '0008_auto_20171115_0653'), ('events', '0008_auto_20171115_0653'),
] ]
@@ -17,16 +16,19 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='event', model_name='event',
name='location', name='location',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='events.Location'), field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT,
to='events.Location'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='photo', model_name='photo',
name='event', name='event',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='events.Event'), field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT,
to='events.Event'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='photo', model_name='photo',
name='photographer', name='photographer',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL), field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT,
to=settings.AUTH_USER_MODEL),
), ),
] ]

View File

@@ -0,0 +1,158 @@
# Generated by Django 2.1.5 on 2019-01-06 18:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0009_auto_20171214_1215'),
]
operations = [
migrations.AlterField(
model_name='event',
name='date_created',
field=models.DateTimeField(auto_now_add=True, db_index=True, null=True,
verbose_name='erstellt am'),
),
migrations.AlterField(
model_name='event',
name='date_modified',
field=models.DateTimeField(auto_now=True,
verbose_name='letzte Aktualisierung am'),
),
migrations.AlterField(
model_name='location',
name='country',
field=models.CharField(
choices=[('GB', 'United Kingdom'), ('AF', 'Afghanistan'),
('AX', 'Aland Islands'), ('AL', 'Albania'), ('DZ', 'Algeria'),
('AS', 'American Samoa'), ('AD', 'Andorra'), ('AO', 'Angola'),
('AI', 'Anguilla'), ('AQ', 'Antarctica'),
('AG', 'Antigua and Barbuda'), ('AR', 'Argentina'),
('AM', 'Armenia'), ('AW', 'Aruba'), ('AU', 'Australia'),
('AT', 'Austria'), ('AZ', 'Azerbaijan'), ('BS', 'Bahamas'),
('BH', 'Bahrain'), ('BD', 'Bangladesh'), ('BB', 'Barbados'),
('BY', 'Belarus'), ('BE', 'Belgium'), ('BZ', 'Belize'),
('BJ', 'Benin'), ('BM', 'Bermuda'), ('BT', 'Bhutan'),
('BO', 'Bolivia'), ('BA', 'Bosnia and Herzegovina'),
('BW', 'Botswana'), ('BV', 'Bouvet Island'), ('BR', 'Brazil'),
('IO', 'British Indian Ocean Territory'),
('BN', 'Brunei Darussalam'), ('BG', 'Bulgaria'),
('BF', 'Burkina Faso'), ('BI', 'Burundi'), ('KH', 'Cambodia'),
('CM', 'Cameroon'), ('CA', 'Canada'), ('CV', 'Cape Verde'),
('KY', 'Cayman Islands'), ('CF', 'Central African Republic'),
('TD', 'Chad'), ('CL', 'Chile'), ('CN', 'China'),
('CX', 'Christmas Island'), ('CC', 'Cocos (Keeling) Islands'),
('CO', 'Colombia'), ('KM', 'Comoros'), ('CG', 'Congo'),
('CD', 'Congo, The Democratic Republic of the'),
('CK', 'Cook Islands'), ('CR', 'Costa Rica'),
('CI', "Cote d'Ivoire"), ('HR', 'Croatia'), ('CU', 'Cuba'),
('CY', 'Cyprus'), ('CZ', 'Czech Republic'), ('DK', 'Denmark'),
('DJ', 'Djibouti'), ('DM', 'Dominica'),
('DO', 'Dominican Republic'), ('EC', 'Ecuador'),
('EG', 'Egypt'), ('SV', 'El Salvador'),
('GQ', 'Equatorial Guinea'), ('ER', 'Eritrea'),
('EE', 'Estonia'), ('ET', 'Ethiopia'),
('FK', 'Falkland Islands (Malvinas)'), ('FO', 'Faroe Islands'),
('FJ', 'Fiji'), ('FI', 'Finland'), ('FR', 'France'),
('GF', 'French Guiana'), ('PF', 'French Polynesia'),
('TF', 'French Southern Territories'), ('GA', 'Gabon'),
('GM', 'Gambia'), ('GE', 'Georgia'), ('DE', 'Germany'),
('GH', 'Ghana'), ('GI', 'Gibraltar'), ('GR', 'Greece'),
('GL', 'Greenland'), ('GD', 'Grenada'), ('GP', 'Guadeloupe'),
('GU', 'Guam'), ('GT', 'Guatemala'), ('GG', 'Guernsey'),
('GN', 'Guinea'), ('GW', 'Guinea-Bissau'), ('GY', 'Guyana'),
('HT', 'Haiti'), ('HM', 'Heard Island and McDonald Islands'),
('VA', 'Holy See (Vatican City State)'), ('HN', 'Honduras'),
('HK', 'Hong Kong'), ('HU', 'Hungary'), ('IS', 'Iceland'),
('IN', 'India'), ('ID', 'Indonesia'),
('IR', 'Iran, Islamic Republic of'), ('IQ', 'Iraq'),
('IE', 'Ireland'), ('IM', 'Isle of Man'), ('IL', 'Israel'),
('IT', 'Italy'), ('JM', 'Jamaica'), ('JP', 'Japan'),
('JE', 'Jersey'), ('JO', 'Jordan'), ('KZ', 'Kazakhstan'),
('KE', 'Kenya'), ('KI', 'Kiribati'),
('KP', "Korea, Democratic People's Republic of"),
('KR', 'Korea, Republic of'), ('KW', 'Kuwait'),
('KG', 'Kyrgyzstan'),
('LA', "Lao People's Democratic Republic"), ('LV', 'Latvia'),
('LB', 'Lebanon'), ('LS', 'Lesotho'), ('LR', 'Liberia'),
('LY', 'Libyan Arab Jamahiriya'), ('LI', 'Liechtenstein'),
('LT', 'Lithuania'), ('LU', 'Luxembourg'), ('MO', 'Macao'),
('MK', 'Macedonia, The Former Yugoslav Republic of'),
('MG', 'Madagascar'), ('MW', 'Malawi'), ('MY', 'Malaysia'),
('MV', 'Maldives'), ('ML', 'Mali'), ('MT', 'Malta'),
('MH', 'Marshall Islands'), ('MQ', 'Martinique'),
('MR', 'Mauritania'), ('MU', 'Mauritius'), ('YT', 'Mayotte'),
('MX', 'Mexico'), ('FM', 'Micronesia, Federated States of'),
('MD', 'Moldova'), ('MC', 'Monaco'), ('MN', 'Mongolia'),
('ME', 'Montenegro'), ('MS', 'Montserrat'), ('MA', 'Morocco'),
('MZ', 'Mozambique'), ('MM', 'Myanmar'), ('NA', 'Namibia'),
('NR', 'Nauru'), ('NP', 'Nepal'), ('NL', 'Netherlands'),
('AN', 'Netherlands Antilles'), ('NC', 'New Caledonia'),
('NZ', 'New Zealand'), ('NI', 'Nicaragua'), ('NE', 'Niger'),
('NG', 'Nigeria'), ('NU', 'Niue'), ('NF', 'Norfolk Island'),
('MP', 'Northern Mariana Islands'), ('NO', 'Norway'),
('OM', 'Oman'), ('PK', 'Pakistan'), ('PW', 'Palau'),
('PS', 'Palestinian Territory, Occupied'), ('PA', 'Panama'),
('PG', 'Papua New Guinea'), ('PY', 'Paraguay'), ('PE', 'Peru'),
('PH', 'Philippines'), ('PN', 'Pitcairn'), ('PL', 'Poland'),
('PT', 'Portugal'), ('PR', 'Puerto Rico'), ('QA', 'Qatar'),
('RE', 'Reunion'), ('RO', 'Romania'),
('RU', 'Russian Federation'), ('RW', 'Rwanda'),
('BL', 'Saint Barthelemy'), ('SH', 'Saint Helena'),
('KN', 'Saint Kitts and Nevis'), ('LC', 'Saint Lucia'),
('MF', 'Saint Martin'), ('PM', 'Saint Pierre and Miquelon'),
('VC', 'Saint Vincent and the Grenadines'), ('WS', 'Samoa'),
('SM', 'San Marino'), ('ST', 'Sao Tome and Principe'),
('SA', 'Saudi Arabia'), ('SN', 'Senegal'), ('RS', 'Serbia'),
('SC', 'Seychelles'), ('SL', 'Sierra Leone'),
('SG', 'Singapore'), ('SK', 'Slovakia'), ('SI', 'Slovenia'),
('SB', 'Solomon Islands'), ('SO', 'Somalia'),
('ZA', 'South Africa'),
('GS', 'South Georgia and the South Sandwich Islands'),
('ES', 'Spain'), ('LK', 'Sri Lanka'), ('SD', 'Sudan'),
('SR', 'Suriname'), ('SJ', 'Svalbard and Jan Mayen'),
('SZ', 'Swaziland'), ('SE', 'Sweden'), ('CH', 'Switzerland'),
('SY', 'Syrian Arab Republic'),
('TW', 'Taiwan, Province of China'), ('TJ', 'Tajikistan'),
('TZ', 'Tanzania, United Republic of'), ('TH', 'Thailand'),
('TL', 'Timor-Leste'), ('TG', 'Togo'), ('TK', 'Tokelau'),
('TO', 'Tonga'), ('TT', 'Trinidad and Tobago'),
('TN', 'Tunisia'), ('TR', 'Turkey'), ('TM', 'Turkmenistan'),
('TC', 'Turks and Caicos Islands'), ('TV', 'Tuvalu'),
('UG', 'Uganda'), ('UA', 'Ukraine'),
('AE', 'United Arab Emirates'), ('US', 'United States'),
('UM', 'United States Minor Outlying Islands'),
('UY', 'Uruguay'), ('UZ', 'Uzbekistan'), ('VU', 'Vanuatu'),
('VE', 'Venezuela'), ('VN', 'Viet Nam'),
('VG', 'Virgin Islands, British'),
('VI', 'Virgin Islands, U.S.'), ('WF', 'Wallis and Futuna'),
('EH', 'Western Sahara'), ('YE', 'Yemen'), ('ZM', 'Zambia'),
('ZW', 'Zimbabwe')], max_length=2, verbose_name='Land'),
),
migrations.AlterField(
model_name='location',
name='date_created',
field=models.DateTimeField(auto_now_add=True, db_index=True, null=True,
verbose_name='erstellt am'),
),
migrations.AlterField(
model_name='location',
name='date_modified',
field=models.DateTimeField(auto_now=True,
verbose_name='letzte Aktualisierung am'),
),
migrations.AlterField(
model_name='photo',
name='date_created',
field=models.DateTimeField(auto_now_add=True, db_index=True, null=True,
verbose_name='erstellt am'),
),
migrations.AlterField(
model_name='photo',
name='date_modified',
field=models.DateTimeField(auto_now=True,
verbose_name='letzte Aktualisierung am'),
),
]

View File

@@ -1,6 +1,10 @@
"""Mixins for Events.""" """Mixins for Events."""
from django.core.exceptions import ImproperlyConfigured
from django.db.models import Q
from django.http import Http404 from django.http import Http404
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.utils.translation import gettext as _
from . import models from . import models
@@ -29,19 +33,19 @@ class EventDetailMixin(object):
event = None event = None
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
"""Add self.event or the related event of self.object to the template """Adds this or the related event of the given object to the
context. template context.
:return: TemplateContext object""" :return: TemplateContext object"""
context = super(EventDetailMixin, self).get_context_data(**kwargs) context = super(EventDetailMixin, self).get_context_data(**kwargs)
if hasattr(self, 'event'): if getattr(self, 'event'):
context['event'] = self.event context['event'] = self.event
elif hasattr(self, 'object') and isinstance(self.object, models.Event): elif isinstance(getattr(self, 'object'), models.Event):
context['event'] = self.object context['event'] = self.object
elif hasattr(self, 'object') and hasattr(self.object, 'event'): elif getattr(getattr(self, 'object'), 'event'):
context['event'] = self.object.event context['event'] = self.object.event
else: else:
print("No Event in Context!") raise ImproperlyConfigured("No Event in Context!")
return context return context
def get_queryset(self): def get_queryset(self):
@@ -52,8 +56,32 @@ class EventDetailMixin(object):
""" """
if self.model == models.Event: if self.model == models.Event:
self.event = get_object_or_404(models.Event, pk=self.kwargs['pk']) self.event = get_object_or_404(models.Event, pk=self.kwargs['pk'])
queryset = self.model.objects.all() queryset = self.model._default_manager.all()
else: elif self.kwargs.get('event'):
self.event = get_object_or_404(models.Event, pk=self.kwargs['event']) self.event = get_object_or_404(
models.Event,
pk=self.kwargs['event'])
queryset = self.model.objects.filter(event=self.event) queryset = self.model.objects.filter(event=self.event)
elif self.model:
queryset = self.model._default_manager.all()
else:
raise ImproperlyConfigured(
"%(cls)s is missing a QuerySet. Define "
"%(cls)s.model, %(cls)s.queryset, or override "
"%(cls)s.get_queryset()." % {
'cls': self.__class__.__name__
})
return queryset.prefetch_related() return queryset.prefetch_related()
class EventPhotoMixin(EventDetailMixin):
def get_queryset(self):
try:
self.event = models.Event.objects.get(id=self.kwargs['event'])
return models.Photo.objects.filter(
Q(event=self.event) |
Q(event__event_series=self.event)
)
except models.Event.DoesNotExist:
raise Http404(_('Event does not exist'))

View File

@@ -1,4 +1,4 @@
"""Models to solitary events, events series with an location and photos.""" """Models to solitary events, events series with a location and photos."""
import os import os
from ckeditor.fields import RichTextField from ckeditor.fields import RichTextField
@@ -9,21 +9,22 @@ from django.db.models import Q
from django.template.defaultfilters import slugify from django.template.defaultfilters import slugify
from django.urls import reverse from django.urls import reverse
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
from easy_thumbnails.fields import ThumbnailerImageField from easy_thumbnails.fields import ThumbnailerImageField
from utils import COUNTRIES, OverwriteStorage from utils import COUNTRIES, OverwriteStorage
from .managers import EventManager from .managers import EventManager
def get_upload_path(instance, filename): def get_upload_path(instance: models.Model, filename: str) -> str:
""" """
Generates the desired file path and filename for an uploaded Image. Generates the desired file path and filename for an uploaded Image.
With this function Django can save the uploaded images to subfolders that With this function Django can save the uploaded images to a subfolder that
also have a meaning for humans. also have a meaning for humans.
@param instance: an Django Object for which the Image has been uploaded. @return: String: path for the image.
@type instance: a instace of an models.Model sub-class. @param instance: a Django Object for which the Image has been uploaded.
@type instance: an instance of a models.Model subclass.
@param filename: The filename of the uploaded image. @param filename: The filename of the uploaded image.
@type filename: String @type filename: String
""" """
@@ -48,7 +49,7 @@ def get_upload_path(instance, filename):
class Event(models.Model): class Event(models.Model):
"""An Event that could be a tournament, a game session, or an convention.""" """An Event that could be a tournament, a game session, or a convention."""
name = models.CharField(_('Name'), max_length=255) name = models.CharField(_('Name'), max_length=255)
description = RichTextField(_("Description"), blank=True) description = RichTextField(_("Description"), blank=True)
location = models.ForeignKey('Location', on_delete=models.PROTECT) location = models.ForeignKey('Location', on_delete=models.PROTECT)
@@ -108,7 +109,7 @@ class Event(models.Model):
try: try:
return "%(name)s (%(date)s)" % {'name': self.name, return "%(name)s (%(date)s)" % {'name': self.name,
'date': self.start.date()} 'date': self.start.date()}
except: except AttributeError:
return "New Event Model" return "New Event Model"
def clean(self): def clean(self):
@@ -131,7 +132,7 @@ class Event(models.Model):
'year': self.start.strftime('%Y'), 'year': self.start.strftime('%Y'),
'month': self.start.strftime('%m') 'month': self.start.strftime('%m')
} }
return reverse('eventseries-form', kwargs=kwargs) return reverse('event-series-form', kwargs=kwargs)
def get_edit_url(self): def get_edit_url(self):
kwargs = { kwargs = {
@@ -167,7 +168,7 @@ class Event(models.Model):
self.photo_count = self.photo_set.count() self.photo_count = self.photo_set.count()
super(Event, self).save(**kwargs) super(Event, self).save(**kwargs)
# Update the Hanchans if necesery: # Update the Hanchans if necessary:
for hanchan in self.hanchan_set.all(): for hanchan in self.hanchan_set.all():
hanchan.save() hanchan.save()
@@ -267,7 +268,7 @@ class Photo(models.Model):
return os.path.basename(self.image.name) return os.path.basename(self.image.name)
def rotate(self, rotate): def rotate(self, rotate):
# TODO: Eine vernüftigte Methode ohne viele Abhängigkeiten finden um # TODO: Eine vernünftige Methode ohne viele Abhängigkeiten finden um
# die Bilder bei Bedarf zu drehen. # die Bilder bei Bedarf zu drehen.
if rotate == 'clockwise': if rotate == 'clockwise':
pass pass

View File

@@ -1,16 +1,16 @@
"""To geneate a Sitemap with all events."""
from kasu.sitemaps import GenericSitemap from kasu.sitemaps import GenericSitemap
from django.utils import timezone
from .models import Event from .models import Event
class EventSitemap(GenericSitemap): class EventSitemap(GenericSitemap):
"""sitemap to help indexing all events on this site.""" """sitemap to help indexing all events on this site."""
changefreq = "never" changefreq: str = "never"
protocol = 'https' protocol: str = 'https'
priority_field = 'start' priority_field: str = 'start'
@staticmethod @staticmethod
def items(): def items(**kwargs) -> Event:
"""add all upcoming and archived events to the sitemap.""" """add all upcoming and archived events to the sitemap.
@param **kwargs:
"""
return Event.objects.all() return Event.objects.all()

View File

@@ -40,7 +40,7 @@
<h4><a href="{{ event.get_absolute_url }}">{{ event.name }}</a></h4> <h4><a href="{{ event.get_absolute_url }}">{{ event.name }}</a></h4>
<p> <p>
<span class="fa fa-calendar-o" title="{% trans 'Date' %}" aria-label="{% trans 'Date' %}"></span> <span class="fa fa-calendar-o" title="{% trans 'Date' %}" aria-label="{% trans 'Date' %}"></span>
<time datetime="{{event.start|date:'c'}}"> <time datetime="{{event.start|date:'Y-m-d\TH:i:sO'}}">
{{ event.start|date:'D' }} {{ event.start|date:'D' }}
{{ event.start|date:'SHORT_DATE_FORMAT' }} {{hanchan.start|time:'H:i'}} {{ event.start|date:'SHORT_DATE_FORMAT' }} {{hanchan.start|time:'H:i'}}
</time> </time>
@@ -66,4 +66,5 @@
</div> </div>
{% endfor %} {% endfor %}
{% endfor %} {% endfor %}
{% block form %}{% endblock %}
{% endblock %} {% endblock %}

View File

@@ -10,7 +10,7 @@
{% include "form.html" %} {% include "form.html" %}
{% if event.id and event.event_set.count %} {% if event.id and event.event_set.count %}
<p class="warning"> <p class="warning">
<strong>Achtung! Das ist eine Veranstaltungsreihe!</strong> Diese kann man im Moment nur im Admin-Interface vernünfig bearbeiten.<br /> <strong>Achtung! Das ist eine Veranstaltungsreihe!</strong> Diese kann man im Moment nur im Admin-Interface vernünftig bearbeiten.<br />
Du bearbeitest hier den "Hauptevent" der Reihe ({{event.event_set.count}}). Alle Änderungen (abgesehen von Name, Start und Ende) werden von den darauf folgendem Veranstaltungen übernommen. Du bearbeitest hier den "Hauptevent" der Reihe ({{event.event_set.count}}). Alle Änderungen (abgesehen von Name, Start und Ende) werden von den darauf folgendem Veranstaltungen übernommen.
</p> </p>
{% endif %} {% endif %}

View File

@@ -20,7 +20,7 @@
<h4><a href="{{ event.get_absolute_url }}">{{ event.name }}</a></h4> <h4><a href="{{ event.get_absolute_url }}">{{ event.name }}</a></h4>
<ul class="info"> <ul class="info">
<li><span class="fa fa-calendar-o" title="{% trans 'Date' %}" aria-label="{% trans 'Date' %}"></span> <li><span class="fa fa-calendar-o" title="{% trans 'Date' %}" aria-label="{% trans 'Date' %}"></span>
<time datetime="{{event.start|date:'c'}}"> <time datetime="{{event.start|date:'Y-m-d\TH:i:sO'}}">
{{ event.start|date:'D' }} {{ event.start|date:'D' }}
{{ event.start|date:'SHORT_DATE_FORMAT' }} {{ event.start|date:'SHORT_DATE_FORMAT' }}
</time> </time>

View File

@@ -5,7 +5,7 @@
<form action="" method="post" class="grid_12"> <form action="" method="post" class="grid_12">
{% csrf_token %} {% csrf_token %}
<header> <header>
<h1 class="grid_12">Dieses Photo wirklich löschen?</h1> <h1 class="grid_12">Dieses Foto wirklich löschen?</h1>
</header> </header>
<p>Sind Sie sicher, dass Sie das Bild &ldquo;{{photo.name}}&rdquo; löschen wollen?</p> <p>Sind Sie sicher, dass Sie das Bild &ldquo;{{photo.name}}&rdquo; löschen wollen?</p>
<img src="{{photo.image|thumbnail_url:'display'}}" alt="{{photo.name}}" title="{{photo.name}}" class="grid_10 push_1"/> <img src="{{photo.image|thumbnail_url:'display'}}" alt="{{photo.name}}" title="{{photo.name}}" class="grid_10 push_1"/>

View File

@@ -59,9 +59,6 @@ if ($('a.next').attr('href')) {
<a class="button" href="http://facebook.com/sharer.php?u=http%3A%2F%2Fwww.kasu.at{{photo.get_absolute_url|urlencode}}" target="_blank" rel="nofollow"> <a class="button" href="http://facebook.com/sharer.php?u=http%3A%2F%2Fwww.kasu.at{{photo.get_absolute_url|urlencode}}" target="_blank" rel="nofollow">
<span class="fa fa-twitter"></span>Facebook <span class="fa fa-twitter"></span>Facebook
</a> </a>
<a class="button" href="https://m.google.com/app/plus/x/?v=compose&amp;content={{photo.headline|urlencode}}+-+http%3A%2F%2Fwww.kasu.at/{{photo.get_absolute_url|urlencode}}" target="_blank" rel="nofollow">
<span class="fa fa-google-plus"></span> Google+
</a>
<a class="button" href="https://twitter.com/share?url=http%3A%2F%2Fwww.kasu.at/{{photo.get_absolute_url|urlencode}}" target='_blank' rel="nofollow"> <a class="button" href="https://twitter.com/share?url=http%3A%2F%2Fwww.kasu.at/{{photo.get_absolute_url|urlencode}}" target='_blank' rel="nofollow">
<span class="fa fa-twitter"></span> Twitter <span class="fa fa-twitter"></span> Twitter
</a> </a>

View File

@@ -1,46 +1,10 @@
{% extends "base.html" %} {% extends "events/event_archive.html" %}
{% load i18n comments thumbnail %} {% load i18n comments thumbnail %}
{% block maincontent %} {% block bottom %}
{% for event in event_list %}
{% get_comment_count for event as comment_count %}
{% ifchanged %}<h3 class="grid_12">{{ event.start|date:'F Y' }}</h3>{% endifchanged %}
<div style="float:left">
<a href="{% url 'event-photo-list' event.pk %}"><img src="{{ event.get_image|thumbnail_url:'thumbnail' }}" alt="" class="thumbnail"/></a>
<div class="grid_4" />
<h4><a href="{% url 'event-photo-list' event.pk %}">{{ event.name }}</a></h4>
<div class="info">
<span class="fa fa-calendar-o" title="{% trans 'Start' %}"></span>
{{ event.start|date }}
{% if event.end %}
{% trans "from" %} {{ event.start|time:'H:i' }} {% trans "to" %} {{ event.end|time:'H:i' }}
{% else %}
{{ event.start|time:'H:i' }}
{% endif %}
</div>
{% if event.description %}<p>{{event.description}}</p>{% endif %}
<div class="info">
<span class="fa fa-map-marker" title="{% trans 'Location' %}"></span>
{{ event.location }}
<span class="fa fa-comments" title="{% trans 'Comments' %}"></span>
<a href="{{event.get_absolute_url}}#comments">{{ comment_count }} {% trans 'Comments' %}</a>
<span class="fa fa-camera-retro" title="{% trans 'Photos' %}"></span>
<a href="{% url 'event-photo-list' event.pk %}">{{ event.photo_count }} {% trans 'Photos' %}</a>
</div>
<p style="text-align:right">
{% if perms.events.add_photo %}
<a href="{% url 'event-photo-list' event.pk %}" class="button">
<span class="fa fa-cloud-upload"></span>
{%trans "Upload" %}</a>
{% endif %}
</p>
</div>
{% endfor %}
<form method="post" enctype="multipart/form-data"> <form method="post" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<fieldset class="grid_8 push_2"> <fieldset id="bottom_buttonbar" class="grid_12">
<legend>Photos hochladen</legend> <legend>Photos hochladen</legend>
{% include "form.html" %} {% include "form.html" %}
<p class="buttonbar"> <p class="buttonbar">
@@ -52,5 +16,5 @@
</fieldset> </fieldset>
</form> </form>
{% endblock %} {% endblock %}
{% block buttonbar %}
{% endblock %}

View File

@@ -1,20 +1,19 @@
""" """
This file should test the functionality of the events app using the unittest This file should test the functionality of the events app using the unittest
module. These will pass when you run "manage.py test". module. These will pass when you run "manage.py test".
useful tests have to be written yet. sorry!
Usefull tests have to been written yet. sorry!
""" """
from django.test import TestCase from django.test import TestCase
class EventTest(TestCase): class EventTest(TestCase):
""" Here we should test the creation and modifiaction of Events. """ """ Here we should test the creation and modification of Events. """
class LocationTest(TestCase): class LocationTest(TestCase):
""" Here we should test the creation and modifiaction of Locations. """ """ Here we should test the creation and modification of Locations. """
class PhotoTest(TestCase): class PhotoTest(TestCase):
""" Here we should test the creation and modifiaction of Photos. """ """ Here we should test the creation and modification of Photos. """

View File

@@ -1,22 +1,21 @@
"""URLS to access upcoming events and the event archive.""" """URLS to access upcoming events and the event archive."""
from django.conf.urls import url from django.urls import path
from django.views.generic import RedirectView from django.views.generic import RedirectView
from . import views from . import views
urlpatterns = [ urlpatterns = [
url(r'^$', RedirectView.as_view(url='/events/upcoming/', permanent=True)), path("", RedirectView.as_view(url='/events/upcoming/', permanent=True)),
url(r'^(?P<year>[\d]{4})/$', views.EventArchiveYear.as_view(), path('<int:year>/', views.EventArchiveYear.as_view(), name='event-archive'),
path('<int:year>/<int:month>/', views.EventArchiveMonth.as_view(),
name='event-archive'), name='event-archive'),
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/$', path('<int:year>/<int:month>/<int:pk>/', views.EventDetail.as_view(),
views.EventArchiveMonth.as_view(), name='event-detail'),
name='event-archive'), path('<int:year>/<int:month>/<int:pk>/add_dates/',
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/(?P<pk>[\d]+)/$', views.EventSeriesForm.as_view(),
views.EventDetail.as_view(), name='event-detail'), name='event-series-form'),
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/(?P<pk>[\d]+)/add_dates/$', path('<int:year>/<int:month>/<int:pk>/edit/', views.EventForm.as_view(),
views.EventSeriesForm.as_view(), name='eventseries-form'), name='event-form'),
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/(?P<pk>[\d]+)/edit/$', path('add/', views.EventForm.as_view(), name='event-form'),
views.EventForm.as_view(), name='event-form'), path('archive/', views.EventArchiveIndex.as_view(), name='event-archive'),
url(r'^add/$', views.EventForm.as_view(), name='event-form'), path('upcoming/', views.UpcomingEvents.as_view(), name='upcoming-events'),
url(r'^archive/$', views.EventArchiveIndex.as_view(), name='event-archive'),
url(r'^upcoming/$', views.UpcomingEvents.as_view(), name='upcoming-events'),
] ]

View File

@@ -4,11 +4,10 @@ from datetime import timedelta
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.urls import reverse from django.urls import reverse
from django.db.models import Q from django.http import HttpResponse
from django.http import HttpResponse, Http404 from django.shortcuts import redirect, get_object_or_404
from django.shortcuts import redirect
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
from django.views import generic from django.views import generic
from extra_views import InlineFormSetView from extra_views import InlineFormSetView
from icalendar import Calendar, Event from icalendar import Calendar, Event
@@ -49,19 +48,16 @@ class EventDetail(mixins.EventDetailMixin, generic.DetailView):
class EventForm(PermissionRequiredMixin, mixins.EventDetailMixin, class EventForm(PermissionRequiredMixin, mixins.EventDetailMixin,
generic.UpdateView): generic.UpdateView):
"""Frontend formular to add or edit a Event.""" """Frontend formular to add or edit an Event."""
form_class = forms.EventForm form_class = forms.EventForm
template_name = 'events/event_form.html' template_name = 'events/event_form.html'
permission_required = 'events.add_event' permission_required = 'events.add_event'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
"""Dynamicle set the title to Add or Edit Event, depanding if an """sets the title to add or edit Event,
event ID was given, or not.""" depending on the fact if an event ID was given."""
context = super(EventForm, self).get_context_data(**kwargs) context = super(EventForm, self).get_context_data(**kwargs)
if self.kwargs.get('pk'): context['title'] = _("Edit Event") if self.kwargs.get('pk') else _("Add Event")
context['title'] = _("Edit Event")
else:
context['title'] = _("Add Event")
return context return context
def get_object(self, queryset=None): def get_object(self, queryset=None):
@@ -73,19 +69,22 @@ class EventForm(PermissionRequiredMixin, mixins.EventDetailMixin,
class EventGallery(generic.ListView): class EventGallery(generic.ListView):
"""Display a overview of all event photo albums.""" """Display an overview of all event photo albums."""
template_name = 'events/photo_gallery.html' template_name = 'events/photo_gallery.html'
paginate_by = 24
def get_queryset(self):
queryset = models.Event.objects.filter( queryset = models.Event.objects.filter(
start__lt=timezone.now(), start__lt=timezone.now(),
event_series__isnull=True, event_series__isnull=True,
photo_count__gt=0 photo_count__gt=0
) )
queryset = queryset.order_by('-start') queryset = queryset.order_by('-start')
paginate_by = 24 return queryset
class EventListIcal(generic.View): class EventListIcal(generic.View):
"""Generates an returns an iCal File with all upcoming events.""" """Generates and returns an iCal File with all upcoming events."""
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
"""Add all upcoming events to an .ics file and send it.""" """Add all upcoming events to an .ics file and send it."""
@@ -110,7 +109,7 @@ class EventListIcal(generic.View):
return response return response
class EventPhoto(mixins.EventDetailMixin, generic.UpdateView): class EventPhoto(mixins.EventPhotoMixin, generic.UpdateView):
"""Display the requested Photo and allows rotation if the user has change """Display the requested Photo and allows rotation if the user has change
permissions.""" permissions."""
form_class = forms.EditPhotoForm form_class = forms.EditPhotoForm
@@ -127,8 +126,8 @@ class EventPhoto(mixins.EventDetailMixin, generic.UpdateView):
return generic.UpdateView.post(self, request, *args, **kwargs) return generic.UpdateView.post(self, request, *args, **kwargs)
class EventPhotoList(mixins.EventDetailMixin, generic.ListView): class EventPhotoList(mixins.EventPhotoMixin, generic.ListView):
"""List all Photos of the event or event series in an album.""" """List all Photos of the event or event series."""
context_object_name = 'photo_list' context_object_name = 'photo_list'
event = None event = None
paginate_by = 36 paginate_by = 36
@@ -139,16 +138,6 @@ class EventPhotoList(mixins.EventDetailMixin, generic.ListView):
initial={'event': self.event, 'photographer': self.request.user}) initial={'event': self.event, 'photographer': self.request.user})
return context return context
def get_queryset(self):
try:
self.event = models.Event.objects.get(id=self.kwargs['event'])
return models.Photo.objects.filter(
Q(event=self.event) |
Q(event__event_series=self.event)
)
except models.Event.DoesNotExist:
raise Http404(_('Event does not exist'))
class EventPhotoUpload(mixins.EventDetailMixin, generic.FormView): class EventPhotoUpload(mixins.EventDetailMixin, generic.FormView):
form_class = forms.PhotoUploadForm form_class = forms.PhotoUploadForm
@@ -187,20 +176,21 @@ class EventSeriesForm(mixins.EventDetailMixin, PermissionRequiredMixin,
InlineFormSetView): InlineFormSetView):
model = models.Event model = models.Event
inline_model = models.Event inline_model = models.Event
fk_name = 'event_series'
fields = ('start', 'end') fields = ('start', 'end')
form_class = forms.EventForm form_class = forms.EventForm
extra = 3 factory_kwargs = {'extra': 3, 'fk_name': 'event_series'}
permission_required = 'events.add_event' permission_required = 'events.add_event'
template_name = 'events/eventseries_form.html' template_name = 'events/eventseries_form.html'
def get_object(self, queryset=None): def get_object(self, queryset=None):
self.event = models.Event.objects.get(pk=self.kwargs['pk']) self.event = get_object_or_404(models.Event, pk=self.kwargs['pk'])
if self.event.event_series: if self.event.event_series:
self.event = self.event.event_series self.event = self.event.event_series
return self.event return self.event
class UpcomingEvents(generic.ListView): class UpcomingEvents(generic.ListView):
queryset = models.Event.objects.upcoming(limit=None)
paginate_by = 16 paginate_by = 16
def get_queryset(self):
return models.Event.objects.upcoming(limit=None)

View File

@@ -7,9 +7,9 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: kasu.utils\n" "Project-Id-Version: kasu.utils\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-08 00:17+0200\n" "POT-Creation-Date: 2023-08-07 20:38+0200\n"
"PO-Revision-Date: 2016-09-28 00:24+0200\n" "PO-Revision-Date: 2018-12-30 11:14+0105\n"
"Last-Translator: Christian Berg <xeniac.at@gmail.com>\n" "Last-Translator: b' <kasu@xendynastie.at>'\n"
"Language-Team: Kasu <verein@kasu.at>\n" "Language-Team: Kasu <verein@kasu.at>\n"
"Language: de\n" "Language: de\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@@ -17,172 +17,183 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 1.8.9\n" "X-Generator: Poedit 1.8.9\n"
"X-Translated-Using: django-rosetta 0.7.6\n" "X-Translated-Using: django-rosetta 0.9.0\n"
#: .\kasu\settings.py:141 #: settings.py:144
msgid "German" msgid "German"
msgstr "Deutsch" msgstr "Deutsch"
#: .\kasu\settings.py:141 #: settings.py:144
msgid "English" msgid "English"
msgstr "Englisch" msgstr "Englisch"
#: .\kasu\templates\404.html:8 #: templates/404.html:8
msgid "The page your requested does not exist on this server." msgid "The page your requested does not exist on this server."
msgstr "Die angeforderte Seite existiert auf diesem Server nicht." msgstr "Die angeforderte Seite existiert auf diesem Server nicht."
#: .\kasu\templates\base.html:22 #: templates/base.html:22
msgid "Current News" msgid "Current News"
msgstr "Aktuelle Neuigkeiten" msgstr "Aktuelle Neuigkeiten"
#: .\kasu\templates\base.html:24 .\kasu\templates\index.html:40 #: templates/base.html:24 templates/index.html:40
msgid "Recent Comments" msgid "Recent Comments"
msgstr "Kürzliche Kommentare" msgstr "Kürzliche Kommentare"
#: .\kasu\templates\base.html:45 #: templates/base.html:45
msgid "Menu" msgid "Menu"
msgstr "Menü" msgstr "Menü"
#: .\kasu\templates\base.html:69 #: templates/base.html:69
msgid "Current Event" msgid "Current Event"
msgstr "Aktuelle Veranstaltung" msgstr "Aktuelle Veranstaltung"
#: .\kasu\templates\base.html:72 #: templates/base.html:72
msgid "Since" msgid "Since"
msgstr "seit" msgstr "seit"
#: .\kasu\templates\base.html:73 .\kasu\templates\base.html:87 #: templates/base.html:73 templates/base.html:87
msgid "Start" msgid "Start"
msgstr "Beginn" msgstr "Beginn"
#: .\kasu\templates\base.html:76 .\kasu\templates\base.html:90 #: templates/base.html:76 templates/base.html:90
msgid "Location" msgid "Location"
msgstr "Ort" msgstr "Ort"
#: .\kasu\templates\base.html:81 .\kasu\templates\base.html:94 #: templates/base.html:81 templates/base.html:94
msgid "More Details" msgid "More Details"
msgstr "Mehr Details" msgstr "Mehr Details"
#: .\kasu\templates\base.html:83 #: templates/base.html:83
msgid "Next Event" msgid "Next Event"
msgstr "Nächste Veranstaltung" msgstr "Nächste Veranstaltung"
#: .\kasu\templates\base.html:86 #: templates/base.html:86
msgid "in" msgid "in"
msgstr "in" msgstr "in"
#: .\kasu\templates\base.html:97 #: templates/base.html:97
msgid "Upcoming events" msgid "Upcoming events"
msgstr "Bevorstehende Veranstaltungen" msgstr "Bevorstehende Veranstaltungen"
#: .\kasu\templates\base.html:147 #: templates/base.html:107
msgid "No events found"
msgstr "Keine Veranstaltungen gefunden"
#: templates/base.html:150
msgid "Add Subpage" msgid "Add Subpage"
msgstr "Unterseite Hinzufügen" msgstr "Unterseite Hinzufügen"
#: .\kasu\templates\base.html:152 #: templates/base.html:154
msgid "Edit Page" msgid "Edit Page"
msgstr "Seite bearbeiten" msgstr "Seite bearbeiten"
#: .\kasu\templates\base.html:160 #: templates/base.html:163
msgid "Imprint" msgid "Imprint"
msgstr "Impressum" msgstr "Impressum"
#: .\kasu\templates\base.html:161 #: templates/base.html:164
msgid "contact" msgid "contact"
msgstr "Kontakt" msgstr "Kontakt"
#: .\kasu\templates\base.html:166 #: templates/base.html:169
msgid "Language" msgid "Language"
msgstr "Sprache" msgstr "Sprache"
#: .\kasu\templates\base.html:175 #: templates/base.html:178
msgid "Go" msgid "Go"
msgstr "Los" msgstr "Los"
#: .\kasu\templates\base.html:180 #: templates/base.html:183
msgid "Logged in as" msgid "Logged in as"
msgstr "Angemeldet als" msgstr "Angemeldet als"
#: .\kasu\templates\base.html:182 #: templates/base.html:185
msgid "Admin" msgid "Admin"
msgstr "Admin" msgstr "Admin"
#: .\kasu\templates\base.html:183 #: templates/base.html:186
msgid "Logout" msgid "Logout"
msgstr "Abmelden" msgstr "Abmelden"
#: .\kasu\templates\base.html:185 #: templates/base.html:188
msgid "no user logged in" msgid "no user logged in"
msgstr "Niemand angemeldet" msgstr "Niemand angemeldet"
#: .\kasu\templates\base.html:186 .\kasu\templates\comments\form.html:43 #: templates/base.html:189 templates/comments/form.html:43
msgid "register" msgid "register"
msgstr "Registrieren" msgstr "Registrieren"
#: .\kasu\templates\base.html:187 .\kasu\templates\comments\form.html:44 #: templates/base.html:190 templates/comments/form.html:44
msgid "login" msgid "login"
msgstr "anmelden" msgstr "anmelden"
#: .\kasu\templates\comments\form.html:5 #: templates/comments/form.html:5
msgid "New Comment" msgid "New Comment"
msgstr "Neuer Kommentar" msgstr "Neuer Kommentar"
#: .\kasu\templates\comments\form.html:20 #: templates/comments/form.html:20
msgid "now" msgid "now"
msgstr "Jetzt" msgstr "Jetzt"
#: .\kasu\templates\comments\form.html:25 #: templates/comments/form.html:25
msgid "Preview" msgid "Preview"
msgstr "Vorschau" msgstr "Vorschau"
#: .\kasu\templates\comments\form.html:26 #: templates/comments/form.html:26
msgid "Post" msgid "Post"
msgstr "Schreiben" msgstr "Schreiben"
#: .\kasu\templates\comments\form.html:34 #: templates/comments/form.html:34
msgid "not logged in" msgid "not logged in"
msgstr "Nicht angemeldet" msgstr "Nicht angemeldet"
#: .\kasu\templates\comments\form.html:38 #: templates/comments/form.html:38
msgid "Register now, or Login to leave a comment here." msgid "Register now, or Login to leave a comment here."
msgstr "Jetzt registrieren, oder anmelden um einen Kommentar zu schreiben." msgstr "Jetzt registrieren, oder anmelden um einen Kommentar zu schreiben."
#: .\kasu\templates\comments\list.html:2 .\kasu\templates\index.html:25 #: templates/comments/list.html:2 templates/index.html:25
msgid "Comments" msgid "Comments"
msgstr "Kommentare" msgstr "Kommentare"
#: .\kasu\templates\comments\posted.html:4 #: templates/comments/posted.html:4 templates/comments/posted.html:7
#: .\kasu\templates\comments\posted.html:7
msgid "Thank you for your comment" msgid "Thank you for your comment"
msgstr "Danke für deinen Kommentar." msgstr "Danke für deinen Kommentar."
#: .\kasu\templates\comments\preview.html:4 #: templates/comments/preview.html:4 templates/comments/preview.html:6
#: .\kasu\templates\comments\preview.html:6
msgid "Preview your comment" msgid "Preview your comment"
msgstr "Vorschau deines Kommentars" msgstr "Vorschau deines Kommentars"
#: .\kasu\templates\comments\preview.html:10 #: templates/comments/preview.html:10
msgid "Please correct the error below" msgid "Please correct the error below"
msgid_plural "Please correct the errors below" msgid_plural "Please correct the errors below"
msgstr[0] "Bitte den Fehler weiter unten beheben" msgstr[0] "Bitte den Fehler weiter unten beheben"
msgstr[1] "Bitte die Fehler weiter unten beheben" msgstr[1] "Bitte die Fehler weiter unten beheben"
#: .\kasu\templates\index.html:4 #: templates/index.html:4
msgid "traditional Asian game culture" msgid "traditional Asian game culture"
msgstr "traditionelle asiatische Spielkultur" msgstr "traditionelle asiatische Spielkultur"
#: .\kasu\templates\index.html:33 #: templates/index.html:33
msgid "Read More" msgid "Read More"
msgstr "Mehr lesen" msgstr "Mehr lesen"
#: .\kasu\templates\index.html:47 #: templates/index.html:47
#, python-format #, fuzzy, python-format
#| msgid ""
#| "\n"
#| " From <a href=\"%(user_link)s\">%(author)s</a> in\n"
#| " <a href=\"%(comment_link)s\">&ldquo;%(object)s&rdquo;</"
#| "a>\n"
#| " since\n"
#| " <time datetime=\"%(submit_date)s\">%(since)s</time>\n"
#| " "
msgid "" msgid ""
"\n" "\n"
" From <a href=\"%(user_link)s\">%(author)s</a> in\n" " From <a href=\"%(user_link)s\">%(author)s</a> in\n"
" <a href=\"%(comment_link)s\">&ldquo;%(object)s&rdquo;</a>\n" " <a href=\"%(comment_link)s\">&ldquo;%(object)s&rdquo;</a>\n"
" since\n" " since\n"
" <time datetime=\"%(submit_date)s\">%(since)s</time>\n" " <time "
"datetime=\"%(submit_date|date:'Y-m-d\\TH:i:sO')s\">%(since)s</time>\n"
" " " "
msgstr "" msgstr ""
"\n" "\n"
@@ -192,23 +203,23 @@ msgstr ""
" <time datetime=\"%(submit_date)s\">%(since)s</time>\n" " <time datetime=\"%(submit_date)s\">%(since)s</time>\n"
" " " "
#: .\kasu\templates\index.html:59 #: templates/index.html:59
msgid "Kasu in the social network" msgid "Kasu in the social network"
msgstr "Kasu im sozialem Netzwerk" msgstr "Kasu im sozialem Netzwerk"
#: .\kasu\templates\index.html:62 .\kasu\templates\index.html:65 #: templates/index.html:62 templates/index.html:65
msgid "Visit us on" msgid "Visit us on"
msgstr "Besuche uns auf" msgstr "Besuche uns auf"
#: .\kasu\templates\index.html:74 #: templates/index.html:74
msgid "Add Article" msgid "Add Article"
msgstr "Artikel hinzufügen" msgstr "Artikel hinzufügen"
#: .\kasu\templates\paginator.html:8 #: templates/paginator.html:5 templates/paginator.html:7
msgid "Previous" msgid "Previous"
msgstr "Vorherige" msgstr "Vorherige"
#: .\kasu\templates\paginator.html:20 #: templates/paginator.html:17 templates/paginator.html:20
msgid "Next" msgid "Next"
msgstr "Nächste" msgstr "Nächste"

View File

@@ -68,17 +68,16 @@ CACHES = {
} }
# Request Middleware # Request Middleware
MIDDLEWARE_CLASSES = [ MIDDLEWARE = [
'csp.middleware.CSPMiddleware', 'csp.middleware.CSPMiddleware',
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.locale.LocaleMiddleware',
'utils.middleware.SetRemoteAddrFromForwardedFor', 'utils.middleware.SetRemoteAddrFromForwardedFor',
'mahjong_ranking.middleware.DenormalizationUpdateMiddleware', 'mahjong_ranking.middleware.DenormalizationUpdateMiddleware',
] ]
@@ -134,6 +133,10 @@ LOGIN_URL = '/membership/login/'
LOGIN_ERROR_URL = '/membership/login/error/' LOGIN_ERROR_URL = '/membership/login/error/'
LOGIN_REDIRECT_URL = '/users/' LOGIN_REDIRECT_URL = '/users/'
# Set the primarykey handing to old django style
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
# Localization # Localization
USE_I18N = True USE_I18N = True
USE_L10N = True USE_L10N = True
@@ -188,7 +191,8 @@ LOGGING = {
}, },
'formatters': { 'formatters': {
'verbose': { 'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s' 'format': '%(levelname)s %(asctime)s %(module)s %(process)d ' +
'%(thread)d %(message)s'
}, },
'simple': { 'simple': {
'format': '%(levelname)s %(message)s' 'format': '%(levelname)s %(message)s'
@@ -272,7 +276,7 @@ KYU_RANKS = (
) )
try: try:
from .local_settings import * # Ignore PyLintBear (W0401, W0614) from .local_settings import * # noqa: F403
except ImportError: except ImportError:
pass pass

View File

@@ -37,7 +37,7 @@
{% block extra_head %}{% endblock %} {% block extra_head %}{% endblock %}
<script src="{{ STATIC_URL }}js/piwik.js"></script> <script src="{{ STATIC_URL }}js/piwik.js"></script>
</head> </head>
<body id="body" {% block itemscope %}{% endblock %}> <body id="body" itemscope>
<header id="siteheader"> <header id="siteheader">
<div id="sitelogo"><a href="/index.html">Kasu - traditionelle asiatische Spielkultur</a></div> <div id="sitelogo"><a href="/index.html">Kasu - traditionelle asiatische Spielkultur</a></div>
<nav id="mainnav"> <nav id="mainnav">
@@ -51,7 +51,7 @@
<ul class="main_dropdown"> <ul class="main_dropdown">
{% for subpage in item.subpages.all %}<li><a {% for subpage in item.subpages.all %}<li><a
href="{{subpage.get_absolute_url}}" href="{{subpage.get_absolute_url}}"
{% ifequal subpage current_page %}class="active"{% endifequal %}>{{subpage.menu_name}}</a></li> >{{subpage.menu_name}}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %} {% endif %}
@@ -71,7 +71,7 @@
<ul class="fa-ul"> <ul class="fa-ul">
<li><span class="fa-li fa fa-clock-o"></span><strong>{% trans "Since" %}:</strong> {{current_event.start|timesince}}</li> <li><span class="fa-li fa fa-clock-o"></span><strong>{% trans "Since" %}:</strong> {{current_event.start|timesince}}</li>
<li><span class="fa-li fa fa-calendar"></span><strong>{% trans "Start" %}:</strong> <li><span class="fa-li fa fa-calendar"></span><strong>{% trans "Start" %}:</strong>
<time datetime="{{current_event.start|date:'c'}}">{{current_event.start|date:'DATETIME_FORMAT'}}</time> <time datetime="{{current_event.start|date:'Y-m-d\TH:i:sO'}}">{{current_event.start|date:'DATETIME_FORMAT'}}</time>
</li> </li>
<li><span class="fa-li fa fa-map-marker"></span><strong>{% trans "Location" %}:</strong> {{ current_event.location }} <li><span class="fa-li fa fa-map-marker"></span><strong>{% trans "Location" %}:</strong> {{ current_event.location }}
- {{current_event.location.street_address}}, {{current_event.location.postal_code}} {{current_event.location.locality}} - {{current_event.location.street_address}}, {{current_event.location.postal_code}} {{current_event.location.locality}}
@@ -79,13 +79,13 @@
</ul> </ul>
<div class="right"><a class="button" href="{{current_event.get_absolute_url}}"> <div class="right"><a class="button" href="{{current_event.get_absolute_url}}">
{% trans "More Details" %} <span class="fa fa-arrow-right"></span></a></div> {% trans "More Details" %} <span class="fa fa-arrow-right"></span></a></div>
{% else %} {% elif next_event %}
<h2>{% trans "Next Event" %}</h2> <h2>{% trans "Next Event" %}</h2>
<h3>{{ next_event.name}}</h3> <h3>{{ next_event.name}}</h3>
<ul class="fa-ul"> <ul class="fa-ul">
<li><span class="fa-li fa fa-clock-o"></span><strong>{% trans "in" %}:</strong> {{next_event.start|timeuntil}}</li> <li><span class="fa-li fa fa-clock-o"></span><strong>{% trans "in" %}:</strong> {{next_event.start|timeuntil}}</li>
<li><span class="fa-li fa fa-calendar"></span><strong>{% trans "Start" %}:</strong> <li><span class="fa-li fa fa-calendar"></span><strong>{% trans "Start" %}:</strong>
<time datetime="{{next_event.start|date:'c'}}">{{next_event.start|date:'DATETIME_FORMAT' }}</time> <time datetime="{{next_event.start|date:'Y-m-d\TH:i:sO'}}">{{next_event.start|date:'DATETIME_FORMAT' }}</time>
</li> </li>
<li><span class="fa-li fa fa-map-marker"></span><strong>{% trans "Location" %}:</strong> {{ next_event.location }} <li><span class="fa-li fa fa-map-marker"></span><strong>{% trans "Location" %}:</strong> {{ next_event.location }}
- {{next_event.location.street_address}}, {{next_event.location.postal_code}} {{next_event.location.locality}} - {{next_event.location.street_address}}, {{next_event.location.postal_code}} {{next_event.location.locality}}
@@ -101,8 +101,10 @@
{% for event in upcoming_events %} {% for event in upcoming_events %}
<li><span class="fa-li fa fa-calendar-o"></span> <li><span class="fa-li fa fa-calendar-o"></span>
<a href="{{ event.get_absolute_url}}"> <a href="{{ event.get_absolute_url}}">
<time datetime="{{event.start|date:'c'}}">{{event.start|date:'D d. M:'}}</time> <time datetime="{{event.start|date:'Y-m-d\TH:i:sO'}}">{{event.start|date:'D d. M:'}}</time>
{{event.name}}</a></li> {{event.name}}</a></li>
{% empty %}
<li>{% trans 'No events found' %}.</li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endblock %} {% endblock %}
@@ -114,9 +116,9 @@
{% block navigation %}{% if current_top_page.subpages.count %} {% block navigation %}{% if current_top_page.subpages.count %}
<ul id="navigation"> <ul id="navigation">
<li><a href="{{current_top_page.get_absolute_url}}" <li><a href="{{current_top_page.get_absolute_url}}"
class="{% ifequal current_page current_top_page %}{% endifequal %}">{{current_top_page.menu_name}}</a></li> class="{% if current_page == current_top_page %}{% endif %}">{{current_top_page.menu_name}}</a></li>
{% for subpage in current_top_page.subpages.all %} {% for subpage in current_top_page.subpages.all %}
<li><a href="{{subpage.get_absolute_url}}" class="{% ifequal subpage current_page %}active{% endifequal %}">{{subpage.menu_name}}</a> <li><a href="{{subpage.get_absolute_url}}" class="{% if subpage == current_page %}active{% endif %}">{{subpage.menu_name}}</a>
</li> </li>
{% endfor %} {% endfor %}
{% block additional_nav_elements %}{% endblock %} {% block additional_nav_elements %}{% endblock %}
@@ -140,20 +142,21 @@
{% endblock %} {% endblock %}
{% block comments %}{% endblock %} {% block comments %}{% endblock %}
<br class="clear"/> <br class="clear"/>
<p id="bottom_buttonbar" class="buttonbar"> {% block bottom %}
<div id="bottom_buttonbar" class="buttonbar">
{% block buttonbar %} {% block buttonbar %}
{% if current_page and perms.content.add_page %} {% if current_page and perms.content.add_page %}
<a href="{% url 'add-page' current_page.path %}" class="button"><span class="fa fa-plus"></span> <a href="{% url 'add-page' current_page.path %}" class="button"><span class="fa fa-plus"></span>
{% trans "Add Subpage" %}</a> {% trans "Add Subpage" %}</a>
{% endif %} {% endif %}
{% if current_page and perms.content.change_page %} {% if current_page and perms.content.change_page %}
<a href="{% url 'edit-page' current_page.path %}" class="button"><span class="fa fa-pencil"></span> <a href="{% url 'edit-page' current_page.path %}" class="button"><span class="fa fa-pencil"></span>
{% trans "Edit Page" %}</a> {% trans "Edit Page" %}</a>
{% endif %} {% endif %}
{% block additional_buttonbar %}{% endblock %} {% block additional_buttonbar %}{% endblock %}
{% endblock %} {% endblock %}
</p> </div>
{% endblock %}
</main> </main>
<footer id="footer"> <footer id="footer">
<p><strong>Herausgeber:</strong> Verein Kasu - traditionelle asiatische Spielkultur (<a <p><strong>Herausgeber:</strong> Verein Kasu - traditionelle asiatische Spielkultur (<a
@@ -167,8 +170,8 @@
<select name="language" id="language"> <select name="language" id="language">
{% get_language_info_list for LANGUAGES as languages %} {% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %} {% for language in languages %}
<option value="{{language.code}}" {% ifequal language.code LANGUAGE_CODE %} <option value="{{language.code}}"
selected="selected" {% endifequal %}>{{ language.name_local }} ({{ language.code }}) selected="selected">{{ language.name_local }} ({{ language.code }})
</option> </option>
{% endfor %} {% endfor %}
</select> </select>

View File

@@ -7,7 +7,7 @@
</div> </div>
<header class="comment_header"> <header class="comment_header">
<h3><a href="{{ comment.user.get_profile.get_absolute_url }}" class="user">{{comment.user}}</a></h3> <h3><a href="{{ comment.user.get_profile.get_absolute_url }}" class="user">{{comment.user}}</a></h3>
<time datetime="{{comment.submit_date|date:'Y-m-d'}}" class="submit_date">{{comment.submit_date|timesince}}</time> <time datetime="{{comment.submit_date|date:'Y-m-d\TH:i:sO'}}" class="submit_date">{{comment.submit_date|timesince}}</time>
</header> </header>
<div class="comment_text">{{comment.comment}}</div> <div class="comment_text">{{comment.comment}}</div>
</article> </article>

View File

@@ -16,7 +16,7 @@
</div> </div>
<header class="comment_header"> <header class="comment_header">
<a href="{{ user.get_profile.get_absolute_url }}" class="user">{{comment.user}}</a> <a href="{{ user.get_profile.get_absolute_url }}" class="user">{{comment.user}}</a>
<div class="submit_date"><time time="{% now 'c' %}">{% now 'DATETIME_FORMAT' %}</time></div> <div class="submit_date"><time>{% now 'DATETIME_FORMAT' %}</time></div>
</header> </header>
<div class="comment_text">{{comment}}</div> <div class="comment_text">{{comment}}</div>
</article> </article>

View File

@@ -1 +1,2 @@
<input type="{{ type|default:'text' }}" name="{{ widget.name }}"{% if widget.value != None %} value="{{ widget.value|stringformat:'s' }}"{% endif %}{% include "django/forms/widgets/attrs.html" %} /> <input type="{{ type|default:'text' }}" name="{{ widget.name }}"
value="{{ widget.value|stringformat:'s' }}" "django/forms/widgets/attrs.html" %} />

View File

@@ -2,7 +2,7 @@
{% for hidden in form.hidden_fields %}{{ hidden }}{% endfor %} {% for hidden in form.hidden_fields %}{{ hidden }}{% endfor %}
{% for field in form.visible_fields %} {% for field in form.visible_fields %}
<div> <div>
<label {% if field.html_name != 'recaptcha' %} for="id_{{ field.html_name}}" {% endif %} <label for="id_{{ field.html_name}}"
class="field_name {{ field.css_classes }}">{{ field.label}}</label> class="field_name {{ field.css_classes }}">{{ field.label}}</label>
{{ field }} {{ field }}
{% if field.field.widget.input_type == 'checkbox' %} {% if field.field.widget.input_type == 'checkbox' %}

View File

@@ -17,7 +17,7 @@
<h2><a href="{{article.get_absolute_url}}">{{article.headline}}</a></h2> <h2><a href="{{article.get_absolute_url}}">{{article.headline}}</a></h2>
<ul class="info"> <ul class="info">
<li><span class="fa fa-calendar"></span> <li><span class="fa fa-calendar"></span>
<time datetime="{{article.date_created|date:'Y-m-d H:i'}}">{{ article.date_created|date:'DATE_FORMAT' }}</time> <time datetime="{{article.date_created|date:'Y-m-d\TH:i:sO'}}">{{ article.date_created|date:'DATE_FORMAT' }}</time>
</li> </li>
<li><span class="fa fa-user"></span> {{ article.author }} <li><span class="fa fa-user"></span> {{ article.author }}
</li> </li>
@@ -48,7 +48,7 @@
From <a href="{{user_link}}">{{author}}</a> in From <a href="{{user_link}}">{{author}}</a> in
<a href="{{comment_link}}">&ldquo;{{object}}&rdquo;</a> <a href="{{comment_link}}">&ldquo;{{object}}&rdquo;</a>
since since
<time datetime="{{submit_date}}">{{since}}</time> <time datetime="{{submit_date|date:'Y-m-d\TH:i:sO'}}">{{since}}</time>
{% endblocktrans %} {% endblocktrans %}
</li> </li>
{% endfor %} {% endfor %}

View File

@@ -1,22 +1,22 @@
{% load i18n %} {% load i18n %}
<nav class="grid_12 pagination"> <nav class="grid_12 pagination">
<a {% if page_obj.has_previous %} {% if page_obj.has_previous %}
class="previous" href="?page={{ page_obj.previous_page_number }}" <a class="previous" href="?page={{ page_obj.previous_page_number }}"><span class="fa fa-arrow-left"></span>
{% trans "Previous" %}</a>
{% else %} {% else %}
class="previous disabled" <a class="previous disabled"><span class="fa fa-arrow-left"></span>{% trans "Previous" %}</a>
{% endif %}> {% endif %}
<span class="fa fa-arrow-left"></span>{% trans "Previous" %}
</a>
{% for page in paginator.page_range %} {% for page in paginator.page_range %}
<a {% ifequal page_obj.number page %}class="active"{% else %}href="?page={{page}}"{% endifequal %}>{{page}}</a> {% if page_obj.number == page %}
{% endfor %} <a class="active">{{page}}</a>
<a {% if page_obj.has_next %}
class="next" href="?page={{ page_obj.next_page_number }}"
{% else %} {% else %}
class="next disabled" <a href="?page={{page}}">{{page}}</a>
{% endif %}> {% endif %}
{% trans "Next" %} <span class="fa fa-arrow-right"></span> {% endfor %}
</a> {% if page_obj.has_next %}
<a class="next" href="?page={{ page_obj.next_page_number }}">{% trans "Next" %}
<span class="fa fa-arrow-right"></span></a>
{% else %}
<a class="next disabled">{% trans "Next" %} <span class="fa fa-arrow-right"></span></a>
{% endif %}
</nav> </nav>

View File

@@ -1,6 +1,6 @@
""" the main URL config that imports many URL configs from the applications. """ """ the main URL config that imports many URL configs from the applications. """
from django.conf import settings from django.conf import settings
from django.conf.urls import include, url from django.urls import include, path
from django.conf.urls.static import static from django.conf.urls.static import static
from django.contrib import admin from django.contrib import admin
from django.contrib.sitemaps.views import sitemap from django.contrib.sitemaps.views import sitemap
@@ -10,58 +10,55 @@ from content import views, feeds
from content.sitemaps import ArticleSitemap, PageSitemap from content.sitemaps import ArticleSitemap, PageSitemap
from events.sitemaps import EventSitemap from events.sitemaps import EventSitemap
from events.views import EventListIcal from events.views import EventListIcal
from mahjong_ranking.sitemaps import *
from maistar_ranking.sitemaps import * import mahjong_ranking.sitemaps
import maistar_ranking.sitemaps
from membership.views import MembershipDetail from membership.views import MembershipDetail
admin.autodiscover() admin.autodiscover()
# register_converter('path')
sitemaps = { sitemaps = {
'event_rankings': EventRankingSitemap, 'event_rankings': mahjong_ranking.sitemaps.EventRankingSitemap,
'event_hanchans': EventHanchanSitemap, 'event_hanchans': mahjong_ranking.sitemaps.EventHanchanSitemap,
'mahjong_seasons': MajongSeasonSitemap, 'mahjong_seasons': mahjong_ranking.sitemaps.MahjongSeasonSitemap,
'maistar_games': MaistarGamesSitemap, 'maistar_games': maistar_ranking.sitemaps.MaistarGamesSitemap,
'articles': ArticleSitemap, 'articles': ArticleSitemap,
'events': EventSitemap, 'events': EventSitemap,
'pages': PageSitemap, 'pages': PageSitemap,
} }
urlpatterns = [ # Ignore PyLintBear (C0103) urlpatterns = [ # Ignore PyLintBear (C0103)
url(r'^$', views.StartPage.as_view()), path("", views.StartPage.as_view(), name="index"),
url(r'^404/$', TemplateView.as_view(template_name='404.html')), path('index.html', views.StartPage.as_view()),
url(r'^add_page/(?P<path>[\+\.\-\d\w\/]+)/$', path('404/', TemplateView.as_view(template_name='404.html'), name="404"),
views.PageAddForm.as_view(), name='add-page'), path('admin/doc/', include('django.contrib.admindocs.urls'), name="django-docs"),
url(r'^admin/doc/', include('django.contrib.admindocs.urls')), path('admin/', admin.site.urls, name="django-admin"),
url(r'^admin/', admin.site.urls), path('ckeditor/', include('ckeditor_uploader.urls')),
url(r'^ckeditor/', include('ckeditor_uploader.urls')), path('comments/', include('django_comments.urls')),
url(r'^comments/', include('django_comments.urls')), path('events/', include('events.urls')),
url(r'^edit_page/(?P<path>[\+\.\-\d\w\/]+)/$', path('events.ics', EventListIcal.as_view(), name='events-ical'),
views.PageEditForm.as_view(), name='edit-page'), path('feeds/latest/', feeds.LatestNews(), name='feed-latest-news'),
url(r'^events/', include('events.urls')), path('feeds/comments/', feeds.LatestComments(), name='feed-latest-comments'),
url(r'^events.ics$', EventListIcal.as_view(), name='events-ical'), path('gallery/', include('events.gallery_urls')),
url(r'^feeds/latest/$', feeds.LatestNews(), name='feed-latest-news'), path('google25dabc1a49a9ef03.html',
url(r'^feeds/comments/$', feeds.LatestComments(), TemplateView.as_view(template_name='google25dabc1a49a9ef03.html')),
name='feed-latest-comments'), path('i18n/', include('django.conf.urls.i18n'), name='start-page'),
url(r'^gallery/', include('events.gallery_urls')), path('manifest.json', TemplateView.as_view(template_name='manifest.json')),
url(r'^google25dabc1a49a9ef03.html$', TemplateView.as_view( path('membership/', include('membership.urls')),
template_name='google25dabc1a49a9ef03.html')), path('news/', include('content.news_urls')),
url(r'^i18n/', include('django.conf.urls.i18n'), name='start-page'), path('ranking/', include('mahjong_ranking.urls')),
url(r'^index.html$', views.StartPage.as_view()), path('ranking/', include('maistar_ranking.urls')),
url(r'^manifest.json$', path('robots.txt', TemplateView.as_view(template_name='robots.txt')),
TemplateView.as_view(template_name='manifest.json')), path('users/', MembershipDetail.as_view(), name='membership-details'),
url(r'^membership/', include('membership.urls')), path('users/<slug:username>/',
url(r'^news/', include('content.news_urls')),
url(r'^ranking/', include('mahjong_ranking.urls')),
url(r'^ranking/', include('maistar_ranking.urls')),
url(r'^sitemap\.xml$', sitemap, {
'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'),
url(r'^robots.txt$', TemplateView.as_view(template_name='robots.txt')),
url(r'^users/$', MembershipDetail.as_view(), name='membership-details'),
url(r'^users/(?P<username>[\-\.\d\w]+)/$',
MembershipDetail.as_view(), name='membership-details'), MembershipDetail.as_view(), name='membership-details'),
url(r'^(?P<path>[\-\d\w\/]+)\.html$', path('add_page/<path:path>', views.PageAddForm.as_view(), name='add-page'),
views.PageHtml.as_view(), name='view-page'), path('edit_page/<path:path>', views.PageEditForm.as_view(), name='edit-page'),
url(r'^(?P<path>[\-\d\w\/]+)\.pdf$', views.PagePdf.as_view()), path('<path:path>.html', views.PageHtml.as_view(), name='view-page'),
path('<path:path>.pdf', views.PagePdf.as_view()),
path('sitemap.xml', sitemap, {'sitemaps': sitemaps},
name='django.contrib.sitemaps.views.sitemap'),
] ]
if settings.DEBUG: if settings.DEBUG:
@@ -70,8 +67,8 @@ if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, urlpatterns += static(settings.STATIC_URL,
document_root=settings.STATIC_ROOT) document_root=settings.STATIC_ROOT)
if 'rosetta' in settings.INSTALLED_APPS: if 'rosetta' in settings.INSTALLED_APPS:
urlpatterns += [url(r'^rosetta/', include('rosetta.urls'))] urlpatterns += [path("rosetta/", include('rosetta.urls'))]
if 'debug_toolbar' in settings.INSTALLED_APPS: if 'debug_toolbar' in settings.INSTALLED_APPS:
import debug_toolbar import debug_toolbar
urlpatterns += [url(r'^__debug__/', include(debug_toolbar.urls)), ] urlpatterns += [path('__debug__/', include(debug_toolbar.urls)), ]

View File

@@ -1,7 +1,7 @@
""" Script to start Django as an WSGI Application. """ Script to start Django as an WSGI Application.
src_path and virtpy_path are host dependet variables. src_path and virtpy_path are host dependet variables.
TODO: Check if this file is really needet and make it host independet TODO: Check if this file is really needed and make it independent from host
""" """
import os import os
import sys import sys
@@ -14,8 +14,8 @@ if VENV_PATH not in sys.path:
if SOURCE_PATH not in sys.path: if SOURCE_PATH not in sys.path:
sys.path.append(SOURCE_PATH) sys.path.append(SOURCE_PATH)
from django.core.wsgi import get_wsgi_application # Ignore PyLintBear (C0413) # Ignore PyLintBear (C0413) from django.core.wsgi import get_wsgi_application # noqa: E402
os.environ['DJANGO_SETTINGS_MODULE'] = 'kasu.settings' os.environ['DJANGO_SETTINGS_MODULE'] = 'kasu.settings'
application = get_wsgi_application() # Ignore PyLintBear (C0103) # Ignore PyLintBear (C0103) application = get_wsgi_application()

View File

@@ -1,6 +1,7 @@
""" """
Helper to generate XLSX Spreadsheets in an uniform way. Helper to generate XLSX Spreadsheets in an uniform way.
""" """
import datetime
from datetime import date from datetime import date
import openpyxl import openpyxl
@@ -61,13 +62,15 @@ def getattr_recursive(obj, attr_string):
:param obj: a python object. :param obj: a python object.
:param attr_string: the desired attribute of the object. :param attr_string: the desired attribute of the object.
:return: a getattr_recursice(obj, 'attr1.attr2') will return the value of attr2 of attr1 from obj :return: return the value from attr2 of attr1 from obj
""" """
attr_list = attr_string.split('.') attr_list = attr_string.split('.')
return_value = None return_value = None
for attr in attr_list: for attr in attr_list:
return_value = getattr(obj, attr) return_value = getattr(obj, attr)
obj = return_value obj = return_value
if isinstance(return_value, datetime.datetime):
return_value = return_value.replace(tzinfo=None)
return return_value return return_value
@@ -80,7 +83,8 @@ class Workbook(object):
:rtype: object :rtype: object
""" """
self.workbook = openpyxl.Workbook() self.workbook = openpyxl.Workbook()
[self.workbook.add_named_style(style) for style in XLSX_STYLES.values()] [self.workbook.add_named_style(style)
for style in XLSX_STYLES.values()]
[self.workbook.remove(sheet) for sheet in self.workbook.worksheets] [self.workbook.remove(sheet) for sheet in self.workbook.worksheets]
def generate_sheet(self, title, columns_settings, object_list, def generate_sheet(self, title, columns_settings, object_list,
@@ -135,7 +139,8 @@ class Workbook(object):
# write table footer # write table footer
for column, settings in enumerate(columns_settings, 1): for column, settings in enumerate(columns_settings, 1):
cell = ws.cell(column=column, row=row, value=settings.get('footer')) cell = ws.cell(column=column, row=row,
value=settings.get('footer'))
cell.style = settings['style'] cell.style = settings['style']
row += 1 row += 1

View File

@@ -1,7 +1,7 @@
""" Adds management of the mahong ranking system to the admin interface. """ """ Adds management of the mahjong ranking system to the admin interface. """
from django.contrib import admin from django.contrib import admin
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
from . import models, set_dirty from . import models, set_dirty
@@ -21,6 +21,8 @@ def recalculate(modeladmin, request, queryset): # Ignore PyLintBear (W0613)
for ladder_ranking in queryset: for ladder_ranking in queryset:
set_dirty(user=ladder_ranking.user_id, set_dirty(user=ladder_ranking.user_id,
season=ladder_ranking.season) season=ladder_ranking.season)
recalculate.short_description = _("Recalculate") recalculate.short_description = _("Recalculate")
@@ -34,14 +36,14 @@ def confirm(modeladmin, request, queryset): # Ignore PyLintBear (W0613)
confirm.short_description = _("Confirm") confirm.short_description = _("Confirm")
def unconfirm(modeladmin, request, queryset): # Ignore PyLintBear (W0613) def reject(modeladmin, request, queryset): # Ignore PyLintBear (W0613)
"""An admin action to quickly set selected hanchans to unconfirmed. """ """An admin action to quickly set selected hanchans to unconfirmed. """
for hanchan in queryset: for hanchan in queryset:
hanchan.confirmed = False hanchan.confirmed = False
hanchan.save() hanchan.save()
unconfirm.short_description = _('Set unconfirmed') reject.short_description = _('Reject')
class EventRankingAdmin(admin.ModelAdmin): class EventRankingAdmin(admin.ModelAdmin):
@@ -55,7 +57,7 @@ class EventRankingAdmin(admin.ModelAdmin):
class HanchanAdmin(admin.ModelAdmin): class HanchanAdmin(admin.ModelAdmin):
""" To administrate the stored Hanchans. """ """ To administrate the stored Hanchans. """
actions = [recalculate, confirm, unconfirm] actions = [recalculate, confirm, reject]
date_hierarchy = 'start' date_hierarchy = 'start'
list_filter = ('season', 'event', 'confirmed') list_filter = ('season', 'event', 'confirmed')
search_fields = ('player_names',) search_fields = ('player_names',)

View File

@@ -7,7 +7,7 @@ Created on 04.10.2011
""" """
from django import forms from django import forms
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
from events.models import Event from events.models import Event
from . import models from . import models
@@ -64,7 +64,7 @@ class HanchanAdminForm(HanchanForm):
""" Extends the HanchanForm for users with admin privileges. """ Extends the HanchanForm for users with admin privileges.
They are allowed to confirm/unconfirm Hanchans, this could be userful if They are allowed to confirm/unconfirm Hanchans, this could be userful if
one games smells fishy and needs the opinion of an referee.""" one games smells fishy and needs the opinion of a referee."""
class Meta(object): class Meta(object):
""" Extend the formfields to add the confirmed checkbox. """ """ Extend the formfields to add the confirmed checkbox. """

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: kasu.mahjong_ranking\n" "Project-Id-Version: kasu.mahjong_ranking\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-08 00:17+0200\n" "POT-Creation-Date: 2023-08-07 20:38+0200\n"
"PO-Revision-Date: 2018-05-08 00:20+0105\n" "PO-Revision-Date: 2018-05-08 00:20+0105\n"
"Last-Translator: b'Christian Berg <kasu@xendynastie.at>'\n" "Last-Translator: b'Christian Berg <kasu@xendynastie.at>'\n"
"Language-Team: Kasu <verein@kasu.at>\n" "Language-Team: Kasu <verein@kasu.at>\n"
@@ -19,385 +19,380 @@ msgstr ""
"X-Generator: Poedit 1.8.9\n" "X-Generator: Poedit 1.8.9\n"
"X-Translated-Using: django-rosetta 0.8.1\n" "X-Translated-Using: django-rosetta 0.8.1\n"
#: .\mahjong_ranking\admin.py:24 #: admin.py:26
msgid "Recalculate" msgid "Recalculate"
msgstr "Neuberechnen" msgstr "Neuberechnen"
#: .\mahjong_ranking\admin.py:34 #: admin.py:36
msgid "Confirm" msgid "Confirm"
msgstr "Bestätigen" msgstr "Bestätigen"
#: .\mahjong_ranking\admin.py:44 #: admin.py:46
msgid "Set unconfirmed" msgid "Reject"
msgstr "Als unbestätigt markieren" msgstr ""
#: .\mahjong_ranking\forms.py:22 #: forms.py:22
msgid "start" msgid "start"
msgstr "Beginn" msgstr "Beginn"
#: .\mahjong_ranking\models.py:91 #: models.py:93 templates/mahjong_ranking/player_dan_score.html:14
#: .\mahjong_ranking\templates\mahjong_ranking\player_dan_score.html:14 #: templates/mahjong_ranking/player_invalid_score.html:13
#: .\mahjong_ranking\templates\mahjong_ranking\player_invalid_score.html:13 #: templates/mahjong_ranking/player_kyu_score.html:15
#: .\mahjong_ranking\templates\mahjong_ranking\player_kyu_score.html:15 #: templates/mahjong_ranking/player_ladder_score.html:15
#: .\mahjong_ranking\templates\mahjong_ranking\player_ladder_score.html:15 #: templates/mahjong_ranking/seasonranking_list.html:10
#: .\mahjong_ranking\templates\mahjong_ranking\seasonranking_list.html:10
msgid "Start" msgid "Start"
msgstr "Beginn" msgstr "Beginn"
#: .\mahjong_ranking\models.py:92 #: models.py:94
msgid "This is crucial to get the right Hanchans that scores" msgid "This is crucial to get the right Hanchans that scores"
msgstr "Wichtig damit die richtigen Hanchans in die Wertung kommen." msgstr "Wichtig damit die richtigen Hanchans in die Wertung kommen."
#: .\mahjong_ranking\models.py:99 #: models.py:101
msgid "Player 1" msgid "Player 1"
msgstr "Spieler 1" msgstr "Spieler 1"
#: .\mahjong_ranking\models.py:100 .\mahjong_ranking\models.py:102 #: models.py:102 models.py:104 models.py:121 models.py:123 models.py:140
#: .\mahjong_ranking\models.py:119 .\mahjong_ranking\models.py:121 #: models.py:142 models.py:159 models.py:161
#: .\mahjong_ranking\models.py:138 .\mahjong_ranking\models.py:140 #: templates/mahjong_ranking/eventhanchan_list.html:19
#: .\mahjong_ranking\models.py:157 .\mahjong_ranking\models.py:159 #: templates/mahjong_ranking/eventranking_list.html:21
#: .\mahjong_ranking\templates\mahjong_ranking\eventhanchan_list.html:19 #: templates/mahjong_ranking/hanchan_confirm_delete.html:16
#: .\mahjong_ranking\templates\mahjong_ranking\eventranking_list.html:21 #: templates/mahjong_ranking/hanchan_form.html:19
#: .\mahjong_ranking\templates\mahjong_ranking\hanchan_confirm_delete.html:16 #: templates/mahjong_ranking/kyudanranking_list.html:30
#: .\mahjong_ranking\templates\mahjong_ranking\hanchan_form.html:19 #: templates/mahjong_ranking/seasonranking_list.html:31
#: .\mahjong_ranking\templates\mahjong_ranking\kyudanranking_list.html:30
#: .\mahjong_ranking\templates\mahjong_ranking\seasonranking_list.html:31
msgid "Score" msgid "Score"
msgstr "Punkte" msgstr "Punkte"
#: .\mahjong_ranking\models.py:112 .\mahjong_ranking\models.py:131 #: models.py:114 models.py:133 models.py:152 models.py:171 models.py:173
#: .\mahjong_ranking\models.py:150 .\mahjong_ranking\models.py:169 #: templates/mahjong_ranking/hanchan_form.html:20
#: .\mahjong_ranking\models.py:171 #: templates/mahjong_ranking/player_dan_score.html:18
#: .\mahjong_ranking\templates\mahjong_ranking\hanchan_form.html:20 #: templates/mahjong_ranking/player_invalid_score.html:17
#: .\mahjong_ranking\templates\mahjong_ranking\player_dan_score.html:18
#: .\mahjong_ranking\templates\mahjong_ranking\player_invalid_score.html:17
msgid "Comment" msgid "Comment"
msgstr "Kommentar" msgstr "Kommentar"
#: .\mahjong_ranking\models.py:118 #: models.py:120
msgid "Player 2" msgid "Player 2"
msgstr "Spieler 2" msgstr "Spieler 2"
#: .\mahjong_ranking\models.py:137 #: models.py:139
msgid "Player 3" msgid "Player 3"
msgstr "Spieler 3" msgstr "Spieler 3"
#: .\mahjong_ranking\models.py:156 #: models.py:158
msgid "Player 4" msgid "Player 4"
msgstr "Spieler 4" msgstr "Spieler 4"
#: .\mahjong_ranking\models.py:173 #: models.py:175
msgid "Has been Confirmed" msgid "Has been Confirmed"
msgstr "Wurde bestätigt" msgstr "Wurde bestätigt"
#: .\mahjong_ranking\models.py:174 #: models.py:176
msgid "Only valid and confirmed Hanchans will be counted in the rating." msgid "Only valid and confirmed Hanchans will be counted in the rating."
msgstr "Nur gültige und bestätigte Hanchans kommen in die Wertung." msgstr "Nur gültige und bestätigte Hanchans kommen in die Wertung."
#: .\mahjong_ranking\models.py:179 .\mahjong_ranking\models.py:607 #: models.py:181 models.py:619 templates/mahjong_ranking/ladder_redbox.html:29
#: .\mahjong_ranking\templates\mahjong_ranking\ladder_redbox.html:29 #: templates/mahjong_ranking/player_ladder_score.html:63
#: .\mahjong_ranking\templates\mahjong_ranking\player_ladder_score.html:63
msgid "Season" msgid "Season"
msgstr "Saison" msgstr "Saison"
#: .\mahjong_ranking\models.py:184 #: models.py:186
msgid "Hanchan" msgid "Hanchan"
msgstr "Hanchan" msgstr "Hanchan"
#: .\mahjong_ranking\models.py:185 #: models.py:187 templates/mahjong_ranking/eventranking_list.html:17
#: .\mahjong_ranking\templates\mahjong_ranking\eventranking_list.html:17
msgid "Hanchans" msgid "Hanchans"
msgstr "Hanchans" msgstr "Hanchans"
#: .\mahjong_ranking\models.py:188 #: models.py:190
msgid "Hanchan from {0:%Y-%m-%d} at {0:%H:%M} with {1}" msgid "Hanchan from {0:%Y-%m-%d} at {0:%H:%M} with {1}"
msgstr "Hanchan vom {0:%Y-%m-%d} um {0:%H:%M} mit {1}" msgstr "Hanchan vom {0:%Y-%m-%d} um {0:%H:%M} mit {1}"
#: .\mahjong_ranking\models.py:215 #: models.py:217
#, python-format #, python-format
msgid "%s can't attend the same game multiple times" msgid "%s can't attend the same game multiple times"
msgstr "%s kann an einem Spiel nicht mehrfach teilnehmen." msgstr "%s kann an einem Spiel nicht mehrfach teilnehmen."
#: .\mahjong_ranking\models.py:223 #: models.py:225
msgid "Games in the future may not be added, Dr. Brown" msgid "Games in the future may not be added, Dr. Brown"
msgstr "Spiele aus der Zukunft dürfen noch nicht erfasst werden. Dr. Brown." msgstr "Spiele aus der Zukunft dürfen noch nicht erfasst werden. Dr. Brown."
#: .\mahjong_ranking\models.py:225 #: models.py:227
msgid "Only games during the event are allowed" msgid "Only games during the event are allowed"
msgstr "Nur Spiele während der Veranstaltung zählen." msgstr "Nur Spiele während der Veranstaltung zählen."
#: .\mahjong_ranking\models.py:228 #: models.py:230
msgid "Gamescore is lower then 100.000 Pt." msgid "Gamescore is lower then 100.000 Pt."
msgstr "Spielstand ist weniger als 100.000 Punkte" msgstr "Spielstand ist weniger als 100.000 Punkte"
#: .\mahjong_ranking\models.py:230 #: models.py:232
msgid "Gamescore is over 100.000 Pt." msgid "Gamescore is over 100.000 Pt."
msgstr "Spielstand ist über 100.000 Punkte." msgstr "Spielstand ist über 100.000 Punkte."
#: .\mahjong_ranking\models.py:362 #: models.py:367
msgid "Kyū/Dan Ranking" msgid "Kyū/Dan Ranking"
msgstr "Kyū/Dan Wertung" msgstr "Kyū/Dan Wertung"
#: .\mahjong_ranking\models.py:363 #: models.py:368
msgid "Kyū/Dan Rankings" msgid "Kyū/Dan Rankings"
msgstr "Kyū/Dan Wertungen" msgstr "Kyū/Dan Wertungen"
#: .\mahjong_ranking\templates\mahjong_ranking\eventhanchan_form.html:11 #: templates/mahjong_ranking/eventhanchan_form.html:11
#: .\mahjong_ranking\templates\mahjong_ranking\eventhanchan_list.html:55 #: templates/mahjong_ranking/eventhanchan_list.html:55
msgid "Edit Hanchans" msgid "Edit Hanchans"
msgstr "Hanchans bearbeiten" msgstr "Hanchans bearbeiten"
#: .\mahjong_ranking\templates\mahjong_ranking\eventhanchan_form.html:49 #: templates/mahjong_ranking/eventhanchan_form.html:49
#: .\mahjong_ranking\templates\mahjong_ranking\hanchan_form.html:58 #: templates/mahjong_ranking/hanchan_form.html:58
msgid "Total" msgid "Total"
msgstr "Total" msgstr "Total"
#: .\mahjong_ranking\templates\mahjong_ranking\eventhanchan_form.html:51 #: templates/mahjong_ranking/eventhanchan_form.html:51
msgid "Difference" msgid "Difference"
msgstr "Unterschied" msgstr "Unterschied"
#: .\mahjong_ranking\templates\mahjong_ranking\eventhanchan_form.html:103 #: templates/mahjong_ranking/eventhanchan_form.html:103
#: .\mahjong_ranking\templates\mahjong_ranking\eventhanchan_list.html:56 #: templates/mahjong_ranking/eventhanchan_list.html:56
#: .\mahjong_ranking\templates\mahjong_ranking\eventranking_list.html:52 #: templates/mahjong_ranking/eventranking_list.html:52
#: .\mahjong_ranking\templates\mahjong_ranking\hanchan_form.html:4 #: templates/mahjong_ranking/hanchan_form.html:4
#: .\mahjong_ranking\templates\mahjong_ranking\hanchan_form.html:14 #: templates/mahjong_ranking/hanchan_form.html:14
msgid "Add Hanchan" msgid "Add Hanchan"
msgstr "Hanchan hinzufügen" msgstr "Hanchan hinzufügen"
#: .\mahjong_ranking\templates\mahjong_ranking\eventhanchan_form.html:105 #: templates/mahjong_ranking/eventhanchan_form.html:105
#: .\mahjong_ranking\templates\mahjong_ranking\eventhanchan_list.html:37 #: templates/mahjong_ranking/eventhanchan_list.html:37
#: .\mahjong_ranking\templates\mahjong_ranking\hanchan_confirm_delete.html:4 #: templates/mahjong_ranking/hanchan_confirm_delete.html:4
#: .\mahjong_ranking\templates\mahjong_ranking\hanchan_confirm_delete.html:33 #: templates/mahjong_ranking/hanchan_confirm_delete.html:33
#: .\mahjong_ranking\templates\mahjong_ranking\player_dan_score.html:44 #: templates/mahjong_ranking/player_dan_score.html:44
#: .\mahjong_ranking\templates\mahjong_ranking\player_invalid_score.html:33 #: templates/mahjong_ranking/player_invalid_score.html:33
#: .\mahjong_ranking\templates\mahjong_ranking\player_kyu_score.html:41 #: templates/mahjong_ranking/player_kyu_score.html:41
#: .\mahjong_ranking\templates\mahjong_ranking\player_ladder_score.html:52 #: templates/mahjong_ranking/player_ladder_score.html:52
msgid "Delete Hanchan" msgid "Delete Hanchan"
msgstr "Hanchan löschen" msgstr "Hanchan löschen"
#: .\mahjong_ranking\templates\mahjong_ranking\eventhanchan_form.html:118 #: templates/mahjong_ranking/eventhanchan_form.html:118
#: .\mahjong_ranking\templates\mahjong_ranking\hanchan_form.html:71 #: templates/mahjong_ranking/hanchan_form.html:71
msgid "back" msgid "back"
msgstr "Zurück" msgstr "Zurück"
#: .\mahjong_ranking\templates\mahjong_ranking\eventhanchan_form.html:119 #: templates/mahjong_ranking/eventhanchan_form.html:119
#: .\mahjong_ranking\templates\mahjong_ranking\hanchan_form.html:72 #: templates/mahjong_ranking/hanchan_form.html:72
msgid "save" msgid "save"
msgstr "Speichern" msgstr "Speichern"
#: .\mahjong_ranking\templates\mahjong_ranking\eventhanchan_list.html:7 #: templates/mahjong_ranking/eventhanchan_list.html:7
msgid "Played Hanchans" msgid "Played Hanchans"
msgstr "Gespielte Hanchans" msgstr "Gespielte Hanchans"
#: .\mahjong_ranking\templates\mahjong_ranking\eventhanchan_list.html:18 #: templates/mahjong_ranking/eventhanchan_list.html:18
#: .\mahjong_ranking\templates\mahjong_ranking\hanchan_confirm_delete.html:15 #: templates/mahjong_ranking/hanchan_confirm_delete.html:15
msgid "Place" msgid "Place"
msgstr "Platz" msgstr "Platz"
#: .\mahjong_ranking\templates\mahjong_ranking\eventhanchan_list.html:21 #: templates/mahjong_ranking/eventhanchan_list.html:21
#: .\mahjong_ranking\templates\mahjong_ranking\hanchan_confirm_delete.html:18 #: templates/mahjong_ranking/hanchan_confirm_delete.html:18
#: .\mahjong_ranking\templates\mahjong_ranking\player_dan_score.html:17 #: templates/mahjong_ranking/player_dan_score.html:17
msgid "Dan Points" msgid "Dan Points"
msgstr "Dan Punkte" msgstr "Dan Punkte"
#: .\mahjong_ranking\templates\mahjong_ranking\eventhanchan_list.html:23 #: templates/mahjong_ranking/eventhanchan_list.html:23
#: .\mahjong_ranking\templates\mahjong_ranking\hanchan_confirm_delete.html:20 #: templates/mahjong_ranking/hanchan_confirm_delete.html:20
#: .\mahjong_ranking\templates\mahjong_ranking\player_invalid_score.html:16 #: templates/mahjong_ranking/player_invalid_score.html:16
#: .\mahjong_ranking\templates\mahjong_ranking\player_kyu_score.html:18 #: templates/mahjong_ranking/player_kyu_score.html:18
msgid "Kyu Points" msgid "Kyu Points"
msgstr "Kyu Punkte" msgstr "Kyu Punkte"
#: .\mahjong_ranking\templates\mahjong_ranking\eventhanchan_list.html:43 #: templates/mahjong_ranking/eventhanchan_list.html:43
#: .\mahjong_ranking\templates\mahjong_ranking\hanchan_form.html:4 #: templates/mahjong_ranking/hanchan_form.html:4
#: .\mahjong_ranking\templates\mahjong_ranking\hanchan_form.html:14 #: templates/mahjong_ranking/hanchan_form.html:14
#: .\mahjong_ranking\templates\mahjong_ranking\player_dan_score.html:47 #: templates/mahjong_ranking/player_dan_score.html:47
#: .\mahjong_ranking\templates\mahjong_ranking\player_invalid_score.html:36 #: templates/mahjong_ranking/player_invalid_score.html:36
#: .\mahjong_ranking\templates\mahjong_ranking\player_kyu_score.html:44 #: templates/mahjong_ranking/player_kyu_score.html:44
#: .\mahjong_ranking\templates\mahjong_ranking\player_ladder_score.html:55 #: templates/mahjong_ranking/player_ladder_score.html:55
msgid "Edit Hanchan" msgid "Edit Hanchan"
msgstr "Hanchan bearbeiten" msgstr "Hanchan bearbeiten"
#: .\mahjong_ranking\templates\mahjong_ranking\eventhanchan_list.html:48 #: templates/mahjong_ranking/eventhanchan_list.html:48
msgid "No Hanchan has been added to this event yet." msgid "No Hanchan has been added to this event yet."
msgstr "Für diese Veranstaltung wurde noch keine Hanchan eingetragen." msgstr "Für diese Veranstaltung wurde noch keine Hanchan eingetragen."
#: .\mahjong_ranking\templates\mahjong_ranking\eventhanchan_list.html:54 #: templates/mahjong_ranking/eventhanchan_list.html:54
#: .\mahjong_ranking\templates\mahjong_ranking\eventranking_list.html:51 #: templates/mahjong_ranking/eventranking_list.html:51
msgid "Edit Event" msgid "Edit Event"
msgstr "Veranstaltung bearbeiten" msgstr "Veranstaltung bearbeiten"
#: .\mahjong_ranking\templates\mahjong_ranking\eventranking_list.html:4 #: templates/mahjong_ranking/eventranking_list.html:4
#: .\mahjong_ranking\templates\mahjong_ranking\eventranking_list.html:5 #: templates/mahjong_ranking/eventranking_list.html:5
msgid "Tournament Ranking" msgid "Tournament Ranking"
msgstr "Turnierwertung" msgstr "Turnierwertung"
#: .\mahjong_ranking\templates\mahjong_ranking\eventranking_list.html:12 #: templates/mahjong_ranking/eventranking_list.html:12
#: .\mahjong_ranking\templates\mahjong_ranking\kyudanranking_list.html:25 #: templates/mahjong_ranking/kyudanranking_list.html:25
#: .\mahjong_ranking\templates\mahjong_ranking\seasonranking_list.html:23 #: templates/mahjong_ranking/seasonranking_list.html:23
msgid "Rank" msgid "Rank"
msgstr "Rang" msgstr "Rang"
#: .\mahjong_ranking\templates\mahjong_ranking\eventranking_list.html:13 #: templates/mahjong_ranking/eventranking_list.html:13
#: .\mahjong_ranking\templates\mahjong_ranking\kyudanranking_list.html:17 #: templates/mahjong_ranking/kyudanranking_list.html:17
#: .\mahjong_ranking\templates\mahjong_ranking\seasonranking_list.html:24 #: templates/mahjong_ranking/seasonranking_list.html:24
msgid "Avatar" msgid "Avatar"
msgstr "Avatar" msgstr "Avatar"
#: .\mahjong_ranking\templates\mahjong_ranking\eventranking_list.html:14 #: templates/mahjong_ranking/eventranking_list.html:14
#: .\mahjong_ranking\templates\mahjong_ranking\kyudanranking_list.html:20 #: templates/mahjong_ranking/kyudanranking_list.html:20
#: .\mahjong_ranking\templates\mahjong_ranking\seasonranking_list.html:25 #: templates/mahjong_ranking/seasonranking_list.html:25
msgid "Nickname" msgid "Nickname"
msgstr "Spitzname" msgstr "Spitzname"
#: .\mahjong_ranking\templates\mahjong_ranking\eventranking_list.html:15 #: templates/mahjong_ranking/eventranking_list.html:15
msgid "Name" msgid "Name"
msgstr "Name" msgstr "Name"
#: .\mahjong_ranking\templates\mahjong_ranking\eventranking_list.html:16 #: templates/mahjong_ranking/eventranking_list.html:16
#: .\mahjong_ranking\templates\mahjong_ranking\seasonranking_list.html:26 #: templates/mahjong_ranking/seasonranking_list.html:26
msgid "Average" msgid "Average"
msgstr "Durchschnitt" msgstr "Durchschnitt"
#: .\mahjong_ranking\templates\mahjong_ranking\eventranking_list.html:20 #: templates/mahjong_ranking/eventranking_list.html:20
#: .\mahjong_ranking\templates\mahjong_ranking\player_dan_score.html:15 #: templates/mahjong_ranking/player_dan_score.html:15
#: .\mahjong_ranking\templates\mahjong_ranking\player_invalid_score.html:15 #: templates/mahjong_ranking/player_invalid_score.html:15
#: .\mahjong_ranking\templates\mahjong_ranking\player_kyu_score.html:16 #: templates/mahjong_ranking/player_kyu_score.html:16
#: .\mahjong_ranking\templates\mahjong_ranking\player_ladder_score.html:16 #: templates/mahjong_ranking/player_ladder_score.html:16
#: .\mahjong_ranking\templates\mahjong_ranking\seasonranking_list.html:30 #: templates/mahjong_ranking/seasonranking_list.html:30
msgid "Placement" msgid "Placement"
msgstr "Platzierung" msgstr "Platzierung"
#: .\mahjong_ranking\templates\mahjong_ranking\eventranking_list.html:22 #: templates/mahjong_ranking/eventranking_list.html:22
#: .\mahjong_ranking\templates\mahjong_ranking\seasonranking_list.html:32 #: templates/mahjong_ranking/seasonranking_list.html:32
msgid "count" msgid "count"
msgstr "Anzahl" msgstr "Anzahl"
#: .\mahjong_ranking\templates\mahjong_ranking\eventranking_list.html:23 #: templates/mahjong_ranking/eventranking_list.html:23
#: .\mahjong_ranking\templates\mahjong_ranking\seasonranking_list.html:33 #: templates/mahjong_ranking/seasonranking_list.html:33
msgid "good" msgid "good"
msgstr "gut" msgstr "gut"
#: .\mahjong_ranking\templates\mahjong_ranking\eventranking_list.html:24 #: templates/mahjong_ranking/eventranking_list.html:24
#: .\mahjong_ranking\templates\mahjong_ranking\seasonranking_list.html:34 #: templates/mahjong_ranking/seasonranking_list.html:34
msgid "won" msgid "won"
msgstr "gewonnen" msgstr "gewonnen"
#: .\mahjong_ranking\templates\mahjong_ranking\hanchan_confirm_delete.html:39 #: templates/mahjong_ranking/hanchan_confirm_delete.html:39
msgid "Cancel" msgid "Cancel"
msgstr "Abbruch" msgstr "Abbruch"
#: .\mahjong_ranking\templates\mahjong_ranking\hanchan_confirm_delete.html:40 #: templates/mahjong_ranking/hanchan_confirm_delete.html:40
msgid "Delete" msgid "Delete"
msgstr "Löschen" msgstr "Löschen"
#: .\mahjong_ranking\templates\mahjong_ranking\hanchan_form.html:18 #: templates/mahjong_ranking/hanchan_form.html:18
msgid "Player" msgid "Player"
msgstr "Spieler" msgstr "Spieler"
#: .\mahjong_ranking\templates\mahjong_ranking\kyudanranking_list.html:4 #: templates/mahjong_ranking/kyudanranking_list.html:4
#: .\mahjong_ranking\templates\mahjong_ranking\kyudanranking_list.html:9 #: templates/mahjong_ranking/kyudanranking_list.html:9
#| msgid "Player List"
msgid "Players list" msgid "Players list"
msgstr "Spielerliste" msgstr "Spielerliste"
#: .\mahjong_ranking\templates\mahjong_ranking\kyudanranking_list.html:35 #: templates/mahjong_ranking/kyudanranking_list.html:35
msgid "Games Total" msgid "Games Total"
msgstr "Spiele total" msgstr "Spiele total"
#: .\mahjong_ranking\templates\mahjong_ranking\ladder_redbox.html:3 #: templates/mahjong_ranking/ladder_redbox.html:3
msgid "Latest Hanchans" msgid "Latest Hanchans"
msgstr "Letzten Hanchans" msgstr "Letzten Hanchans"
#: .\mahjong_ranking\templates\mahjong_ranking\ladder_redbox.html:15 #: templates/mahjong_ranking/ladder_redbox.html:15
msgid "Latest Events" msgid "Latest Events"
msgstr "Letzte Veranstaltungen" msgstr "Letzte Veranstaltungen"
#: .\mahjong_ranking\templates\mahjong_ranking\ladder_redbox.html:27 #: templates/mahjong_ranking/ladder_redbox.html:27
msgid "Ladder Archive" msgid "Ladder Archive"
msgstr "Ladder Archiv" msgstr "Ladder Archiv"
#: .\mahjong_ranking\templates\mahjong_ranking\player_dan_score.html:4 #: templates/mahjong_ranking/player_dan_score.html:4
#: .\mahjong_ranking\templates\mahjong_ranking\player_dan_score.html:5 #: templates/mahjong_ranking/player_dan_score.html:5
msgid "Dan Score for" msgid "Dan Score for"
msgstr "Dan Wertung für" msgstr "Dan Wertung für"
#: .\mahjong_ranking\templates\mahjong_ranking\player_dan_score.html:8 #: templates/mahjong_ranking/player_dan_score.html:8
msgid "Hanchans that apply to the Dan Score" msgid "Hanchans that apply to the Dan Score"
msgstr "Hanchans welche zur Dan Wertung zählen" msgstr "Hanchans welche zur Dan Wertung zählen"
#: .\mahjong_ranking\templates\mahjong_ranking\player_dan_score.html:12 #: templates/mahjong_ranking/player_dan_score.html:12
#: .\mahjong_ranking\templates\mahjong_ranking\player_kyu_score.html:13 #: templates/mahjong_ranking/player_kyu_score.html:13
#: .\mahjong_ranking\templates\mahjong_ranking\player_ladder_score.html:13 #: templates/mahjong_ranking/player_ladder_score.html:13
msgid "Date" msgid "Date"
msgstr "Datum" msgstr "Datum"
#: .\mahjong_ranking\templates\mahjong_ranking\player_dan_score.html:13 #: templates/mahjong_ranking/player_dan_score.html:13
#: .\mahjong_ranking\templates\mahjong_ranking\player_invalid_score.html:12 #: templates/mahjong_ranking/player_invalid_score.html:12
#: .\mahjong_ranking\templates\mahjong_ranking\player_kyu_score.html:14 #: templates/mahjong_ranking/player_kyu_score.html:14
#: .\mahjong_ranking\templates\mahjong_ranking\player_ladder_score.html:14 #: templates/mahjong_ranking/player_ladder_score.html:14
msgid "Event" msgid "Event"
msgstr "Veranstaltung" msgstr "Veranstaltung"
#: .\mahjong_ranking\templates\mahjong_ranking\player_dan_score.html:16 #: templates/mahjong_ranking/player_dan_score.html:16
#: .\mahjong_ranking\templates\mahjong_ranking\player_invalid_score.html:14 #: templates/mahjong_ranking/player_invalid_score.html:14
#: .\mahjong_ranking\templates\mahjong_ranking\player_kyu_score.html:17 #: templates/mahjong_ranking/player_kyu_score.html:17
#: .\mahjong_ranking\templates\mahjong_ranking\player_ladder_score.html:17 #: templates/mahjong_ranking/player_ladder_score.html:17
msgid "Players" msgid "Players"
msgstr "Spieler" msgstr "Spieler"
#: .\mahjong_ranking\templates\mahjong_ranking\player_invalid_score.html:4 #: templates/mahjong_ranking/player_invalid_score.html:4
#: .\mahjong_ranking\templates\mahjong_ranking\player_invalid_score.html:6 #: templates/mahjong_ranking/player_invalid_score.html:6
msgid "Unconfirmed Hanchans from" msgid "Unconfirmed Hanchans from"
msgstr "Nicht bestätigte Hanchans von" msgstr "Nicht bestätigte Hanchans von"
#: .\mahjong_ranking\templates\mahjong_ranking\player_invalid_score.html:9 #: templates/mahjong_ranking/player_invalid_score.html:9
msgid "Invalid hanchans with" msgid "Invalid hanchans with"
msgstr "Ungültige Hanchans mit" msgstr "Ungültige Hanchans mit"
#: .\mahjong_ranking\templates\mahjong_ranking\player_kyu_score.html:4 #: templates/mahjong_ranking/player_kyu_score.html:4
#: .\mahjong_ranking\templates\mahjong_ranking\player_kyu_score.html:6 #: templates/mahjong_ranking/player_kyu_score.html:6
msgid "Kyu Score for" msgid "Kyu Score for"
msgstr "Kyu Wertung für" msgstr "Kyu Wertung für"
#: .\mahjong_ranking\templates\mahjong_ranking\player_kyu_score.html:9 #: templates/mahjong_ranking/player_kyu_score.html:9
msgid "Hanchans that apply to the Kyu Score" msgid "Hanchans that apply to the Kyu Score"
msgstr "Hanchans welche zur Kyu Wertung zählen" msgstr "Hanchans welche zur Kyu Wertung zählen"
#: .\mahjong_ranking\templates\mahjong_ranking\player_ladder_score.html:4 #: templates/mahjong_ranking/player_ladder_score.html:4
#: .\mahjong_ranking\templates\mahjong_ranking\player_ladder_score.html:5 #: templates/mahjong_ranking/player_ladder_score.html:5
msgid "Ladder Score for" msgid "Ladder Score for"
msgstr "Ladder Wertung für" msgstr "Ladder Wertung für"
#: .\mahjong_ranking\templates\mahjong_ranking\player_ladder_score.html:8 #: templates/mahjong_ranking/player_ladder_score.html:8
msgid "Hanchans that apply to the Ladder Score" msgid "Hanchans that apply to the Ladder Score"
msgstr "Hanchans welche in der Ladder zählen" msgstr "Hanchans welche in der Ladder zählen"
#: .\mahjong_ranking\templates\mahjong_ranking\player_ladder_score.html:71 #: templates/mahjong_ranking/player_ladder_score.html:71
msgid "Go" msgid "Go"
msgstr "Los" msgstr "Los"
#: .\mahjong_ranking\templates\mahjong_ranking\seasonranking_list.html:11 #: templates/mahjong_ranking/seasonranking_list.html:11
msgid "End" msgid "End"
msgstr "Ende" msgstr "Ende"
#: .\mahjong_ranking\templates\mahjong_ranking\seasonranking_list.html:12 #: templates/mahjong_ranking/seasonranking_list.html:12
msgid "Participants" msgid "Participants"
msgstr "Teilnehmer" msgstr "Teilnehmer"
#: .\mahjong_ranking\views.py:104 #: views.py:116
#, python-format #, python-format
msgid "%s has been updated successfully." msgid "%s has been updated successfully."
msgstr "%s wurde erfolgreich aktualisiert." msgstr "%s wurde erfolgreich aktualisiert."
#: .\mahjong_ranking\views.py:107 #: views.py:119
#, python-format #, python-format
msgid "%s has been added successfully. You can now add a new one." msgid "%s has been added successfully. You can now add a new one."
msgstr "%s wurde erfolgreich hinzugefügt. Du kannst eine neue eintragen." msgstr "%s wurde erfolgreich hinzugefügt. Du kannst eine neue eintragen."
#: .\mahjong_ranking\views.py:207 #: views.py:222
msgid "No user found matching the name {}" msgid "No user found matching the name {}"
msgstr "Kein Benutzer mit dem Namen %s gefunden" msgstr "Kein Benutzer mit dem Namen %s gefunden"
#~ msgid "Set unconfirmed"
#~ msgstr "Als unbestätigt markieren"
#~ msgid "Full Name" #~ msgid "Full Name"
#~ msgstr "Voller Name" #~ msgstr "Voller Name"

View File

@@ -1,4 +1,4 @@
"""Export Mahjong Rankings as excel files.""" """Export Mahjong Rankings as Excel files."""
import os import os
from datetime import date, time, datetime from datetime import date, time, datetime

View File

@@ -0,0 +1,58 @@
"""
Recalculates all Kyu/Dan Rankings until the given date
and writes them to the legacy fields.
"""
from django.core.management.base import BaseCommand
from datetime import datetime, date, time
from mahjong_ranking import models
from django.utils.dateparse import parse_date
from django.utils import timezone
LEGACY_ATTRIBUTES = (
"dan",
"dan_points",
"max_dan_points",
"kyu",
"kyu_points",
"hanchan_count",
"good_hanchans",
"won_hanchans"
)
class Command(BaseCommand):
help = "Recalculates all Kyu/Dan Rankings until the given date " + \
"and writes them to the legacy fields."
def add_arguments(self, parser):
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.')
def handle(self, *args, **options):
since = options.get('since', None)
until = options.get('until', None)
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=True)
# write the updated values to the legacy fields
for ranking in models.KyuDanRanking.objects.all():
print(ranking)
for attribute in LEGACY_ATTRIBUTES:
setattr(ranking, f"legacy_{attribute}", getattr(ranking, attribute))
value = getattr(ranking, attribute)
legacy_value = getattr(ranking, f"legacy_{attribute}")
print(f"{attribute}: {value}, legacy_{attribute}: {legacy_value}")
ranking.legacy_date = until.date()
print(f"legacy_date: {ranking.legacy_date}")
ranking.save()

View File

@@ -3,13 +3,15 @@ Rest all dan points to 0 at a given date.
""" """
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from datetime import date, datetime, time from datetime import datetime
from datetime import time
from mahjong_ranking import models from mahjong_ranking import models
from django.utils.dateparse import parse_date from django.utils.dateparse import parse_date
from django.utils import timezone from django.utils import timezone
class Command(BaseCommand): class Command(BaseCommand):
""" Recalculate all Kyu/Dan Rankings """ """ reset every dan player to 1st dan with 0 points. """
help = "reset every dan player to 1st dan with 0 points." help = "reset every dan player to 1st dan with 0 points."
@@ -22,7 +24,8 @@ class Command(BaseCommand):
legacy_attrs.remove('legacy_date') legacy_attrs.remove('legacy_date')
reset_date = timezone.make_aware(datetime.combine( reset_date = timezone.make_aware(datetime.combine(
options.get('reset_date'), time(23, 59, 59))) options.get('reset_date'), time(23, 59, 59)))
models.KyuDanRanking.objects.update(until=reset_date, force_recalc=True) models.KyuDanRanking.objects.update(
until=reset_date, force_recalc=True)
for ranking in models.KyuDanRanking.objects.filter(dan__gt=0): for ranking in models.KyuDanRanking.objects.filter(dan__gt=0):
print(ranking) print(ranking)
ranking.dan = 1 ranking.dan = 1

View File

@@ -74,11 +74,13 @@ class HanchanManager(models.Manager):
[hanchan.get_playerdata(user) for hanchan in queryset] [hanchan.get_playerdata(user) for hanchan in queryset]
return queryset return queryset
def season_hanchans(self, user=None, season=None, until=None): def season_hanchans(self, user: object = None,
season: int = None, until: date = None):
"""Return all Hanchans that belong to a given or the current season. """Return all Hanchans that belong to a given or the current season.
:param user: Only return Hanchans where this user participated. :param user: Only return Hanchans where this user participated.
:param season: the year of the wanted season, current year if None. :param season: the year of the wanted season, current year if None.
:param until: only return hanchans played until the given date.
:return: QuerySet Object :return: QuerySet Object
""" """
try: try:
@@ -92,6 +94,7 @@ class HanchanManager(models.Manager):
:param user: Return Hanchans where this user participated. :param user: Return Hanchans where this user participated.
:param since: only return Hanchans played since the given datetime :param since: only return Hanchans played since the given datetime
:param until: only return hanchans played until the given date.
:param filter_args: To add specific arguments to the Django filter. :param filter_args: To add specific arguments to the Django filter.
:return: a QuerySet Object :return: a QuerySet Object
""" """
@@ -199,10 +202,8 @@ class SeasonRankingManager(models.Manager):
class KyuDanRankingManager(models.Manager): class KyuDanRankingManager(models.Manager):
def json_data(self): def json_data(self):
""" Get all Rankings for a given Season and return them as a list of """ Get all Rankings for a given Season and return them as a list of
dict objects, suitable for JSON exports and other processings. dict objects, suitable for JSON exports and other processing.
:return: a list() of dict() objects suitable for JSON export.
:param season: Season that should be exported, current season if empty
:return: a list() of dict() objects suiteable for JSON export.
""" """
json_data = list() json_data = list()
values = self.all() values = self.all()
@@ -237,8 +238,9 @@ class KyuDanRankingManager(models.Manager):
def update(self, since=None, until=None, force_recalc=False): def update(self, since=None, until=None, force_recalc=False):
old_attr = {'dan': None, 'dan_points': None, old_attr = {'dan': None, 'dan_points': None,
'kyu': None, 'kyu_points': None, 'won_hanchans': None, 'kyu': None, 'kyu_points': None,
'good_hanchans': None, 'hanchan_count': None} 'won_hanchans': None, 'good_hanchans': None,
'hanchan_count': None}
for ranking in self.all(): for ranking in self.all():
old_attr = {attr: getattr(ranking, attr) for attr in old_attr = {attr: getattr(ranking, attr) for attr in
old_attr.keys()} old_attr.keys()}
@@ -247,7 +249,8 @@ class KyuDanRankingManager(models.Manager):
for attr, old_value in old_attr.items(): for attr, old_value in old_attr.items():
if getattr(ranking, attr) != old_value: if getattr(ranking, attr) != old_value:
LOGGER.warning( LOGGER.warning(
"%(user)s recalc shows differences in %(attr)s! old: %(old)d, new: %(new)d", "%(user)s recalc shows differences in %(attr)s! ' + "
"'old: %(old)d, new: %(new)d",
{'user': ranking.user, 'attr': attr, {'user': ranking.user, 'attr': attr,
'old': old_value or 0, 'new': getattr(ranking, attr) or 0} 'old': old_value or 0, 'new': getattr(ranking, attr, 0)}
) )

View File

@@ -1,12 +1,26 @@
"""Middleware to defer slow denormalization at the end of a request.""" """Middleware to defer slow denormalization at the end of a request."""
from django.core.cache import cache from django.core.cache import cache
from mahjong_ranking import models from mahjong_ranking import models
from . import LOGGER from . import LOGGER
class DenormalizationUpdateMiddleware(object): # Ignore PyLintBear (R0903) class DenormalizationUpdateMiddleware:
"""To recalculate everything in the queues at the end of a POST request.""" """To recalculate everything in the queues at the end of a POST request."""
def __init__(self, get_response=None):
self.get_response = get_response
super().__init__()
def __call__(self, request):
response = None
if hasattr(self, 'process_request'):
response = self.process_request(request)
response = response or self.get_response(request)
if hasattr(self, 'process_response'):
response = self.process_response(request, response)
return response
def process_response(self, request, response): # Ignore PyLintBear (R0201) def process_response(self, request, response): # Ignore PyLintBear (R0201)
"""Check and process the recalculation queues on each POST request. """Check and process the recalculation queues on each POST request.
@@ -16,7 +30,6 @@ class DenormalizationUpdateMiddleware(object): # Ignore PyLintBear (R0903)
""" """
event_queue = set() event_queue = set()
season_queue = set() season_queue = set()
if request.method != 'POST': if request.method != 'POST':
return response return response

View File

@@ -41,7 +41,8 @@ class Migration(migrations.Migration):
serialize=False, auto_created=True, serialize=False, auto_created=True,
primary_key=True)), primary_key=True)),
('start', models.DateTimeField( ('start', models.DateTimeField(
help_text='Wichtig damit die richtigen Hanchans in die Wertung kommen.', help_text="Wichtig damit die richtigen Hanchans in die " + \
"Wertung kommen.",
verbose_name='Beginn')), verbose_name='Beginn')),
('player1_input_score', ('player1_input_score',
models.IntegerField(verbose_name='Punkte')), models.IntegerField(verbose_name='Punkte')),
@@ -55,14 +56,14 @@ class Migration(migrations.Migration):
null=True, editable=False, blank=True)), null=True, editable=False, blank=True)),
('player1_bonus_points', models.SmallIntegerField( ('player1_bonus_points', models.SmallIntegerField(
null=True, editable=False, blank=True)), null=True, editable=False, blank=True)),
('player1_comment', models.CharField(verbose_name='Anmerkung', ('player1_comment', models.CharField(verbose_name="Anmerkung",
max_length=255, max_length=255,
editable=False, editable=False,
blank=True)), blank=True)),
('player2_input_score', ('player2_input_score',
models.IntegerField(verbose_name='Punkte')), models.IntegerField(verbose_name="Punkte")),
('player2_game_score', models.PositiveIntegerField( ('player2_game_score', models.PositiveIntegerField(
default=0, verbose_name='Punkte', editable=False)), default=0, verbose_name="Punkte", editable=False)),
('player2_placement', models.PositiveSmallIntegerField( ('player2_placement', models.PositiveSmallIntegerField(
default=0, editable=False)), default=0, editable=False)),
('player2_kyu_points', models.SmallIntegerField( ('player2_kyu_points', models.SmallIntegerField(
@@ -111,7 +112,8 @@ class Migration(migrations.Migration):
models.TextField(verbose_name='Anmerkung', blank=True)), models.TextField(verbose_name='Anmerkung', blank=True)),
('confirmed', models.BooleanField( ('confirmed', models.BooleanField(
default=True, default=True,
help_text='Nur g\xfcltige und best\xe4tigte Hanchans kommen in die Wertung.', help_text="Nur g\xfcltige und best\xe4tigte Hanchans kommen " + \
"in die Wertung.",
verbose_name='Wurde best\xe4tigt')), verbose_name='Wurde best\xe4tigt')),
('player_names', ('player_names',
models.CharField(max_length=255, editable=False)), models.CharField(max_length=255, editable=False)),

View File

@@ -6,7 +6,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('mahjong_ranking', '0004_auto_20170218_1947'), ('mahjong_ranking', '0004_auto_20170218_1947'),
] ]
@@ -14,7 +13,9 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='kyudanranking', name='kyudanranking',
options={'ordering': ('-dan_points', 'dan', '-kyu_points'), 'verbose_name': 'Kyū/Dan Wertung', 'verbose_name_plural': 'Kyū/Dan Wertungen'}, options={'ordering': ('-dan_points', 'dan', '-kyu_points'),
'verbose_name': 'Kyū/Dan Wertung',
'verbose_name_plural': 'Kyū/Dan Wertungen'},
), ),
migrations.AddField( migrations.AddField(
model_name='kyudanranking', model_name='kyudanranking',

View File

@@ -8,7 +8,6 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('mahjong_ranking', '0005_auto_20171115_0653'), ('mahjong_ranking', '0005_auto_20171115_0653'),
] ]
@@ -42,27 +41,40 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='eventranking', model_name='eventranking',
name='user', name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL), field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT,
to=settings.AUTH_USER_MODEL),
), ),
migrations.AlterField( migrations.AlterField(
model_name='hanchan', model_name='hanchan',
name='player1', 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'), 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( migrations.AlterField(
model_name='hanchan', model_name='hanchan',
name='player2', 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'), 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( migrations.AlterField(
model_name='hanchan', model_name='hanchan',
name='player3', 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'), 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( migrations.AlterField(
model_name='hanchan', model_name='hanchan',
name='player4', 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'), 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( migrations.AlterField(
model_name='kyudanranking', model_name='kyudanranking',
@@ -82,6 +94,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='seasonranking', model_name='seasonranking',
name='user', name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL), field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT,
to=settings.AUTH_USER_MODEL),
), ),
] ]

View File

@@ -0,0 +1,25 @@
# Generated by Django 4.2.2 on 2023-07-19 18:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mahjong_ranking', '0006_auto_20171214_1318'),
]
operations = [
migrations.AlterModelOptions(
name='kyudanranking',
options={'ordering': (
models.OrderBy(models.F('dan'), descending=True, nulls_last=True),
'-dan_points', '-kyu_points', '-won_hanchans', '-good_hanchans',
'-last_hanchan_date'), 'verbose_name': 'Kyū/Dan Wertung',
'verbose_name_plural': 'Kyū/Dan Wertungen'},
),
migrations.AddField(
model_name='kyudanranking',
name='legacy_max_dan_points',
field=models.PositiveIntegerField(default=0),
),
]

View File

@@ -12,7 +12,7 @@ from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
from events.models import Event from events.models import Event
from . import DAN_RANKS_DICT, LOGGER, set_dirty from . import DAN_RANKS_DICT, LOGGER, set_dirty
@@ -29,7 +29,8 @@ class EventRanking(models.Model):
Sie beschränken sich aber auf einen Event und werden nur dann angestossen, Sie beschränken sich aber auf einen Event und werden nur dann angestossen,
wenn der Event als Turnier markiert wurde. wenn der Event als Turnier markiert wurde.
""" """
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT) user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.PROTECT)
event = models.ForeignKey(Event, on_delete=models.CASCADE) event = models.ForeignKey(Event, on_delete=models.CASCADE)
placement = models.PositiveIntegerField(blank=True, null=True) placement = models.PositiveIntegerField(blank=True, null=True)
avg_placement = models.FloatField(default=4) avg_placement = models.FloatField(default=4)
@@ -46,9 +47,9 @@ class EventRanking(models.Model):
def recalculate(self): def recalculate(self):
""" """
Berechnet die durschnittliche Platzierung und Punkte, u.v.m. neu. Berechnet die durchschnittliche Platzierung und Punkte, u.v.m. neu.
Diese Daten werden benötigt um die Platzierung zu erstellen. Sie Diese Daten werden benötigt, um die Platzierung zu erstellen. Sie
können zwar sehr leicht errechnet werden, es macht trotzdem Sinn können zwar sehr leicht errechnet werden, es macht trotzdem Sinn
sie zwischenzuspeichern. sie zwischenzuspeichern.
""" """
@@ -289,9 +290,8 @@ class Hanchan(models.Model):
self.bonus_points = getattr(self, '%s_bonus_points' % player) self.bonus_points = getattr(self, '%s_bonus_points' % player)
self.player_comment = getattr(self, '%s_comment' % player) self.player_comment = getattr(self, '%s_comment' % player)
def update_playerdata(self, user, **kwargs): def update_player_data(self, user, **kwargs):
"""i small workaround to access score, placement of a specific user """to access scores and placement of a specific user from templates"""
prominent from a in the user templates"""
for player in ('player1', 'player2', 'player3', 'player4'): for player in ('player1', 'player2', 'player3', 'player4'):
if getattr(self, player) == user: if getattr(self, player) == user:
setattr(self, '%s_input_score' % player, self.input_score) setattr(self, '%s_input_score' % player, self.input_score)
@@ -348,6 +348,7 @@ class KyuDanRanking(models.Model):
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(blank=True, null=True) legacy_dan_points = models.PositiveIntegerField(blank=True, null=True)
legacy_max_dan_points = models.PositiveIntegerField(default=0)
legacy_kyu = models.PositiveSmallIntegerField(blank=True, null=True) legacy_kyu = models.PositiveSmallIntegerField(blank=True, null=True)
legacy_kyu_points = models.PositiveIntegerField(blank=True, null=True) legacy_kyu_points = models.PositiveIntegerField(blank=True, null=True)
legacy_hanchan_count = models.PositiveIntegerField(blank=True, null=True) legacy_hanchan_count = models.PositiveIntegerField(blank=True, null=True)
@@ -358,7 +359,10 @@ class KyuDanRanking(models.Model):
objects = managers.KyuDanRankingManager() objects = managers.KyuDanRankingManager()
class Meta(object): class Meta(object):
ordering = ('-dan_points', 'dan', '-kyu_points') ordering = (models.F("dan").desc(nulls_last=True),
'-dan_points', '-kyu_points',
'-won_hanchans', '-good_hanchans',
'-last_hanchan_date')
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')
@@ -417,7 +421,7 @@ class KyuDanRanking(models.Model):
def append_tournament_bonuspoints(self, hanchan): def append_tournament_bonuspoints(self, hanchan):
""" """
Prüft ob es die letzte Hanchan in einem Turnier war. Wenn ja werden Prüft, ob es die letzte Hanchan in einem Turnier war. Wenn ja, werden
bei Bedarf Bonuspunkte vergeben, falls der Spieler das Turnier bei Bedarf Bonuspunkte vergeben, falls der Spieler das Turnier
gewonnen hat. gewonnen hat.
:param hanchan: Ein Player Objekt :param hanchan: Ein Player Objekt
@@ -428,7 +432,8 @@ class KyuDanRanking(models.Model):
).order_by('-start') ).order_by('-start')
last_hanchan_this_event = hanchans_this_event[0] last_hanchan_this_event = hanchans_this_event[0]
# Das braucht nur am Ende eines Turnieres gemacht werden. # Das braucht nur am Ende eines Turnieres gemacht werden.
if hanchan != last_hanchan_this_event: return False if hanchan != last_hanchan_this_event:
return False
event_ranking = EventRanking.objects.get( event_ranking = EventRanking.objects.get(
user=self.user, user=self.user,
event=hanchan.event event=hanchan.event
@@ -439,8 +444,8 @@ class KyuDanRanking(models.Model):
settings.TOURNAMENT_WIN_BONUSPOINTS) settings.TOURNAMENT_WIN_BONUSPOINTS)
if event_ranking.avg_placement == 1: if event_ranking.avg_placement == 1:
bonus_points += settings.TOURNAMENT_FLAWLESS_VICTORY_BONUSPOINTS bonus_points += settings.TOURNAMENT_FLAWLESS_VICTORY_BONUSPOINTS
hanchan.player_comment += u'+{0:d} Pkt: alle Spiele des Turnieres gewonnen. '.format( hanchan.player_comment += '+{0:d} Pkt: alle Spiele des Turnieres \
settings.TOURNAMENT_FLAWLESS_VICTORY_BONUSPOINTS) gewonnen. '.format(settings.TOURNAMENT_FLAWLESS_VICTORY_BONUSPOINTS)
if bonus_points and self.dan: if bonus_points and self.dan:
hanchan.dan_points += bonus_points hanchan.dan_points += bonus_points
@@ -472,7 +477,7 @@ 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.max_dan_points = self.dan_points self.max_dan_points = self.legacy_max_dan_points or 0
self.kyu = self.legacy_kyu 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
@@ -489,19 +494,27 @@ class KyuDanRanking(models.Model):
since = timezone.make_aware( since = timezone.make_aware(
datetime.combine(self.legacy_date, time(0, 0, 0)) datetime.combine(self.legacy_date, time(0, 0, 0))
) )
LOGGER.info(
"recalculating Kyu/Dan points for %(user)s since %(since)s...",
{'user': self.user, 'since': str(since)}
)
if since: if since:
valid_hanchans = valid_hanchans.filter(start__gt=since) valid_hanchans = valid_hanchans.filter(start__gt=since)
else:
since = valid_hanchans.aggregate(since=models.Min("start"))["since"]
if until: if until:
valid_hanchans = valid_hanchans.filter(start__lte=until) valid_hanchans = valid_hanchans.filter(start__lte=until)
else:
until = valid_hanchans.aggregate(until=models.Max("start"))["until"]
if valid_hanchans.count() > 0:
LOGGER.info(f"recalculating Kyu/Dan points for {self.user} " +
f"({since:%Y-%m-%d} - {until:%Y-%m-%d})...")
else:
LOGGER.info(f"No new valid Hanchans for {self.user}...")
for hanchan in valid_hanchans: for hanchan in valid_hanchans:
self.hanchan_count += 1 self.hanchan_count += 1
LOGGER.info(
f"{self.user} Hanchan no. {self.hanchan_count} " +
f"from {hanchan.start}")
hanchan.get_playerdata(self.user) hanchan.get_playerdata(self.user)
if since and hanchan.start < since: if since and hanchan.start < since:
LOGGER.debug(hanchan, "<", since, "no recalc") LOGGER.info(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()
@@ -513,7 +526,7 @@ class KyuDanRanking(models.Model):
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)
hanchan.update_playerdata(self.user) hanchan.update_player_data(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
@@ -524,7 +537,7 @@ class KyuDanRanking(models.Model):
""" """
Berechne die Kyu bzw. Dan Punkte für eine Hanchan neu. Berechne die Kyu bzw. Dan Punkte für eine Hanchan neu.
:type hanchan: Hanchan :type hanchan: Hanchan
:param hanchan: Das Player Objekt das neuberechnet werden soll. :param hanchan: das Player-Objekt, welches neu berechnet werden soll.
""" """
hanchan.kyu_points = None hanchan.kyu_points = None
hanchan.dan_points = None hanchan.dan_points = None
@@ -567,14 +580,16 @@ class KyuDanRanking(models.Model):
# Only substract so much points that player has 0 Points: # Only substract so much points that player has 0 Points:
if self.dan_points + hanchan.dan_points < 0: if self.dan_points + hanchan.dan_points < 0:
hanchan.player_comment = 'Spieler unterschreitet 0 Punkte.' \ hanchan.player_comment = 'Spieler unterschreitet 0 Punkte.' \
'(Original {} Punkte)'.format(hanchan.dan_points) '(Original {} Punkte)'.format(
hanchan.dan_points)
hanchan.dan_points -= (self.dan_points + hanchan.dan_points) hanchan.dan_points -= (self.dan_points + hanchan.dan_points)
self.dan_points += hanchan.dan_points self.dan_points += hanchan.dan_points
else: else:
# Only substract so much points that player has 0 Points: # Only substract so much points that player has 0 Points:
if self.kyu_points + hanchan.kyu_points < 0: if self.kyu_points + hanchan.kyu_points < 0:
hanchan.player_comment = 'Spieler unterschreitet 0 Punkte.' \ hanchan.player_comment = 'Spieler unterschreitet 0 Punkte.' \
'(Original {} Punkte)'.format(hanchan.kyu_points) '(Original {} Punkte)'.format(
hanchan.kyu_points)
hanchan.kyu_points -= (self.kyu_points + hanchan.kyu_points) hanchan.kyu_points -= (self.kyu_points + hanchan.kyu_points)
self.kyu_points += hanchan.kyu_points self.kyu_points += hanchan.kyu_points
@@ -603,7 +618,8 @@ class KyuDanRanking(models.Model):
class SeasonRanking(models.Model): class SeasonRanking(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT) user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.PROTECT)
season = models.PositiveSmallIntegerField(_('Season')) season = models.PositiveSmallIntegerField(_('Season'))
placement = models.PositiveIntegerField(blank=True, null=True) placement = models.PositiveIntegerField(blank=True, null=True)
avg_placement = models.FloatField(blank=True, null=True) avg_placement = models.FloatField(blank=True, null=True)

View File

@@ -11,38 +11,42 @@ from .models import SeasonRanking
class EventRankingSitemap(GenericSitemap): class EventRankingSitemap(GenericSitemap):
@staticmethod @staticmethod
def items(): def items(**kwargs):
"""add all upcoming and archived events to the sitemap.""" """add all upcoming and archived events to the sitemap.
@param **kwargs:
"""
return Event.objects.all().exclude(eventranking=None) return Event.objects.all().exclude(eventranking=None)
@staticmethod @staticmethod
def location(event): def location(event, **kwargs):
return reverse('event-ranking', kwargs={'event': event.id}) return reverse('event-ranking', kwargs={'event': event.id})
class EventHanchanSitemap(GenericSitemap): class EventHanchanSitemap(GenericSitemap):
@staticmethod @staticmethod
def items(): def items(**kwargs):
"""add all upcoming and archived events to the sitemap.""" """add all upcoming and archived events to the sitemap.
@param **kwargs:
"""
return Event.objects.all().exclude(eventranking=None) return Event.objects.all().exclude(eventranking=None)
@staticmethod @staticmethod
def location(event): def location(event, **kwargs):
return reverse('event-hanchan-list', kwargs={'event': event.id}) return reverse('event-hanchan-list', kwargs={'event': event.id})
class MajongSeasonSitemap(Sitemap): class MahjongSeasonSitemap(Sitemap):
priority = 0.5 priority = 0.5
@staticmethod @staticmethod
def items(): def items(**kwargs):
seasons = SeasonRanking.objects.all().distinct('season').order_by( seasons = SeasonRanking.objects.all().distinct('season').order_by(
'season') 'season')
seasons = seasons.values_list('season', flat=True) seasons = seasons.values_list('season', flat=True)
return seasons return seasons
@staticmethod @staticmethod
def location(season): def location(season, **kwargs):
return reverse('mahjong-ladder', kwargs={'season': season}) return reverse('mahjong-ladder', kwargs={'season': season})
@staticmethod @staticmethod

View File

@@ -18,7 +18,7 @@
<fieldset class="hanchan"> <fieldset class="hanchan">
{% for hidden in form.hidden_fields %} {{ hidden }} {% endfor %} {% for hidden in form.hidden_fields %} {{ hidden }} {% endfor %}
<p> <p>
<label for="id_{{ form.start.html_name }}_0" class="field_name {{ form.start.css_classes }}">{{ form.start.label }}:</label> <label for="{{ form.start.if_for_label }}" class="field_name {{ form.start.css_classes }}">{{ form.start.label }}:</label>
{{ form.start }} {{ form.start }}
{{ form.start.errors }} {{ form.start.errors }}
</p> </p>

View File

@@ -7,7 +7,7 @@
<h3 class="grid_12" id="{{ hanchan.pk }}">{{hanchan.start|time:'H:i'}}: {{ hanchan.player_names }}</h3> <h3 class="grid_12" id="{{ hanchan.pk }}">{{hanchan.start|time:'H:i'}}: {{ hanchan.player_names }}</h3>
{% for player in hanchan.player_list %} {% for player in hanchan.player_list %}
<a class="grid_1" href="{% url 'player-ladder-score' player.user %}"><img <a class="grid_1" href="{% url 'player-ladder-score' player.user %}"><img
src="{% thumbnail user.avatar|default:'unknown_profile.jpg' 'avatar' %}" src="{% thumbnail player.user.avatar|default:'unknown_profile.jpg' 'avatar' %}"
width="70" height="70" width="70" height="70"
class="avatar" alt="{{ player.user }}" title="{{ player.user }}"/></a> class="avatar" alt="{{ player.user }}" title="{{ player.user }}"/></a>
<div class="grid_2"> <div class="grid_2">
@@ -33,7 +33,7 @@
<legend>{% trans "Delete Hanchan" %}</legend> <legend>{% trans "Delete Hanchan" %}</legend>
{% include 'form.html' %} {% include 'form.html' %}
<label class="field_name fa fa-exclamation-triangle fa-5x fa-pull-left" ></label> <label class="field_name fa fa-exclamation-triangle fa-5x fa-pull-left" ></label>
<p>Bist du sicher dass du diese Hanchan und alle Ergebnisse und Wertungen welche sich hierauf beziehen löschen möchtest?</p> <p>Bist du sicher, dass du diese Hanchan und alle Ergebnisse und Wertungen, welche sich hierauf beziehen löschen möchtest?</p>
<br class="clear"> <br class="clear">
<p class="buttonbar"> <p class="buttonbar">
<button type="button" onclick="window.history.back()"><span class="fa fa-close"></span> {% trans 'Cancel' %}</button> <button type="button" onclick="window.history.back()"><span class="fa fa-close"></span> {% trans 'Cancel' %}</button>

View File

@@ -5,7 +5,7 @@
{% for hanchan in latest_hanchan_list %} {% for hanchan in latest_hanchan_list %}
<li><span class="fa-li fa fa-table"></span> <li><span class="fa-li fa fa-table"></span>
<a href="{% url 'event-hanchan-list' hanchan.event_id %}"> <a href="{% url 'event-hanchan-list' hanchan.event_id %}">
<time datetime="{{ hanchan.start|date:'c' }}">{{ hanchan.start|date:'D' }} <time datetime="{{ hanchan.start|date:'Y-m-d\TH:i:sO' }}">{{ hanchan.start|date:'D' }}
{{ hanchan.start|date:'SHORT_DATE_FORMAT' }} {{hanchan.start|time:'H:i'}} {{ hanchan.start|date:'SHORT_DATE_FORMAT' }} {{hanchan.start|time:'H:i'}}
</time></a>:<br /> </time></a>:<br />
<small>{{hanchan.player_names}}</small> <small>{{hanchan.player_names}}</small>
@@ -18,7 +18,7 @@
<li> <li>
<span class="fa-li fa fa-calendar-o"></span> <span class="fa-li fa fa-calendar-o"></span>
<a href="{% url 'event-hanchan-list' event.pk %}"> <a href="{% url 'event-hanchan-list' event.pk %}">
<time datetime="{{event.start|date:'c'}}">{{ event.start|date:'D' }} {{ event.start|date:'SHORT_DATE_FORMAT' }}</time>: <time datetime="{{event.start|date:'Y-m-d\TH:i:sO'}}">{{ event.start|date:'D' }} {{ event.start|date:'SHORT_DATE_FORMAT' }}</time>:
{{event.name}}</a></li> {{event.name}}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
@@ -31,7 +31,7 @@
{% for season_link in season_list%} {% for season_link in season_list%}
<option <option
value="{% url 'mahjong-ladder' season_link %}" value="{% url 'mahjong-ladder' season_link %}"
{% ifequal season season_link %} selected="selected"{% endifequal %}>{{ season_link }} selected="selected">{{ season_link }}
</option> </option>
{% endfor %} {% endfor %}
</select> </select>

View File

@@ -64,7 +64,7 @@
<select id="season" name="season" size="1"> <select id="season" name="season" size="1">
{% for season_link in season_list%} {% for season_link in season_list%}
<option <option
{% ifequal season season_link %} selected="selected"{% endifequal %} selected="selected"
value="{{ season_link }}">{{ season_link }}</option> value="{{ season_link }}">{{ season_link }}</option>
{% endfor %} {% endfor %}
</select> </select>
@@ -75,5 +75,5 @@
{% endblock %} {% endblock %}
{% block buttonbar %} {% block buttonbar %}
<a href="?download=xlsx" class="button"><span class="fa fa-table"></span> Download</a> <a href="?download=xlsx{% if season %}&season={{ season }}{% endif %}" class="button"><span class="fa fa-table"></span> Download</a>
{% endblock %} {% endblock %}

View File

@@ -49,8 +49,8 @@
</tr> </tr>
{% empty %} {% empty %}
<tr> <tr>
<td colspan="9">Leider hat es noch niemand in das Ranking geschafft. <td colspan="8">Leider hat es noch niemand in das Ranking geschafft.
Ein Spieler wird erst ins Ranking genommen wenn er 5 Hanchans absolviert hat. Spieler werden erst ins Ranking genommen, wenn sie 5 Hanchans absolviert haben.
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@@ -97,7 +97,6 @@ class KyuDanTest(TestCase):
self.assertEqual( self.assertEqual(
points[dan_kyu], points[dan_kyu],
getattr(ranking, dan_kyu), getattr(ranking, dan_kyu),
"{dan_kyu} for {player} won't compute! ranking: {ranking}, sum: {sum}".format( f"{dan_kyu} for {ranking.user} won't compute! " +
dan_kyu=dan_kyu, player=ranking.user, f"ranking: {ranking.dan_kyu}, sum: {points[dan_kyu]}"
ranking=getattr(ranking, dan_kyu), sum=points[dan_kyu])
) )

View File

@@ -1,39 +1,38 @@
""" URLS to display the Riichi Mahjong Rankings and the Ladder system.""" """ URLS to display the Riichi Mahjong Rankings and the Ladder system."""
from django.conf.urls import url from django.urls import path
from django.views.generic import RedirectView from django.views.generic import RedirectView
from . import views from . import views
urlpatterns = [ # Ignore PyLintBear (C0103)
url(r'^$',
RedirectView.as_view(url='/ranking/mahjong-ladder/', permanent=True)),
url(r'^event/(?P<event>[\d]+)/add-hanchan/$',
views.HanchanForm.as_view(), name="add-hanchan-form"),
url(r'^event/(?P<event>[\d]+)/edit/$',
views.EventHanchanForm.as_view(), name="event-hanchan-form"),
url(r'^event/(?P<event>[\d]+)/mahjong/$',
views.EventHanchanList.as_view(), name="event-hanchan-list"),
url(r'^event/(?P<event>[\d]+)/mahjong-ranking/$',
views.EventRankingList.as_view(), name="event-ranking"),
url(r'^hanchan/(?P<hanchan>[\d]+)/edit/$',
views.HanchanForm.as_view(), name="edit-hanchan"),
url(r'^hanchan/(?P<hanchan>[\d]+)/delete/$',
views.DeleteHanchan.as_view(), name="delete-hanchan"),
url(r'^mahjong-ladder/$', views.SeasonRankingList.as_view(),
name="mahjong-ladder"),
url(r'^mahjong-ladder/(?P<season>[\d]+)/$',
views.SeasonRankingList.as_view(), name="mahjong-ladder"),
url(r'^player/(?P<username>[\-\.\d\w]+)/dan/$',
views.PlayerDanScore.as_view(), name="player-dan-score"),
url(r'^player/(?P<username>[\-\.\d\w]+)/invalid/$',
views.PlayerInvalidScore.as_view(), name="player-invalid-score"),
url(r'^player/(?P<username>[\-\.\d\w]+)/kyu/$',
views.PlayerKyuScore.as_view(), name="player-kyu-score"),
url(r'^player/(?P<username>[\-\.\d\w]+)/ladder/$',
views.PlayerLadderScore.as_view(), name="player-ladder-score"),
url(r'^mahjong/$', views.KyuDanRankingList.as_view(),
name="kyudanranking-list"),
url(r'^mahjong/(?P<order_by>[\+\-][a-z_]+)/$',
views.KyuDanRankingList.as_view(), name="kyudanranking-list"),
]
urlpatterns = [
path("", RedirectView.as_view(url='/ranking/mahjong-ladder/', permanent=True)),
path('event/<int:event>/add-hanchan/', views.HanchanForm.as_view(),
name="add-hanchan-form"),
path('event/<int:event>/edit/', views.EventHanchanForm.as_view(),
name="event-hanchan-form"),
path('event/<int:event>/mahjong/', views.EventHanchanList.as_view(),
name="event-hanchan-list"),
path('event/<int:event>/mahjong-ranking/', views.EventRankingList.as_view(),
name="event-ranking"),
path('hanchan/<int:hanchan>/edit/', views.HanchanForm.as_view(),
name="edit-hanchan"),
path('hanchan/<int:hanchan>/delete/', views.DeleteHanchan.as_view(),
name="delete-hanchan"),
path('mahjong-ladder/', views.SeasonRankingList.as_view(),
name="mahjong-ladder"),
path('mahjong-ladder/<int:season>/', views.SeasonRankingList.as_view(),
name="mahjong-ladder"),
path('player/<slug:username>/dan/', views.PlayerDanScore.as_view(),
name="player-dan-score"),
path('player/<slug:username>/invalid/', views.PlayerInvalidScore.as_view(),
name="player-invalid-score"),
path('player/<slug:username>/kyu/', views.PlayerKyuScore.as_view(),
name="player-kyu-score"),
path('player/<slug:username>/ladder/', views.PlayerLadderScore.as_view(),
name="player-ladder-score"),
path('mahjong/', views.KyuDanRankingList.as_view(),
name="kyudanranking-list"),
path('mahjong/<str:order_by>/', views.KyuDanRankingList.as_view(),
name="kyudanranking-list"),
]

View File

@@ -8,7 +8,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin, \
PermissionRequiredMixin PermissionRequiredMixin
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse from django.urls import reverse
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
from django.views import generic from django.views import generic
from events.mixins import EventDetailMixin from events.mixins import EventDetailMixin
@@ -45,7 +45,7 @@ def get_kyu_dan_ranking(user=None):
class DeleteHanchan(EventDetailMixin, PermissionRequiredMixin, class DeleteHanchan(EventDetailMixin, PermissionRequiredMixin,
generic.DeleteView): generic.DeleteView):
"""Deletes a Hanchan if confimration has been answerd with 'yes'.""" """Deletes a Hanchan if confirmation has been answered with 'yes'."""
form_class = forms.HanchanForm form_class = forms.HanchanForm
model = models.Hanchan model = models.Hanchan
permission_required = 'mahjong_ranking.delete_hanchan' permission_required = 'mahjong_ranking.delete_hanchan'
@@ -53,7 +53,7 @@ class DeleteHanchan(EventDetailMixin, PermissionRequiredMixin,
def get_success_url(self): def get_success_url(self):
""" """
Return to the HachanList of the event form the deleted hanchan. Return to the HanchanList of the event form the deleted hanchan.
:return: URL of the EventHanchanList for the event :return: URL of the EventHanchanList for the event
""" """
return reverse('event-hanchan-list', return reverse('event-hanchan-list',
@@ -71,7 +71,7 @@ class HanchanForm(SuccessMessageMixin, EventDetailMixin,
def get_form_class(self): def get_form_class(self):
""" """
Users with hanchan edit persmission can also un-/confirm hanchans. Users with hanchan edit permission can also un-/confirm a Hanchan.
:return: forms.HanchanForm, or forms.HanchanAdminForm :return: forms.HanchanForm, or forms.HanchanAdminForm
""" """
return forms.HanchanAdminForm if self.request.user.has_perm( return forms.HanchanAdminForm if self.request.user.has_perm(
@@ -123,27 +123,27 @@ class HanchanForm(SuccessMessageMixin, EventDetailMixin,
class EventHanchanForm(EventDetailMixin, PermissionRequiredMixin, class EventHanchanForm(EventDetailMixin, PermissionRequiredMixin,
generic.TemplateView): generic.TemplateView):
"""Display a Formset to add and Edit Hanchans of the specific Event.""" """Display a Formset to add and Edit Hanchans of the specific Event."""
formset: forms.HanchanFormset
permission_required = 'mahjong_ranking.add_hanchan' permission_required = 'mahjong_ranking.add_hanchan'
template_name = 'mahjong_ranking/eventhanchan_form.html' template_name = 'mahjong_ranking/eventhanchan_form.html'
model = models.Hanchan model = models.Hanchan
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
self.event = models.Event.objects.get(pk=self.kwargs['event']) self.event = models.Event.objects.get(pk=self.kwargs['event'])
self.formset = forms.HanchanFormset(
instance=self.event,
initial=[{'start': self.event.start}]
)
context = super(EventHanchanForm, self).get_context_data() context = super(EventHanchanForm, self).get_context_data()
context['formset'] = self.formset context['formset'] = self.formset
return context return context
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.get_queryset() self.get_queryset()
self.formset = forms.HanchanFormset(
instance=self.event,
initial=[{'start': self.event.start}]
)
context = self.get_context_data(**kwargs) context = self.get_context_data(**kwargs)
return self.render_to_response(context) return self.render_to_response(context)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
print("ICH WURDE GEPOSTET!!!!")
self.get_queryset() self.get_queryset()
self.formset = forms.HanchanFormset( self.formset = forms.HanchanFormset(
self.request.POST, self.request.POST,
@@ -160,7 +160,6 @@ class EventHanchanForm(EventDetailMixin, PermissionRequiredMixin,
return self.render_to_response(context) return self.render_to_response(context)
class EventHanchanList(EventDetailMixin, generic.ListView): class EventHanchanList(EventDetailMixin, generic.ListView):
"List all hanchans played on a given event." "List all hanchans played on a given event."
model = models.Hanchan model = models.Hanchan
@@ -173,9 +172,9 @@ class EventRankingList(EventDetailMixin, generic.ListView):
class KyuDanRankingList(MahjongMixin, generic.ListView): class KyuDanRankingList(MahjongMixin, generic.ListView):
"""List all Players with an Kyu or Dan score. """ """List all Players with a Kyu or Dan score. """
order_by = None order_by = None
paginate_by = 25 paginate_by = 100
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
"""Set the order_by settings, revert to default_order if necessary.""" """Set the order_by settings, revert to default_order if necessary."""
@@ -193,7 +192,7 @@ class KyuDanRankingList(MahjongMixin, generic.ListView):
class SeasonRankingList(MahjongMixin, generic.ListView): class SeasonRankingList(MahjongMixin, generic.ListView):
model = models.SeasonRanking model = models.SeasonRanking
paginate_by = 25 paginate_by = 100
season = None season = None
def get_queryset(self): def get_queryset(self):
@@ -206,18 +205,23 @@ class SeasonRankingList(MahjongMixin, generic.ListView):
class PlayerScore(LoginRequiredMixin, generic.ListView): class PlayerScore(LoginRequiredMixin, generic.ListView):
paginate_by = 25 paginate_by: int = 100
user = auth.get_user_model() user = auth.get_user_model()
kyu_dan_ranking = None
season = None
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
user_model = auth.get_user_model() user_model = auth.get_user_model()
try: try:
self.user = user_model.objects.get( self.user = user_model.objects.get(
username=self.kwargs.get('username')) username=self.kwargs.get('username'))
self.kyu_dan_ranking = get_kyu_dan_ranking(user=self.user)
self.season = int(self.request.GET.get('season', date.today().year))
except user_model.DoesNotExist: except user_model.DoesNotExist:
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')))
if request.GET.get('download') == 'xlsx': if request.GET.get('download') == 'xlsx':
return self.get_xlsx(request, *args, **kwargs) return self.get_xlsx(request, *args, **kwargs)
return super(PlayerScore, self).get(request, *args, **kwargs) return super(PlayerScore, self).get(request, *args, **kwargs)
@@ -229,7 +233,7 @@ class PlayerScore(LoginRequiredMixin, generic.ListView):
context['kyu_dan_ranking'] = models.KyuDanRanking.objects.get( context['kyu_dan_ranking'] = models.KyuDanRanking.objects.get(
user=self.user) user=self.user)
except models.KyuDanRanking.DoesNotExist: except models.KyuDanRanking.DoesNotExist:
context['ranking'] = None context['kyu_dan_ranking'] = None
try: try:
context['ladder_ranking'] = models.SeasonRanking.objects.get( context['ladder_ranking'] = models.SeasonRanking.objects.get(
user=self.user, user=self.user,
@@ -239,27 +243,31 @@ class PlayerScore(LoginRequiredMixin, generic.ListView):
return context return context
def get_xlsx(self, request, *args, **kwargs): def get_xlsx(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
response = django.http.HttpResponse( response = django.http.HttpResponse(
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
response['Content-Disposition'] = 'attachment; ' \ response['Content-Disposition'] = f'attachment; filename="{self.xlsx_filename}"'
'filename="{xlsx_filename}"'.format( xlsx_workbook = xlsx.Workbook()
xlsx_filename=self.xlsx_filename) xlsx_workbook.generate_sheet(
xlxs_workbook = xlsx.Workbook()
xlxs_workbook.generate_sheet(
title=self.xlsx_filename.split('.')[0], title=self.xlsx_filename.split('.')[0],
columns_settings=self.xlsx_columns, columns_settings=self.xlsx_columns,
object_list=self.object_list object_list=self.get_queryset()
) )
xlxs_workbook.save(response) xlsx_workbook.save(response)
return response return response
@property
def xlsx_columns(self):
return {}
@property
def xlsx_filename(self):
return ""
class PlayerDanScore(PlayerScore): class PlayerDanScore(PlayerScore):
template_name = 'mahjong_ranking/player_dan_score.html' template_name = 'mahjong_ranking/player_dan_score.html'
def get_queryset(self): def get_queryset(self):
self.kyu_dan_ranking = get_kyu_dan_ranking(user=self.user)
return models.Hanchan.objects.dan_hanchans( return models.Hanchan.objects.dan_hanchans(
user=self.user, user=self.user,
since=self.kyu_dan_ranking.legacy_date) since=self.kyu_dan_ranking.legacy_date)
@@ -299,17 +307,20 @@ class PlayerDanScore(PlayerScore):
@property @property
def xlsx_filename(self): def xlsx_filename(self):
return "{username}_dan_score.xlsx".format(username=self.user.username) return f"{self.user.username}_dan_score.xlsx"
class PlayerInvalidScore(PlayerScore): 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)
@property
def xlsx_filename(self):
return f"{self.user.username}_invalid_score.xlsx"
class PlayerKyuScore(PlayerScore): class PlayerKyuScore(PlayerScore):
template_name = 'mahjong_ranking/player_kyu_score.html' template_name = 'mahjong_ranking/player_kyu_score.html'
@@ -355,7 +366,7 @@ class PlayerKyuScore(PlayerScore):
@property @property
def xlsx_filename(self): def xlsx_filename(self):
return "{username}_kyu_score.xlsx".format(username=self.user.username) return f"{self.user.username}_kyu_score.xlsx"
class PlayerLadderScore(PlayerScore): class PlayerLadderScore(PlayerScore):
@@ -371,7 +382,6 @@ class PlayerLadderScore(PlayerScore):
return context return context
def get_queryset(self, **kwargs): def get_queryset(self, **kwargs):
self.season = int(self.request.GET.get('season', date.today().year))
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
@@ -409,7 +419,4 @@ class PlayerLadderScore(PlayerScore):
@property @property
def xlsx_filename(self): def xlsx_filename(self):
return "{username}_ladder_score_{season}.xlsx".format( return f"{self.user.username}_ladder_{self.season}_score.xlsx"
username=self.user.username,
season=self.season
)

View File

@@ -1,6 +1,6 @@
""" Admin Interface to manipulate the maistar ranking """ """ Admin Interface to manipulate the maistar ranking """
from django.contrib import admin from django.contrib import admin
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
from . import forms, models from . import forms, models

View File

@@ -1,7 +1,7 @@
"""Django Forms to add and edit Mai-Star games.""" """Django Forms to add and edit Mai-Star games."""
from django import forms from django import forms
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
from . import models from . import models

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: kasu.mahjong_ranking\n" "Project-Id-Version: kasu.mahjong_ranking\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-08 00:17+0200\n" "POT-Creation-Date: 2023-07-26 18:31+0200\n"
"PO-Revision-Date: 2016-09-28 00:24+0200\n" "PO-Revision-Date: 2016-09-28 00:24+0200\n"
"Last-Translator: Christian Berg <xeniac.at@gmail.com>\n" "Last-Translator: Christian Berg <xeniac.at@gmail.com>\n"
"Language-Team: Kasu <verein@kasu.at>\n" "Language-Team: Kasu <verein@kasu.at>\n"
@@ -19,208 +19,207 @@ msgstr ""
"X-Generator: Poedit 1.8.9\n" "X-Generator: Poedit 1.8.9\n"
"X-Translated-Using: django-rosetta 0.7.2\n" "X-Translated-Using: django-rosetta 0.7.2\n"
#: .\maistar_ranking\admin.py:24 #: admin.py:24
msgid "Recalculate" msgid "Recalculate"
msgstr "Neuberechnen" msgstr "Neuberechnen"
#: .\maistar_ranking\forms.py:35 #: forms.py:35
#, python-format #, python-format
msgid "%s may only participate once." msgid "%s may only participate once."
msgstr "%s darf nur einmal teilnehmen." msgstr "%s darf nur einmal teilnehmen."
#: .\maistar_ranking\models.py:21 #: models.py:21
msgid "Comment" msgid "Comment"
msgstr "Kommentar" msgstr "Kommentar"
#: .\maistar_ranking\models.py:24 #: models.py:24
msgid "Player 1" msgid "Player 1"
msgstr "Spieler 1" msgstr "Spieler 1"
#: .\maistar_ranking\models.py:26 .\maistar_ranking\models.py:33 #: models.py:26 models.py:33 models.py:40 models.py:47 models.py:54
#: .\maistar_ranking\models.py:40 .\maistar_ranking\models.py:47 #: models.py:61 templates/maistar_ranking/ranking_list.html:19
#: .\maistar_ranking\models.py:54 .\maistar_ranking\models.py:61
#: .\maistar_ranking\templates\maistar_ranking\ranking_list.html:19
msgid "Score" msgid "Score"
msgstr "Punkte" msgstr "Punkte"
#: .\maistar_ranking\models.py:31 #: models.py:31
msgid "Player 2" msgid "Player 2"
msgstr "Spieler 2" msgstr "Spieler 2"
#: .\maistar_ranking\models.py:38 #: models.py:38
msgid "Player 3" msgid "Player 3"
msgstr "Spieler 3" msgstr "Spieler 3"
#: .\maistar_ranking\models.py:45 #: models.py:45
msgid "Player 4" msgid "Player 4"
msgstr "Spieler 4" msgstr "Spieler 4"
#: .\maistar_ranking\models.py:52 #: models.py:52
msgid "Player 5" msgid "Player 5"
msgstr "Spieler 5" msgstr "Spieler 5"
#: .\maistar_ranking\models.py:59 #: models.py:59
msgid "Player 6" msgid "Player 6"
msgstr "Spieler 6" msgstr "Spieler 6"
#: .\maistar_ranking\models.py:65 #: models.py:65
msgid "Has been confirmed" msgid "Has been confirmed"
msgstr "Wurde bestätigt" msgstr "Wurde bestätigt"
#: .\maistar_ranking\models.py:67 #: models.py:67
msgid "the game only counts whe it has been confirmed" msgid "the game only counts whe it has been confirmed"
msgstr "das Spiel zählt nur wenn es bestätigt wurde" msgstr "das Spiel zählt nur wenn es bestätigt wurde"
#: .\maistar_ranking\models.py:70 .\maistar_ranking\models.py:153 #: models.py:70 models.py:154 templates/maistar_ranking/player_game_list.html:6
#: .\maistar_ranking\templates\maistar_ranking\player_game_list.html:6 #: templates/maistar_ranking/ranking_list.html:4
#: .\maistar_ranking\templates\maistar_ranking\ranking_list.html:4 #: templates/maistar_ranking/ranking_list.html:72
#: .\maistar_ranking\templates\maistar_ranking\ranking_list.html:72
msgid "Season" msgid "Season"
msgstr "Saison" msgstr "Saison"
#: .\maistar_ranking\models.py:80 #: models.py:80
msgid "Mai-Star Game with {0} from {1:%Y-%m-%d}" msgid "Mai-Star Game with {0} from {1:%Y-%m-%d}"
msgstr "Mai-Star Spiel mit {0} vom {1:%Y-%m-%d}" msgstr "Mai-Star Spiel mit {0} vom {1:%Y-%m-%d}"
#: .\maistar_ranking\templates\maistar_ranking\game_form.html:5 #: templates/maistar_ranking/game_confirm_delete.html:4
#: .\maistar_ranking\templates\maistar_ranking\game_form.html:16 #: templates/maistar_ranking/game_confirm_delete.html:9
#: .\maistar_ranking\templates\maistar_ranking\game_list.html:27
#: .\maistar_ranking\templates\maistar_ranking\player_game_list.html:44
msgid "Edit Game"
msgstr "Spiel bearbeiten"
#: .\maistar_ranking\templates\maistar_ranking\game_form.html:5
#: .\maistar_ranking\templates\maistar_ranking\game_form.html:16
#: .\maistar_ranking\templates\maistar_ranking\game_list.html:41
msgid "Add Game"
msgstr "Spiel hinzufügen"
#: .\maistar_ranking\templates\maistar_ranking\game_form.html:76
msgid "Back"
msgstr "Zurück"
#: .\maistar_ranking\templates\maistar_ranking\game_form.html:77
msgid "Save"
msgstr "Speichern"
#: .\maistar_ranking\templates\maistar_ranking\game_list.html:4
#: .\maistar_ranking\templates\maistar_ranking\player_game_list.html:6
msgid "Mai-Star Games"
msgstr "Mai-Star Spiele"
#: .\maistar_ranking\templates\maistar_ranking\game_list.html:7
msgid "Played Mai-Star Games"
msgstr "Gespielte Mai-Star Spiele"
#: .\maistar_ranking\templates\maistar_ranking\game_list.html:11
msgid "Game"
msgstr "Spiel"
#: .\maistar_ranking\templates\maistar_ranking\game_list.html:14
msgid "Place"
msgstr "Platz"
#: .\maistar_ranking\templates\maistar_ranking\game_list.html:19
#: .\maistar_ranking\templates\maistar_ranking\player_game_list.html:36
msgid "Points"
msgstr "Punkte"
#: .\maistar_ranking\templates\maistar_ranking\game_list.html:24
#: .\maistar_ranking\templates\maistar_ranking\player_game_list.html:41
msgid "Delete Game"
msgstr "Spiel löschen"
#: .\maistar_ranking\templates\maistar_ranking\game_list.html:33
msgid "No Mai-Star games have been added to this event yet."
msgstr "Für diese Veranstaltung wurden noch keine Mai-Star Spiele erfasst."
#: .\maistar_ranking\templates\maistar_ranking\game_list.html:40
msgid "Edit Event"
msgstr "Veranstaltung bearbeiten"
#: .\maistar_ranking\templates\maistar_ranking\hanchan_confirm_delete.html:4
#: .\maistar_ranking\templates\maistar_ranking\hanchan_confirm_delete.html:10
msgid "Delete game" msgid "Delete game"
msgstr "Spiel löschen" msgstr "Spiel löschen"
#: .\maistar_ranking\templates\maistar_ranking\hanchan_confirm_delete.html:13 #: templates/maistar_ranking/game_confirm_delete.html:12
#: templates/maistar_ranking/game_list.html:14
msgid "Place"
msgstr "Platz"
#: templates/maistar_ranking/game_confirm_delete.html:18
#: templates/maistar_ranking/game_list.html:19
#: templates/maistar_ranking/player_game_list.html:36
msgid "Points"
msgstr "Punkte"
#: templates/maistar_ranking/game_confirm_delete.html:23
msgid "Cancel" msgid "Cancel"
msgstr "Abbrechen" msgstr "Abbrechen"
#: .\maistar_ranking\templates\maistar_ranking\hanchan_confirm_delete.html:14 #: templates/maistar_ranking/game_confirm_delete.html:25
msgid "Delete" msgid "Delete"
msgstr "Löschen" msgstr "Löschen"
#: .\maistar_ranking\templates\maistar_ranking\page.html:5 #: templates/maistar_ranking/game_form.html:5
#: templates/maistar_ranking/game_form.html:16
#: templates/maistar_ranking/game_list.html:27
#: templates/maistar_ranking/player_game_list.html:44
msgid "Edit Game"
msgstr "Spiel bearbeiten"
#: templates/maistar_ranking/game_form.html:5
#: templates/maistar_ranking/game_form.html:16
#: templates/maistar_ranking/game_list.html:41
msgid "Add Game"
msgstr "Spiel hinzufügen"
#: templates/maistar_ranking/game_form.html:76
msgid "Back"
msgstr "Zurück"
#: templates/maistar_ranking/game_form.html:77
msgid "Save"
msgstr "Speichern"
#: templates/maistar_ranking/game_list.html:4
#: templates/maistar_ranking/player_game_list.html:6
msgid "Mai-Star Games"
msgstr "Mai-Star Spiele"
#: templates/maistar_ranking/game_list.html:7
msgid "Played Mai-Star Games"
msgstr "Gespielte Mai-Star Spiele"
#: templates/maistar_ranking/game_list.html:11
msgid "Game"
msgstr "Spiel"
#: templates/maistar_ranking/game_list.html:24
#: templates/maistar_ranking/player_game_list.html:41
msgid "Delete Game"
msgstr "Spiel löschen"
#: templates/maistar_ranking/game_list.html:33
msgid "No Mai-Star games have been added to this event yet."
msgstr "Für diese Veranstaltung wurden noch keine Mai-Star Spiele erfasst."
#: templates/maistar_ranking/game_list.html:40
msgid "Edit Event"
msgstr "Veranstaltung bearbeiten"
#: templates/maistar_ranking/page.html:5
msgid "Archive" msgid "Archive"
msgstr "Archiv" msgstr "Archiv"
#: .\maistar_ranking\templates\maistar_ranking\page.html:7 #: templates/maistar_ranking/page.html:7
msgid "Add Event" msgid "Add Event"
msgstr "Veranstaltung hinzufügen" msgstr "Veranstaltung hinzufügen"
#: .\maistar_ranking\templates\maistar_ranking\player_game_list.html:4 #: templates/maistar_ranking/player_game_list.html:4
msgid "Ladder Score for" msgid "Ladder Score for"
msgstr "Ladder Wertung für" msgstr "Ladder Wertung für"
#: .\maistar_ranking\templates\maistar_ranking\player_game_list.html:9 #: templates/maistar_ranking/player_game_list.html:9
msgid "Mai-Star Games with" msgid "Mai-Star Games with"
msgstr "Mai-Star Spiele mit" msgstr "Mai-Star Spiele mit"
#: .\maistar_ranking\templates\maistar_ranking\player_game_list.html:14 #: templates/maistar_ranking/player_game_list.html:14
msgid "Date" msgid "Date"
msgstr "Datum" msgstr "Datum"
#: .\maistar_ranking\templates\maistar_ranking\player_game_list.html:15 #: templates/maistar_ranking/player_game_list.html:15
msgid "Event" msgid "Event"
msgstr "Veranstaltung" msgstr "Veranstaltung"
#: .\maistar_ranking\templates\maistar_ranking\player_game_list.html:16 #: templates/maistar_ranking/player_game_list.html:16
msgid "Players" msgid "Players"
msgstr "Spieler" msgstr "Spieler"
#: .\maistar_ranking\templates\maistar_ranking\ranking_list.html:4 #: templates/maistar_ranking/ranking_list.html:4
msgid "Mai-Star Ranking" msgid "Mai-Star Ranking"
msgstr "Mai-Star Platzierung" msgstr "Mai-Star Platzierung"
#: .\maistar_ranking\templates\maistar_ranking\ranking_list.html:10 #: templates/maistar_ranking/ranking_list.html:10
#: .\maistar_ranking\templates\maistar_ranking\ranking_list.html:18 #: templates/maistar_ranking/ranking_list.html:18
msgid "Placement" msgid "Placement"
msgstr "Platzierung" msgstr "Platzierung"
#: .\maistar_ranking\templates\maistar_ranking\ranking_list.html:11 #: templates/maistar_ranking/ranking_list.html:11
msgid "Avatar" msgid "Avatar"
msgstr "Avatar" msgstr "Avatar"
#: .\maistar_ranking\templates\maistar_ranking\ranking_list.html:12 #: templates/maistar_ranking/ranking_list.html:12
msgid "Nickname" msgid "Nickname"
msgstr "Spitzname" msgstr "Spitzname"
#: .\maistar_ranking\templates\maistar_ranking\ranking_list.html:13 #: templates/maistar_ranking/ranking_list.html:13
msgid "Name" msgid "Name"
msgstr "Name" msgstr "Name"
#: .\maistar_ranking\templates\maistar_ranking\ranking_list.html:14 #: templates/maistar_ranking/ranking_list.html:14
msgid "Average" msgid "Average"
msgstr "Durchschnitt" msgstr "Durchschnitt"
#: .\maistar_ranking\templates\maistar_ranking\ranking_list.html:15 #: templates/maistar_ranking/ranking_list.html:15
msgid "Games" msgid "Games"
msgstr "Spiele" msgstr "Spiele"
#: .\maistar_ranking\templates\maistar_ranking\ranking_list.html:20 #: templates/maistar_ranking/ranking_list.html:20
msgid "count" msgid "count"
msgstr "Anzahl" msgstr "Anzahl"
#: .\maistar_ranking\templates\maistar_ranking\ranking_list.html:21 #: templates/maistar_ranking/ranking_list.html:21
msgid "good" msgid "good"
msgstr "Gut" msgstr "Gut"
#: .\maistar_ranking\templates\maistar_ranking\ranking_list.html:22 #: templates/maistar_ranking/ranking_list.html:22
msgid "won" msgid "won"
msgstr "Gewonnen" msgstr "Gewonnen"
#: .\maistar_ranking\templates\maistar_ranking\ranking_list.html:43 #: templates/maistar_ranking/ranking_list.html:43
msgid "" msgid ""
"Unfortunately, nobody has it been done in the ranking.\n" "Unfortunately, nobody has it been done in the ranking.\n"
" A player must have 6 games done, to be added to the ranking." " A player must have 6 games done, to be added to the ranking."
@@ -229,15 +228,15 @@ msgstr ""
"als 6 Spiele innerhalb einer Saison absolviert haben, werden für das " "als 6 Spiele innerhalb einer Saison absolviert haben, werden für das "
"Endergebnis nicht gewertet." "Endergebnis nicht gewertet."
#: .\maistar_ranking\templates\maistar_ranking\ranking_list.html:52 #: templates/maistar_ranking/ranking_list.html:52
msgid "Latest Games" msgid "Latest Games"
msgstr "Letzten Spiele" msgstr "Letzten Spiele"
#: .\maistar_ranking\templates\maistar_ranking\ranking_list.html:63 #: templates/maistar_ranking/ranking_list.html:63
msgid "Latest Events" msgid "Latest Events"
msgstr "Letzten Veranstaltungen" msgstr "Letzten Veranstaltungen"
#: .\maistar_ranking\templates\maistar_ranking\ranking_list.html:70 #: templates/maistar_ranking/ranking_list.html:70
msgid "Ladder Archive" msgid "Ladder Archive"
msgstr "Archiv" msgstr "Archiv"

View File

@@ -6,7 +6,6 @@ from django.conf import settings
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('events', '__first__'), ('events', '__first__'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
@@ -17,7 +16,8 @@ class Migration(migrations.Migration):
name='Game', name='Game',
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', ('id', models.AutoField(verbose_name='ID',
serialize=False, auto_created=True, primary_key=True)), serialize=False, auto_created=True,
primary_key=True)),
('comment', models.TextField(verbose_name='Anmerkung', blank=True)), ('comment', models.TextField(verbose_name='Anmerkung', blank=True)),
('player1_score', models.SmallIntegerField(verbose_name='Punkte')), ('player1_score', models.SmallIntegerField(verbose_name='Punkte')),
('player1_placement', models.PositiveSmallIntegerField(editable=False)), ('player1_placement', models.PositiveSmallIntegerField(editable=False)),
@@ -32,31 +32,47 @@ class Migration(migrations.Migration):
('player6_score', models.SmallIntegerField(verbose_name='Punkte')), ('player6_score', models.SmallIntegerField(verbose_name='Punkte')),
('player6_placement', models.PositiveSmallIntegerField(editable=False)), ('player6_placement', models.PositiveSmallIntegerField(editable=False)),
('confirmed', models.BooleanField( ('confirmed', models.BooleanField(
default=True, help_text='das Spiel z\xe4hlt nur wenn es best\xe4tigt wurde', verbose_name='Wurde best\xe4tigt')), default=True,
help_text='das Spiel z\xe4hlt nur wenn es best\xe4tigt wurde',
verbose_name='Wurde best\xe4tigt')),
('player_names', models.CharField(max_length=255, editable=False)), ('player_names', models.CharField(max_length=255, editable=False)),
('season', models.PositiveSmallIntegerField( ('season', models.PositiveSmallIntegerField(
verbose_name='Saison', editable=False, db_index=True)), verbose_name='Saison', editable=False, db_index=True)),
('event', models.ForeignKey( ('event', models.ForeignKey(
related_name='maistargame_set', to='events.Event', on_delete=models.CASCADE)), related_name='maistargame_set', to='events.Event',
on_delete=models.CASCADE)),
('player1', models.ForeignKey(related_name='+', ('player1', models.ForeignKey(related_name='+',
verbose_name='Spieler 1', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), verbose_name='Spieler 1',
to=settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)),
('player2', models.ForeignKey(related_name='+', ('player2', models.ForeignKey(related_name='+',
verbose_name='Spieler 2', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), verbose_name='Spieler 2',
to=settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)),
('player3', models.ForeignKey(related_name='+', ('player3', models.ForeignKey(related_name='+',
verbose_name='Spieler 3', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), verbose_name='Spieler 3',
to=settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)),
('player4', models.ForeignKey(related_name='+', ('player4', models.ForeignKey(related_name='+',
verbose_name='Spieler 4', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), verbose_name='Spieler 4',
to=settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)),
('player5', models.ForeignKey(related_name='+', ('player5', models.ForeignKey(related_name='+',
verbose_name='Spieler 5', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), verbose_name='Spieler 5',
to=settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)),
('player6', models.ForeignKey(related_name='+', ('player6', models.ForeignKey(related_name='+',
verbose_name='Spieler 6', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), verbose_name='Spieler 6',
to=settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
name='Ranking', name='Ranking',
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', ('id', models.AutoField(verbose_name='ID',
serialize=False, auto_created=True, primary_key=True)), serialize=False, auto_created=True,
primary_key=True)),
('season', models.PositiveSmallIntegerField(verbose_name='Saison')), ('season', models.PositiveSmallIntegerField(verbose_name='Saison')),
('placement', models.PositiveIntegerField(null=True, blank=True)), ('placement', models.PositiveIntegerField(null=True, blank=True)),
('avg_placement', models.PositiveSmallIntegerField( ('avg_placement', models.PositiveSmallIntegerField(
@@ -65,7 +81,8 @@ class Migration(migrations.Migration):
('games_count', models.PositiveSmallIntegerField(default=0)), ('games_count', models.PositiveSmallIntegerField(default=0)),
('games_good', models.PositiveSmallIntegerField(default=0)), ('games_good', models.PositiveSmallIntegerField(default=0)),
('games_won', models.PositiveSmallIntegerField(default=0)), ('games_won', models.PositiveSmallIntegerField(default=0)),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)),
], ],
options={ options={
'ordering': ('-season', 'placement', 'avg_placement', '-avg_score'), 'ordering': ('-season', 'placement', 'avg_placement', '-avg_score'),

View File

@@ -8,7 +8,6 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('maistar_ranking', '0006_auto_20171115_0653'), ('maistar_ranking', '0006_auto_20171115_0653'),
] ]
@@ -17,36 +16,49 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='game', model_name='game',
name='player1', name='player1',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Spieler 1'), field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT,
related_name='+', to=settings.AUTH_USER_MODEL,
verbose_name='Spieler 1'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='game', model_name='game',
name='player2', name='player2',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Spieler 2'), field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT,
related_name='+', to=settings.AUTH_USER_MODEL,
verbose_name='Spieler 2'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='game', model_name='game',
name='player3', name='player3',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Spieler 3'), field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT,
related_name='+', to=settings.AUTH_USER_MODEL,
verbose_name='Spieler 3'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='game', model_name='game',
name='player4', name='player4',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Spieler 4'), field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT,
related_name='+', to=settings.AUTH_USER_MODEL,
verbose_name='Spieler 4'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='game', model_name='game',
name='player5', name='player5',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Spieler 5'), field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT,
related_name='+', to=settings.AUTH_USER_MODEL,
verbose_name='Spieler 5'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='game', model_name='game',
name='player6', name='player6',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Spieler 6'), field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT,
related_name='+', to=settings.AUTH_USER_MODEL,
verbose_name='Spieler 6'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='ranking', model_name='ranking',
name='user', name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL), field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT,
to=settings.AUTH_USER_MODEL),
), ),
] ]

View File

@@ -6,7 +6,7 @@ from django.db import models
from django.db.models.signals import post_delete, post_save from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.urls import reverse from django.urls import reverse
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
from events.models import Event from events.models import Event
from . import settings, managers from . import settings, managers
@@ -149,7 +149,8 @@ class Game(models.Model):
class Ranking(models.Model): class Ranking(models.Model):
"""the player scores in the ladder for one season. """ """the player scores in the ladder for one season. """
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT) user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.PROTECT)
season = models.PositiveSmallIntegerField(_("Season")) season = models.PositiveSmallIntegerField(_("Season"))
placement = models.PositiveIntegerField(blank=True, null=True) placement = models.PositiveIntegerField(blank=True, null=True)
avg_placement = models.PositiveSmallIntegerField(blank=True, null=True) avg_placement = models.PositiveSmallIntegerField(blank=True, null=True)
@@ -210,7 +211,8 @@ def update_maistar_ranking(sender, **kwargs):
""" """
A Django signal hook to trigger a recalculation of the rankings as soon A Django signal hook to trigger a recalculation of the rankings as soon
as a Mai-Star game has been added, deleted, or modified. as a Mai-Star game has been added, deleted, or modified.
:param sender: The callback function which will be connected to this signal. See Receiver functions for more information. :param sender: The callback function which will be connected to this signal.
See Receiver functions for more information.
:param kwargs: """ :param kwargs: """
instance = kwargs['instance'] instance = kwargs['instance']
for player in instance.player_list: for player in instance.player_list:

Some files were not shown because too many files have changed in this diff Show More