Compare commits
167 Commits
css3_redes
...
kyu_change
| Author | SHA1 | Date | |
|---|---|---|---|
| 06785a3e49 | |||
| 4c00211432 | |||
| c62d2ee010 | |||
| ef00fc25f7 | |||
| fbfabb982f | |||
| 3efc4335bb | |||
|
|
e3daccafb1 | ||
|
|
1f4b978fd2 | ||
| 18d7f8a926 | |||
| 2885ef9d5c | |||
| 3f403b3f52 | |||
| 0d3c47d8b3 | |||
| 95f5e0b920 | |||
| b12203911e | |||
| a733130b65 | |||
| 483e6f215d | |||
| 2bdf8c9692 | |||
| 36e24b7da8 | |||
| 79a5cdb868 | |||
| 5ed8976074 | |||
| 12930c2ec6 | |||
| e792da7459 | |||
| b01dc33cb4 | |||
| 1bdce47375 | |||
| bb7d4c8a08 | |||
| a3ac722acf | |||
| 1313d96e8f | |||
| f2836baae5 | |||
| d67719cc17 | |||
| 12773463a6 | |||
| a8536fc776 | |||
| ba4620705f | |||
| 4db5c09387 | |||
| 40d2120d7e | |||
| 0768641184 | |||
| f7e9585932 | |||
| fae5f30cfa | |||
| 99ceb54db1 | |||
| e95ad8bf0a | |||
| f841c1736e | |||
| 4b68f62929 | |||
| 436a77d036 | |||
| fc8daaaf25 | |||
| a10b2706c3 | |||
| c3d213934e | |||
| afc471d27f | |||
| baa9660b7c | |||
| 1d18356c8c | |||
| 3dcd1aeffa | |||
| 5a32dbf0af | |||
| 5affdb0f5c | |||
| 65b8208987 | |||
| 9f0126be54 | |||
| 4f61b1e73b | |||
| 0f71bb5360 | |||
| df4d8c83a6 | |||
| 3e64f4d757 | |||
| f2533273e9 | |||
| 121167f1fe | |||
| 595341a53b | |||
| ac9b3e06c0 | |||
| af46768e7d | |||
| 9fdf725702 | |||
| 10bb990539 | |||
| 2f5f834bba | |||
| 90cefd8739 | |||
| 722c155c17 | |||
| a4c4f96c06 | |||
| 3b162fc59c | |||
| 19bbb5a226 | |||
| e9e997e38d | |||
| 3a3c8ec0c5 | |||
| 4aab611026 | |||
| d5995bc612 | |||
| 3ef947f128 | |||
| 28f7292c9d | |||
| 7b631230da | |||
| 6804319c28 | |||
| 0a793b7954 | |||
| a2df81d62e | |||
| 4b0a5c9c82 | |||
| afd163298c | |||
| 6796b58d4c | |||
| cce6ac6014 | |||
| 0b2e040fc9 | |||
| 192721452e | |||
| fdbf819092 | |||
| 10c27784ee | |||
| 9f6fffa4f4 | |||
| 8ab99ec039 | |||
| b7fab97715 | |||
| 1fdf88c6d2 | |||
| c030a31e2b | |||
| b20b988e5d | |||
| ade2a568f7 | |||
| cf0e5e778c | |||
| c5781246fe | |||
| c7b714c41b | |||
| f51155cfac | |||
| 1315bc4225 | |||
| 97749bfd2e | |||
| bb5081a78b | |||
| 84880281c6 | |||
| 854fd38740 | |||
| cffbd30d7e | |||
| 6de1ecb102 | |||
| 3a611ca9da | |||
| bf12060c3b | |||
| 68c484afc9 | |||
| 5ad628f33a | |||
| 92470514c4 | |||
| 36272c60d6 | |||
| d33e5fc8c6 | |||
| c428f6ed1f | |||
| bb110da5a2 | |||
| 9276e97c36 | |||
| 35a51091bf | |||
| fd244f10e8 | |||
| c0c48f950a | |||
| 0a45cf1fd8 | |||
| d9e0d5596c | |||
| a3c02ae73a | |||
| e6f2528a0e | |||
| 638ec96c25 | |||
| 34d327f183 | |||
| 002eb40ea5 | |||
| d97c9db539 | |||
| 7ef69849a4 | |||
| 8719c2377a | |||
| abeb86d48f | |||
| 42a6ebedf9 | |||
| b3ab9798b5 | |||
| ce218080b2 | |||
| 63e099b7c5 | |||
| a26a91c360 | |||
| cf0bbb4c8f | |||
| 321531c160 | |||
| 5437b7b8de | |||
| 10bdaaa98c | |||
| 9b4ab374c6 | |||
| ae87414584 | |||
| 120b4ea17f | |||
| fcb15c7e7e | |||
| 299418ee62 | |||
| e5f0d7f6fe | |||
| 53974dcd48 | |||
| 222dd060b1 | |||
| bbe16b2d13 | |||
| ba44e97e9a | |||
| 8595959872 | |||
| 9b0967adc8 | |||
| ac78dc3d75 | |||
| 57b7d5a84b | |||
| 6b37aa84b0 | |||
|
|
0c7bd466c7 | ||
| 0bdd409dc2 | |||
|
|
9c4ec20394 | ||
|
|
6f1512906d | ||
|
|
12c9a6e0e9 | ||
|
|
8cac20dd2b | ||
|
|
b1586efbab | ||
|
|
088efe2f39 | ||
|
|
b9ec418e5e | ||
|
|
836eee983d | ||
|
|
79eaeb34ad | ||
|
|
bafbf38612 | ||
|
|
b96b485b61 |
2
.coafile
2
.coafile
@@ -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
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -62,6 +62,7 @@ docs/_build/
|
|||||||
target/
|
target/
|
||||||
|
|
||||||
#Django Development
|
#Django Development
|
||||||
|
backup/
|
||||||
/bower_components/
|
/bower_components/
|
||||||
/media/
|
/media/
|
||||||
/node_modules/
|
/node_modules/
|
||||||
@@ -70,3 +71,4 @@ target/
|
|||||||
.[a-zA-Z]*
|
.[a-zA-Z]*
|
||||||
local_settings.py
|
local_settings.py
|
||||||
sample.xlsx
|
sample.xlsx
|
||||||
|
venv/
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ module.exports = function(grunt) {
|
|||||||
options: {
|
options: {
|
||||||
paths: ['src/kasu/static/less'],
|
paths: ['src/kasu/static/less'],
|
||||||
compress: false,
|
compress: false,
|
||||||
optimization:9,
|
optimization: 9,
|
||||||
ieCompat: false,
|
ieCompat: false,
|
||||||
},
|
},
|
||||||
kasu: {
|
kasu: {
|
||||||
@@ -21,9 +21,10 @@ module.exports = function(grunt) {
|
|||||||
report: 'min'
|
report: 'min'
|
||||||
},
|
},
|
||||||
kasu: {
|
kasu: {
|
||||||
src: 'static/css/kasu.css',
|
files: {
|
||||||
dest: 'static/css/kasu.css'
|
'src/kasu/static/css/kasu.min.css': ['src/kasu/static/css/kasu.css'],
|
||||||
}
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
styles: {
|
styles: {
|
||||||
|
|||||||
80
Makefile
Normal file
80
Makefile
Normal 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
|
||||||
@@ -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
|
|
||||||
23
bin/sync.sh
23
bin/sync.sh
@@ -1,23 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
SSH_LOGIN="kasu@s21.wservices.ch"
|
|
||||||
SYNC_ASSESTS="requirements"
|
|
||||||
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"
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
export DJANGO_SETTINGS_MODULE=kasu.settings
|
|
||||||
source .virtualenv/bin/activate
|
|
||||||
./manage.py runserver 0.0.0.0:8000
|
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
"grunt": ">=0.4.5",
|
"grunt": ">=0.4.5",
|
||||||
"grunt-contrib-less": ">=1.0.1",
|
"grunt-contrib-less": ">=1.0.1",
|
||||||
"grunt-contrib-watch": ">=0.6.1",
|
"grunt-contrib-watch": ">=0.6.1",
|
||||||
"grunt-more-css": ">=0.1.0"
|
"grunt-more-css": "^0.1.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ckeditor-dev": "git://github.com/ckeditor/ckeditor-dev.git"
|
"ckeditor-dev": "git://github.com/ckeditor/ckeditor-dev.git"
|
||||||
|
|||||||
@@ -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,14 +8,13 @@ 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
|
||||||
requests-oauthlib
|
requests-oauthlib
|
||||||
social-auth-app-django
|
|
||||||
social-auth-core
|
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ django-rosetta
|
|||||||
sqlparse
|
sqlparse
|
||||||
|
|
||||||
# Code Linting
|
# Code Linting
|
||||||
coala-bears
|
pylint>=2.0
|
||||||
pylint-django
|
pylint-django
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
|
from utils import STATUS_PUBLISHED
|
||||||
|
|
||||||
|
|
||||||
def content_menus(request):
|
def content_menus(request):
|
||||||
@@ -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')
|
||||||
@@ -50,7 +51,7 @@ def content_menus(request):
|
|||||||
break
|
break
|
||||||
current_path = current_path[0:current_path.rfind('.')]
|
current_path = current_path[0:current_path.rfind('.')]
|
||||||
|
|
||||||
return {'top_menu_items': top_level_pages,
|
return {'top_menu_items': top_level_pages.filter(status=STATUS_PUBLISHED),
|
||||||
'current_top_page': current_top_page,
|
'current_top_page': current_top_page,
|
||||||
'current_path': current_path,
|
'current_path': current_path,
|
||||||
'current_page': current_page}
|
'current_page': current_page}
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -8,8 +8,8 @@ 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-04-27 09:49+0200\n"
|
"POT-Creation-Date: 2023-08-07 20:38+0200\n"
|
||||||
"PO-Revision-Date: 2018-04-27 10:05+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"
|
||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
@@ -20,7 +20,7 @@ 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"
|
||||||
|
|
||||||
#: 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"
|
||||||
|
|
||||||
@@ -32,130 +32,125 @@ msgstr "Neueste Kommentare auf Kasu.at "
|
|||||||
msgid "Kasu - latest comments"
|
msgid "Kasu - latest comments"
|
||||||
msgstr "Kasu - neue Kommentare"
|
msgstr "Kasu - neue Kommentare"
|
||||||
|
|
||||||
#: forms.py:57 models.py:315
|
#: 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."
|
||||||
|
|
||||||
#: models.py:68
|
#: models.py:76
|
||||||
msgid "Headline"
|
msgid "Headline"
|
||||||
msgstr "Schlagzeile"
|
msgstr "Schlagzeile"
|
||||||
|
|
||||||
#: models.py:70
|
#: models.py:78
|
||||||
msgid "Content"
|
msgid "Content"
|
||||||
msgstr "Inhalt"
|
msgstr "Inhalt"
|
||||||
|
|
||||||
#: models.py:72 models.py:143 templates/content/article_detail.html:25
|
#: models.py:82 models.py:151 templates/content/article_detail.html:28
|
||||||
msgid "Category"
|
msgid "Category"
|
||||||
msgstr "Kategorie"
|
msgstr "Kategorie"
|
||||||
|
|
||||||
#: models.py:73 models.py:136
|
#: models.py:83 models.py:144
|
||||||
msgid "Image"
|
msgid "Image"
|
||||||
msgstr "Bild"
|
msgstr "Bild"
|
||||||
|
|
||||||
#: models.py:75 models.py:138
|
#: models.py:85 models.py:146
|
||||||
msgid "Slug"
|
msgid "Slug"
|
||||||
msgstr "Slug"
|
msgstr "Slug"
|
||||||
|
|
||||||
#: models.py:77 templates/content/article_detail.html:23
|
#: models.py:88 templates/content/article_detail.html:21
|
||||||
msgid "Author"
|
msgid "Author"
|
||||||
msgstr "Autor"
|
msgstr "Autor"
|
||||||
|
|
||||||
#: models.py:78
|
#: models.py:89
|
||||||
msgid "Status"
|
msgid "Status"
|
||||||
msgstr "Status"
|
msgstr "Status"
|
||||||
|
|
||||||
#: models.py:80
|
#: models.py:91
|
||||||
msgid "Created"
|
msgid "Created"
|
||||||
msgstr "Erstellt"
|
msgstr "Erstellt"
|
||||||
|
|
||||||
#: models.py:81
|
#: models.py:92
|
||||||
msgid "Modified"
|
msgid "Modified"
|
||||||
msgstr "Bearbeitet"
|
msgstr "Bearbeitet"
|
||||||
|
|
||||||
#: models.py:86
|
#: models.py:97
|
||||||
msgid "Article"
|
msgid "Article"
|
||||||
msgstr "Artikel"
|
msgstr "Artikel"
|
||||||
|
|
||||||
#: models.py:87
|
#: models.py:98
|
||||||
msgid "Articles"
|
msgid "Articles"
|
||||||
msgstr "Artikel"
|
msgstr "Artikel"
|
||||||
|
|
||||||
#: models.py:132 models.py:133
|
#: models.py:140 models.py:141
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Name"
|
msgstr "Name"
|
||||||
|
|
||||||
#: models.py:134 models.py:135
|
#: models.py:142 models.py:143
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr "Beschreibung"
|
msgstr "Beschreibung"
|
||||||
|
|
||||||
#: models.py:144
|
#: models.py:152
|
||||||
msgid "Categories"
|
msgid "Categories"
|
||||||
msgstr "Kategorien"
|
msgstr "Kategorien"
|
||||||
|
|
||||||
#: models.py:176 models.py:182
|
#: 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"
|
||||||
|
|
||||||
#: models.py:187 models.py:192
|
#: 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 gemacht wird."
|
msgstr "Der Seitentitel der öffentlich angezeigt werden soll"
|
||||||
|
|
||||||
#: models.py:194
|
#: models.py:202
|
||||||
msgid "slug"
|
msgid "slug"
|
||||||
msgstr "Slug"
|
msgstr "Slug"
|
||||||
|
|
||||||
#: models.py:197
|
#: models.py:205
|
||||||
msgid ""
|
msgid ""
|
||||||
"The name of the page as it will appear in URLs e.g "
|
"The name of the page as it will appear in URLs e.g http://domain.com/blog/"
|
||||||
"http://domain.com/blog/[my-slug]/"
|
"[my-slug]/"
|
||||||
msgstr ""
|
msgstr "Wie die Seite in der URL aufscheint also http://domain.com/blog/[slug]"
|
||||||
"Der Seitenname wie er in der URL erscheint. z.B: "
|
|
||||||
"http://domain.com/blog/[slug]/"
|
|
||||||
|
|
||||||
#: models.py:206
|
#: models.py:214
|
||||||
msgid "Path"
|
msgid "Path"
|
||||||
msgstr "Pfad"
|
msgstr "Pfad"
|
||||||
|
|
||||||
#: models.py:218
|
#: models.py:226
|
||||||
msgid "Position"
|
msgid "Position"
|
||||||
msgstr "Position"
|
msgstr "Position"
|
||||||
|
|
||||||
#: models.py:223
|
#: models.py:231
|
||||||
msgid "status"
|
msgid "status"
|
||||||
msgstr "Status"
|
msgstr "Status"
|
||||||
|
|
||||||
#: models.py:226 models.py:228
|
#: models.py:234 models.py:236
|
||||||
#| msgid "Description"
|
|
||||||
msgid "search description"
|
msgid "search description"
|
||||||
msgstr "Suchbeschreibung"
|
msgstr "Beschreibung für Suchfunktion"
|
||||||
|
|
||||||
#: models.py:231
|
#: models.py:239
|
||||||
#| msgid "Content"
|
|
||||||
msgid "content type"
|
msgid "content type"
|
||||||
msgstr "Inhaltstyp"
|
msgstr "Inhaltstyp"
|
||||||
|
|
||||||
#: models.py:236
|
#: models.py:244
|
||||||
msgid "enable comments"
|
msgid "enable comments"
|
||||||
msgstr "Kommentare möglich"
|
msgstr "Kommentare möglich"
|
||||||
|
|
||||||
#: models.py:241
|
#: models.py:249
|
||||||
msgid "Template"
|
msgid "Template"
|
||||||
msgstr "Vorlage"
|
msgstr "Vorlage"
|
||||||
|
|
||||||
#: models.py:249
|
#: models.py:257
|
||||||
#| msgid "created on"
|
|
||||||
msgid "first created at"
|
msgid "first created at"
|
||||||
msgstr "erstellt am"
|
msgstr "erstellt am"
|
||||||
|
|
||||||
#: models.py:254
|
#: models.py:262
|
||||||
msgid "latest updated at"
|
msgid "latest updated at"
|
||||||
msgstr "letzte Änderung"
|
msgstr "letzte Aktualisierung am"
|
||||||
|
|
||||||
#: models.py:328
|
#: models.py:333
|
||||||
msgid "Page"
|
msgid "Page"
|
||||||
msgstr "Seite"
|
msgstr "Seite"
|
||||||
|
|
||||||
#: models.py:329
|
#: models.py:334
|
||||||
msgid "Pages"
|
msgid "Pages"
|
||||||
msgstr "Seiten"
|
msgstr "Seiten"
|
||||||
|
|
||||||
@@ -203,33 +198,35 @@ msgstr "neuer Artikel "
|
|||||||
msgid "back"
|
msgid "back"
|
||||||
msgstr "Zurück"
|
msgstr "Zurück"
|
||||||
|
|
||||||
#: templates/content/article_detail.html:24
|
#: templates/content/article_detail.html:25
|
||||||
msgid "Created on"
|
msgid "Created on"
|
||||||
msgstr "Erstellt am"
|
msgstr "Erstellt am"
|
||||||
|
|
||||||
#: templates/content/article_detail.html:36
|
#: templates/content/article_detail.html:39
|
||||||
msgid "share on"
|
msgid "share on"
|
||||||
msgstr "Teile auf"
|
msgstr "Teile auf"
|
||||||
|
|
||||||
#: templates/content/article_detail.html:51 views.py:156
|
#: templates/content/article_detail.html:48 views.py:160
|
||||||
msgid "Edit Article"
|
msgid "Edit Article"
|
||||||
msgstr "Artikel bearbeiten"
|
msgstr "Artikel bearbeiten"
|
||||||
|
|
||||||
#: templates/content/article_form.html:32 templates/content/page_form.html:42
|
#: templates/content/article_form.html:24
|
||||||
|
#: templates/content/article_form.html:31 templates/content/page_form.html:42
|
||||||
#: templates/content/page_form.html:49
|
#: templates/content/page_form.html:49
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr "Deutsch"
|
msgstr "Deutsch"
|
||||||
|
|
||||||
#: templates/content/article_form.html:33 templates/content/page_form.html:43
|
#: templates/content/article_form.html:25
|
||||||
|
#: templates/content/article_form.html:39 templates/content/page_form.html:43
|
||||||
#: templates/content/page_form.html:57
|
#: templates/content/page_form.html:57
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr "Englisch"
|
msgstr "Englisch"
|
||||||
|
|
||||||
#: templates/content/article_form.html:59 templates/content/page_form.html:66
|
#: templates/content/article_form.html:47 templates/content/page_form.html:66
|
||||||
msgid "reset"
|
msgid "reset"
|
||||||
msgstr "Zurücksetzen"
|
msgstr "Zurücksetzen"
|
||||||
|
|
||||||
#: templates/content/article_form.html:60 templates/content/page_form.html:67
|
#: templates/content/article_form.html:48 templates/content/page_form.html:67
|
||||||
msgid "save"
|
msgid "save"
|
||||||
msgstr "Speichern"
|
msgstr "Speichern"
|
||||||
|
|
||||||
@@ -254,16 +251,16 @@ msgstr "HTML spezifisch"
|
|||||||
msgid "This Category does not exist."
|
msgid "This Category does not exist."
|
||||||
msgstr "Diese Kategorie existiert nicht."
|
msgstr "Diese Kategorie existiert nicht."
|
||||||
|
|
||||||
#: views.py:157
|
#: views.py:161
|
||||||
msgid "Create Article"
|
msgid "Create Article"
|
||||||
msgstr "Artikel erstellen"
|
msgstr "Artikel erstellen"
|
||||||
|
|
||||||
#: 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"
|
||||||
|
|
||||||
#: 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."
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models, migrations
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.db import models, migrations
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
@@ -44,7 +44,8 @@ class Migration(migrations.Migration):
|
|||||||
('date_modified', models.DateTimeField(
|
('date_modified', models.DateTimeField(
|
||||||
auto_now=True, verbose_name='Bearbeitet')),
|
auto_now=True, verbose_name='Bearbeitet')),
|
||||||
('author', models.ForeignKey(
|
('author', models.ForeignKey(
|
||||||
verbose_name='Autor', to=settings.AUTH_USER_MODEL)),
|
verbose_name='Autor', to=settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.CASCADE))
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ('-date_created',),
|
'ordering': ('-date_created',),
|
||||||
@@ -144,7 +145,8 @@ class Migration(migrations.Migration):
|
|||||||
model_name='article',
|
model_name='article',
|
||||||
name='category',
|
name='category',
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
verbose_name='Kategorie', to='content.Category'),
|
verbose_name='Kategorie', to='content.Category',
|
||||||
|
on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='page',
|
name='page',
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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'),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
30
src/content/migrations/0007_auto_20171214_1215.py
Normal file
30
src/content/migrations/0007_auto_20171214_1215.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.8 on 2017-12-14 11:15
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
('content', '0006_auto_20171115_0653'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='article',
|
||||||
|
name='author',
|
||||||
|
field=models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
to=settings.AUTH_USER_MODEL, verbose_name='Autor'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='article',
|
||||||
|
name='category',
|
||||||
|
field=models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
to='content.Category', verbose_name='Kategorie'),
|
||||||
|
),
|
||||||
|
]
|
||||||
74
src/content/migrations/0008_auto_20190106_1954.py
Normal file
74
src/content/migrations/0008_auto_20190106_1954.py
Normal 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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
src/content/migrations/0009_alter_page_id.py
Normal file
18
src/content/migrations/0009_alter_page_id.py
Normal 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),
|
||||||
|
),
|
||||||
|
]
|
||||||
20
src/content/migrations/0010_alter_page_parent.py
Normal file
20
src/content/migrations/0010_alter_page_parent.py
Normal 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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
19
src/content/migrations/0011_alter_page_id.py
Normal file
19
src/content/migrations/0011_alter_page_id.py
Normal 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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -3,12 +3,12 @@ from ckeditor_uploader.fields import RichTextUploadingField
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.template.defaultfilters import slugify
|
from django.template.defaultfilters import slugify
|
||||||
|
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
|
||||||
|
|
||||||
@@ -41,6 +41,14 @@ def get_upload_path(instance, filename):
|
|||||||
return "categories/%s.%s" % (instance.slug, extension)
|
return "categories/%s.%s" % (instance.slug, extension)
|
||||||
|
|
||||||
|
|
||||||
|
def get_localized(obj, attr):
|
||||||
|
""" Return the localilzed field, or the fallback if the localized is empty.
|
||||||
|
"""
|
||||||
|
fallback = attr + '_de'
|
||||||
|
localized = attr + '_' + get_language()[:2]
|
||||||
|
return getattr(obj, localized) or getattr(obj, fallback)
|
||||||
|
|
||||||
|
|
||||||
class ArticleManager(models.Manager):
|
class ArticleManager(models.Manager):
|
||||||
"""Adds some predifined querys and joins some tables for faster querys."""
|
"""Adds some predifined querys and joins some tables for faster querys."""
|
||||||
|
|
||||||
@@ -69,11 +77,14 @@ class Article(models.Model):
|
|||||||
headline_en = models.CharField('Headline', max_length=255, blank=True)
|
headline_en = models.CharField('Headline', max_length=255, blank=True)
|
||||||
content_de = RichTextUploadingField(_('Content'))
|
content_de = RichTextUploadingField(_('Content'))
|
||||||
content_en = RichTextUploadingField('Content', blank=True)
|
content_en = RichTextUploadingField('Content', blank=True)
|
||||||
category = models.ForeignKey('Category', verbose_name=_('Category'))
|
category = models.ForeignKey('Category',
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
verbose_name=_('Category'))
|
||||||
image = models.ImageField(_('Image'), upload_to='news/',
|
image = models.ImageField(_('Image'), upload_to='news/',
|
||||||
blank=True, null=True)
|
blank=True, null=True)
|
||||||
slug = models.SlugField(_('Slug'), unique_for_month='date_created')
|
slug = models.SlugField(_('Slug'), unique_for_month='date_created')
|
||||||
author = models.ForeignKey(settings.AUTH_USER_MODEL,
|
author = models.ForeignKey(settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.PROTECT,
|
||||||
verbose_name=_('Author'))
|
verbose_name=_('Author'))
|
||||||
status = models.SmallIntegerField(_('Status'), choices=STATUS_CHOICES,
|
status = models.SmallIntegerField(_('Status'), choices=STATUS_CHOICES,
|
||||||
default=STATUS_PUBLISHED)
|
default=STATUS_PUBLISHED)
|
||||||
@@ -100,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."""
|
||||||
@@ -115,16 +127,12 @@ class Article(models.Model):
|
|||||||
@property
|
@property
|
||||||
def headline(self):
|
def headline(self):
|
||||||
"""Return the localized headline, fallback to german if necessary."""
|
"""Return the localized headline, fallback to german if necessary."""
|
||||||
return mark_safe(
|
return mark_safe(get_localized(self, 'headline'))
|
||||||
getattr(self, "headline_%s" % get_language(), self.headline_de)
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def content(self):
|
def content(self):
|
||||||
"""Return the localized content, fallback to german if necessary."""
|
"""Return the localized content, fallback to german if necessary."""
|
||||||
return mark_safe(
|
return mark_safe(get_localized(self, 'content'))
|
||||||
getattr(self, "content_%s" % get_language(), self.content_de)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Category(models.Model):
|
class Category(models.Model):
|
||||||
@@ -146,13 +154,12 @@ class Category(models.Model):
|
|||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Return the localized name, fallback to german if necessary."""
|
"""Return the localized name, fallback to german if necessary."""
|
||||||
return getattr(self, "name_%s" % get_language(), self.name_de)
|
return get_localized(self, 'name')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def description(self):
|
def description(self):
|
||||||
"""Return the localized description, fallback to german if necessary."""
|
"""Return the localized description, fallback to german if necessary."""
|
||||||
return getattr(self, "description_%s" % get_language(),
|
return get_localized(self, 'description')
|
||||||
self.description_de)
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
"""Return the URL of the article archive, filtered on this category."""
|
"""Return the URL of the article archive, filtered on this category."""
|
||||||
@@ -163,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.
|
||||||
@@ -210,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,
|
||||||
@@ -261,9 +269,7 @@ class Page(models.Model):
|
|||||||
@property
|
@property
|
||||||
def content(self):
|
def content(self):
|
||||||
"""Return the localized content, fallback to german if necessary."""
|
"""Return the localized content, fallback to german if necessary."""
|
||||||
return mark_safe(
|
return mark_safe(get_localized(self, 'content'))
|
||||||
getattr(self, "content_%s" % get_language(), self.content_de)
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def css_class(self):
|
def css_class(self):
|
||||||
@@ -275,23 +281,22 @@ class Page(models.Model):
|
|||||||
@property
|
@property
|
||||||
def description(self):
|
def description(self):
|
||||||
"""Return the localized description, fallback to german if necessary."""
|
"""Return the localized description, fallback to german if necessary."""
|
||||||
return getattr(self, "description_%s" % get_language(),
|
return get_localized(self, 'description')
|
||||||
self.description_de)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def menu_name(self):
|
def menu_name(self):
|
||||||
"""Return the localized menu name, fallback to german if necessary."""
|
"""Return the localized menu name, fallback to german if necessary."""
|
||||||
return getattr(self, "menu_name_%s" % get_language(), self.menu_name_de)
|
return get_localized(self, 'menu_name')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pdf_file(self):
|
def pdf_file(self):
|
||||||
"""Return the localized PDF file, fallback to german if necessary."""
|
"""Return the localized PDF file, fallback to german if necessary."""
|
||||||
return getattr(self, "pdf_%s" % get_language(), self.pdf_de)
|
return get_localized(self, 'pdf_file')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def title(self):
|
def title(self):
|
||||||
"""Return the localized title, fallback to german if necessary."""
|
"""Return the localized title, fallback to german if necessary."""
|
||||||
return getattr(self, "title_%s" % get_language(), self.title_de)
|
return get_localized(self, 'title')
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
"""set the URL path, the right content type, and scrub the HTML code."""
|
"""set the URL path, the right content type, and scrub the HTML code."""
|
||||||
@@ -314,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."""
|
||||||
|
|||||||
@@ -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'),
|
||||||
name='article-archive'),
|
path('<int:year>/<int:month>/', views.ArticleMonthArchive.as_view(),
|
||||||
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/$', ArticleMonthArchive.as_view(),
|
name='article-archive'),
|
||||||
name='article-archive'),
|
path('<int:year>/<int:month>/<slug:slug>/', views.ArticleDetail.as_view(),
|
||||||
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/(?P<slug>[\-\d\w]+)/$',
|
name='show-article'),
|
||||||
ArticleDetail.as_view(), name='show-article'),
|
path('<slug:category>/', views.ArticleArchiveIndex.as_view(),
|
||||||
url(r'^(?P<category>[\-\d\w]+)/$', ArticleArchiveIndex.as_view(),
|
name='article-archive'),
|
||||||
name='article-archive'),
|
path('<slug:category>/<int:year>/', views.ArticleYearArchive.as_view(),
|
||||||
url(r'^(?P<category>[\-\d\w]+)/(?P<year>[\d]{4})/$',
|
name='article-archive'),
|
||||||
ArticleYearArchive.as_view(), name='article-archive'),
|
path('<slug:category>/<int:year>/<int:month>/', views.ArticleMonthArchive.as_view(),
|
||||||
url(r'^(?P<category>[\-\d\w]+)/(?P<year>[\d]{4})/(?P<month>[\d]+)/$',
|
name='article-archive'),
|
||||||
ArticleMonthArchive.as_view(), name='article-archive'),
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -1,53 +1,48 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}{% load i18n comments thumbnail %}
|
||||||
{% load i18n comments thumbnail %}
|
|
||||||
|
|
||||||
{% block title %}{{ article.headline }}{% endblock %}
|
{% block title %}{{ article.headline }}{% endblock %}
|
||||||
|
|
||||||
{% block description %}{{article.content|striptags|truncatewords:16}}{% endblock %}
|
{% block description %}{{article.content|striptags|truncatewords:16}}{% endblock %}
|
||||||
|
|
||||||
{% block opengraph %}
|
{% block opengraph %}
|
||||||
<meta property="og:type" content="article" />
|
<meta property="og:type" content="article"/>
|
||||||
<meta property="og:title" content="{{ article.headline|force_escape }}" />
|
<meta property="og:title" content="{{ article.headline|force_escape }}"/>
|
||||||
<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 %}
|
||||||
|
|
||||||
{% block teaser %}
|
{% block teaser %}
|
||||||
<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>
|
||||||
</ul>
|
<li><span class="fa fa-calendar-o"></span>
|
||||||
</div>
|
<strong>{% trans 'Created on' %}:</strong>
|
||||||
{% endblock %}
|
<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>
|
||||||
|
</div>{% 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>{% endblock %}
|
||||||
</p>
|
|
||||||
{% 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 %}
|
|
||||||
|
|||||||
@@ -9,56 +9,44 @@
|
|||||||
{% 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>
|
</fieldset>
|
||||||
{{ 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>
|
<ul class="tabs">
|
||||||
|
<li><a href="#de">{% trans "German" %}</a></li>
|
||||||
|
<li><a href="#en">{% trans "English" %}</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<ul class="tabs grid_12">
|
<div class="tab_container">
|
||||||
<li><a href="#de">{% trans "German" %}</a></li>
|
<section id="de" class="tab_content">
|
||||||
<li><a href="#en">{% trans "English" %}</a></li>
|
<fieldset class="grid_12">
|
||||||
</ul>
|
<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>
|
</fieldset>
|
||||||
<p class="grid_10">{{ form.headline_de }}</p>
|
<br>
|
||||||
|
{{form.content_de}}
|
||||||
<div class="tab_container">
|
</section>
|
||||||
<div id="de" class="tab_content">
|
<section id="en" class="tab_content">
|
||||||
<fieldset>
|
<fieldset class="grid_12">
|
||||||
<legend>Deutsch</legend>
|
<legend>{% trans "English" %}</legend>
|
||||||
<label for="id_{{ form.headline_de.html_name }}" class="fieldname {{ form.headline_de.css_classes }}">
|
{% with fieldset_en as form %}{% include "form.html" %}{% endwith %}
|
||||||
{{ form.headline_de.label }}</label>
|
</fieldset>
|
||||||
<p class="grid_10">{{ form.headline_de }}</p>
|
<br>
|
||||||
</fieldset>
|
{{form.content_en}}
|
||||||
{{ form.content_de }}
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div id="en" class="tab_content">
|
<p class="buttonbar">
|
||||||
<h3 class="grid_12">English</h3>
|
<button type="reset"><span class="fa fa-undo"></span> {% trans 'reset' %}</button>
|
||||||
<label for="id_{{ form.headline_en.html_name }}" class="grid_2 {{ form.headline_en.css_classes }}">
|
<button type="submit"><span class="fa fa-hdd-o"></span> {% trans 'save' %}</button>
|
||||||
{{ form.headline_en.label }}</label>
|
</p>
|
||||||
<p class="grid_10">{{ form.headline_en }}</p>
|
|
||||||
<p> </p>
|
|
||||||
{{ form.content_en }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p class="buttonbar">
|
|
||||||
<button type="reset"><span class="fa fa-undo"></span> {% trans 'reset' %}</button>
|
|
||||||
<button type="submit"><span class="fa fa-hdd-o"></span> {% trans 'save' %}</button>
|
|
||||||
</p>
|
|
||||||
</form>{% endblock %}
|
</form>{% endblock %}
|
||||||
|
|
||||||
{% block buttonbar %}{% endblock %}
|
{% block buttonbar %}{% endblock %}
|
||||||
@@ -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>
|
|
||||||
@@ -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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
queryset = models.Article.objects.filter(status=models.STATUS_PUBLISHED)
|
def get_queryset(self):
|
||||||
|
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,17 +183,20 @@ 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."""
|
||||||
path = os.path.splitext(self.kwargs['path'])[0]
|
if self.kwargs['path']:
|
||||||
if path.startswith('/'):
|
path = os.path.splitext(self.kwargs['path'])[0]
|
||||||
path = path[1:]
|
if path.startswith('/'):
|
||||||
if path.endswith('/'):
|
path = path[1:]
|
||||||
path = path[:-1]
|
if path.endswith('/'):
|
||||||
parent = models.Page.objects.get(path=path)
|
path = path[:-1]
|
||||||
return {'parent': parent}
|
parent = models.Page.objects.get(path=path)
|
||||||
|
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
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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'),
|
||||||
]
|
]
|
||||||
|
|||||||
Binary file not shown.
@@ -7,8 +7,8 @@ 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-04-27 09:49+0200\n"
|
"POT-Creation-Date: 2023-08-07 20:38+0200\n"
|
||||||
"PO-Revision-Date: 2018-04-27 10:29+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"
|
||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
@@ -19,59 +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"
|
||||||
|
|
||||||
#: admin.py:14 models.py:82
|
#: admin.py:14 models.py:83
|
||||||
msgid "Event Series"
|
msgid "Event Series"
|
||||||
msgstr "Veranstaltungsreihen"
|
msgstr "Veranstaltungsreihen"
|
||||||
|
|
||||||
#: forms.py:17
|
#: forms.py:38
|
||||||
msgid "Images"
|
msgid "Images"
|
||||||
msgstr "Bilder"
|
msgstr "Bilder"
|
||||||
|
|
||||||
#: forms.py:46
|
#: forms.py:61
|
||||||
msgid "start"
|
msgid "start"
|
||||||
msgstr "Beginn"
|
msgstr "Beginn"
|
||||||
|
|
||||||
#: forms.py:49
|
#: forms.py:64
|
||||||
msgid "end"
|
msgid "end"
|
||||||
msgstr "Ende"
|
msgstr "Ende"
|
||||||
|
|
||||||
#: mixins.py:57 views.py:149
|
#: mixins.py:86
|
||||||
msgid "Event does not exist"
|
msgid "Event does not exist"
|
||||||
msgstr "Veranstaltung gibt es nicht"
|
msgstr "Veranstaltung gibt es nicht"
|
||||||
|
|
||||||
#: models.py:52 models.py:176 models.py:217
|
#: models.py:53 models.py:177 models.py:218
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Name"
|
msgstr "Name"
|
||||||
|
|
||||||
#: models.py:53 models.py:177 models.py:225
|
#: models.py:54 models.py:178 models.py:226
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr "Beschreibung"
|
msgstr "Beschreibung"
|
||||||
|
|
||||||
#: models.py:55 templates/events/event_detail.html:29
|
#: models.py:56 templates/events/event_detail.html:29
|
||||||
#: templates/events/event_detail.html:87 templates/events/event_list.html:28
|
#: templates/events/event_detail.html:87 templates/events/event_list.html:28
|
||||||
#: templates/events/photo_upload.html:13
|
|
||||||
msgid "Start"
|
msgid "Start"
|
||||||
msgstr "Beginn"
|
msgstr "Beginn"
|
||||||
|
|
||||||
#: models.py:56 templates/events/event_detail.html:30
|
#: models.py:57 templates/events/event_detail.html:30
|
||||||
#: templates/events/event_detail.html:89
|
#: templates/events/event_detail.html:89
|
||||||
msgid "End"
|
msgid "End"
|
||||||
msgstr "Ende"
|
msgstr "Ende"
|
||||||
|
|
||||||
#: models.py:57 models.py:185 templates/events/event_detail.html:34
|
#: models.py:58 models.py:186 templates/events/event_detail.html:34
|
||||||
#: templates/events/event_detail.html:80 templates/events/event_detail.html:92
|
#: templates/events/event_detail.html:80 templates/events/event_detail.html:92
|
||||||
msgid "Homepage"
|
msgid "Homepage"
|
||||||
msgstr "Homepage"
|
msgstr "Homepage"
|
||||||
|
|
||||||
#: models.py:59 models.py:179 models.py:219
|
#: models.py:60 models.py:180 models.py:220
|
||||||
msgid "Image"
|
msgid "Image"
|
||||||
msgstr "Bild"
|
msgstr "Bild"
|
||||||
|
|
||||||
#: models.py:66
|
#: models.py:67
|
||||||
msgid "Mahjong Tournament"
|
msgid "Mahjong Tournament"
|
||||||
msgstr "Mahjong Turnier"
|
msgstr "Mahjong Turnier"
|
||||||
|
|
||||||
#: 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."
|
||||||
@@ -79,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."
|
||||||
|
|
||||||
#: models.py:72
|
#: models.py:73
|
||||||
msgid "Mahjong Season"
|
msgid "Mahjong Season"
|
||||||
msgstr "Mahjong Saison"
|
msgstr "Mahjong Saison"
|
||||||
|
|
||||||
#: 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 "
|
||||||
@@ -92,72 +91,72 @@ 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."
|
||||||
|
|
||||||
#: models.py:92 models.py:195 models.py:247
|
#: models.py:93 models.py:196 models.py:249
|
||||||
msgid "first created at"
|
msgid "first created at"
|
||||||
msgstr "Erstellt am"
|
msgstr "erstellt am"
|
||||||
|
|
||||||
#: models.py:97 models.py:200 models.py:252
|
#: models.py:98 models.py:201 models.py:254
|
||||||
msgid "latest updated at"
|
msgid "latest updated at"
|
||||||
msgstr "Geändert am"
|
msgstr "letzte Aktualisierung am"
|
||||||
|
|
||||||
#: models.py:103
|
#: models.py:104
|
||||||
msgid "Event"
|
msgid "Event"
|
||||||
msgstr "Termin"
|
msgstr "Termin"
|
||||||
|
|
||||||
#: models.py:104
|
#: models.py:105
|
||||||
msgid "Events"
|
msgid "Events"
|
||||||
msgstr "Termine"
|
msgstr "Termine"
|
||||||
|
|
||||||
#: 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"
|
||||||
|
|
||||||
#: models.py:186
|
#: models.py:187
|
||||||
msgid "Postal Code"
|
msgid "Postal Code"
|
||||||
msgstr "Postleitzahl"
|
msgstr "Postleitzahl"
|
||||||
|
|
||||||
#: models.py:187
|
#: models.py:188
|
||||||
msgid "Street Address"
|
msgid "Street Address"
|
||||||
msgstr "Straße"
|
msgstr "Straße"
|
||||||
|
|
||||||
#: models.py:188
|
#: models.py:189
|
||||||
msgid "Locality"
|
msgid "Locality"
|
||||||
msgstr "Ort"
|
msgstr "Ort"
|
||||||
|
|
||||||
#: models.py:189
|
#: models.py:190
|
||||||
msgid "Country"
|
msgid "Country"
|
||||||
msgstr "Land"
|
msgstr "Land"
|
||||||
|
|
||||||
#: models.py:204
|
#: models.py:205
|
||||||
msgid "Venue"
|
msgid "Venue"
|
||||||
msgstr "Veranstaltungsort"
|
msgstr "Veranstaltungsort"
|
||||||
|
|
||||||
#: models.py:205
|
#: models.py:206
|
||||||
msgid "Venues"
|
msgid "Venues"
|
||||||
msgstr "Veranstaltungsorte"
|
msgstr "Veranstaltungsorte"
|
||||||
|
|
||||||
#: models.py:231
|
#: models.py:233
|
||||||
msgid "Startpage"
|
msgid "Startpage"
|
||||||
msgstr "Startseite"
|
msgstr "Startseite"
|
||||||
|
|
||||||
#: models.py:234
|
#: 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."
|
||||||
|
|
||||||
#: models.py:236
|
#: models.py:238
|
||||||
msgid "Published on"
|
msgid "Published on"
|
||||||
msgstr "Veröffentlicht am"
|
msgstr "Veröffentlicht am"
|
||||||
|
|
||||||
#: models.py:238
|
#: models.py:240
|
||||||
msgid "Number of views"
|
msgid "Number of views"
|
||||||
msgstr "Wie oft gesehen"
|
msgstr "Wie oft gesehen"
|
||||||
|
|
||||||
#: models.py:262 templates/events/event_archive.html:38
|
#: models.py:264 templates/events/event_archive.html:38
|
||||||
#: templates/events/event_list.html:18
|
#: templates/events/event_list.html:18
|
||||||
msgid "Event Image"
|
msgid "Event Image"
|
||||||
msgstr "Veranstaltungsbild"
|
msgstr "Veranstaltungsbild"
|
||||||
|
|
||||||
#: models.py:263
|
#: models.py:265
|
||||||
msgid "Event Images"
|
msgid "Event Images"
|
||||||
msgstr "Veranstaltungsbilder"
|
msgstr "Veranstaltungsbilder"
|
||||||
|
|
||||||
@@ -165,9 +164,8 @@ msgstr "Veranstaltungsbilder"
|
|||||||
msgid "Event Archive"
|
msgid "Event Archive"
|
||||||
msgstr "Veranstaltungsarchiv"
|
msgstr "Veranstaltungsarchiv"
|
||||||
|
|
||||||
#: templates/events/event_archive.html:42
|
#: templates/events/event_archive.html:42 templates/events/event_detail.html:85
|
||||||
#: templates/events/event_detail.html:85 templates/events/event_list.html:22
|
#: templates/events/event_list.html:22 templates/events/photo_detail.html:53
|
||||||
#: templates/events/photo_detail.html:53
|
|
||||||
msgid "Date"
|
msgid "Date"
|
||||||
msgstr "Datum"
|
msgstr "Datum"
|
||||||
|
|
||||||
@@ -176,36 +174,30 @@ msgid "Time"
|
|||||||
msgstr "Zeit"
|
msgstr "Zeit"
|
||||||
|
|
||||||
#: templates/events/event_archive.html:49
|
#: templates/events/event_archive.html:49
|
||||||
#: templates/events/photo_upload.html:16
|
|
||||||
msgid "from"
|
msgid "from"
|
||||||
msgstr "von"
|
msgstr "von"
|
||||||
|
|
||||||
#: templates/events/event_archive.html:49
|
#: templates/events/event_archive.html:49
|
||||||
#: templates/events/photo_upload.html:16
|
|
||||||
msgid "to"
|
msgid "to"
|
||||||
msgstr "bis"
|
msgstr "bis"
|
||||||
|
|
||||||
#: templates/events/event_archive.html:57
|
#: templates/events/event_archive.html:57 templates/events/event_detail.html:31
|
||||||
#: templates/events/event_detail.html:31 templates/events/event_detail.html:72
|
#: templates/events/event_detail.html:72 templates/events/event_list.html:32
|
||||||
#: templates/events/event_list.html:32 templates/events/photo_upload.html:23
|
|
||||||
msgid "Location"
|
msgid "Location"
|
||||||
msgstr "Ort"
|
msgstr "Ort"
|
||||||
|
|
||||||
#: templates/events/event_archive.html:58 templates/events/event_list.html:35
|
#: templates/events/event_archive.html:58 templates/events/event_list.html:35
|
||||||
#: templates/events/photo_upload.html:25 templates/events/photo_upload.html:26
|
|
||||||
msgid "Comments"
|
msgid "Comments"
|
||||||
msgstr "Kommentare"
|
msgstr "Kommentare"
|
||||||
|
|
||||||
#: templates/events/event_archive.html:59
|
#: templates/events/event_archive.html:59 templates/events/event_detail.html:36
|
||||||
#: templates/events/event_detail.html:36 templates/events/event_detail.html:48
|
#: templates/events/event_detail.html:48 templates/events/photo_list.html:4
|
||||||
#: templates/events/photo_list.html:4 templates/events/photo_upload.html:28
|
|
||||||
#: templates/events/photo_upload.html:29
|
|
||||||
msgid "Photos"
|
msgid "Photos"
|
||||||
msgstr "Fotos"
|
msgstr "Fotos"
|
||||||
|
|
||||||
#: templates/events/event_archive.html:60
|
#: templates/events/event_archive.html:60
|
||||||
#: templates/events/event_archive.html:61
|
#: templates/events/event_archive.html:61 templates/events/event_detail.html:35
|
||||||
#: templates/events/event_detail.html:35 templates/events/event_detail.html:51
|
#: templates/events/event_detail.html:51
|
||||||
msgid "Hanchans"
|
msgid "Hanchans"
|
||||||
msgstr "Hanchans"
|
msgstr "Hanchans"
|
||||||
|
|
||||||
@@ -246,7 +238,7 @@ msgid "Show on Google Maps"
|
|||||||
msgstr "Auf Google Maps zeigen"
|
msgstr "Auf Google Maps zeigen"
|
||||||
|
|
||||||
#: templates/events/event_detail.html:127 templates/events/event_form.html:9
|
#: templates/events/event_detail.html:127 templates/events/event_form.html:9
|
||||||
#: views.py:62
|
#: views.py:61
|
||||||
msgid "Edit Event"
|
msgid "Edit Event"
|
||||||
msgstr "Termin bearbeiten"
|
msgstr "Termin bearbeiten"
|
||||||
|
|
||||||
@@ -254,7 +246,7 @@ msgstr "Termin bearbeiten"
|
|||||||
msgid "Add Dates"
|
msgid "Add Dates"
|
||||||
msgstr "Termine hinzufügen"
|
msgstr "Termine hinzufügen"
|
||||||
|
|
||||||
#: templates/events/event_form.html:9 templates/events/page.html:9 views.py:64
|
#: templates/events/event_form.html:9 templates/events/page.html:9 views.py:61
|
||||||
msgid "Add Event"
|
msgid "Add Event"
|
||||||
msgstr "Neuer Termin"
|
msgstr "Neuer Termin"
|
||||||
|
|
||||||
@@ -296,24 +288,23 @@ msgstr "Fotograf"
|
|||||||
msgid "share on"
|
msgid "share on"
|
||||||
msgstr "Teile auf"
|
msgstr "Teile auf"
|
||||||
|
|
||||||
#: templates/events/photo_detail.html:81
|
#: templates/events/photo_detail.html:78
|
||||||
msgid "download"
|
msgid "download"
|
||||||
msgstr "Herunterladen"
|
msgstr "Herunterladen"
|
||||||
|
|
||||||
#: 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"
|
||||||
|
|
||||||
#: 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"
|
||||||
|
|
||||||
#: templates/events/photo_detail.html:84
|
#: templates/events/photo_detail.html:81
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "Speichern"
|
msgstr "Speichern"
|
||||||
|
|
||||||
#: templates/events/photo_list.html:36 templates/events/photo_upload.html:35
|
#: templates/events/photo_list.html:36 templates/events/photo_upload.html:13
|
||||||
#: templates/events/photo_upload.html:49
|
|
||||||
msgid "Upload"
|
msgid "Upload"
|
||||||
msgstr "Hochladen"
|
msgstr "Hochladen"
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models, migrations
|
|
||||||
import events.models
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
from django.db import models, migrations
|
||||||
|
|
||||||
|
import events.models
|
||||||
import utils
|
import utils
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -17,7 +17,8 @@ class Migration(migrations.Migration):
|
|||||||
name='Event',
|
name='Event',
|
||||||
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)),
|
||||||
('name', models.CharField(max_length=255, verbose_name='Name')),
|
('name', models.CharField(max_length=255, verbose_name='Name')),
|
||||||
('description', models.TextField(
|
('description', models.TextField(
|
||||||
verbose_name='Beschreibung', blank=True)),
|
verbose_name='Beschreibung', blank=True)),
|
||||||
@@ -26,13 +27,25 @@ class Migration(migrations.Migration):
|
|||||||
null=True, verbose_name='Ende', blank=True)),
|
null=True, verbose_name='Ende', blank=True)),
|
||||||
('url', models.URLField(verbose_name='Homepage', blank=True)),
|
('url', models.URLField(verbose_name='Homepage', blank=True)),
|
||||||
('image', models.ImageField(storage=utils.OverwriteStorage(
|
('image', models.ImageField(storage=utils.OverwriteStorage(
|
||||||
), upload_to=events.models.get_upload_path, null=True, verbose_name='Bild', blank=True)),
|
), upload_to=events.models.get_upload_path, null=True,
|
||||||
('is_tournament', models.BooleanField(default=False,
|
verbose_name='Bild', blank=True)),
|
||||||
help_text='Diese Veranstaltung ist ein Turnier, es gelten andere Regeln f\xfcr das Kyu Ranking.', verbose_name='Turnier')),
|
('is_tournament', models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text='Diese Veranstaltung ist ein Turnier, ' +
|
||||||
|
'es gelten andere Regeln f\xfcr das Kyu Ranking.',
|
||||||
|
verbose_name='Turnier')),
|
||||||
('photo_count', models.PositiveIntegerField(
|
('photo_count', models.PositiveIntegerField(
|
||||||
default=0, editable=False)),
|
default=0, editable=False)),
|
||||||
('event_series', models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, editable=False, to='events.Event', blank=True,
|
('event_series',
|
||||||
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')),
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
editable=False, to='events.Event',
|
||||||
|
blank=True,
|
||||||
|
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')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ('-start', '-end'),
|
'ordering': ('-start', '-end'),
|
||||||
@@ -44,20 +57,273 @@ class Migration(migrations.Migration):
|
|||||||
name='Location',
|
name='Location',
|
||||||
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)),
|
||||||
('name', models.CharField(max_length=200, verbose_name='Name')),
|
('name', models.CharField(max_length=200, verbose_name='Name')),
|
||||||
('description', models.TextField(
|
('description', models.TextField(
|
||||||
verbose_name='Beschreibung', blank=True)),
|
verbose_name='Beschreibung', blank=True)),
|
||||||
('image', models.ImageField(storage=utils.OverwriteStorage(
|
('image', models.ImageField(storage=utils.OverwriteStorage(
|
||||||
), upload_to=events.models.get_upload_path, null=True, verbose_name='Bild', blank=True)),
|
), upload_to=events.models.get_upload_path, null=True,
|
||||||
|
verbose_name='Bild', blank=True)),
|
||||||
('url', models.URLField(verbose_name='Homepage', blank=True)),
|
('url', models.URLField(verbose_name='Homepage', blank=True)),
|
||||||
('postal_code', models.CharField(
|
('postal_code', models.CharField(
|
||||||
max_length=6, verbose_name='Postleitzahl')),
|
max_length=6, verbose_name='Postleitzahl')),
|
||||||
('street_address', models.CharField(
|
('street_address', models.CharField(
|
||||||
max_length=127, verbose_name='Stra\xdfe')),
|
max_length=127, verbose_name='Stra\xdfe')),
|
||||||
('locality', models.CharField(max_length=127, verbose_name='Ort')),
|
('locality',
|
||||||
('country', models.CharField(max_length=2, verbose_name='Land', choices=[(b'GB', 'Vereinigtes K\xf6nigreich'), (b'AF', 'Afghanistan'), (b'AX', 'Aland Islands'), (b'AL', 'Albanien'), (b'DZ', 'Algerien'), (b'AS', 'Amerikanisch-Samoa'), (b'AD', 'Andorra'), (b'AO', 'Angola'), (b'AI', 'Anguilla'), (b'AQ', 'Antarktika'), (b'AG', 'Antigua und Barbuda'), (b'AR', 'Argentinien'), (b'AM', 'Armenien'), (b'AW', 'Aruba'), (b'AU', 'Australien'), (b'AT', '\xd6sterreich'), (b'AZ', 'Aserbaidschan'), (b'BS', 'Bahamas'), (b'BH', 'Bahrein'), (b'BD', 'Bangladesch'), (b'BB', 'Barbados'), (b'BY', 'Wei\xdfrussland'), (b'BE', 'Belgien'), (b'BZ', 'Belize'), (b'BJ', 'Benin'), (b'BM', 'Bermuda'), (b'BT', 'Bhutan'), (b'BO', 'Bolivien'), (b'BA', 'Bosnien und Herzegowina'), (b'BW', 'Botswana'), (b'BV', 'Bouvet Island'), (b'BR', 'Brasilien'), (b'IO', 'British Indian Ocean Territory'), (b'BN', 'Brunei Darussalam'), (b'BG', 'Bulgarien'), (b'BF', 'Burkina Faso'), (b'BI', 'Burundi'), (b'KH', 'Kambodscha'), (b'CM', 'Kamerun'), (b'CA', 'Kanada'), (b'CV', 'Cape Verde'), (b'KY', 'Cayman Islands'), (b'CF', 'Zentralafrikanische Republik'), (b'TD', 'Tschad'), (b'CL', 'Chile'), (b'CN', 'China'), (b'CX', 'Christmas Island'), (b'CC', 'Cocos (Keeling) Islands'), (b'CO', 'Kolumbien'), (b'KM', 'Komoren'), (b'CG', 'Kongo'), (b'CD', 'Kongo, Demokratische Republik'), (b'CK', 'Cook-Inseln'), (b'CR', 'Costa Rica'), (b'CI', "Cote d'Ivoire"), (b'HR', 'Kroatien'), (b'CU', 'Kuba'), (b'CY', 'Zypern'), (b'CZ', 'Tschechische Republik'), (b'DK', 'D\xe4nemark'), (b'DJ', 'Dschibuti'), (b'DM', 'Dominica'), (b'DO', 'Dominikanische Republik'), (b'EC', 'Ecuador'), (b'EG', '\xc4gypten'), (b'SV', 'El Salvador'), (b'GQ', '\xc4quatorial-Guinea'), (b'ER', 'Eritrea'), (b'EE', 'Estland'), (b'ET', '\xc4thiopien'), (b'FK', 'Falklandinseln (Malvinas)'), (b'FO', 'F\xe4r\xf6er-Inseln'), (b'FJ', 'Fidschi'), (b'FI', 'Finnland'), (b'FR', 'Frankreich'), (b'GF', 'Franz\xf6sisch-Guayana'), (b'PF', 'Franz\xf6sisch-Polynesien'), (b'TF', 'Franz\xf6sisch S\xfcdliche Territorien'), (b'GA', 'Gabun'), (b'GM', 'Gambia'), (b'GE', 'Georgia'), (b'DE', 'Deutschland'), (b'GH', 'Ghana'), (b'GI', 'Gibraltar'), (b'GR', 'Griechenland'), (b'GL', 'Gr\xf6nland'), (b'GD', 'Grenada'), (b'GP', 'Guadeloupe'), (b'GU', 'Guam'), (b'GT', 'Guatemala'), (b'GG', 'Guernsey'), (b'GN', 'Guinea'), (b'GW', 'Guinea-Bissau'), (b'GY', 'Guyana'), (b'HT', 'Haiti'), (b'HM', 'Heard und McDonald Inseln'), (b'VA', 'Heiliger Stuhl (Vatikanstadt)'), (b'HN', 'Honduras'), (b'HK', 'Hongkong'), (b'HU', 'Ungarn'), (b'IS', 'Island'), (b'IN', 'Indien'), (b'ID', 'Indonesien'), (b'IR', 'Iran, Islamische Republik'), (b'IQ', 'Irak'), (b'IE', 'Irland'), (b'IM', 'Isle of Man'), (b'IL', 'Israel'), (b'IT', 'Italien'), (b'JM', 'Jamaika'), (b'JP', 'Japan'), (b'JE', 'Jersey'), (b'JO', 'Jordan'), (b'KZ', 'Kasachstan'), (b'KE', 'Kenia'), (b'KI', 'Kiribati'), (b'KP', 'Korea, Demokratische Volksrepublik'), (b'KR', 'Korea, Republik'), (b'KW', 'Kuwait'), (b'KG', 'Kirgisistan'), (b'LA', 'Lao Demokratischen Volksrepublik'), (b'LV', 'Lettland'), (b'LB', 'Libanon'), (
|
models.CharField(max_length=127, verbose_name='Ort')),
|
||||||
b'LS', 'Lesotho'), (b'LR', 'Liberia'), (b'LY', 'Libyen'), (b'LI', 'Liechtenstein'), (b'LT', 'Litauen'), (b'LU', 'Luxemburg'), (b'MO', 'Macao'), (b'MK', 'Mazedonien, die ehemalige jugoslawische Republik'), (b'MG', 'Madagaskar'), (b'MW', 'Malawi'), (b'MY', 'Malaysia'), (b'MV', 'Malediven'), (b'ML', 'Mali'), (b'MT', 'Malta'), (b'MH', 'Marshall Islands'), (b'MQ', 'Martinique'), (b'MR', 'Mauretanien'), (b'MU', 'Mauritius'), (b'YT', 'Mayotte'), (b'MX', 'Mexiko'), (b'FM', 'Mikronesien, F\xf6derierte Staaten von'), (b'MD', 'Moldawien'), (b'MC', 'Monaco'), (b'MN', 'Mongolei'), (b'ME', 'Montenegro'), (b'MS', 'Montserrat'), (b'MA', 'Marokko'), (b'MZ', 'Mosambik'), (b'MM', 'Myanmar'), (b'NA', 'Namibia'), (b'NR', 'Nauru'), (b'NP', 'Nepal'), (b'NL', 'Niederlande'), (b'AN', 'Niederl\xe4ndische Antillen'), (b'NC', 'Neukaledonien'), (b'NZ', 'New Zealand'), (b'NI', 'Nicaragua'), (b'NE', 'Niger'), (b'NG', 'Nigeria'), (b'NU', 'Niue'), (b'NF', 'Norfolk Island'), (b'MP', 'Northern Mariana Islands'), (b'NO', 'Norwegen'), (b'OM', 'Oman'), (b'PK', 'Pakistan'), (b'PW', 'Palau'), (b'PS', 'Pal\xe4stinensische Autonomiegebiete'), (b'PA', 'Panama'), (b'PG', 'Papua-Neuguinea'), (b'PY', 'Paraguay'), (b'PE', 'Peru'), (b'PH', 'Philippinen'), (b'PN', 'Pitcairn'), (b'PL', 'Polen'), (b'PT', 'Portugal'), (b'PR', 'Puerto Rico'), (b'QA', 'Katar'), (b'RE', 'Wiedervereinigung'), (b'RO', 'Rum\xe4nien'), (b'RU', 'Russischen F\xf6deration'), (b'RW', 'Ruanda'), (b'BL', 'Saint Barthelemy'), (b'SH', 'Saint Helena'), (b'KN', 'Saint Kitts und Nevis'), (b'LC', 'Santa Lucia'), (b'MF', 'Santa Martin'), (b'PM', 'Saint Pierre und Miquelon'), (b'VC', 'Saint Vincent und die Grenadinen'), (b'WS', 'Samoa'), (b'SM', 'San Marino'), (b'ST', 'Sao Tome und Principe'), (b'SA', 'Saudi-Arabien'), (b'SN', 'Senegal'), (b'RS', 'Serbien'), (b'SC', 'Seychellen'), (b'SL', 'Sierra Leone'), (b'SG', 'Singapur'), (b'SK', 'Slowakei'), (b'SI', 'Slowenien'), (b'SB', 'Salomon-Inseln'), (b'SO', 'Somalia'), (b'ZA', 'S\xfcdafrika'), (b'GS', 'S\xfcdgeorgien und die S\xfcdlichen Sandwichinseln'), (b'ES', 'Spanien'), (b'LK', 'Sri Lanka'), (b'SD', 'Sudan'), (b'SR', 'Suriname'), (b'SJ', 'Svalbard und Jan Mayen'), (b'SZ', 'Swaziland'), (b'SE', 'Schweden'), (b'CH', 'Schweiz'), (b'SY', 'Arabische Republik Syrien'), (b'TW', 'Taiwan, Province of China'), (b'TJ', 'Tadschikistan'), (b'TZ', 'Tansania, Vereinigte Republik'), (b'TH', 'Thailand'), (b'TL', 'Timor-Leste'), (b'TG', 'Togo'), (b'TK', 'Tokelau'), (b'TO', 'Tonga'), (b'TT', 'Trinidad und Tobago'), (b'TN', 'Tunesien'), (b'TR', 'T\xfcrkei'), (b'TM', 'Turkmenistan'), (b'TC', 'Turks-und Caicosinseln'), (b'TV', 'Tuvalu'), (b'UG', 'Uganda'), (b'UA', 'Ukraine'), (b'AE', 'Vereinigte Arabische Emirate'), (b'US', 'Vereinigte Staaten'), (b'UM', 'United States Minor Outlying Islands'), (b'UY', 'Uruguay'), (b'UZ', 'Usbekistan'), (b'VU', 'Vanuatu'), (b'VE', 'Venezuela'), (b'VN', 'Vietnam'), (b'VG', 'Virgin Islands, British'), (b'VI', 'Virgin Islands, US'), (b'WF', 'Wallis und Futuna'), (b'EH', 'Westsahara'), (b'YE', 'Jemen'), (b'ZM', 'Sambia'), (b'ZW', 'Zimbabwe')])),
|
('country', models.CharField(
|
||||||
|
max_length=2, verbose_name='Land',
|
||||||
|
choices=[(b'GB', 'Vereinigtes K\xf6nigreich'),
|
||||||
|
(b'AF', 'Afghanistan'),
|
||||||
|
(b'AX', 'Aland Islands'),
|
||||||
|
(b'AL', 'Albanien'),
|
||||||
|
(b'DZ', 'Algerien'),
|
||||||
|
(b'AS', 'Amerikanisch-Samoa'),
|
||||||
|
(b'AD', 'Andorra'),
|
||||||
|
(b'AO', 'Angola'),
|
||||||
|
(b'AI', 'Anguilla'),
|
||||||
|
(b'AQ', 'Antarktika'),
|
||||||
|
(b'AG', 'Antigua und Barbuda'),
|
||||||
|
(b'AR', 'Argentinien'),
|
||||||
|
(b'AM', 'Armenien'),
|
||||||
|
(b'AW', 'Aruba'),
|
||||||
|
(b'AU', 'Australien'),
|
||||||
|
(b'AT', '\xd6sterreich'),
|
||||||
|
(b'AZ', 'Aserbaidschan'),
|
||||||
|
(b'BS', 'Bahamas'),
|
||||||
|
(b'BH', 'Bahrein'),
|
||||||
|
(b'BD', 'Bangladesch'),
|
||||||
|
(b'BB', 'Barbados'),
|
||||||
|
(b'BY', 'Wei\xdfrussland'),
|
||||||
|
(b'BE', 'Belgien'),
|
||||||
|
(b'BZ', 'Belize'),
|
||||||
|
(b'BJ', 'Benin'),
|
||||||
|
(b'BM', 'Bermuda'),
|
||||||
|
(b'BT', 'Bhutan'),
|
||||||
|
(b'BO', 'Bolivien'),
|
||||||
|
(b'BA', 'Bosnien und Herzegowina'),
|
||||||
|
(b'BW', 'Botswana'),
|
||||||
|
(b'BV', 'Bouvet Island'),
|
||||||
|
(b'BR', 'Brasilien'),
|
||||||
|
(b'IO', 'British Indian Ocean Territory'),
|
||||||
|
(b'BN', 'Brunei Darussalam'),
|
||||||
|
(b'BG', 'Bulgarien'),
|
||||||
|
(b'BF', 'Burkina Faso'),
|
||||||
|
(b'BI', 'Burundi'),
|
||||||
|
(b'KH', 'Kambodscha'),
|
||||||
|
(b'CM', 'Kamerun'),
|
||||||
|
(b'CA', 'Kanada'),
|
||||||
|
(b'CV', 'Cape Verde'),
|
||||||
|
(b'KY', 'Cayman Islands'),
|
||||||
|
(b'CF', 'Zentralafrikanische Republik'),
|
||||||
|
(b'TD', 'Tschad'),
|
||||||
|
(b'CL', 'Chile'),
|
||||||
|
(b'CN', 'China'),
|
||||||
|
(b'CX', 'Christmas Island'),
|
||||||
|
(b'CC', 'Cocos (Keeling) Islands'),
|
||||||
|
(b'CO', 'Kolumbien'),
|
||||||
|
(b'KM', 'Komoren'),
|
||||||
|
(b'CG', 'Kongo'),
|
||||||
|
(b'CD', 'Kongo, Demokratische Republik'),
|
||||||
|
(b'CK', 'Cook-Inseln'),
|
||||||
|
(b'CR', 'Costa Rica'),
|
||||||
|
(b'CI', "Cote d'Ivoire"),
|
||||||
|
(b'HR', 'Kroatien'),
|
||||||
|
(b'CU', 'Kuba'),
|
||||||
|
(b'CY', 'Zypern'),
|
||||||
|
(b'CZ', 'Tschechische Republik'),
|
||||||
|
(b'DK', 'D\xe4nemark'),
|
||||||
|
(b'DJ', 'Dschibuti'),
|
||||||
|
(b'DM', 'Dominica'),
|
||||||
|
(b'DO', 'Dominikanische Republik'),
|
||||||
|
(b'EC', 'Ecuador'),
|
||||||
|
(b'EG', '\xc4gypten'),
|
||||||
|
(b'SV', 'El Salvador'),
|
||||||
|
(b'GQ', '\xc4quatorial-Guinea'),
|
||||||
|
(b'ER', 'Eritrea'),
|
||||||
|
(b'EE', 'Estland'),
|
||||||
|
(b'ET', '\xc4thiopien'),
|
||||||
|
(b'FK', 'Falklandinseln (Malvinas)'),
|
||||||
|
(b'FO', 'F\xe4r\xf6er-Inseln'),
|
||||||
|
(b'FJ', 'Fidschi'),
|
||||||
|
(b'FI', 'Finnland'),
|
||||||
|
(b'FR', 'Frankreich'),
|
||||||
|
(b'GF', 'Franz\xf6sisch-Guayana'),
|
||||||
|
(b'PF', 'Franz\xf6sisch-Polynesien'),
|
||||||
|
(b'TF', 'Franz\xf6sisch S\xfcdliche Territorien'),
|
||||||
|
(b'GA', 'Gabun'),
|
||||||
|
(b'GM', 'Gambia'),
|
||||||
|
(b'GE', 'Georgia'),
|
||||||
|
(b'DE', 'Deutschland'),
|
||||||
|
(b'GH', 'Ghana'),
|
||||||
|
(b'GI', 'Gibraltar'),
|
||||||
|
(b'GR', 'Griechenland'),
|
||||||
|
(b'GL', 'Gr\xf6nland'),
|
||||||
|
(b'GD', 'Grenada'),
|
||||||
|
(b'GP', 'Guadeloupe'),
|
||||||
|
(b'GU', 'Guam'),
|
||||||
|
(b'GT', 'Guatemala'),
|
||||||
|
(b'GG', 'Guernsey'),
|
||||||
|
(b'GN', 'Guinea'),
|
||||||
|
(b'GW', 'Guinea-Bissau'),
|
||||||
|
(b'GY', 'Guyana'),
|
||||||
|
(b'HT', 'Haiti'),
|
||||||
|
(b'HM', 'Heard und McDonald Inseln'),
|
||||||
|
(b'VA', 'Heiliger Stuhl (Vatikanstadt)'),
|
||||||
|
(b'HN', 'Honduras'),
|
||||||
|
(b'HK', 'Hongkong'),
|
||||||
|
(b'HU', 'Ungarn'),
|
||||||
|
(b'IS', 'Island'),
|
||||||
|
(b'IN', 'Indien'),
|
||||||
|
(b'ID', 'Indonesien'),
|
||||||
|
(b'IR', 'Iran, Islamische Republik'),
|
||||||
|
(b'IQ', 'Irak'),
|
||||||
|
(b'IE', 'Irland'),
|
||||||
|
(b'IM', 'Isle of Man'),
|
||||||
|
(b'IL', 'Israel'),
|
||||||
|
(b'IT', 'Italien'),
|
||||||
|
(b'JM', 'Jamaika'),
|
||||||
|
(b'JP', 'Japan'),
|
||||||
|
(b'JE', 'Jersey'),
|
||||||
|
(b'JO', 'Jordan'),
|
||||||
|
(b'KZ', 'Kasachstan'),
|
||||||
|
(b'KE', 'Kenia'),
|
||||||
|
(b'KI', 'Kiribati'),
|
||||||
|
(b'KP', 'Korea, Demokratische Volksrepublik'),
|
||||||
|
(b'KR', 'Korea, Republik'),
|
||||||
|
(b'KW', 'Kuwait'),
|
||||||
|
(b'KG', 'Kirgisistan'),
|
||||||
|
(b'LA', 'Lao Demokratischen Volksrepublik'),
|
||||||
|
(b'LV', 'Lettland'),
|
||||||
|
(b'LB', 'Libanon'),
|
||||||
|
(b'LS', 'Lesotho'),
|
||||||
|
(b'LR', 'Liberia'),
|
||||||
|
(b'LY', 'Libyen'),
|
||||||
|
(b'LI', 'Liechtenstein'),
|
||||||
|
(b'LT', 'Litauen'),
|
||||||
|
(b'LU', 'Luxemburg'),
|
||||||
|
(b'MO', 'Macao'),
|
||||||
|
(b'MK',
|
||||||
|
'Mazedonien, die ehemalige jugoslawische Republik'),
|
||||||
|
(b'MG', 'Madagaskar'),
|
||||||
|
(b'MW', 'Malawi'),
|
||||||
|
(b'MY', 'Malaysia'),
|
||||||
|
(b'MV', 'Malediven'),
|
||||||
|
(b'ML', 'Mali'),
|
||||||
|
(b'MT', 'Malta'),
|
||||||
|
(b'MH', 'Marshall Islands'),
|
||||||
|
(b'MQ', 'Martinique'),
|
||||||
|
(b'MR', 'Mauretanien'),
|
||||||
|
(b'MU', 'Mauritius'),
|
||||||
|
(b'YT', 'Mayotte'),
|
||||||
|
(b'MX', 'Mexiko'),
|
||||||
|
(b'FM', 'Mikronesien, F\xf6derierte Staaten von'),
|
||||||
|
(b'MD', 'Moldawien'),
|
||||||
|
(b'MC', 'Monaco'),
|
||||||
|
(b'MN', 'Mongolei'),
|
||||||
|
(b'ME', 'Montenegro'),
|
||||||
|
(b'MS', 'Montserrat'),
|
||||||
|
(b'MA', 'Marokko'),
|
||||||
|
(b'MZ', 'Mosambik'),
|
||||||
|
(b'MM', 'Myanmar'),
|
||||||
|
(b'NA', 'Namibia'),
|
||||||
|
(b'NR', 'Nauru'),
|
||||||
|
(b'NP', 'Nepal'),
|
||||||
|
(b'NL', 'Niederlande'),
|
||||||
|
(b'AN', 'Niederl\xe4ndische Antillen'),
|
||||||
|
(b'NC', 'Neukaledonien'),
|
||||||
|
(b'NZ', 'New Zealand'),
|
||||||
|
(b'NI', 'Nicaragua'),
|
||||||
|
(b'NE', 'Niger'),
|
||||||
|
(b'NG', 'Nigeria'),
|
||||||
|
(b'NU', 'Niue'),
|
||||||
|
(b'NF', 'Norfolk Island'),
|
||||||
|
(b'MP',
|
||||||
|
'Northern Mariana Islands'),
|
||||||
|
(b'NO', 'Norwegen'),
|
||||||
|
(b'OM', 'Oman'),
|
||||||
|
(b'PK', 'Pakistan'),
|
||||||
|
(b'PW', 'Palau'),
|
||||||
|
(b'PS', 'Pal\xe4stinensische Autonomiegebiete'),
|
||||||
|
(b'PA', 'Panama'),
|
||||||
|
(b'PG', 'Papua-Neuguinea'),
|
||||||
|
(b'PY', 'Paraguay'),
|
||||||
|
(b'PE', 'Peru'),
|
||||||
|
(b'PH', 'Philippinen'),
|
||||||
|
(b'PN', 'Pitcairn'),
|
||||||
|
(b'PL', 'Polen'),
|
||||||
|
(b'PT', 'Portugal'),
|
||||||
|
(b'PR', 'Puerto Rico'),
|
||||||
|
(b'QA', 'Katar'),
|
||||||
|
(b'RE', 'Wiedervereinigung'),
|
||||||
|
(b'RO', 'Rum\xe4nien'),
|
||||||
|
(b'RU', 'Russischen F\xf6deration'),
|
||||||
|
(b'RW', 'Ruanda'),
|
||||||
|
(b'BL', 'Saint Barthelemy'),
|
||||||
|
(b'SH', 'Saint Helena'),
|
||||||
|
(b'KN', 'Saint Kitts und Nevis'),
|
||||||
|
(b'LC', 'Santa Lucia'),
|
||||||
|
(b'MF', 'Santa Martin'),
|
||||||
|
(b'PM', 'Saint Pierre und Miquelon'),
|
||||||
|
(b'VC', 'Saint Vincent und die Grenadinen'),
|
||||||
|
(b'WS', 'Samoa'),
|
||||||
|
(b'SM', 'San Marino'),
|
||||||
|
(b'ST', 'Sao Tome und Principe'),
|
||||||
|
(b'SA', 'Saudi-Arabien'),
|
||||||
|
(b'SN', 'Senegal'),
|
||||||
|
(b'RS', 'Serbien'),
|
||||||
|
(b'SC', 'Seychellen'),
|
||||||
|
(b'SL', 'Sierra Leone'),
|
||||||
|
(b'SG', 'Singapur'),
|
||||||
|
(b'SK', 'Slowakei'),
|
||||||
|
(b'SI', 'Slowenien'),
|
||||||
|
(b'SB', 'Salomon-Inseln'),
|
||||||
|
(b'SO', 'Somalia'),
|
||||||
|
(b'ZA', 'S\xfcdafrika'),
|
||||||
|
(b'GS',
|
||||||
|
'S\xfcdgeorgien und die S\xfcdlichen Sandwichinseln'),
|
||||||
|
(b'ES', 'Spanien'),
|
||||||
|
(b'LK', 'Sri Lanka'),
|
||||||
|
(b'SD', 'Sudan'),
|
||||||
|
(b'SR', 'Suriname'),
|
||||||
|
(b'SJ', 'Svalbard und Jan Mayen'),
|
||||||
|
(b'SZ', 'Swaziland'),
|
||||||
|
(b'SE', 'Schweden'),
|
||||||
|
(b'CH', 'Schweiz'),
|
||||||
|
(b'SY', 'Arabische Republik Syrien'),
|
||||||
|
(b'TW',
|
||||||
|
'Taiwan, Province of China'),
|
||||||
|
(b'TJ', 'Tadschikistan'),
|
||||||
|
(b'TZ', 'Tansania, Vereinigte Republik'),
|
||||||
|
(b'TH', 'Thailand'),
|
||||||
|
(b'TL', 'Timor-Leste'),
|
||||||
|
(b'TG', 'Togo'),
|
||||||
|
(b'TK', 'Tokelau'),
|
||||||
|
(b'TO', 'Tonga'),
|
||||||
|
(b'TT', 'Trinidad und Tobago'),
|
||||||
|
(b'TN', 'Tunesien'),
|
||||||
|
(b'TR', 'T\xfcrkei'),
|
||||||
|
(b'TM', 'Turkmenistan'),
|
||||||
|
(b'TC', 'Turks-und Caicosinseln'),
|
||||||
|
(b'TV', 'Tuvalu'),
|
||||||
|
(b'UG', 'Uganda'),
|
||||||
|
(b'UA', 'Ukraine'),
|
||||||
|
(b'AE', 'Vereinigte Arabische Emirate'),
|
||||||
|
(b'US', 'Vereinigte Staaten'),
|
||||||
|
(b'UM', 'United States Minor Outlying Islands'),
|
||||||
|
(b'UY', 'Uruguay'),
|
||||||
|
(b'UZ', 'Usbekistan'),
|
||||||
|
(b'VU', 'Vanuatu'),
|
||||||
|
(b'VE', 'Venezuela'),
|
||||||
|
(b'VN', 'Vietnam'),
|
||||||
|
(b'VG', 'Virgin Islands, British'),
|
||||||
|
(b'VI', 'Virgin Islands, US'),
|
||||||
|
(b'WF', 'Wallis und Futuna'),
|
||||||
|
(b'EH', 'Westsahara'),
|
||||||
|
(b'YE', 'Jemen'),
|
||||||
|
(b'ZM', 'Sambia'),
|
||||||
|
(b'ZW', 'Zimbabwe')])),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Veranstaltungsort',
|
'verbose_name': 'Veranstaltungsort',
|
||||||
@@ -67,6 +333,8 @@ class Migration(migrations.Migration):
|
|||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='event',
|
model_name='event',
|
||||||
name='location',
|
name='location',
|
||||||
field=models.ForeignKey(to='events.Location'),
|
field=models.ForeignKey(
|
||||||
|
to='events.Location',
|
||||||
|
on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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'),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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'),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models, migrations
|
|
||||||
import ckeditor.fields
|
import ckeditor.fields
|
||||||
import events.models
|
|
||||||
import easy_thumbnails.fields
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import utils
|
import easy_thumbnails.fields
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.db import models, migrations
|
||||||
|
|
||||||
|
import events.models
|
||||||
|
import utils
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
('events', '0003_auto_20150823_2232'),
|
('events', '0003_auto_20150823_2232'),
|
||||||
@@ -22,18 +22,26 @@ class Migration(migrations.Migration):
|
|||||||
name='Photo',
|
name='Photo',
|
||||||
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)),
|
||||||
('name', models.CharField(max_length=100,
|
('name', models.CharField(max_length=100,
|
||||||
verbose_name='Name', blank=True)),
|
verbose_name='Name', blank=True)),
|
||||||
('image', easy_thumbnails.fields.ThumbnailerImageField(
|
('image', easy_thumbnails.fields.ThumbnailerImageField(
|
||||||
upload_to=events.models.get_upload_path, storage=utils.OverwriteStorage(), verbose_name='Bild')),
|
upload_to=events.models.get_upload_path,
|
||||||
|
storage=utils.OverwriteStorage(), verbose_name='Bild')),
|
||||||
('description', models.TextField(max_length=300,
|
('description', models.TextField(max_length=300,
|
||||||
verbose_name='Beschreibung', blank=True)),
|
verbose_name='Beschreibung',
|
||||||
('on_startpage', models.BooleanField(default=False,
|
blank=True)),
|
||||||
help_text='Display this Photo on the Startpage Teaser', verbose_name='Startpage')),
|
('on_startpage', models.BooleanField(
|
||||||
('created_date', models.DateTimeField(verbose_name='Published on')),
|
default=False,
|
||||||
('views', models.PositiveIntegerField(default=0,
|
help_text='Display this Photo on the Startpage Teaser',
|
||||||
verbose_name='Number of views', editable=False)),
|
verbose_name='Startpage')),
|
||||||
|
('created_date',
|
||||||
|
models.DateTimeField(verbose_name='Published on')),
|
||||||
|
('views', models.PositiveIntegerField(
|
||||||
|
default=0,
|
||||||
|
verbose_name='Number of views',
|
||||||
|
editable=False)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ['created_date'],
|
'ordering': ['created_date'],
|
||||||
@@ -46,7 +54,8 @@ class Migration(migrations.Migration):
|
|||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='event',
|
name='event',
|
||||||
options={'ordering': (
|
options={'ordering': (
|
||||||
'start', 'end'), 'verbose_name': 'Termin', 'verbose_name_plural': 'Termine'},
|
'start', 'end'), 'verbose_name': 'Termin',
|
||||||
|
'verbose_name_plural': 'Termine'},
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='event',
|
model_name='event',
|
||||||
@@ -57,14 +66,21 @@ class Migration(migrations.Migration):
|
|||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='event',
|
model_name='event',
|
||||||
name='event_series',
|
name='event_series',
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, blank=True, to='events.Event',
|
field=models.ForeignKey(
|
||||||
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'),
|
on_delete=django.db.models.deletion.SET_NULL, blank=True,
|
||||||
|
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.',
|
||||||
|
null=True, verbose_name='Veranstaltungsreihen'),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='event',
|
model_name='event',
|
||||||
name='image',
|
name='image',
|
||||||
field=easy_thumbnails.fields.ThumbnailerImageField(storage=utils.OverwriteStorage(
|
field=easy_thumbnails.fields.ThumbnailerImageField(
|
||||||
), upload_to=events.models.get_upload_path, null=True, verbose_name='Bild', blank=True),
|
storage=utils.OverwriteStorage(
|
||||||
|
), upload_to=events.models.get_upload_path, null=True,
|
||||||
|
verbose_name='Bild', blank=True),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='location',
|
model_name='location',
|
||||||
@@ -75,17 +91,21 @@ class Migration(migrations.Migration):
|
|||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='location',
|
model_name='location',
|
||||||
name='image',
|
name='image',
|
||||||
field=easy_thumbnails.fields.ThumbnailerImageField(storage=utils.OverwriteStorage(
|
field=easy_thumbnails.fields.ThumbnailerImageField(
|
||||||
), upload_to=events.models.get_upload_path, null=True, verbose_name='Bild', blank=True),
|
storage=utils.OverwriteStorage(
|
||||||
|
), upload_to=events.models.get_upload_path, null=True,
|
||||||
|
verbose_name='Bild', blank=True),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='photo',
|
model_name='photo',
|
||||||
name='event',
|
name='event',
|
||||||
field=models.ForeignKey(to='events.Event'),
|
field=models.ForeignKey(
|
||||||
|
to='events.Event', on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='photo',
|
model_name='photo',
|
||||||
name='photographer',
|
name='photographer',
|
||||||
field=models.ForeignKey(to=settings.AUTH_USER_MODEL),
|
field=models.ForeignKey(
|
||||||
|
to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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'),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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
34
src/events/migrations/0009_auto_20171214_1215.py
Normal file
34
src/events/migrations/0009_auto_20171214_1215.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.8 on 2017-12-14 11:15
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
('events', '0008_auto_20171115_0653'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='event',
|
||||||
|
name='location',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
to='events.Location'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='photo',
|
||||||
|
name='event',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
to='events.Event'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='photo',
|
||||||
|
name='photographer',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
||||||
158
src/events/migrations/0010_auto_20190106_1954.py
Normal file
158
src/events/migrations/0010_auto_20190106_1954.py
Normal 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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
"""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.utils.translation import gettext as _
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
|
|
||||||
@@ -29,17 +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') and 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:
|
||||||
|
raise ImproperlyConfigured("No Event in Context!")
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
@@ -49,10 +55,33 @@ class EventDetailMixin(object):
|
|||||||
:return: a django QuerySets
|
:return: a django QuerySets
|
||||||
"""
|
"""
|
||||||
if self.model == models.Event:
|
if self.model == models.Event:
|
||||||
return models.Event.objects.all()
|
self.event = get_object_or_404(models.Event, pk=self.kwargs['pk'])
|
||||||
try:
|
queryset = self.model._default_manager.all()
|
||||||
self.event = models.Event.objects.get(pk=self.kwargs['event'])
|
elif self.kwargs.get('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()
|
||||||
|
|
||||||
|
|
||||||
|
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:
|
except models.Event.DoesNotExist:
|
||||||
raise Http404(_('Event does not exist'))
|
raise Http404(_('Event does not exist'))
|
||||||
return queryset.prefetch_related()
|
|
||||||
|
|||||||
@@ -1,29 +1,30 @@
|
|||||||
"""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
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Q
|
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.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,10 +49,10 @@ 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')
|
location = models.ForeignKey('Location', on_delete=models.PROTECT)
|
||||||
start = models.DateTimeField(_('Start'))
|
start = models.DateTimeField(_('Start'))
|
||||||
end = models.DateTimeField(_('End'), blank=True, null=True)
|
end = models.DateTimeField(_('End'), blank=True, null=True)
|
||||||
url = models.URLField(_('Homepage'), blank=True)
|
url = models.URLField(_('Homepage'), blank=True)
|
||||||
@@ -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()
|
||||||
|
|
||||||
@@ -220,13 +221,14 @@ class Photo(models.Model):
|
|||||||
upload_to=get_upload_path,
|
upload_to=get_upload_path,
|
||||||
storage=OverwriteStorage()
|
storage=OverwriteStorage()
|
||||||
)
|
)
|
||||||
event = models.ForeignKey('events.Event')
|
event = models.ForeignKey('events.Event', on_delete=models.PROTECT, )
|
||||||
description = models.TextField(
|
description = models.TextField(
|
||||||
_("Description"),
|
_("Description"),
|
||||||
max_length=300,
|
max_length=300,
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
photographer = models.ForeignKey(settings.AUTH_USER_MODEL)
|
photographer = models.ForeignKey(settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.PROTECT)
|
||||||
on_startpage = models.BooleanField(
|
on_startpage = models.BooleanField(
|
||||||
_("Startpage"),
|
_("Startpage"),
|
||||||
default=False,
|
default=False,
|
||||||
@@ -266,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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -52,13 +52,13 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>{{event.description|truncatewords_html:20|safe}}</p>
|
{{event.description|truncatewords_html:20|safe}}
|
||||||
<ul class="info">
|
<ul class="info">
|
||||||
<li><span class="fa fa-map-marker" title="{% trans 'Location' %}"></span> {{ event.location }}</li>
|
<li><span class="fa fa-map-marker" title="{% trans 'Location' %}"></span> {{ event.location }}</li>
|
||||||
<li><span class="fa fa-comments" title="{% trans 'Comments' %}"></span> <a href="{{event.get_absolute_url}}#comments">{{ comment_count }}</a></li>
|
<li><span class="fa fa-comments" title="{% trans 'Comments' %}"></span> <a href="{{event.get_absolute_url}}#comments">{{ comment_count }}</a></li>
|
||||||
<li><span class="fa fa-camera-retro" title="{% trans 'Photos' %}"></span> <a href="{% url 'event-photo-list' event.pk %}">{{ event.photo_count }}</a></li>
|
<li><span class="fa fa-camera-retro" title="{% trans 'Photos' %}"></span> <a href="{% url 'event-photo-list' event.pk %}">{{ event.photo_count }}</a></li>
|
||||||
<li><span class="fa fa-table" title="{% trans 'Hanchans' %}"></span> <a href="{% url 'event-hanchan-list' event.pk %}">{{ event.hanchan_set.count }}</a></li>
|
<li><span class="fa fa-table" title="{% trans 'Hanchans' %}"></span> <a href="{% url 'event-hanchan-list' event.pk %}">{{ event.hanchan_set.count }}</a></li>
|
||||||
<li><span class="fa fa-glass" title="{% trans 'Hanchans' %}"></span> <a href="{% url 'maistar-game-list' event.pk %}">{{ event.maistargame_set.count }}</a></li>
|
<li><span class="fa fa -glass" title="{% trans 'Hanchans' %}"></span> <a href="{% url 'maistar-game-list' event.pk %}">{{ event.maistargame_set.count }}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
{% if perms.events.change_event %}
|
{% if perms.events.change_event %}
|
||||||
<p class="right"><a href="{{ event.get_edit_url }}" class="button"><span class="fa fa-pencil"></span></a></p>
|
<p class="right"><a href="{{ event.get_edit_url }}" class="button"><span class="fa fa-pencil"></span></a></p>
|
||||||
@@ -66,4 +66,5 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% block form %}{% endblock %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -36,10 +36,9 @@
|
|||||||
<a href="{{event.get_absolute_url}}#comments">{{ comment_count }}</a>
|
<a href="{{event.get_absolute_url}}#comments">{{ comment_count }}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>{{event.description|truncatewords_html:25|safe}}</p>
|
{{event.description|truncatewords_html:25|safe}}
|
||||||
</div>
|
</div>
|
||||||
{% if forloop.counter|divisibleby:2 %}<br class="clear">{% endif %}
|
{% if forloop.counter|divisibleby:2 %}<br class="clear">{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if page_obj.has_other_pages %}{% include 'paginator.html' %}{% endif %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -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 “{{photo.name}}” löschen wollen?</p>
|
<p>Sind Sie sicher, dass Sie das Bild “{{photo.name}}” 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"/>
|
||||||
|
|||||||
@@ -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&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>
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -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. """
|
||||||
|
|||||||
@@ -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'),
|
||||||
name='event-archive'),
|
path('<int:year>/<int:month>/', views.EventArchiveMonth.as_view(),
|
||||||
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/$',
|
name='event-archive'),
|
||||||
views.EventArchiveMonth.as_view(),
|
path('<int:year>/<int:month>/<int:pk>/', views.EventDetail.as_view(),
|
||||||
name='event-archive'),
|
name='event-detail'),
|
||||||
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/(?P<pk>[\d]+)/$',
|
path('<int:year>/<int:month>/<int:pk>/add_dates/',
|
||||||
views.EventDetail.as_view(), name='event-detail'),
|
views.EventSeriesForm.as_view(),
|
||||||
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/(?P<pk>[\d]+)/add_dates/$',
|
name='event-series-form'),
|
||||||
views.EventSeriesForm.as_view(), name='eventseries-form'),
|
path('<int:year>/<int:month>/<int:pk>/edit/', views.EventForm.as_view(),
|
||||||
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/(?P<pk>[\d]+)/edit/$',
|
name='event-form'),
|
||||||
views.EventForm.as_view(), name='event-form'),
|
path('add/', views.EventForm.as_view(), name='event-form'),
|
||||||
url(r'^add/$', views.EventForm.as_view(), name='event-form'),
|
path('archive/', views.EventArchiveIndex.as_view(), name='event-archive'),
|
||||||
url(r'^archive/$', views.EventArchiveIndex.as_view(), name='event-archive'),
|
path('upcoming/', views.UpcomingEvents.as_view(), name='upcoming-events'),
|
||||||
url(r'^upcoming/$', views.UpcomingEvents.as_view(), name='upcoming-events'),
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -3,12 +3,11 @@ 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.core.urlresolvers 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'
|
||||||
queryset = models.Event.objects.filter(
|
|
||||||
start__lt=timezone.now(),
|
|
||||||
event_series__isnull=True,
|
|
||||||
photo_count__gt=0
|
|
||||||
)
|
|
||||||
queryset = queryset.order_by('-start')
|
|
||||||
paginate_by = 24
|
paginate_by = 24
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = models.Event.objects.filter(
|
||||||
|
start__lt=timezone.now(),
|
||||||
|
event_series__isnull=True,
|
||||||
|
photo_count__gt=0
|
||||||
|
)
|
||||||
|
queryset = queryset.order_by('-start')
|
||||||
|
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)
|
||||||
|
|||||||
Binary file not shown.
@@ -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-04-27 09:49+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,13 +17,13 @@ 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"
|
||||||
|
|
||||||
#: settings.py:157
|
#: settings.py:144
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr "Deutsch"
|
msgstr "Deutsch"
|
||||||
|
|
||||||
#: settings.py:157
|
#: settings.py:144
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr "Englisch"
|
msgstr "Englisch"
|
||||||
|
|
||||||
@@ -43,101 +43,90 @@ msgstr "Kürzliche Kommentare"
|
|||||||
msgid "Menu"
|
msgid "Menu"
|
||||||
msgstr "Menü"
|
msgstr "Menü"
|
||||||
|
|
||||||
#: templates/base.html:69 templates/redbox.html:3
|
#: templates/base.html:69
|
||||||
msgid "Current Event"
|
msgid "Current Event"
|
||||||
msgstr "Aktuelle Veranstaltung"
|
msgstr "Aktuelle Veranstaltung"
|
||||||
|
|
||||||
#: templates/base.html:72 templates/redbox.html:6
|
#: templates/base.html:72
|
||||||
msgid "Since"
|
msgid "Since"
|
||||||
msgstr "seit"
|
msgstr "seit"
|
||||||
|
|
||||||
#: templates/base.html:73 templates/base.html:85 templates/redbox.html:8
|
#: templates/base.html:73 templates/base.html:87
|
||||||
#: templates/redbox.html:22
|
|
||||||
msgid "Start"
|
msgid "Start"
|
||||||
msgstr "Beginn"
|
msgstr "Beginn"
|
||||||
|
|
||||||
#: templates/base.html:76 templates/base.html:88 templates/redbox.html:11
|
#: templates/base.html:76 templates/base.html:90
|
||||||
#: templates/redbox.html:25
|
|
||||||
msgid "Location"
|
msgid "Location"
|
||||||
msgstr "Ort"
|
msgstr "Ort"
|
||||||
|
|
||||||
#: templates/base.html:79 templates/base.html:90 templates/redbox.html:14
|
#: templates/base.html:81 templates/base.html:94
|
||||||
#: templates/redbox.html:28
|
|
||||||
msgid "More Details"
|
msgid "More Details"
|
||||||
msgstr "Mehr Details"
|
msgstr "Mehr Details"
|
||||||
|
|
||||||
#: templates/base.html:81 templates/redbox.html:17
|
#: templates/base.html:83
|
||||||
msgid "Next Event"
|
msgid "Next Event"
|
||||||
msgstr "Nächste Veranstaltung"
|
msgstr "Nächste Veranstaltung"
|
||||||
|
|
||||||
#: templates/base.html:84 templates/redbox.html:20
|
#: templates/base.html:86
|
||||||
msgid "in"
|
msgid "in"
|
||||||
msgstr "in"
|
msgstr "in"
|
||||||
|
|
||||||
#: templates/base.html:93 templates/redbox.html:30
|
#: templates/base.html:97
|
||||||
msgid "Upcoming events"
|
msgid "Upcoming events"
|
||||||
msgstr "Bevorstehende Veranstaltungen"
|
msgstr "Bevorstehende Veranstaltungen"
|
||||||
|
|
||||||
#: templates/base.html:143
|
#: 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"
|
||||||
|
|
||||||
#: templates/base.html:148
|
#: templates/base.html:154
|
||||||
msgid "Edit Page"
|
msgid "Edit Page"
|
||||||
msgstr "Seite bearbeiten"
|
msgstr "Seite bearbeiten"
|
||||||
|
|
||||||
#: templates/base.html:156
|
#: templates/base.html:163
|
||||||
msgid "Imprint"
|
msgid "Imprint"
|
||||||
msgstr "Impressum"
|
msgstr "Impressum"
|
||||||
|
|
||||||
#: templates/base.html:157
|
#: templates/base.html:164
|
||||||
msgid "contact"
|
msgid "contact"
|
||||||
msgstr "Kontakt"
|
msgstr "Kontakt"
|
||||||
|
|
||||||
#: templates/base.html:162
|
#: templates/base.html:169
|
||||||
msgid "Language"
|
msgid "Language"
|
||||||
msgstr "Sprache"
|
msgstr "Sprache"
|
||||||
|
|
||||||
#: templates/base.html:171
|
#: templates/base.html:178
|
||||||
msgid "Go"
|
msgid "Go"
|
||||||
msgstr "Los"
|
msgstr "Los"
|
||||||
|
|
||||||
#: templates/base.html:176
|
#: templates/base.html:183
|
||||||
msgid "Logged in as"
|
msgid "Logged in as"
|
||||||
msgstr "Angemeldet als"
|
msgstr "Angemeldet als"
|
||||||
|
|
||||||
#: templates/base.html:178
|
#: templates/base.html:185
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Admin"
|
msgstr "Admin"
|
||||||
|
|
||||||
#: templates/base.html:179
|
#: templates/base.html:186
|
||||||
msgid "Logout"
|
msgid "Logout"
|
||||||
msgstr "Abmelden"
|
msgstr "Abmelden"
|
||||||
|
|
||||||
#: templates/base.html:181
|
#: templates/base.html:188
|
||||||
msgid "no user logged in"
|
msgid "no user logged in"
|
||||||
msgstr "Niemand angemeldet"
|
msgstr "Niemand angemeldet"
|
||||||
|
|
||||||
#: templates/base.html:182 templates/comments/form.html:43
|
#: templates/base.html:189 templates/comments/form.html:43
|
||||||
msgid "register"
|
msgid "register"
|
||||||
msgstr "Registrieren"
|
msgstr "Registrieren"
|
||||||
|
|
||||||
#: templates/base.html:183 templates/comments/form.html:44
|
#: templates/base.html:190 templates/comments/form.html:44
|
||||||
msgid "login"
|
msgid "login"
|
||||||
msgstr "anmelden"
|
msgstr "anmelden"
|
||||||
|
|
||||||
#: templates/base.html:185
|
|
||||||
msgid "Login with Facebook"
|
|
||||||
msgstr "über Facebook anmelden"
|
|
||||||
|
|
||||||
#: templates/base.html:187
|
|
||||||
msgid "Login with Twitter"
|
|
||||||
msgstr "über Twitter anmelden"
|
|
||||||
|
|
||||||
#: templates/base.html:189
|
|
||||||
msgid "Login with Google"
|
|
||||||
msgstr "über Google anmelden"
|
|
||||||
|
|
||||||
#: templates/comments/form.html:5
|
#: templates/comments/form.html:5
|
||||||
msgid "New Comment"
|
msgid "New Comment"
|
||||||
msgstr "Neuer Kommentar"
|
msgstr "Neuer Kommentar"
|
||||||
@@ -189,13 +178,22 @@ msgid "Read More"
|
|||||||
msgstr "Mehr lesen"
|
msgstr "Mehr lesen"
|
||||||
|
|
||||||
#: 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\">“%(object)s”</"
|
||||||
|
#| "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\">“%(object)s”</a>\n"
|
" <a href=\"%(comment_link)s\">“%(object)s”</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"
|
||||||
@@ -217,14 +215,23 @@ msgstr "Besuche uns auf"
|
|||||||
msgid "Add Article"
|
msgid "Add Article"
|
||||||
msgstr "Artikel hinzufügen"
|
msgstr "Artikel hinzufügen"
|
||||||
|
|
||||||
#: templates/paginator.html:8
|
#: templates/paginator.html:5 templates/paginator.html:7
|
||||||
msgid "Previous"
|
msgid "Previous"
|
||||||
msgstr "Vorherige"
|
msgstr "Vorherige"
|
||||||
|
|
||||||
#: templates/paginator.html:20
|
#: templates/paginator.html:17 templates/paginator.html:20
|
||||||
msgid "Next"
|
msgid "Next"
|
||||||
msgstr "Nächste"
|
msgstr "Nächste"
|
||||||
|
|
||||||
|
#~ msgid "Login with Facebook"
|
||||||
|
#~ msgstr "über Facebook anmelden"
|
||||||
|
|
||||||
|
#~ msgid "Login with Twitter"
|
||||||
|
#~ msgstr "über Twitter anmelden"
|
||||||
|
|
||||||
|
#~ msgid "Login with Google"
|
||||||
|
#~ msgstr "über Google anmelden"
|
||||||
|
|
||||||
#~ msgid "United Kingdom"
|
#~ msgid "United Kingdom"
|
||||||
#~ msgstr "Vereinigtes Königreich"
|
#~ msgstr "Vereinigtes Königreich"
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ PREREQ_APPS = [
|
|||||||
'ckeditor',
|
'ckeditor',
|
||||||
'ckeditor_uploader',
|
'ckeditor_uploader',
|
||||||
'easy_thumbnails',
|
'easy_thumbnails',
|
||||||
'social_django',
|
|
||||||
]
|
]
|
||||||
PROJECT_APPS = [
|
PROJECT_APPS = [
|
||||||
'kasu',
|
'kasu',
|
||||||
@@ -69,17 +68,17 @@ 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',
|
||||||
'mahjong_ranking.middleware.DenormalizationUpdateMiddleware',
|
'mahjong_ranking.middleware.DenormalizationUpdateMiddleware',
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -102,8 +101,6 @@ TEMPLATES = [
|
|||||||
'django.contrib.messages.context_processors.messages',
|
'django.contrib.messages.context_processors.messages',
|
||||||
'django.contrib.messages.context_processors.messages',
|
'django.contrib.messages.context_processors.messages',
|
||||||
'events.context_processors.events_overview',
|
'events.context_processors.events_overview',
|
||||||
'social_django.context_processors.backends',
|
|
||||||
'social_django.context_processors.login_redirect'
|
|
||||||
],
|
],
|
||||||
'loaders': [
|
'loaders': [
|
||||||
('django.template.loaders.cached.Loader', [
|
('django.template.loaders.cached.Loader', [
|
||||||
@@ -131,24 +128,14 @@ DEFAULT_FROM_EMAIL = ""
|
|||||||
# Login Settings
|
# Login Settings
|
||||||
ACCOUNT_ACTIVATION_DAYS = 5
|
ACCOUNT_ACTIVATION_DAYS = 5
|
||||||
AUTH_USER_MODEL = 'membership.Membership'
|
AUTH_USER_MODEL = 'membership.Membership'
|
||||||
AUTHENTICATION_BACKENDS = ('social_core.backends.facebook.FacebookOAuth2',
|
AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',)
|
||||||
'social_core.backends.google.GoogleOAuth2',
|
|
||||||
'social_core.backends.twitter.TwitterOAuth',
|
|
||||||
'django.contrib.auth.backends.ModelBackend',)
|
|
||||||
LOGIN_URL = '/membership/login/'
|
LOGIN_URL = '/membership/login/'
|
||||||
LOGIN_ERROR_URL = '/membership/login/error/'
|
LOGIN_ERROR_URL = '/membership/login/error/'
|
||||||
LOGIN_REDIRECT_URL = '/users/'
|
LOGIN_REDIRECT_URL = '/users/'
|
||||||
SOCIAL_AUTH_CHANGE_SIGNAL_ONLY = False
|
|
||||||
SOCIAL_AUTH_ENABLED_BACKENDS = ('facebook-oauth2', 'google-oauth2', 'twitter')
|
|
||||||
SOCIAL_AUTH_NEW_USER_REDIRECT_URL = '/users/'
|
# Set the primarykey handing to old django style
|
||||||
SOCIAL_AUTH_SLUGIFY_USERNAMES = True
|
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
||||||
SOCIAL_AUTH_FACEBOOK_KEY = ''
|
|
||||||
SOCIAL_AUTH_FACEBOOK_SECRET = ''
|
|
||||||
SOCIAL_AUTH_FACEBOOK_SCOPE = ['user_about_me', 'email']
|
|
||||||
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = ''
|
|
||||||
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = ''
|
|
||||||
SOCIAL_AUTH_TWITTER_KEY = ''
|
|
||||||
SOCIAL_AUTH_TWITTER_SECRET = ''
|
|
||||||
|
|
||||||
# Localization
|
# Localization
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
@@ -204,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'
|
||||||
@@ -229,7 +217,7 @@ LOGGING = {
|
|||||||
'loggers': {
|
'loggers': {
|
||||||
'django': {
|
'django': {
|
||||||
'handlers': ['console'],
|
'handlers': ['console'],
|
||||||
'level': 'INFO',
|
'level': 'DEBUG',
|
||||||
'propagate': True,
|
'propagate': True,
|
||||||
},
|
},
|
||||||
'django.request': {
|
'django.request': {
|
||||||
@@ -246,6 +234,22 @@ LOGGING = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
################################
|
||||||
|
# Settings for mahjong_ranking #
|
||||||
|
################################
|
||||||
|
|
||||||
|
MIN_HANCHANS_FOR_LADDER = 5
|
||||||
|
RANKING_EXPORT_PATH = path.join(PROJECT_PATH, 'backup', 'mahjong_ranking')
|
||||||
|
|
||||||
|
# Old Tournament System
|
||||||
|
TOURNAMENT_POINT_SYSTEM = True
|
||||||
|
TOURNAMENT_WIN_BONUSPOINTS = 4
|
||||||
|
TOURNAMENT_FLAWLESS_VICTORY_BONUSPOINTS = 8
|
||||||
|
|
||||||
|
# Old Dan System
|
||||||
|
DAN_3_WINS_IN_A_ROW = True
|
||||||
|
DAN_ALLOW_DROP_DOWN = True
|
||||||
|
|
||||||
DAN_RANKS = (
|
DAN_RANKS = (
|
||||||
(80, 9),
|
(80, 9),
|
||||||
(70, 8),
|
(70, 8),
|
||||||
@@ -255,7 +259,7 @@ DAN_RANKS = (
|
|||||||
(30, 4),
|
(30, 4),
|
||||||
(20, 3),
|
(20, 3),
|
||||||
(10, 2),
|
(10, 2),
|
||||||
(0, 1),
|
(-1, 1),
|
||||||
)
|
)
|
||||||
|
|
||||||
KYU_RANKS = (
|
KYU_RANKS = (
|
||||||
@@ -268,15 +272,11 @@ KYU_RANKS = (
|
|||||||
(15, 7),
|
(15, 7),
|
||||||
(10, 8),
|
(10, 8),
|
||||||
(5, 9),
|
(5, 9),
|
||||||
(0, 10),
|
(-1, 10),
|
||||||
)
|
)
|
||||||
|
|
||||||
DAN_ALLOW_DROP_DOWN = True
|
|
||||||
MIN_HANCHANS_FOR_LADDER = 5
|
|
||||||
RANKING_EXPORT_PATH = path.join(PROJECT_PATH, 'backup', 'mahjong_ranking')
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from .local_settings import * # Ignore PyLintBear (W0401, W0614)
|
from .local_settings import * # noqa: F403
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
231
src/kasu/static/js/jquery.formset.js
Normal file
231
src/kasu/static/js/jquery.formset.js
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
/**
|
||||||
|
* jQuery Formset 1.3-pre
|
||||||
|
* @author Stanislaus Madueke (stan DOT madueke AT gmail DOT com)
|
||||||
|
* @requires jQuery 1.2.6 or later
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009, Stanislaus Madueke
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the New BSD License
|
||||||
|
* See: http://www.opensource.org/licenses/bsd-license.php
|
||||||
|
*/
|
||||||
|
;(function($) {
|
||||||
|
$.fn.formset = function(opts)
|
||||||
|
{
|
||||||
|
var options = $.extend({}, $.fn.formset.defaults, opts),
|
||||||
|
flatExtraClasses = options.extraClasses.join(' '),
|
||||||
|
totalForms = $('#id_' + options.prefix + '-TOTAL_FORMS'),
|
||||||
|
maxForms = $('#id_' + options.prefix + '-MAX_NUM_FORMS'),
|
||||||
|
minForms = $('#id_' + options.prefix + '-MIN_NUM_FORMS'),
|
||||||
|
childElementSelector = 'input,select,textarea,label,div',
|
||||||
|
$$ = $(this),
|
||||||
|
|
||||||
|
applyExtraClasses = function(row, ndx) {
|
||||||
|
if (options.extraClasses) {
|
||||||
|
row.removeClass(flatExtraClasses);
|
||||||
|
row.addClass(options.extraClasses[ndx % options.extraClasses.length]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateElementIndex = function(elem, prefix, ndx) {
|
||||||
|
var idRegex = new RegExp(prefix + '-(\\d+|__prefix__)-'),
|
||||||
|
replacement = prefix + '-' + ndx + '-';
|
||||||
|
if (elem.attr("for")) elem.attr("for", elem.attr("for").replace(idRegex, replacement));
|
||||||
|
if (elem.attr('id')) elem.attr('id', elem.attr('id').replace(idRegex, replacement));
|
||||||
|
if (elem.attr('name')) elem.attr('name', elem.attr('name').replace(idRegex, replacement));
|
||||||
|
},
|
||||||
|
|
||||||
|
hasChildElements = function(row) {
|
||||||
|
return row.find(childElementSelector).length > 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
showAddButton = function() {
|
||||||
|
return maxForms.length == 0 || // For Django versions pre 1.2
|
||||||
|
(maxForms.val() == '' || (maxForms.val() - totalForms.val() > 0));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether delete link(s) can be displayed - when total forms > min forms
|
||||||
|
*/
|
||||||
|
showDeleteLinks = function() {
|
||||||
|
return minForms.length == 0 || // For Django versions pre 1.7
|
||||||
|
(minForms.val() == '' || (totalForms.val() - minForms.val() > 0));
|
||||||
|
},
|
||||||
|
|
||||||
|
insertDeleteLink = function(row) {
|
||||||
|
var delCssSelector = $.trim(options.deleteCssClass).replace(/\s+/g, '.'),
|
||||||
|
addCssSelector = $.trim(options.addCssClass).replace(/\s+/g, '.');
|
||||||
|
if (row.is('TR')) {
|
||||||
|
// If the forms are laid out in table rows, insert
|
||||||
|
// the remove button into the last table cell:
|
||||||
|
row.children(':last').append('<a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + '</a>');
|
||||||
|
} else if (row.is('UL') || row.is('OL')) {
|
||||||
|
// If they're laid out as an ordered/unordered list,
|
||||||
|
// insert an <li> after the last list item:
|
||||||
|
row.append('<li><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText +'</a></li>');
|
||||||
|
} else {
|
||||||
|
// Otherwise, just insert the remove button as the
|
||||||
|
// last child element of the form's container:
|
||||||
|
row.append('<a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText +'</a>');
|
||||||
|
}
|
||||||
|
// Check if we're under the minimum number of forms - not to display delete link at rendering
|
||||||
|
if (!showDeleteLinks()){
|
||||||
|
row.find('a.' + delCssSelector).hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
row.find('a.' + delCssSelector).click(function() {
|
||||||
|
var row = $(this).parents('.' + options.formCssClass),
|
||||||
|
del = row.find('input:hidden[id $= "-DELETE"]'),
|
||||||
|
buttonRow = row.siblings("a." + addCssSelector + ', .' + options.formCssClass + '-add'),
|
||||||
|
forms;
|
||||||
|
if (del.length) {
|
||||||
|
// We're dealing with an inline formset.
|
||||||
|
// Rather than remove this form from the DOM, we'll mark it as deleted
|
||||||
|
// and hide it, then let Django handle the deleting:
|
||||||
|
del.val('on');
|
||||||
|
row.hide();
|
||||||
|
forms = $('.' + options.formCssClass).not(':hidden');
|
||||||
|
} else {
|
||||||
|
row.remove();
|
||||||
|
// Update the TOTAL_FORMS count:
|
||||||
|
forms = $('.' + options.formCssClass).not('.formset-custom-template');
|
||||||
|
totalForms.val(forms.length);
|
||||||
|
}
|
||||||
|
for (var i=0, formCount=forms.length; i<formCount; i++) {
|
||||||
|
// Apply `extraClasses` to form rows so they're nicely alternating:
|
||||||
|
applyExtraClasses(forms.eq(i), i);
|
||||||
|
if (!del.length) {
|
||||||
|
// Also update names and IDs for all child controls (if this isn't
|
||||||
|
// a delete-able inline formset) so they remain in sequence:
|
||||||
|
forms.eq(i).find(childElementSelector).each(function() {
|
||||||
|
updateElementIndex($(this), options.prefix, i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if we've reached the minimum number of forms - hide all delete link(s)
|
||||||
|
if (!showDeleteLinks()){
|
||||||
|
$('a.' + delCssSelector).each(function(){$(this).hide();});
|
||||||
|
}
|
||||||
|
// Check if we need to show the add button:
|
||||||
|
if (buttonRow.is(':hidden') && showAddButton()) buttonRow.show();
|
||||||
|
// If a post-delete callback was provided, call it with the deleted form:
|
||||||
|
if (options.removed) options.removed(row);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$$.each(function(i) {
|
||||||
|
var row = $(this),
|
||||||
|
del = row.find('input:checkbox[id $= "-DELETE"]');
|
||||||
|
if (del.length) {
|
||||||
|
// If you specify "can_delete = True" when creating an inline formset,
|
||||||
|
// Django adds a checkbox to each form in the formset.
|
||||||
|
// Replace the default checkbox with a hidden field:
|
||||||
|
if (del.is(':checked')) {
|
||||||
|
// If an inline formset containing deleted forms fails validation, make sure
|
||||||
|
// we keep the forms hidden (thanks for the bug report and suggested fix Mike)
|
||||||
|
del.before('<input type="hidden" name="' + del.attr('name') +'" id="' + del.attr('id') +'" value="on" />');
|
||||||
|
row.hide();
|
||||||
|
} else {
|
||||||
|
del.before('<input type="hidden" name="' + del.attr('name') +'" id="' + del.attr('id') +'" />');
|
||||||
|
}
|
||||||
|
// Hide any labels associated with the DELETE checkbox:
|
||||||
|
$('label[for="' + del.attr('id') + '"]').hide();
|
||||||
|
del.remove();
|
||||||
|
}
|
||||||
|
if (hasChildElements(row)) {
|
||||||
|
row.addClass(options.formCssClass);
|
||||||
|
if (row.is(':visible')) {
|
||||||
|
insertDeleteLink(row);
|
||||||
|
applyExtraClasses(row, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($$.length) {
|
||||||
|
var hideAddButton = !showAddButton(),
|
||||||
|
addButton, template;
|
||||||
|
if (options.formTemplate) {
|
||||||
|
// If a form template was specified, we'll clone it to generate new form instances:
|
||||||
|
template = (options.formTemplate instanceof $) ? options.formTemplate : $(options.formTemplate);
|
||||||
|
template.removeAttr('id').addClass(options.formCssClass + ' formset-custom-template');
|
||||||
|
template.find(childElementSelector).each(function() {
|
||||||
|
updateElementIndex($(this), options.prefix, '__prefix__');
|
||||||
|
});
|
||||||
|
insertDeleteLink(template);
|
||||||
|
} else {
|
||||||
|
// Otherwise, use the last form in the formset; this works much better if you've got
|
||||||
|
// extra (>= 1) forms (thnaks to justhamade for pointing this out):
|
||||||
|
template = $('.' + options.formCssClass + ':last').clone(true).removeAttr('id');
|
||||||
|
template.find('input:hidden[id $= "-DELETE"]').remove();
|
||||||
|
// Clear all cloned fields, except those the user wants to keep (thanks to brunogola for the suggestion):
|
||||||
|
template.find(childElementSelector).not(options.keepFieldValues).each(function() {
|
||||||
|
var elem = $(this);
|
||||||
|
// If this is a checkbox or radiobutton, uncheck it.
|
||||||
|
// This fixes Issue 1, reported by Wilson.Andrew.J:
|
||||||
|
if (elem.is('input:checkbox') || elem.is('input:radio')) {
|
||||||
|
elem.attr('checked', false);
|
||||||
|
} else {
|
||||||
|
elem.val('');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// FIXME: Perhaps using $.data would be a better idea?
|
||||||
|
options.formTemplate = template;
|
||||||
|
|
||||||
|
if ($$.is('TR')) {
|
||||||
|
// If forms are laid out as table rows, insert the
|
||||||
|
// "add" button in a new table row:
|
||||||
|
var numCols = $$.eq(0).children().length, // This is a bit of an assumption :|
|
||||||
|
buttonRow = $('<tr><td colspan="' + numCols + '"><a class="' + options.addCssClass + '" href="javascript:void(0)">' + options.addText + '</a></tr>')
|
||||||
|
.addClass(options.formCssClass + '-add');
|
||||||
|
$$.parent().append(buttonRow);
|
||||||
|
if (hideAddButton) buttonRow.hide();
|
||||||
|
addButton = buttonRow.find('a');
|
||||||
|
} else {
|
||||||
|
// Otherwise, insert it immediately after the last form:
|
||||||
|
$$.filter(':last').after('<a class="' + options.addCssClass + '" href="javascript:void(0)">' + options.addText + '</a>');
|
||||||
|
addButton = $$.filter(':last').next();
|
||||||
|
if (hideAddButton) addButton.hide();
|
||||||
|
}
|
||||||
|
addButton.click(function() {
|
||||||
|
var formCount = parseInt(totalForms.val()),
|
||||||
|
row = options.formTemplate.clone(true).removeClass('formset-custom-template'),
|
||||||
|
buttonRow = $($(this).parents('tr.' + options.formCssClass + '-add').get(0) || this),
|
||||||
|
delCssSelector = $.trim(options.deleteCssClass).replace(/\s+/g, '.');
|
||||||
|
applyExtraClasses(row, formCount);
|
||||||
|
row.insertBefore(buttonRow).show();
|
||||||
|
row.find(childElementSelector).each(function() {
|
||||||
|
updateElementIndex($(this), options.prefix, formCount);
|
||||||
|
});
|
||||||
|
totalForms.val(formCount + 1);
|
||||||
|
// Check if we're above the minimum allowed number of forms -> show all delete link(s)
|
||||||
|
if (showDeleteLinks()){
|
||||||
|
$('a.' + delCssSelector).each(function(){$(this).show();});
|
||||||
|
}
|
||||||
|
// Check if we've exceeded the maximum allowed number of forms:
|
||||||
|
if (!showAddButton()) buttonRow.hide();
|
||||||
|
// If a post-add callback was supplied, call it with the added form:
|
||||||
|
if (options.added) options.added(row);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return $$;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Setup plugin defaults */
|
||||||
|
$.fn.formset.defaults = {
|
||||||
|
prefix: 'form', // The form prefix for your django formset
|
||||||
|
formTemplate: null, // The jQuery selection cloned to generate new form instances
|
||||||
|
addText: 'add another', // Text for the add link
|
||||||
|
deleteText: 'remove', // Text for the delete link
|
||||||
|
addCssClass: 'add-row', // CSS class applied to the add link
|
||||||
|
deleteCssClass: 'delete-row', // CSS class applied to the delete link
|
||||||
|
formCssClass: 'dynamic-form', // CSS class applied to each form in a formset
|
||||||
|
extraClasses: [], // Additional CSS classes, which will be applied to each form in turn
|
||||||
|
keepFieldValues: '', // jQuery selector for fields whose values should be kept when the form is cloned
|
||||||
|
added: null, // Function called each time a new form is added
|
||||||
|
removed: null // Function called each time a form is deleted
|
||||||
|
};
|
||||||
|
})(jQuery);
|
||||||
@@ -4,9 +4,11 @@
|
|||||||
_paq.push(['trackPageView']);
|
_paq.push(['trackPageView']);
|
||||||
_paq.push(['enableLinkTracking']);
|
_paq.push(['enableLinkTracking']);
|
||||||
(function() {
|
(function() {
|
||||||
var u="//kasu.at/piwik/";
|
var u="/piwik/";
|
||||||
_paq.push(['setTrackerUrl', u+'piwik.php']);
|
_paq.push(['setTrackerUrl', u+'piwik.php']);
|
||||||
_paq.push(['setSiteId', '1']);
|
_paq.push(['setSiteId', '1']);
|
||||||
|
_paq.push(['disableCookies']);
|
||||||
|
_paq.push(['trackPageView']);
|
||||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||||
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
|
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -258,6 +258,7 @@ ul.tabs {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
.error, ul.errorlist li {color: #a40000;}
|
.error, ul.errorlist li {color: #a40000;}
|
||||||
|
input.error {border-color:#a40000; background-color: rgba(164, 0, 0, 0.25);}
|
||||||
.game h2 {margin: 0.5em 0;}
|
.game h2 {margin: 0.5em 0;}
|
||||||
.grid_1,.grid_2,.grid_3,.grid_4,.grid_5,.grid_6,.grid_7,.grid_8,.grid_9,.grid_10,.grid_11,.grid_12
|
.grid_1,.grid_2,.grid_3,.grid_4,.grid_5,.grid_6,.grid_7,.grid_8,.grid_9,.grid_10,.grid_11,.grid_12
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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">
|
||||||
@@ -46,12 +46,12 @@
|
|||||||
<ul class="main_menu">
|
<ul class="main_menu">
|
||||||
{% for item in top_menu_items %}
|
{% for item in top_menu_items %}
|
||||||
<li><a href="{{item.get_absolute_url}}" title="{{ item.title }}"
|
<li><a href="{{item.get_absolute_url}}" title="{{ item.title }}"
|
||||||
class="{%if item.active %}active{% endif %}">{{item.menu_name}}</a>
|
class="{% if item.active %}active{% endif %}">{{item.menu_name}}</a>
|
||||||
{% if item.subpages.all %}
|
{% if item.subpages.all %}
|
||||||
<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>
|
||||||
@@ -185,12 +188,6 @@
|
|||||||
{% trans "no user logged in" %} -
|
{% trans "no user logged in" %} -
|
||||||
<a rel="nofollow" href="{% url 'membership-register' %}">{% trans "register" %}</a>
|
<a rel="nofollow" href="{% url 'membership-register' %}">{% trans "register" %}</a>
|
||||||
<a rel="nofollow" href="{% url 'login' %}?next={{ request.path_info }}">{% trans "login" %}</a>
|
<a rel="nofollow" href="{% url 'login' %}?next={{ request.path_info }}">{% trans "login" %}</a>
|
||||||
<a rel="nofollow" href="{% url 'social:begin' 'facebook' %}" class="fa fa-facebook"
|
|
||||||
title="{% trans 'Login with Facebook' %}" aria-label="{% trans 'Login with Facebook' %}"></a>
|
|
||||||
<a rel="nofollow" href="{% url 'social:begin' 'twitter' %}" class="fa fa-twitter"
|
|
||||||
title="{% trans 'Login with Twitter' %}" aria-label="{% trans 'Login with Twitter' %}"></a>
|
|
||||||
<a rel="nofollow" href="{% url 'social:begin' 'google-oauth2' %}" class="fa fa-google"
|
|
||||||
title="{% trans 'Login with Google' %}" aria-label="{% trans 'Login with Google' %}"></a>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</nav>
|
</nav>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
4
src/kasu/templates/django/forms/widgets/date.html
Normal file
4
src/kasu/templates/django/forms/widgets/date.html
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{% with type="date" %}
|
||||||
|
{% include "django/forms/widgets/html5input.html" %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
4
src/kasu/templates/django/forms/widgets/datetime.html
Normal file
4
src/kasu/templates/django/forms/widgets/datetime.html
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{% with type="datetime-local" %}
|
||||||
|
{% include "django/forms/widgets/html5input.html" %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
2
src/kasu/templates/django/forms/widgets/html5input.html
Normal file
2
src/kasu/templates/django/forms/widgets/html5input.html
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<input type="{{ type|default:'text' }}" name="{{ widget.name }}"
|
||||||
|
value="{{ widget.value|stringformat:'s' }}" "django/forms/widgets/attrs.html" %} />
|
||||||
4
src/kasu/templates/django/forms/widgets/time.html
Normal file
4
src/kasu/templates/django/forms/widgets/time.html
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{% with type="time" %}
|
||||||
|
{% include "django/forms/widgets/html5input.html" %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
@@ -2,11 +2,11 @@
|
|||||||
{% 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.help_text and not field.field.widget.input_type %}
|
{% if field.field.widget.input_type == 'checkbox' %}
|
||||||
{{field.help_text}}
|
<label for="id_{{field.name}}">{{field.help_text|safe}}</label>
|
||||||
{% elif field.help_text %}
|
{% elif field.help_text %}
|
||||||
<p class="help_text">{{field.help_text}}</p>
|
<p class="help_text">{{field.help_text}}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -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}}">“{{object}}”</a>
|
<a href="{{comment_link}}">“{{object}}”</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 %}
|
||||||
|
|||||||
@@ -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>
|
||||||
{% else %}
|
{% trans "Previous" %}</a>
|
||||||
class="previous disabled"
|
{% else %}
|
||||||
{% endif %}>
|
<a class="previous disabled"><span class="fa fa-arrow-left"></span>{% trans "Previous" %}</a>
|
||||||
<span class="fa fa-arrow-left"></span>{% trans "Previous" %}
|
{% endif %}
|
||||||
</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 %}
|
||||||
|
<a class="active">{{page}}</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="?page={{page}}">{{page}}</a>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% if page_obj.has_next %}
|
||||||
<a {% if page_obj.has_next %}
|
<a class="next" href="?page={{ page_obj.next_page_number }}">{% trans "Next" %}
|
||||||
class="next" href="?page={{ page_obj.next_page_number }}"
|
<span class="fa fa-arrow-right"></span></a>
|
||||||
{% else %}
|
{% else %}
|
||||||
class="next disabled"
|
<a class="next disabled">{% trans "Next" %} <span class="fa fa-arrow-right"></span></a>
|
||||||
{% endif %}>
|
{% endif %}
|
||||||
{% trans "Next" %} <span class="fa fa-arrow-right"></span>
|
|
||||||
</a>
|
|
||||||
</nav>
|
</nav>
|
||||||
@@ -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,59 +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/', include(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')),
|
MembershipDetail.as_view(), name='membership-details'),
|
||||||
url(r'^ranking/', include('mahjong_ranking.urls')),
|
path('add_page/<path:path>', views.PageAddForm.as_view(), name='add-page'),
|
||||||
url(r'^ranking/', include('maistar_ranking.urls')),
|
path('edit_page/<path:path>', views.PageEditForm.as_view(), name='edit-page'),
|
||||||
url(r'^sitemap\.xml$', sitemap, {
|
path('<path:path>.html', views.PageHtml.as_view(), name='view-page'),
|
||||||
'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'),
|
path('<path:path>.pdf', views.PagePdf.as_view()),
|
||||||
url(r'^robots.txt$', TemplateView.as_view(template_name='robots.txt')),
|
path('sitemap.xml', sitemap, {'sitemaps': sitemaps},
|
||||||
url(r'^users/$', MembershipDetail.as_view(), name='membership-details'),
|
name='django.contrib.sitemaps.views.sitemap'),
|
||||||
url(r'^users/(?P<username>[\-\.\d\w]+)/$',
|
|
||||||
MembershipDetail.as_view(), name='membership-details'),
|
|
||||||
url(r'^(?P<path>[\-\d\w\/]+)\.html$',
|
|
||||||
views.PageHtml.as_view(), name='view-page'),
|
|
||||||
url(r'^(?P<path>[\-\d\w\/]+)\.pdf$', views.PagePdf.as_view()),
|
|
||||||
url('', include('social_django.urls', namespace='social'))
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
@@ -71,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)), ]
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
152
src/kasu/xlsx.py
Normal file
152
src/kasu/xlsx.py
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
"""
|
||||||
|
Helper to generate XLSX Spreadsheets in an uniform way.
|
||||||
|
"""
|
||||||
|
import datetime
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
|
import openpyxl
|
||||||
|
from openpyxl.styles import NamedStyle, Font, Border, Side
|
||||||
|
|
||||||
|
DEFAULT_FONT = Font(name='Philosopher', size=10, bold=False, color='000000')
|
||||||
|
THIN_BORDER = Border(
|
||||||
|
bottom=Side(style='thin', color="d3d7cf"),
|
||||||
|
top=Side(style='thin', color="d3d7cf"))
|
||||||
|
XLSX_STYLES = dict()
|
||||||
|
|
||||||
|
XLSX_STYLES['Content'] = NamedStyle(
|
||||||
|
name='Content',
|
||||||
|
font=DEFAULT_FONT,
|
||||||
|
border=THIN_BORDER
|
||||||
|
)
|
||||||
|
|
||||||
|
XLSX_STYLES['Headline'] = NamedStyle(
|
||||||
|
name="Headline",
|
||||||
|
font=openpyxl.styles.Font(name='Philosopher', size=11,
|
||||||
|
bold=True, color='ffffff'),
|
||||||
|
fill=openpyxl.styles.PatternFill(fill_type='solid',
|
||||||
|
start_color='a40000',
|
||||||
|
end_color='a40000')
|
||||||
|
)
|
||||||
|
|
||||||
|
XLSX_STYLES['Date'] = NamedStyle(
|
||||||
|
name='Date',
|
||||||
|
font=DEFAULT_FONT,
|
||||||
|
border=THIN_BORDER,
|
||||||
|
number_format='dd.mm.yyyy'
|
||||||
|
)
|
||||||
|
|
||||||
|
XLSX_STYLES['Date Time'] = NamedStyle(
|
||||||
|
name='Date Time',
|
||||||
|
font=DEFAULT_FONT,
|
||||||
|
border=THIN_BORDER,
|
||||||
|
number_format='dd.mm.yyyy hh:MM'
|
||||||
|
)
|
||||||
|
|
||||||
|
XLSX_STYLES['Float'] = NamedStyle(
|
||||||
|
name='Float',
|
||||||
|
font=DEFAULT_FONT,
|
||||||
|
border=THIN_BORDER,
|
||||||
|
number_format='#,##0.00'
|
||||||
|
)
|
||||||
|
|
||||||
|
XLSX_STYLES['Integer'] = NamedStyle(
|
||||||
|
name='Integer',
|
||||||
|
font=DEFAULT_FONT,
|
||||||
|
border=THIN_BORDER,
|
||||||
|
number_format='#,##0'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def getattr_recursive(obj, attr_string):
|
||||||
|
"""A recusive version of gettattr. the attr_string is splitted on the ".".
|
||||||
|
|
||||||
|
:param obj: a python object.
|
||||||
|
:param attr_string: the desired attribute of the object.
|
||||||
|
:return: return the value from attr2 of attr1 from obj
|
||||||
|
"""
|
||||||
|
attr_list = attr_string.split('.')
|
||||||
|
return_value = None
|
||||||
|
for attr in attr_list:
|
||||||
|
return_value = getattr(obj, attr)
|
||||||
|
obj = return_value
|
||||||
|
if isinstance(return_value, datetime.datetime):
|
||||||
|
return_value = return_value.replace(tzinfo=None)
|
||||||
|
return return_value
|
||||||
|
|
||||||
|
|
||||||
|
class Workbook(object):
|
||||||
|
workbook = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""Generate an XLSX Workbook in memory
|
||||||
|
|
||||||
|
:rtype: object
|
||||||
|
"""
|
||||||
|
self.workbook = openpyxl.Workbook()
|
||||||
|
[self.workbook.add_named_style(style)
|
||||||
|
for style in XLSX_STYLES.values()]
|
||||||
|
[self.workbook.remove(sheet) for sheet in self.workbook.worksheets]
|
||||||
|
|
||||||
|
def generate_sheet(self, title, columns_settings, object_list,
|
||||||
|
orientation='landscape'):
|
||||||
|
"""
|
||||||
|
|
||||||
|
:param title: Title of the Sheet
|
||||||
|
:param columns_settings: a list of dicts for the settings of each column
|
||||||
|
:param object_list: List of objects that should be added to the sheet
|
||||||
|
:param orientation: Paper Orientation 'landscape' or 'portrait'
|
||||||
|
"""
|
||||||
|
row = 1
|
||||||
|
ws = self.workbook.create_sheet()
|
||||||
|
ws.title = title
|
||||||
|
ws.syncHorizontal = True
|
||||||
|
ws.filterMode = True
|
||||||
|
|
||||||
|
# setup print orientation
|
||||||
|
ws.page_setup.fitToHeight = 0
|
||||||
|
ws.page_setup.fitToWidth = 1
|
||||||
|
if orientation == 'landscape':
|
||||||
|
ws.page_setup.orientation = ws.ORIENTATION_LANDSCAPE
|
||||||
|
else:
|
||||||
|
ws.page_setup.orientation = ws.ORIENTATION_PORTRAIT
|
||||||
|
ws.page_setup.paperSize = ws.PAPERSIZE_A4
|
||||||
|
ws.print_options.horizontalCentered = True
|
||||||
|
|
||||||
|
# setup page header
|
||||||
|
ws.oddHeader.left.text = title
|
||||||
|
ws.oddHeader.left.size = 14
|
||||||
|
ws.oddHeader.left.font = "Amerika Sans"
|
||||||
|
ws.oddHeader.left.color = "000000"
|
||||||
|
|
||||||
|
ws.oddHeader.right.text = str(date.today())
|
||||||
|
ws.oddHeader.right.size = 14
|
||||||
|
ws.oddHeader.right.font = "Amerika Sans"
|
||||||
|
ws.oddHeader.right.color = "000000"
|
||||||
|
|
||||||
|
# write table header
|
||||||
|
for column, data in enumerate(columns_settings, 1):
|
||||||
|
cell = ws.cell(column=column, row=row, value=data['title'])
|
||||||
|
cell.style = 'Headline'
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
# write the table content
|
||||||
|
for line in object_list:
|
||||||
|
for column, settings in enumerate(columns_settings, 1):
|
||||||
|
cell = ws.cell(column=column, row=row,
|
||||||
|
value=getattr_recursive(line, settings['attr']))
|
||||||
|
cell.style = settings['style']
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
# write table footer
|
||||||
|
for column, settings in enumerate(columns_settings, 1):
|
||||||
|
cell = ws.cell(column=column, row=row,
|
||||||
|
value=settings.get('footer'))
|
||||||
|
cell.style = settings['style']
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
# set column widths
|
||||||
|
for settings in columns_settings:
|
||||||
|
ws.column_dimensions[settings['col']].width = settings['width']
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
return self.workbook.save(*args, **kwargs)
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -36,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):
|
||||||
@@ -57,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',)
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ Created on 04.10.2011
|
|||||||
|
|
||||||
@author: christian
|
@author: christian
|
||||||
"""
|
"""
|
||||||
from django.contrib.auth import get_user_model
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext as _
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
from events.models import Event
|
||||||
from . import models
|
from . import models
|
||||||
|
|
||||||
USER_MODEL = get_user_model()
|
USER_MODEL = get_user_model()
|
||||||
@@ -47,14 +48,31 @@ class HanchanForm(forms.ModelForm):
|
|||||||
self.fields[player_input_score].widget.attrs['type'] = 'number'
|
self.fields[player_input_score].widget.attrs['type'] = 'number'
|
||||||
self.fields[player].queryset = player_queryset
|
self.fields[player].queryset = player_queryset
|
||||||
|
|
||||||
|
def is_valid(self):
|
||||||
|
ret = forms.Form.is_valid(self)
|
||||||
|
for field, errors in self.errors.items():
|
||||||
|
message = ", ".join(set(errors))
|
||||||
|
print(type(field), type(errors))
|
||||||
|
self.fields[field].widget.attrs.update({
|
||||||
|
'class': self.fields[field].widget.attrs.get('class', '') + ' error',
|
||||||
|
'title': message
|
||||||
|
})
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class HanchanAdminForm(HanchanForm):
|
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. """
|
||||||
model = models.Hanchan
|
model = models.Hanchan
|
||||||
fields = HanchanForm.Meta.fields + ('confirmed',)
|
fields = HanchanForm.Meta.fields + ('confirmed',)
|
||||||
|
|
||||||
|
|
||||||
|
HanchanFormset = forms.inlineformset_factory(Event, models.Hanchan,
|
||||||
|
form=HanchanForm,
|
||||||
|
extra=1,
|
||||||
|
can_delete=True)
|
||||||
|
|||||||
Binary file not shown.
@@ -7,8 +7,8 @@ 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-04-27 09:49+0200\n"
|
"POT-Creation-Date: 2023-08-07 20:38+0200\n"
|
||||||
"PO-Revision-Date: 2018-04-27 09:54+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"
|
||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
@@ -17,7 +17,7 @@ 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.14\n"
|
"X-Translated-Using: django-rosetta 0.8.1\n"
|
||||||
|
|
||||||
#: admin.py:26
|
#: admin.py:26
|
||||||
msgid "Recalculate"
|
msgid "Recalculate"
|
||||||
@@ -28,14 +28,14 @@ msgid "Confirm"
|
|||||||
msgstr "Bestätigen"
|
msgstr "Bestätigen"
|
||||||
|
|
||||||
#: admin.py:46
|
#: admin.py:46
|
||||||
msgid "Set unconfirmed"
|
msgid "Reject"
|
||||||
msgstr "Als unbestätigt markieren"
|
msgstr ""
|
||||||
|
|
||||||
#: forms.py:21
|
#: forms.py:22
|
||||||
msgid "start"
|
msgid "start"
|
||||||
msgstr "Beginn"
|
msgstr "Beginn"
|
||||||
|
|
||||||
#: models.py:91 templates/mahjong_ranking/player_dan_score.html:14
|
#: models.py:93 templates/mahjong_ranking/player_dan_score.html:14
|
||||||
#: templates/mahjong_ranking/player_invalid_score.html:13
|
#: templates/mahjong_ranking/player_invalid_score.html:13
|
||||||
#: templates/mahjong_ranking/player_kyu_score.html:15
|
#: templates/mahjong_ranking/player_kyu_score.html:15
|
||||||
#: templates/mahjong_ranking/player_ladder_score.html:15
|
#: templates/mahjong_ranking/player_ladder_score.html:15
|
||||||
@@ -43,98 +43,141 @@ msgstr "Beginn"
|
|||||||
msgid "Start"
|
msgid "Start"
|
||||||
msgstr "Beginn"
|
msgstr "Beginn"
|
||||||
|
|
||||||
#: 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."
|
||||||
|
|
||||||
#: models.py:99
|
#: models.py:101
|
||||||
msgid "Player 1"
|
msgid "Player 1"
|
||||||
msgstr "Spieler 1"
|
msgstr "Spieler 1"
|
||||||
|
|
||||||
#: models.py:100 models.py:102 models.py:119 models.py:121 models.py:138
|
#: models.py:102 models.py:104 models.py:121 models.py:123 models.py:140
|
||||||
#: models.py:140 models.py:157 models.py:159
|
#: models.py:142 models.py:159 models.py:161
|
||||||
#: templates/mahjong_ranking/eventhanchan_list.html:19
|
#: templates/mahjong_ranking/eventhanchan_list.html:19
|
||||||
#: templates/mahjong_ranking/eventranking_list.html:21
|
#: templates/mahjong_ranking/eventranking_list.html:21
|
||||||
#: templates/mahjong_ranking/hanchan_confirm_delete.html:16
|
#: templates/mahjong_ranking/hanchan_confirm_delete.html:16
|
||||||
#: templates/mahjong_ranking/hanchan_form.html:19
|
#: templates/mahjong_ranking/hanchan_form.html:19
|
||||||
#: templates/mahjong_ranking/kyudanranking_list.html:35
|
#: templates/mahjong_ranking/kyudanranking_list.html:30
|
||||||
#: templates/mahjong_ranking/seasonranking_list.html:32
|
#: templates/mahjong_ranking/seasonranking_list.html:31
|
||||||
msgid "Score"
|
msgid "Score"
|
||||||
msgstr "Punkte"
|
msgstr "Punkte"
|
||||||
|
|
||||||
#: models.py:112 models.py:131 models.py:150 models.py:169 models.py:171
|
#: models.py:114 models.py:133 models.py:152 models.py:171 models.py:173
|
||||||
#: templates/mahjong_ranking/hanchan_form.html:20
|
#: templates/mahjong_ranking/hanchan_form.html:20
|
||||||
#: templates/mahjong_ranking/player_dan_score.html:18
|
#: templates/mahjong_ranking/player_dan_score.html:18
|
||||||
#: templates/mahjong_ranking/player_invalid_score.html:17
|
#: templates/mahjong_ranking/player_invalid_score.html:17
|
||||||
msgid "Comment"
|
msgid "Comment"
|
||||||
msgstr "Kommentar"
|
msgstr "Kommentar"
|
||||||
|
|
||||||
#: models.py:118
|
#: models.py:120
|
||||||
msgid "Player 2"
|
msgid "Player 2"
|
||||||
msgstr "Spieler 2"
|
msgstr "Spieler 2"
|
||||||
|
|
||||||
#: models.py:137
|
#: models.py:139
|
||||||
msgid "Player 3"
|
msgid "Player 3"
|
||||||
msgstr "Spieler 3"
|
msgstr "Spieler 3"
|
||||||
|
|
||||||
#: models.py:156
|
#: models.py:158
|
||||||
msgid "Player 4"
|
msgid "Player 4"
|
||||||
msgstr "Spieler 4"
|
msgstr "Spieler 4"
|
||||||
|
|
||||||
#: models.py:173
|
#: models.py:175
|
||||||
msgid "Has been Confirmed"
|
msgid "Has been Confirmed"
|
||||||
msgstr "Wurde bestätigt"
|
msgstr "Wurde bestätigt"
|
||||||
|
|
||||||
#: 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."
|
||||||
|
|
||||||
#: models.py:179 models.py:603 templates/mahjong_ranking/ladder_redbox.html:29
|
#: models.py:181 models.py:619 templates/mahjong_ranking/ladder_redbox.html:29
|
||||||
#: templates/mahjong_ranking/player_ladder_score.html:63
|
#: templates/mahjong_ranking/player_ladder_score.html:63
|
||||||
msgid "Season"
|
msgid "Season"
|
||||||
msgstr "Saison"
|
msgstr "Saison"
|
||||||
|
|
||||||
#: models.py:184
|
#: models.py:186
|
||||||
msgid "Hanchan"
|
msgid "Hanchan"
|
||||||
msgstr "Hanchan"
|
msgstr "Hanchan"
|
||||||
|
|
||||||
#: models.py:185 templates/mahjong_ranking/eventranking_list.html:17
|
#: models.py:187 templates/mahjong_ranking/eventranking_list.html:17
|
||||||
msgid "Hanchans"
|
msgid "Hanchans"
|
||||||
msgstr "Hanchans"
|
msgstr "Hanchans"
|
||||||
|
|
||||||
#: 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}"
|
||||||
|
|
||||||
#: 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."
|
||||||
|
|
||||||
#: 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."
|
||||||
|
|
||||||
#: 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."
|
||||||
|
|
||||||
#: 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"
|
||||||
|
|
||||||
#: 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."
|
||||||
|
|
||||||
#: models.py:356
|
#: models.py:367
|
||||||
msgid "Kyū/Dan Ranking"
|
msgid "Kyū/Dan Ranking"
|
||||||
msgstr "Kyū/Dan Wertung"
|
msgstr "Kyū/Dan Wertung"
|
||||||
|
|
||||||
#: models.py:357
|
#: models.py:368
|
||||||
msgid "Kyū/Dan Rankings"
|
msgid "Kyū/Dan Rankings"
|
||||||
msgstr "Kyū/Dan Wertungen"
|
msgstr "Kyū/Dan Wertungen"
|
||||||
|
|
||||||
|
#: templates/mahjong_ranking/eventhanchan_form.html:11
|
||||||
|
#: templates/mahjong_ranking/eventhanchan_list.html:55
|
||||||
|
msgid "Edit Hanchans"
|
||||||
|
msgstr "Hanchans bearbeiten"
|
||||||
|
|
||||||
|
#: templates/mahjong_ranking/eventhanchan_form.html:49
|
||||||
|
#: templates/mahjong_ranking/hanchan_form.html:58
|
||||||
|
msgid "Total"
|
||||||
|
msgstr "Total"
|
||||||
|
|
||||||
|
#: templates/mahjong_ranking/eventhanchan_form.html:51
|
||||||
|
msgid "Difference"
|
||||||
|
msgstr "Unterschied"
|
||||||
|
|
||||||
|
#: templates/mahjong_ranking/eventhanchan_form.html:103
|
||||||
|
#: templates/mahjong_ranking/eventhanchan_list.html:56
|
||||||
|
#: templates/mahjong_ranking/eventranking_list.html:52
|
||||||
|
#: templates/mahjong_ranking/hanchan_form.html:4
|
||||||
|
#: templates/mahjong_ranking/hanchan_form.html:14
|
||||||
|
msgid "Add Hanchan"
|
||||||
|
msgstr "Hanchan hinzufügen"
|
||||||
|
|
||||||
|
#: templates/mahjong_ranking/eventhanchan_form.html:105
|
||||||
|
#: templates/mahjong_ranking/eventhanchan_list.html:37
|
||||||
|
#: templates/mahjong_ranking/hanchan_confirm_delete.html:4
|
||||||
|
#: templates/mahjong_ranking/hanchan_confirm_delete.html:33
|
||||||
|
#: templates/mahjong_ranking/player_dan_score.html:44
|
||||||
|
#: templates/mahjong_ranking/player_invalid_score.html:33
|
||||||
|
#: templates/mahjong_ranking/player_kyu_score.html:41
|
||||||
|
#: templates/mahjong_ranking/player_ladder_score.html:52
|
||||||
|
msgid "Delete Hanchan"
|
||||||
|
msgstr "Hanchan löschen"
|
||||||
|
|
||||||
|
#: templates/mahjong_ranking/eventhanchan_form.html:118
|
||||||
|
#: templates/mahjong_ranking/hanchan_form.html:71
|
||||||
|
msgid "back"
|
||||||
|
msgstr "Zurück"
|
||||||
|
|
||||||
|
#: templates/mahjong_ranking/eventhanchan_form.html:119
|
||||||
|
#: templates/mahjong_ranking/hanchan_form.html:72
|
||||||
|
msgid "save"
|
||||||
|
msgstr "Speichern"
|
||||||
|
|
||||||
#: 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"
|
||||||
@@ -157,16 +200,6 @@ msgstr "Dan Punkte"
|
|||||||
msgid "Kyu Points"
|
msgid "Kyu Points"
|
||||||
msgstr "Kyu Punkte"
|
msgstr "Kyu Punkte"
|
||||||
|
|
||||||
#: templates/mahjong_ranking/eventhanchan_list.html:37
|
|
||||||
#: templates/mahjong_ranking/hanchan_confirm_delete.html:4
|
|
||||||
#: templates/mahjong_ranking/hanchan_confirm_delete.html:33
|
|
||||||
#: templates/mahjong_ranking/player_dan_score.html:44
|
|
||||||
#: templates/mahjong_ranking/player_invalid_score.html:33
|
|
||||||
#: templates/mahjong_ranking/player_kyu_score.html:41
|
|
||||||
#: templates/mahjong_ranking/player_ladder_score.html:52
|
|
||||||
msgid "Delete Hanchan"
|
|
||||||
msgstr "Hanchan löschen"
|
|
||||||
|
|
||||||
#: templates/mahjong_ranking/eventhanchan_list.html:43
|
#: templates/mahjong_ranking/eventhanchan_list.html:43
|
||||||
#: templates/mahjong_ranking/hanchan_form.html:4
|
#: templates/mahjong_ranking/hanchan_form.html:4
|
||||||
#: templates/mahjong_ranking/hanchan_form.html:14
|
#: templates/mahjong_ranking/hanchan_form.html:14
|
||||||
@@ -186,20 +219,13 @@ msgstr "Für diese Veranstaltung wurde noch keine Hanchan eingetragen."
|
|||||||
msgid "Edit Event"
|
msgid "Edit Event"
|
||||||
msgstr "Veranstaltung bearbeiten"
|
msgstr "Veranstaltung bearbeiten"
|
||||||
|
|
||||||
#: templates/mahjong_ranking/eventhanchan_list.html:55
|
|
||||||
#: templates/mahjong_ranking/eventranking_list.html:52
|
|
||||||
#: templates/mahjong_ranking/hanchan_form.html:4
|
|
||||||
#: templates/mahjong_ranking/hanchan_form.html:14
|
|
||||||
msgid "Add Hanchan"
|
|
||||||
msgstr "Hanchan hinzufügen"
|
|
||||||
|
|
||||||
#: templates/mahjong_ranking/eventranking_list.html:4
|
#: templates/mahjong_ranking/eventranking_list.html:4
|
||||||
#: templates/mahjong_ranking/eventranking_list.html:5
|
#: templates/mahjong_ranking/eventranking_list.html:5
|
||||||
msgid "Tournament Ranking"
|
msgid "Tournament Ranking"
|
||||||
msgstr "Turnierwertung"
|
msgstr "Turnierwertung"
|
||||||
|
|
||||||
#: templates/mahjong_ranking/eventranking_list.html:12
|
#: templates/mahjong_ranking/eventranking_list.html:12
|
||||||
#: templates/mahjong_ranking/kyudanranking_list.html:30
|
#: templates/mahjong_ranking/kyudanranking_list.html:25
|
||||||
#: templates/mahjong_ranking/seasonranking_list.html:23
|
#: templates/mahjong_ranking/seasonranking_list.html:23
|
||||||
msgid "Rank"
|
msgid "Rank"
|
||||||
msgstr "Rang"
|
msgstr "Rang"
|
||||||
@@ -217,12 +243,11 @@ msgid "Nickname"
|
|||||||
msgstr "Spitzname"
|
msgstr "Spitzname"
|
||||||
|
|
||||||
#: templates/mahjong_ranking/eventranking_list.html:15
|
#: templates/mahjong_ranking/eventranking_list.html:15
|
||||||
#: templates/mahjong_ranking/seasonranking_list.html:26
|
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Name"
|
msgstr "Name"
|
||||||
|
|
||||||
#: templates/mahjong_ranking/eventranking_list.html:16
|
#: templates/mahjong_ranking/eventranking_list.html:16
|
||||||
#: templates/mahjong_ranking/seasonranking_list.html:27
|
#: templates/mahjong_ranking/seasonranking_list.html:26
|
||||||
msgid "Average"
|
msgid "Average"
|
||||||
msgstr "Durchschnitt"
|
msgstr "Durchschnitt"
|
||||||
|
|
||||||
@@ -231,22 +256,22 @@ msgstr "Durchschnitt"
|
|||||||
#: templates/mahjong_ranking/player_invalid_score.html:15
|
#: templates/mahjong_ranking/player_invalid_score.html:15
|
||||||
#: templates/mahjong_ranking/player_kyu_score.html:16
|
#: templates/mahjong_ranking/player_kyu_score.html:16
|
||||||
#: templates/mahjong_ranking/player_ladder_score.html:16
|
#: templates/mahjong_ranking/player_ladder_score.html:16
|
||||||
#: templates/mahjong_ranking/seasonranking_list.html:31
|
#: templates/mahjong_ranking/seasonranking_list.html:30
|
||||||
msgid "Placement"
|
msgid "Placement"
|
||||||
msgstr "Platzierung"
|
msgstr "Platzierung"
|
||||||
|
|
||||||
#: templates/mahjong_ranking/eventranking_list.html:22
|
#: templates/mahjong_ranking/eventranking_list.html:22
|
||||||
#: templates/mahjong_ranking/seasonranking_list.html:33
|
#: templates/mahjong_ranking/seasonranking_list.html:32
|
||||||
msgid "count"
|
msgid "count"
|
||||||
msgstr "Anzahl"
|
msgstr "Anzahl"
|
||||||
|
|
||||||
#: templates/mahjong_ranking/eventranking_list.html:23
|
#: templates/mahjong_ranking/eventranking_list.html:23
|
||||||
#: templates/mahjong_ranking/seasonranking_list.html:34
|
#: templates/mahjong_ranking/seasonranking_list.html:33
|
||||||
msgid "good"
|
msgid "good"
|
||||||
msgstr "gut"
|
msgstr "gut"
|
||||||
|
|
||||||
#: templates/mahjong_ranking/eventranking_list.html:24
|
#: templates/mahjong_ranking/eventranking_list.html:24
|
||||||
#: templates/mahjong_ranking/seasonranking_list.html:35
|
#: templates/mahjong_ranking/seasonranking_list.html:34
|
||||||
msgid "won"
|
msgid "won"
|
||||||
msgstr "gewonnen"
|
msgstr "gewonnen"
|
||||||
|
|
||||||
@@ -262,32 +287,12 @@ msgstr "Löschen"
|
|||||||
msgid "Player"
|
msgid "Player"
|
||||||
msgstr "Spieler"
|
msgstr "Spieler"
|
||||||
|
|
||||||
#: templates/mahjong_ranking/hanchan_form.html:58
|
|
||||||
msgid "Total"
|
|
||||||
msgstr "Total"
|
|
||||||
|
|
||||||
#: templates/mahjong_ranking/hanchan_form.html:71
|
|
||||||
msgid "back"
|
|
||||||
msgstr "Zurück"
|
|
||||||
|
|
||||||
#: templates/mahjong_ranking/hanchan_form.html:72
|
|
||||||
msgid "save"
|
|
||||||
msgstr "Speichern"
|
|
||||||
|
|
||||||
#: templates/mahjong_ranking/kyudanranking_list.html:4
|
#: templates/mahjong_ranking/kyudanranking_list.html:4
|
||||||
#| msgid "Player List"
|
#: templates/mahjong_ranking/kyudanranking_list.html:9
|
||||||
msgid "Players list"
|
msgid "Players list"
|
||||||
msgstr "Spielerliste"
|
msgstr "Spielerliste"
|
||||||
|
|
||||||
#: templates/mahjong_ranking/kyudanranking_list.html:9
|
#: templates/mahjong_ranking/kyudanranking_list.html:35
|
||||||
msgid "Player List"
|
|
||||||
msgstr "Spieler Liste"
|
|
||||||
|
|
||||||
#: templates/mahjong_ranking/kyudanranking_list.html:25
|
|
||||||
msgid "Full Name"
|
|
||||||
msgstr "Voller Name"
|
|
||||||
|
|
||||||
#: templates/mahjong_ranking/kyudanranking_list.html:40
|
|
||||||
msgid "Games Total"
|
msgid "Games Total"
|
||||||
msgstr "Spiele total"
|
msgstr "Spiele total"
|
||||||
|
|
||||||
@@ -371,20 +376,26 @@ msgstr "Ende"
|
|||||||
msgid "Participants"
|
msgid "Participants"
|
||||||
msgstr "Teilnehmer"
|
msgstr "Teilnehmer"
|
||||||
|
|
||||||
#: views.py:102
|
#: 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."
|
||||||
|
|
||||||
#: views.py:105
|
#: 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."
|
||||||
|
|
||||||
#: views.py:169
|
#: 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"
|
||||||
|
#~ msgstr "Voller Name"
|
||||||
|
|
||||||
#~ msgid "Event does not exist"
|
#~ msgid "Event does not exist"
|
||||||
#~ msgstr "Veranstaltung existiert nicht"
|
#~ msgstr "Veranstaltung existiert nicht"
|
||||||
|
|
||||||
|
|||||||
@@ -1,214 +0,0 @@
|
|||||||
"""Export Mahjong Rankings as excel files."""
|
|
||||||
|
|
||||||
import os
|
|
||||||
from datetime import date, time, datetime
|
|
||||||
|
|
||||||
import openpyxl
|
|
||||||
from django.conf import settings
|
|
||||||
from django.core.management.base import BaseCommand
|
|
||||||
from django.utils import timezone
|
|
||||||
from django.utils.dateparse import parse_date
|
|
||||||
from django.core.mail import EmailMessage
|
|
||||||
|
|
||||||
from mahjong_ranking.models import SeasonRanking, KyuDanRanking
|
|
||||||
|
|
||||||
THIN_BORDER = openpyxl.styles.Side(style='thin', color="d3d7cf")
|
|
||||||
|
|
||||||
HEADING_STYLE = openpyxl.styles.NamedStyle(name="heading")
|
|
||||||
HEADING_STYLE.font = openpyxl.styles.Font(name='Philosopher', size=11,
|
|
||||||
bold=True, color='ffffff')
|
|
||||||
HEADING_STYLE.fill = openpyxl.styles.PatternFill(fill_type='solid',
|
|
||||||
start_color='a40000',
|
|
||||||
end_color='a40000')
|
|
||||||
|
|
||||||
DEFAULT_STYLE = openpyxl.styles.NamedStyle(name='content')
|
|
||||||
DEFAULT_STYLE.font = openpyxl.styles.Font(name='Philosopher', size=10,
|
|
||||||
bold=False, color='000000')
|
|
||||||
DEFAULT_STYLE.border = openpyxl.styles.Border(bottom=THIN_BORDER,
|
|
||||||
top=THIN_BORDER)
|
|
||||||
|
|
||||||
INT_STYLE = openpyxl.styles.NamedStyle(name='int')
|
|
||||||
INT_STYLE.font = DEFAULT_STYLE.font
|
|
||||||
INT_STYLE.border = DEFAULT_STYLE.border
|
|
||||||
INT_STYLE.number_format = '#,##0'
|
|
||||||
|
|
||||||
FLOAT_STYLE = openpyxl.styles.NamedStyle(name='float')
|
|
||||||
FLOAT_STYLE.font = DEFAULT_STYLE.font
|
|
||||||
FLOAT_STYLE.border = DEFAULT_STYLE.border
|
|
||||||
FLOAT_STYLE.number_format = '#,##0.00'
|
|
||||||
|
|
||||||
DATE_STYLE = openpyxl.styles.NamedStyle(name='date')
|
|
||||||
DATE_STYLE.font = DEFAULT_STYLE.font
|
|
||||||
DATE_STYLE.border = DEFAULT_STYLE.border
|
|
||||||
DATE_STYLE.number_format = 'dd.mm.yyyy'
|
|
||||||
|
|
||||||
MAIL_BODY = """
|
|
||||||
Hallo! Ich bin's dein Server.
|
|
||||||
|
|
||||||
Ich habe gerade die Mahjong Rankings als Excel exportiert und dachte mir das
|
|
||||||
ich sie dir am besten gleich schicke.
|
|
||||||
|
|
||||||
Bitte versuche nicht auf diese E-Mail zu antworten.
|
|
||||||
Ich bin nur ein dummes Programm.
|
|
||||||
|
|
||||||
mit lieben Grüßen
|
|
||||||
|
|
||||||
Der Kasu Server
|
|
||||||
"""
|
|
||||||
def geneate_excel():
|
|
||||||
"""Generate an excel .xlsx spreadsheet from json data of the kyu/dan
|
|
||||||
rankings.
|
|
||||||
|
|
||||||
:param json_data: The ladder ranking as JSON export."""
|
|
||||||
workbook = openpyxl.Workbook()
|
|
||||||
workbook.add_named_style(HEADING_STYLE)
|
|
||||||
workbook.add_named_style(DEFAULT_STYLE)
|
|
||||||
workbook.add_named_style(INT_STYLE)
|
|
||||||
workbook.add_named_style(FLOAT_STYLE)
|
|
||||||
workbook.add_named_style(DATE_STYLE)
|
|
||||||
|
|
||||||
for sheet in workbook.worksheets:
|
|
||||||
workbook.remove(sheet)
|
|
||||||
return workbook
|
|
||||||
|
|
||||||
|
|
||||||
def generate_sheet(workbook, title, columns_settings, json_data):
|
|
||||||
row = 1
|
|
||||||
ws = workbook.create_sheet()
|
|
||||||
ws.title = title
|
|
||||||
ws.syncHorizontal = True
|
|
||||||
ws.filterMode = True
|
|
||||||
|
|
||||||
# setup print orientation
|
|
||||||
ws.page_setup.orientation = ws.ORIENTATION_PORTRAIT
|
|
||||||
ws.page_setup.paperSize = ws.PAPERSIZE_A4
|
|
||||||
ws.page_setup.fitToWidth = True
|
|
||||||
ws.print_options.horizontalCentered = True
|
|
||||||
|
|
||||||
# setup page header
|
|
||||||
ws.oddHeader.left.text = title
|
|
||||||
ws.oddHeader.left.size = 14
|
|
||||||
ws.oddHeader.left.font = "Amerika Sans"
|
|
||||||
ws.oddHeader.left.color = "000000"
|
|
||||||
|
|
||||||
ws.oddHeader.right.text = str(date.today())
|
|
||||||
ws.oddHeader.right.size = 14
|
|
||||||
ws.oddHeader.right.font = "Amerika Sans"
|
|
||||||
ws.oddHeader.right.color = "000000"
|
|
||||||
|
|
||||||
# write table header
|
|
||||||
for column, data in enumerate(columns_settings, 1):
|
|
||||||
cell = ws.cell(column=column, row=row, value=data['title'])
|
|
||||||
cell.style = 'heading'
|
|
||||||
|
|
||||||
# write the table content
|
|
||||||
for line in json_data:
|
|
||||||
row += 1
|
|
||||||
for column, settings in enumerate(columns_settings, 1):
|
|
||||||
cell = ws.cell(column=column, row=row, value=line[settings['attr']])
|
|
||||||
cell.style = settings['style']
|
|
||||||
|
|
||||||
# set column widths
|
|
||||||
for settings in columns_settings:
|
|
||||||
ws.column_dimensions[settings['col']].width = settings['width']
|
|
||||||
|
|
||||||
|
|
||||||
def export_season_rankings(workbook, until):
|
|
||||||
SeasonRanking.objects.update(until=until)
|
|
||||||
json_data = SeasonRanking.objects.json_data()
|
|
||||||
title = "Mahjong Ladder - {}".format(until.year)
|
|
||||||
columns_settings = (
|
|
||||||
{'col': 'A', 'title': 'Rang', 'attr': 'placement', 'style': 'int',
|
|
||||||
'width': 8},
|
|
||||||
{'col': 'B', 'title': 'Spitzname', 'attr': 'username',
|
|
||||||
'style': 'content',
|
|
||||||
'width': 25},
|
|
||||||
{'col': 'C', 'title': '⌀ Platz', 'attr': 'avg_placement',
|
|
||||||
'style': 'float', 'width': 8},
|
|
||||||
{'col': 'D', 'title': '⌀ Punkte', 'attr': 'avg_score',
|
|
||||||
'style': 'float', 'width': 12},
|
|
||||||
{'col': 'E', 'title': 'Hanchans', 'attr': 'hanchan_count',
|
|
||||||
'style': 'int', 'width': 10},
|
|
||||||
{'col': 'F', 'title': 'Gut', 'attr': 'good_hanchans',
|
|
||||||
'style': 'int', 'width': 5},
|
|
||||||
{'col': 'G', 'title': 'Gewonnen', 'attr': 'won_hanchans',
|
|
||||||
'style': 'int', 'width': 10},
|
|
||||||
)
|
|
||||||
generate_sheet(
|
|
||||||
workbook=workbook,
|
|
||||||
title=title,
|
|
||||||
columns_settings=columns_settings,
|
|
||||||
json_data=json_data)
|
|
||||||
|
|
||||||
|
|
||||||
def export_kyu_dan_rankings(workbook, until):
|
|
||||||
KyuDanRanking.objects.update(until=until)
|
|
||||||
json_data = KyuDanRanking.objects.json_data()
|
|
||||||
title = "Kyū & Dan Rankings"
|
|
||||||
columns_settings = (
|
|
||||||
{'col': 'A', 'title': 'Spitzname', 'attr': 'username',
|
|
||||||
'style': 'content', 'width': 14},
|
|
||||||
{'col': 'B', 'title': 'Voller Name', 'attr': 'full_name',
|
|
||||||
'style': 'content', 'width': 20},
|
|
||||||
{'col': 'C', 'title': 'Rang', 'attr': 'rank',
|
|
||||||
'style': 'content', 'width': 8},
|
|
||||||
{'col': 'D', 'title': 'Punkte', 'attr': 'points',
|
|
||||||
'style': 'int', 'width': 8},
|
|
||||||
{'col': 'E', 'title': 'Hanchans', 'attr': 'hanchan_count',
|
|
||||||
'style': 'int', 'width': 10},
|
|
||||||
{'col': 'F', 'title': 'Gut', 'attr': 'good_hanchans',
|
|
||||||
'style': 'int', 'width': 5},
|
|
||||||
{'col': 'G', 'title': 'Gewonnen', 'attr': 'won_hanchans',
|
|
||||||
'style': 'int', 'width': 8},
|
|
||||||
{'col': 'H', 'title': 'letzte Hanchan', 'attr': 'last_hanchan_date',
|
|
||||||
'style': 'date', 'width': 16},
|
|
||||||
)
|
|
||||||
generate_sheet(
|
|
||||||
workbook=workbook,
|
|
||||||
title=title,
|
|
||||||
columns_settings=columns_settings,
|
|
||||||
json_data=json_data)
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
"""Exports the SeasonRankings"""
|
|
||||||
filename = str()
|
|
||||||
until = datetime
|
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
|
||||||
parser.add_argument(
|
|
||||||
'--until', nargs='?', type=parse_date,
|
|
||||||
default=date.today(), metavar='YYYY-MM-DD',
|
|
||||||
help='Calculate and export rankings until the given date.')
|
|
||||||
parser.add_argument(
|
|
||||||
'--mail', nargs='*', type=str, metavar='user@example.com',
|
|
||||||
help='Send the spreadsheet via eMail to the given recipient.')
|
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
|
||||||
"""Exports the current ladder ranking in a spreadsheet.
|
|
||||||
This is useful as a backup in form of a hardcopy."""
|
|
||||||
self.until = timezone.make_aware(datetime.combine(
|
|
||||||
options['until'], time(23, 59, 59)
|
|
||||||
))
|
|
||||||
|
|
||||||
self.filename = os.path.join(
|
|
||||||
settings.RANKING_EXPORT_PATH,
|
|
||||||
'mahjong_rankings_{:%Y-%m-%d}.xlsx'.format(self.until)
|
|
||||||
)
|
|
||||||
workbook = geneate_excel()
|
|
||||||
export_season_rankings(workbook, until=self.until)
|
|
||||||
export_kyu_dan_rankings(workbook, until=self.until)
|
|
||||||
os.makedirs(settings.RANKING_EXPORT_PATH, exist_ok=True)
|
|
||||||
workbook.save(self.filename)
|
|
||||||
if options['mail']:
|
|
||||||
self.send_mail(options['mail'])
|
|
||||||
|
|
||||||
def send_mail(self, recipients):
|
|
||||||
mail = EmailMessage(
|
|
||||||
subject='Mahjong Rankings vom {:%d.%m.%Y}'.format(self.until),
|
|
||||||
body=MAIL_BODY,
|
|
||||||
from_email=settings.DEFAULT_FROM_EMAIL,
|
|
||||||
to=recipients)
|
|
||||||
mail.attach_file(self.filename)
|
|
||||||
mail.send()
|
|
||||||
|
|
||||||
126
src/mahjong_ranking/management/commands/exportranking.py
Normal file
126
src/mahjong_ranking/management/commands/exportranking.py
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
"""Export Mahjong Rankings as Excel files."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from datetime import date, time, datetime
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.mail import EmailMessage
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.utils.dateparse import parse_date
|
||||||
|
|
||||||
|
from kasu import xlsx
|
||||||
|
from mahjong_ranking.models import SeasonRanking, KyuDanRanking
|
||||||
|
|
||||||
|
MAIL_BODY = """
|
||||||
|
Hallo! Ich bin's dein Server.
|
||||||
|
|
||||||
|
Ich habe gerade die Mahjong Rankings als Excel exportiert und dachte mir das
|
||||||
|
ich sie dir am besten gleich schicke.
|
||||||
|
|
||||||
|
Bitte versuche nicht auf diese E-Mail zu antworten.
|
||||||
|
Ich bin nur ein dummes Programm.
|
||||||
|
|
||||||
|
mit lieben Grüßen
|
||||||
|
|
||||||
|
Der Kasu Server
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def export_season_rankings(workbook, until):
|
||||||
|
SeasonRanking.objects.update(until=until)
|
||||||
|
season = until.year if until else date.today().year
|
||||||
|
object_list = SeasonRanking.objects.season_rankings()
|
||||||
|
title = "Mahjong Ladder - {}".format(season)
|
||||||
|
columns_settings = (
|
||||||
|
{'col': 'A', 'title': 'Rang', 'attr': 'placement', 'style': 'Integer',
|
||||||
|
'width': 8},
|
||||||
|
{'col': 'B', 'title': 'Spitzname', 'attr': 'user.username',
|
||||||
|
'style': 'Content',
|
||||||
|
'width': 25},
|
||||||
|
{'col': 'C', 'title': '⌀ Platz', 'attr': 'avg_placement',
|
||||||
|
'style': 'Float', 'width': 8},
|
||||||
|
{'col': 'D', 'title': '⌀ Punkte', 'attr': 'avg_score',
|
||||||
|
'style': 'Float', 'width': 12},
|
||||||
|
{'col': 'E', 'title': 'Hanchans', 'attr': 'hanchan_count',
|
||||||
|
'style': 'Integer', 'width': 10},
|
||||||
|
{'col': 'F', 'title': 'Gut', 'attr': 'good_hanchans',
|
||||||
|
'style': 'Integer', 'width': 5},
|
||||||
|
{'col': 'G', 'title': 'Gewonnen', 'attr': 'won_hanchans',
|
||||||
|
'style': 'Integer', 'width': 10},
|
||||||
|
)
|
||||||
|
workbook.generate_sheet(
|
||||||
|
title=title,
|
||||||
|
columns_settings=columns_settings,
|
||||||
|
object_list=object_list)
|
||||||
|
|
||||||
|
|
||||||
|
def export_kyu_dan_rankings(workbook, until):
|
||||||
|
KyuDanRanking.objects.update(until=until, force_recalc=True)
|
||||||
|
object_list = KyuDanRanking.objects.all()
|
||||||
|
title = "Kyū & Dan Rankings"
|
||||||
|
columns_settings = (
|
||||||
|
{'col': 'A', 'title': 'Spitzname', 'attr': 'user.username',
|
||||||
|
'style': 'Content', 'width': 14},
|
||||||
|
{'col': 'B', 'title': 'Voller Name', 'attr': 'user.full_name',
|
||||||
|
'style': 'Content', 'width': 20},
|
||||||
|
{'col': 'C', 'title': 'Rang', 'attr': 'rank',
|
||||||
|
'style': 'Content', 'width': 8},
|
||||||
|
{'col': 'D', 'title': 'Punkte', 'attr': 'points',
|
||||||
|
'style': 'Integer', 'width': 8},
|
||||||
|
{'col': 'E', 'title': 'Hanchans', 'attr': 'hanchan_count',
|
||||||
|
'style': 'Integer', 'width': 10},
|
||||||
|
{'col': 'F', 'title': 'Gut', 'attr': 'good_hanchans',
|
||||||
|
'style': 'Integer', 'width': 5},
|
||||||
|
{'col': 'G', 'title': 'Gewonnen', 'attr': 'won_hanchans',
|
||||||
|
'style': 'Integer', 'width': 10},
|
||||||
|
{'col': 'H', 'title': 'letzte Hanchan', 'attr': 'last_hanchan_date',
|
||||||
|
'style': 'Date', 'width': 16},
|
||||||
|
)
|
||||||
|
workbook.generate_sheet(
|
||||||
|
title=title,
|
||||||
|
columns_settings=columns_settings,
|
||||||
|
object_list=object_list)
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
"""Exports the SeasonRankings"""
|
||||||
|
filename = str()
|
||||||
|
until = datetime
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
'--until', nargs='?', type=parse_date,
|
||||||
|
default=date.today(), metavar='YYYY-MM-DD',
|
||||||
|
help='Calculate and export rankings until the given date.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--mail', nargs='*', type=str, metavar='user@example.com',
|
||||||
|
help='Send the spreadsheet via eMail to the given recipient.')
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
"""Exports the current ladder ranking in a spreadsheet.
|
||||||
|
This is useful as a backup in form of a hardcopy."""
|
||||||
|
self.until = timezone.make_aware(datetime.combine(
|
||||||
|
options['until'], time(23, 59, 59)
|
||||||
|
))
|
||||||
|
|
||||||
|
self.filename = os.path.join(
|
||||||
|
settings.RANKING_EXPORT_PATH,
|
||||||
|
'mahjong_rankings_{:%Y-%m-%d}.xlsx'.format(self.until)
|
||||||
|
)
|
||||||
|
workbook = xlsx.Workbook()
|
||||||
|
export_season_rankings(workbook, until=self.until)
|
||||||
|
export_kyu_dan_rankings(workbook, until=self.until)
|
||||||
|
os.makedirs(settings.RANKING_EXPORT_PATH, exist_ok=True)
|
||||||
|
workbook.save(self.filename)
|
||||||
|
if options['mail']:
|
||||||
|
self.send_mail(options['mail'])
|
||||||
|
|
||||||
|
def send_mail(self, recipients):
|
||||||
|
mail = EmailMessage(
|
||||||
|
subject='Mahjong Rankings vom {:%d.%m.%Y}'.format(self.until),
|
||||||
|
body=MAIL_BODY,
|
||||||
|
from_email=settings.DEFAULT_FROM_EMAIL,
|
||||||
|
to=recipients)
|
||||||
|
mail.attach_file(self.filename)
|
||||||
|
mail.send()
|
||||||
58
src/mahjong_ranking/management/commands/fixateranking.py
Normal file
58
src/mahjong_ranking/management/commands/fixateranking.py
Normal 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()
|
||||||
@@ -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."
|
||||||
|
|
||||||
@@ -17,16 +19,23 @@ class Command(BaseCommand):
|
|||||||
parser.add_argument('reset_date', type=parse_date)
|
parser.add_argument('reset_date', type=parse_date)
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
reset_date = timezone.make_aware(datetime.combine(options.get('reset_date'), time(23, 59, 59)))
|
legacy_attrs = [key for key in models.KyuDanRanking.__dict__.keys()
|
||||||
# models.KyuDanRanking.objects.update(until=reset_date, force_recalc=True)
|
if key.startswith('legacy')]
|
||||||
dan_rankigns = models.KyuDanRanking.objects.filter(dan__isnull=False)
|
legacy_attrs.remove('legacy_date')
|
||||||
for ranking in dan_rankigns:
|
reset_date = timezone.make_aware(datetime.combine(
|
||||||
|
options.get('reset_date'), time(23, 59, 59)))
|
||||||
|
models.KyuDanRanking.objects.update(
|
||||||
|
until=reset_date, force_recalc=True)
|
||||||
|
for ranking in models.KyuDanRanking.objects.filter(dan__gt=0):
|
||||||
|
print(ranking)
|
||||||
ranking.dan = 1
|
ranking.dan = 1
|
||||||
ranking.dan_points = 0
|
ranking.dan_points = 0
|
||||||
|
ranking.kyu = None
|
||||||
|
ranking.kyu_points = 0
|
||||||
|
ranking.wins_in_a_row = 0
|
||||||
ranking.legacy_date = reset_date.date()
|
ranking.legacy_date = reset_date.date()
|
||||||
ranking.legacy_hanchan_count = ranking.hanchan_count
|
for legacy_attr in legacy_attrs:
|
||||||
ranking.legacy_dan_points = ranking.dan_points
|
attr = legacy_attr.split("_", maxsplit=1)[1]
|
||||||
ranking.legacy_kyu_points = ranking.kyu_points
|
print(ranking, legacy_attr, attr, getattr(ranking, attr))
|
||||||
|
setattr(ranking, legacy_attr, getattr(ranking, attr))
|
||||||
ranking.save()
|
ranking.save()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,30 +4,39 @@
|
|||||||
Recalculate Mahjong Rankings...
|
Recalculate Mahjong Rankings...
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
|
||||||
from datetime import date, datetime, time
|
from datetime import date, datetime, time
|
||||||
from mahjong_ranking import models
|
|
||||||
from django.utils.dateparse import parse_date
|
from django.core.management.base import BaseCommand
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.utils.dateparse import parse_date
|
||||||
|
|
||||||
|
from mahjong_ranking import models
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
""" Recalculate all Kyu/Dan Rankings """
|
""" Recalculate all Kyu/Dan Rankings """
|
||||||
|
|
||||||
help = "Recalculate all Kyu/Dan Rankings"
|
help = "Recalculate the Kyu/Dan Rankings"
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument('--since', nargs='?', type=parse_date)
|
parser.add_argument('-s', '--since', nargs='?', type=parse_date,
|
||||||
parser.add_argument('--until', nargs='?', type=parse_date)
|
metavar='YYYY-MM-DD',
|
||||||
parser.add_argument('--forcerecalc', action='store_true')
|
help='Use all Hanchans since the given date.')
|
||||||
|
parser.add_argument('-u', '--until', nargs='?', type=parse_date,
|
||||||
|
metavar='YYYY-MM-DD',
|
||||||
|
help='Only use Hanchans until the given date.')
|
||||||
|
parser.add_argument('-f', '--force', action='store_true',
|
||||||
|
help="Force the recalculation of all Hanchans.")
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
since = options.get('since', None)
|
since = options.get('since', None)
|
||||||
until = options.get('until', None)
|
until = options.get('until', None)
|
||||||
force_recalc = options.get('forecerecalc', False)
|
force_recalc = options.get('force')
|
||||||
if isinstance(since, date):
|
if isinstance(since, date):
|
||||||
since = datetime.combine(since, time(0, 0, 0))
|
since = datetime.combine(since, time(0, 0, 0))
|
||||||
since = timezone.make_aware(since)
|
since = timezone.make_aware(since)
|
||||||
if isinstance(until, date):
|
if isinstance(until, date):
|
||||||
until = datetime.combine(until, time(23, 59, 59))
|
until = datetime.combine(until, time(23, 59, 59))
|
||||||
until = timezone.make_aware(until)
|
until = timezone.make_aware(until)
|
||||||
models.KyuDanRanking.objects.update(since=since, until=until, force_recalc=force_recalc)
|
models.KyuDanRanking.objects.update(since=since, until=until,
|
||||||
|
force_recalc=force_recalc)
|
||||||
@@ -23,8 +23,8 @@ class HanchanManager(models.Manager):
|
|||||||
:return: QuerySet Object
|
:return: QuerySet Object
|
||||||
"""
|
"""
|
||||||
if user:
|
if user:
|
||||||
return self.user_hanchans(
|
return self.user_hanchans(user, confirmed=True, until=until,
|
||||||
user, confirmed=True, since=since, until=until, **filter_args)
|
**filter_args)
|
||||||
hanchans = self.filter(confirmed=True, **filter_args)
|
hanchans = self.filter(confirmed=True, **filter_args)
|
||||||
if since:
|
if since:
|
||||||
hanchans = hanchans.filter(start__gt=since)
|
hanchans = hanchans.filter(start__gt=since)
|
||||||
@@ -32,7 +32,7 @@ class HanchanManager(models.Manager):
|
|||||||
hanchans = hanchans.filter(start__lte=until)
|
hanchans = hanchans.filter(start__lte=until)
|
||||||
return hanchans
|
return hanchans
|
||||||
|
|
||||||
def dan_hanchans(self, user, since = None, **filter_args):
|
def dan_hanchans(self, user, since=None, **filter_args):
|
||||||
""" Return all Hanchans where a specific user has participated and had
|
""" Return all Hanchans where a specific user has participated and had
|
||||||
gain dan points and make his gamestats availabale.
|
gain dan points and make his gamestats availabale.
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ 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 kyu_hanchans(self, user, since = None, **filter_args):
|
def kyu_hanchans(self, user, since=None, **filter_args):
|
||||||
""" Return all Hanchans where a specific user has participated and had
|
""" Return all Hanchans where a specific user has participated and had
|
||||||
gain kyū points and make his gamestats availabale.
|
gain kyū points and make his gamestats availabale.
|
||||||
|
|
||||||
@@ -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
|
||||||
"""
|
"""
|
||||||
@@ -101,7 +104,7 @@ class HanchanManager(models.Manager):
|
|||||||
)
|
)
|
||||||
queryset = queryset.filter(**filter_args)
|
queryset = queryset.filter(**filter_args)
|
||||||
if since:
|
if since:
|
||||||
queryset = queryset.filter(start__gt=since)
|
queryset = queryset.filter(start__gte=since)
|
||||||
if until:
|
if until:
|
||||||
queryset = queryset.filter(start__lte=until)
|
queryset = queryset.filter(start__lte=until)
|
||||||
queryset = queryset.select_related().order_by('-start')
|
queryset = queryset.select_related().order_by('-start')
|
||||||
@@ -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)}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models, migrations
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.db import models, migrations
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
('events', '0005_auto_20150907_2021'),
|
('events', '0005_auto_20150907_2021'),
|
||||||
@@ -17,15 +16,19 @@ class Migration(migrations.Migration):
|
|||||||
name='EventRanking',
|
name='EventRanking',
|
||||||
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,
|
||||||
('placement', models.PositiveIntegerField(null=True, blank=True)),
|
primary_key=True)),
|
||||||
|
('placement',
|
||||||
|
models.PositiveIntegerField(null=True, blank=True)),
|
||||||
('avg_placement', models.FloatField(default=4)),
|
('avg_placement', models.FloatField(default=4)),
|
||||||
('avg_score', models.FloatField(default=0)),
|
('avg_score', models.FloatField(default=0)),
|
||||||
('hanchan_count', models.PositiveIntegerField(default=0)),
|
('hanchan_count', models.PositiveIntegerField(default=0)),
|
||||||
('good_hanchans', models.PositiveIntegerField(default=0)),
|
('good_hanchans', models.PositiveIntegerField(default=0)),
|
||||||
('won_hanchans', models.PositiveIntegerField(default=0)),
|
('won_hanchans', models.PositiveIntegerField(default=0)),
|
||||||
('event', models.ForeignKey(to='events.Event')),
|
('event', models.ForeignKey(to='events.Event',
|
||||||
('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': ('placement', 'avg_placement', '-avg_score'),
|
'ordering': ('placement', 'avg_placement', '-avg_score'),
|
||||||
@@ -35,10 +38,14 @@ class Migration(migrations.Migration):
|
|||||||
name='Hanchan',
|
name='Hanchan',
|
||||||
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)),
|
||||||
('start', models.DateTimeField(
|
('start', models.DateTimeField(
|
||||||
help_text='Wichtig damit die richtigen Hanchans in die Wertung kommen.', verbose_name='Beginn')),
|
help_text="Wichtig damit die richtigen Hanchans in die " + \
|
||||||
('player1_input_score', models.IntegerField(verbose_name='Punkte')),
|
"Wertung kommen.",
|
||||||
|
verbose_name='Beginn')),
|
||||||
|
('player1_input_score',
|
||||||
|
models.IntegerField(verbose_name='Punkte')),
|
||||||
('player1_game_score', models.PositiveIntegerField(
|
('player1_game_score', models.PositiveIntegerField(
|
||||||
default=0, verbose_name='Punkte', editable=False)),
|
default=0, verbose_name='Punkte', editable=False)),
|
||||||
('player1_placement', models.PositiveSmallIntegerField(
|
('player1_placement', models.PositiveSmallIntegerField(
|
||||||
@@ -49,11 +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, editable=False, blank=True)),
|
max_length=255,
|
||||||
('player2_input_score', models.IntegerField(verbose_name='Punkte')),
|
editable=False,
|
||||||
|
blank=True)),
|
||||||
|
('player2_input_score',
|
||||||
|
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(
|
||||||
@@ -63,8 +73,11 @@ class Migration(migrations.Migration):
|
|||||||
('player2_bonus_points', models.SmallIntegerField(
|
('player2_bonus_points', models.SmallIntegerField(
|
||||||
null=True, editable=False, blank=True)),
|
null=True, editable=False, blank=True)),
|
||||||
('player2_comment', models.CharField(verbose_name='Anmerkung',
|
('player2_comment', models.CharField(verbose_name='Anmerkung',
|
||||||
max_length=255, editable=False, blank=True)),
|
max_length=255,
|
||||||
('player3_input_score', models.IntegerField(verbose_name='Punkte')),
|
editable=False,
|
||||||
|
blank=True)),
|
||||||
|
('player3_input_score',
|
||||||
|
models.IntegerField(verbose_name='Punkte')),
|
||||||
('player3_game_score', models.PositiveIntegerField(
|
('player3_game_score', models.PositiveIntegerField(
|
||||||
default=0, verbose_name='Punkte', editable=False)),
|
default=0, verbose_name='Punkte', editable=False)),
|
||||||
('player3_placement', models.PositiveSmallIntegerField(
|
('player3_placement', models.PositiveSmallIntegerField(
|
||||||
@@ -76,8 +89,11 @@ class Migration(migrations.Migration):
|
|||||||
('player3_bonus_points', models.SmallIntegerField(
|
('player3_bonus_points', models.SmallIntegerField(
|
||||||
null=True, editable=False, blank=True)),
|
null=True, editable=False, blank=True)),
|
||||||
('player3_comment', models.CharField(verbose_name='Anmerkung',
|
('player3_comment', models.CharField(verbose_name='Anmerkung',
|
||||||
max_length=255, editable=False, blank=True)),
|
max_length=255,
|
||||||
('player4_input_score', models.IntegerField(verbose_name='Punkte')),
|
editable=False,
|
||||||
|
blank=True)),
|
||||||
|
('player4_input_score',
|
||||||
|
models.IntegerField(verbose_name='Punkte')),
|
||||||
('player4_game_score', models.PositiveIntegerField(
|
('player4_game_score', models.PositiveIntegerField(
|
||||||
default=0, verbose_name='Punkte', editable=False)),
|
default=0, verbose_name='Punkte', editable=False)),
|
||||||
('player4_placement', models.PositiveSmallIntegerField(
|
('player4_placement', models.PositiveSmallIntegerField(
|
||||||
@@ -89,22 +105,38 @@ class Migration(migrations.Migration):
|
|||||||
('player4_bonus_points', models.SmallIntegerField(
|
('player4_bonus_points', models.SmallIntegerField(
|
||||||
null=True, editable=False, blank=True)),
|
null=True, editable=False, blank=True)),
|
||||||
('player4_comment', models.CharField(verbose_name='Anmerkung',
|
('player4_comment', models.CharField(verbose_name='Anmerkung',
|
||||||
max_length=255, editable=False, blank=True)),
|
max_length=255,
|
||||||
('comment', models.TextField(verbose_name='Anmerkung', blank=True)),
|
editable=False,
|
||||||
|
blank=True)),
|
||||||
|
('comment',
|
||||||
|
models.TextField(verbose_name='Anmerkung', blank=True)),
|
||||||
('confirmed', models.BooleanField(
|
('confirmed', models.BooleanField(
|
||||||
default=True, help_text='Nur g\xfcltige und best\xe4tigte Hanchans kommen in die Wertung.', verbose_name='Wurde best\xe4tigt')),
|
default=True,
|
||||||
('player_names', models.CharField(max_length=255, editable=False)),
|
help_text="Nur g\xfcltige und best\xe4tigte Hanchans kommen " + \
|
||||||
|
"in die Wertung.",
|
||||||
|
verbose_name='Wurde best\xe4tigt')),
|
||||||
|
('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(to='events.Event')),
|
('event', models.ForeignKey(to='events.Event',
|
||||||
|
on_delete=models.CASCADE)),
|
||||||
('player1', models.ForeignKey(related_name='user_hanchan+',
|
('player1', models.ForeignKey(related_name='user_hanchan+',
|
||||||
verbose_name='Spieler 1', to=settings.AUTH_USER_MODEL)),
|
verbose_name='Spieler 1',
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.CASCADE)),
|
||||||
('player2', models.ForeignKey(related_name='user_hanchan+',
|
('player2', models.ForeignKey(related_name='user_hanchan+',
|
||||||
verbose_name='Spieler 2', to=settings.AUTH_USER_MODEL)),
|
verbose_name='Spieler 2',
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.CASCADE)),
|
||||||
('player3', models.ForeignKey(related_name='user_hanchan+',
|
('player3', models.ForeignKey(related_name='user_hanchan+',
|
||||||
verbose_name='Spieler 3', to=settings.AUTH_USER_MODEL)),
|
verbose_name='Spieler 3',
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.CASCADE)),
|
||||||
('player4', models.ForeignKey(related_name='user_hanchan+',
|
('player4', models.ForeignKey(related_name='user_hanchan+',
|
||||||
verbose_name='Spieler 4', to=settings.AUTH_USER_MODEL)),
|
verbose_name='Spieler 4',
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.CASCADE)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ('-start',),
|
'ordering': ('-start',),
|
||||||
@@ -116,8 +148,11 @@ class Migration(migrations.Migration):
|
|||||||
name='KyuDanRanking',
|
name='KyuDanRanking',
|
||||||
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,
|
||||||
('dan', models.PositiveSmallIntegerField(null=True, blank=True)),
|
primary_key=True)),
|
||||||
|
(
|
||||||
|
'dan',
|
||||||
|
models.PositiveSmallIntegerField(null=True, blank=True)),
|
||||||
('dan_points', models.PositiveIntegerField(default=0)),
|
('dan_points', models.PositiveIntegerField(default=0)),
|
||||||
('kyu', models.PositiveSmallIntegerField(
|
('kyu', models.PositiveSmallIntegerField(
|
||||||
default=10, null=True, blank=True)),
|
default=10, null=True, blank=True)),
|
||||||
@@ -128,7 +163,8 @@ class Migration(migrations.Migration):
|
|||||||
('legacy_date', models.DateField(null=True, blank=True)),
|
('legacy_date', models.DateField(null=True, blank=True)),
|
||||||
('legacy_dan_points', models.PositiveIntegerField(default=0)),
|
('legacy_dan_points', models.PositiveIntegerField(default=0)),
|
||||||
('legacy_kyu_points', models.PositiveIntegerField(default=0)),
|
('legacy_kyu_points', models.PositiveIntegerField(default=0)),
|
||||||
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL)),
|
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.CASCADE)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ('-dan', '-dan_points', '-kyu_points'),
|
'ordering': ('-dan', '-dan_points', '-kyu_points'),
|
||||||
@@ -140,15 +176,19 @@ class Migration(migrations.Migration):
|
|||||||
name='SeasonRanking',
|
name='SeasonRanking',
|
||||||
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,
|
||||||
('season', models.PositiveSmallIntegerField(verbose_name='Saison')),
|
primary_key=True)),
|
||||||
('placement', models.PositiveIntegerField(null=True, blank=True)),
|
('season',
|
||||||
|
models.PositiveSmallIntegerField(verbose_name='Saison')),
|
||||||
|
('placement',
|
||||||
|
models.PositiveIntegerField(null=True, blank=True)),
|
||||||
('avg_placement', models.FloatField(null=True, blank=True)),
|
('avg_placement', models.FloatField(null=True, blank=True)),
|
||||||
('avg_score', models.FloatField(null=True, blank=True)),
|
('avg_score', models.FloatField(null=True, blank=True)),
|
||||||
('hanchan_count', models.PositiveIntegerField(default=0)),
|
('hanchan_count', models.PositiveIntegerField(default=0)),
|
||||||
('good_hanchans', models.PositiveIntegerField(default=0)),
|
('good_hanchans', models.PositiveIntegerField(default=0)),
|
||||||
('won_hanchans', models.PositiveIntegerField(default=0)),
|
('won_hanchans', models.PositiveIntegerField(default=0)),
|
||||||
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
|
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.CASCADE)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ('placement', 'avg_placement', '-avg_score'),
|
'ordering': ('placement', 'avg_placement', '-avg_score'),
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
100
src/mahjong_ranking/migrations/0006_auto_20171214_1318.py
Normal file
100
src/mahjong_ranking/migrations/0006_auto_20171214_1318.py
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.8 on 2017-12-14 12:18
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
('mahjong_ranking', '0005_auto_20171115_0653'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='kyudanranking',
|
||||||
|
name='legacy_dan',
|
||||||
|
field=models.PositiveSmallIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='kyudanranking',
|
||||||
|
name='legacy_good_hanchans',
|
||||||
|
field=models.PositiveIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='kyudanranking',
|
||||||
|
name='legacy_kyu',
|
||||||
|
field=models.PositiveSmallIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='kyudanranking',
|
||||||
|
name='legacy_won_hanchans',
|
||||||
|
field=models.PositiveIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='kyudanranking',
|
||||||
|
name='max_dan_points',
|
||||||
|
field=models.PositiveIntegerField(default=0),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='eventranking',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='hanchan',
|
||||||
|
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'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='hanchan',
|
||||||
|
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'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='hanchan',
|
||||||
|
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'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='hanchan',
|
||||||
|
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'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='kyudanranking',
|
||||||
|
name='legacy_dan_points',
|
||||||
|
field=models.PositiveIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='kyudanranking',
|
||||||
|
name='legacy_hanchan_count',
|
||||||
|
field=models.PositiveIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='kyudanranking',
|
||||||
|
name='legacy_kyu_points',
|
||||||
|
field=models.PositiveIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='seasonranking',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -9,10 +9,10 @@ from datetime import datetime, time
|
|||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
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,8 +29,9 @@ 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)
|
user = models.ForeignKey(settings.AUTH_USER_MODEL,
|
||||||
event = models.ForeignKey(Event)
|
on_delete=models.PROTECT)
|
||||||
|
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)
|
||||||
avg_score = models.FloatField(default=0)
|
avg_score = models.FloatField(default=0)
|
||||||
@@ -46,11 +47,11 @@ 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 zwischen zu speichern.
|
sie zwischenzuspeichern.
|
||||||
"""
|
"""
|
||||||
LOGGER.info(
|
LOGGER.info(
|
||||||
u'Recalculate EventRanking for Player %s in %s',
|
u'Recalculate EventRanking for Player %s in %s',
|
||||||
@@ -86,7 +87,7 @@ class Hanchan(models.Model):
|
|||||||
Es werden aber noch andere Tests durchgeführt, ob sie gültig ist.
|
Es werden aber noch andere Tests durchgeführt, ob sie gültig ist.
|
||||||
Außerdem gehört jede Hanchan zu einer Veranstaltung.
|
Außerdem gehört jede Hanchan zu einer Veranstaltung.
|
||||||
"""
|
"""
|
||||||
event = models.ForeignKey(Event)
|
event = models.ForeignKey(Event, on_delete=models.CASCADE)
|
||||||
start = models.DateTimeField(
|
start = models.DateTimeField(
|
||||||
_('Start'),
|
_('Start'),
|
||||||
help_text=_('This is crucial to get the right Hanchans that scores')
|
help_text=_('This is crucial to get the right Hanchans that scores')
|
||||||
@@ -94,7 +95,7 @@ class Hanchan(models.Model):
|
|||||||
|
|
||||||
player1 = models.ForeignKey(
|
player1 = models.ForeignKey(
|
||||||
settings.AUTH_USER_MODEL,
|
settings.AUTH_USER_MODEL,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.PROTECT,
|
||||||
related_name='user_hanchan+',
|
related_name='user_hanchan+',
|
||||||
verbose_name=_('Player 1'))
|
verbose_name=_('Player 1'))
|
||||||
player1_input_score = models.IntegerField(_('Score'))
|
player1_input_score = models.IntegerField(_('Score'))
|
||||||
@@ -113,7 +114,7 @@ class Hanchan(models.Model):
|
|||||||
|
|
||||||
player2 = models.ForeignKey(
|
player2 = models.ForeignKey(
|
||||||
settings.AUTH_USER_MODEL,
|
settings.AUTH_USER_MODEL,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.PROTECT,
|
||||||
related_name='user_hanchan+',
|
related_name='user_hanchan+',
|
||||||
verbose_name=_('Player 2'))
|
verbose_name=_('Player 2'))
|
||||||
player2_input_score = models.IntegerField(_('Score'))
|
player2_input_score = models.IntegerField(_('Score'))
|
||||||
@@ -132,7 +133,7 @@ class Hanchan(models.Model):
|
|||||||
|
|
||||||
player3 = models.ForeignKey(
|
player3 = models.ForeignKey(
|
||||||
settings.AUTH_USER_MODEL,
|
settings.AUTH_USER_MODEL,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.PROTECT,
|
||||||
related_name='user_hanchan+',
|
related_name='user_hanchan+',
|
||||||
verbose_name=_('Player 3'))
|
verbose_name=_('Player 3'))
|
||||||
player3_input_score = models.IntegerField(_('Score'))
|
player3_input_score = models.IntegerField(_('Score'))
|
||||||
@@ -151,7 +152,7 @@ class Hanchan(models.Model):
|
|||||||
|
|
||||||
player4 = models.ForeignKey(
|
player4 = models.ForeignKey(
|
||||||
settings.AUTH_USER_MODEL,
|
settings.AUTH_USER_MODEL,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.PROTECT,
|
||||||
related_name='user_hanchan+',
|
related_name='user_hanchan+',
|
||||||
verbose_name=_('Player 4'))
|
verbose_name=_('Player 4'))
|
||||||
player4_input_score = models.IntegerField(_('Score'))
|
player4_input_score = models.IntegerField(_('Score'))
|
||||||
@@ -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)
|
||||||
@@ -335,32 +335,53 @@ class KyuDanRanking(models.Model):
|
|||||||
Im Gegensatz zum Ladder Ranking ist das nicht Saison gebunden.
|
Im Gegensatz zum Ladder Ranking ist das nicht Saison gebunden.
|
||||||
Deswegen läuft es getrennt.
|
Deswegen läuft es getrennt.
|
||||||
"""
|
"""
|
||||||
user = models.OneToOneField(settings.AUTH_USER_MODEL)
|
user = models.OneToOneField(settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.CASCADE)
|
||||||
dan = models.PositiveSmallIntegerField(blank=True, null=True)
|
dan = models.PositiveSmallIntegerField(blank=True, null=True)
|
||||||
dan_points = models.PositiveIntegerField(default=0)
|
dan_points = models.PositiveIntegerField(default=0)
|
||||||
|
max_dan_points = models.PositiveIntegerField(default=0)
|
||||||
kyu = models.PositiveSmallIntegerField(default=10, blank=True, null=True)
|
kyu = models.PositiveSmallIntegerField(default=10, blank=True, null=True)
|
||||||
kyu_points = models.PositiveIntegerField(default=0)
|
kyu_points = models.PositiveIntegerField(default=0)
|
||||||
won_hanchans = models.PositiveIntegerField(default=0)
|
won_hanchans = models.PositiveIntegerField(default=0)
|
||||||
good_hanchans = models.PositiveIntegerField(default=0)
|
good_hanchans = models.PositiveIntegerField(default=0)
|
||||||
hanchan_count = models.PositiveIntegerField(default=0)
|
hanchan_count = models.PositiveIntegerField(default=0)
|
||||||
legacy_date = models.DateField(blank=True, null=True)
|
legacy_date = models.DateField(blank=True, null=True)
|
||||||
legacy_hanchan_count = models.PositiveIntegerField(default=0)
|
legacy_dan = models.PositiveSmallIntegerField(blank=True, null=True)
|
||||||
legacy_dan_points = models.PositiveIntegerField(default=0)
|
legacy_dan_points = models.PositiveIntegerField(blank=True, null=True)
|
||||||
legacy_kyu_points = models.PositiveIntegerField(default=0)
|
legacy_max_dan_points = models.PositiveIntegerField(default=0)
|
||||||
|
legacy_kyu = models.PositiveSmallIntegerField(blank=True, null=True)
|
||||||
|
legacy_kyu_points = models.PositiveIntegerField(blank=True, null=True)
|
||||||
|
legacy_hanchan_count = models.PositiveIntegerField(blank=True, null=True)
|
||||||
|
legacy_good_hanchans = models.PositiveIntegerField(blank=True, null=True)
|
||||||
|
legacy_won_hanchans = models.PositiveIntegerField(blank=True, null=True)
|
||||||
wins_in_a_row = models.PositiveIntegerField(default=0)
|
wins_in_a_row = models.PositiveIntegerField(default=0)
|
||||||
last_hanchan_date = models.DateTimeField(blank=True, null=True)
|
last_hanchan_date = models.DateTimeField(blank=True, null=True)
|
||||||
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')
|
||||||
|
|
||||||
def __unicode__(self):
|
@property
|
||||||
if self.dan_points is not None:
|
def rank(self):
|
||||||
|
if self.dan is not None:
|
||||||
|
return "{0:d}. Dan".format(self.dan)
|
||||||
|
else:
|
||||||
|
return "{0:d}. Kyū".format(self.kyu or 10)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def points(self):
|
||||||
|
return self.dan_points if self.dan is not None else self.kyu_points
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self.dan is not None:
|
||||||
return u"%s - %d. Dan" % (self.user.username, self.dan or 1)
|
return u"%s - %d. Dan" % (self.user.username, self.dan or 1)
|
||||||
else:
|
else:
|
||||||
return u"%s - %d. Kyu" % (self.user.username, self.kyu or 10)
|
return u"%s - %d. Kyū" % (self.user.username, self.kyu or 10)
|
||||||
|
|
||||||
def append_3_in_a_row_bonuspoints(self, hanchan):
|
def append_3_in_a_row_bonuspoints(self, hanchan):
|
||||||
u"""
|
u"""
|
||||||
@@ -368,12 +389,14 @@ class KyuDanRanking(models.Model):
|
|||||||
das er einen Dan Rang aufsteigt. Dies wird als Kommentar abgespeichert,
|
das er einen Dan Rang aufsteigt. Dies wird als Kommentar abgespeichert,
|
||||||
um es besser nachvollziehen zu können.
|
um es besser nachvollziehen zu können.
|
||||||
"""
|
"""
|
||||||
if self.dan and hanchan.placement == 1:
|
if not self.dan or not settings.DAN_3_WINS_IN_A_ROW:
|
||||||
|
return
|
||||||
|
if hanchan.placement == 1:
|
||||||
self.wins_in_a_row += 1
|
self.wins_in_a_row += 1
|
||||||
else:
|
else:
|
||||||
self.wins_in_a_row = 0
|
self.wins_in_a_row = 0
|
||||||
|
return
|
||||||
if self.dan and self.wins_in_a_row >= 3 and self.dan < 9:
|
if self.wins_in_a_row >= 3 and self.dan < 9:
|
||||||
LOGGER.info(
|
LOGGER.info(
|
||||||
'adding bonuspoints for 3 wins in a row for %s', self.user)
|
'adding bonuspoints for 3 wins in a row for %s', self.user)
|
||||||
new_dan_rank = self.dan + 1
|
new_dan_rank = self.dan + 1
|
||||||
@@ -394,11 +417,11 @@ class KyuDanRanking(models.Model):
|
|||||||
bonus_points, new_dan_rank)
|
bonus_points, new_dan_rank)
|
||||||
self.dan_points += bonus_points
|
self.dan_points += bonus_points
|
||||||
self.wins_in_a_row = 0
|
self.wins_in_a_row = 0
|
||||||
|
self.update_rank()
|
||||||
|
|
||||||
# TODO: Komplett Überabreiten!
|
|
||||||
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
|
||||||
@@ -408,29 +431,30 @@ class KyuDanRanking(models.Model):
|
|||||||
user=self.user, event=hanchan.event
|
user=self.user, event=hanchan.event
|
||||||
).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.
|
||||||
if hanchan != last_hanchan_this_event:
|
if hanchan != last_hanchan_this_event:
|
||||||
# Das braucht nur am Ende eines Turnieres gemacht werden.
|
|
||||||
return False
|
return False
|
||||||
else:
|
event_ranking = EventRanking.objects.get(
|
||||||
event_ranking = EventRanking.objects.get(
|
user=self.user,
|
||||||
user=self.user,
|
event=hanchan.event
|
||||||
event=hanchan.event
|
)
|
||||||
)
|
if event_ranking.placement == 1:
|
||||||
if event_ranking.placement == 1:
|
bonus_points += settings.TOURNAMENT_WIN_BONUSPOINTS
|
||||||
bonus_points += 4
|
hanchan.player_comment += u'+{0:d} Punkte Turnier gewonnen. '.format(
|
||||||
hanchan.player_comment += u'+4 Punkte Turnier gewonnen. '
|
settings.TOURNAMENT_WIN_BONUSPOINTS)
|
||||||
if event_ranking.avg_placement == 1:
|
if event_ranking.avg_placement == 1:
|
||||||
bonus_points += 8
|
bonus_points += settings.TOURNAMENT_FLAWLESS_VICTORY_BONUSPOINTS
|
||||||
hanchan.player_comment += u'+8 Pkt: alle Spiele des Turnieres gewonnen. '
|
hanchan.player_comment += '+{0:d} Pkt: alle Spiele des Turnieres \
|
||||||
|
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
|
||||||
self.dan_points += bonus_points
|
self.dan_points += bonus_points
|
||||||
elif bonus_points:
|
elif bonus_points:
|
||||||
hanchan.kyu_points += bonus_points
|
hanchan.kyu_points += bonus_points
|
||||||
self.kyu_points += bonus_points
|
self.kyu_points += bonus_points
|
||||||
hanchan.bonus_points += bonus_points
|
hanchan.bonus_points += bonus_points
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
if self.dan or self.dan_points > 0:
|
if self.dan or self.dan_points > 0:
|
||||||
@@ -451,79 +475,73 @@ class KyuDanRanking(models.Model):
|
|||||||
force_recalc = True
|
force_recalc = True
|
||||||
if force_recalc:
|
if force_recalc:
|
||||||
# Setze alles auf die legacy Werte und berechne alles von neuem.
|
# Setze alles auf die legacy Werte und berechne alles von neuem.
|
||||||
self.dan = None
|
self.dan = self.legacy_dan
|
||||||
self.dan_points = self.legacy_dan_points or 0
|
self.dan_points = self.legacy_dan_points or 0
|
||||||
self.kyu = None
|
self.max_dan_points = self.legacy_max_dan_points or 0
|
||||||
|
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
|
||||||
self.good_hanchans = 0
|
self.good_hanchans = self.legacy_good_hanchans or 0
|
||||||
self.won_hanchans = 0
|
self.won_hanchans = self.legacy_won_hanchans or 0
|
||||||
self.update_rank()
|
|
||||||
self.last_hanchan_date = None
|
self.last_hanchan_date = None
|
||||||
if self.legacy_date:
|
self.update_rank()
|
||||||
since = timezone.make_aware(
|
since = timezone.make_aware(datetime.combine(
|
||||||
datetime.combine(self.legacy_date, time(0, 0, 0)))
|
self.legacy_date,
|
||||||
else:
|
time(23, 59, 59))) if self.legacy_date else None
|
||||||
since = None
|
|
||||||
elif self.last_hanchan_date:
|
elif self.last_hanchan_date:
|
||||||
since = self.last_hanchan_date
|
since = self.last_hanchan_date
|
||||||
elif self.legacy_date:
|
elif self.legacy_date:
|
||||||
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:
|
||||||
self.hanchan_count += valid_hanchans.count()
|
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
|
||||||
|
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:
|
||||||
print(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()
|
||||||
else:
|
else:
|
||||||
hanchan.bonus_points = 0
|
hanchan.bonus_points = 0
|
||||||
hanchan.player_comment = u""
|
hanchan.player_comment = ""
|
||||||
self.update_hanchan_points(hanchan)
|
self.update_hanchan_points(hanchan)
|
||||||
if hanchan.event.mahjong_tournament:
|
if hanchan.event.mahjong_tournament:
|
||||||
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)
|
||||||
self.update_rank()
|
hanchan.update_player_data(self.user)
|
||||||
hanchan.update_playerdata(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
|
||||||
self.last_hanchan_date = hanchan.start
|
self.last_hanchan_date = hanchan.start
|
||||||
LOGGER.debug(
|
|
||||||
'id: %(id)d, start: %(start)s, placement: %(placement)d, '
|
|
||||||
'score: %(score)d, kyu points: %(kyu_points)d, dan points: '
|
|
||||||
'%(dan_points)d, bonus points: %(bonus_points)d',
|
|
||||||
{'id': hanchan.pk, 'start': hanchan.start,
|
|
||||||
'placement': hanchan.placement, 'score': hanchan.game_score,
|
|
||||||
'kyu_points': hanchan.kyu_points or 0,
|
|
||||||
'dan_points': hanchan.dan_points or 0,
|
|
||||||
'bonus_points': hanchan.bonus_points or 0}
|
|
||||||
)
|
|
||||||
self.save(force_update=True)
|
self.save(force_update=True)
|
||||||
|
|
||||||
|
|
||||||
def update_hanchan_points(self, hanchan):
|
def update_hanchan_points(self, hanchan):
|
||||||
"""
|
"""
|
||||||
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
|
||||||
if hanchan.event.mahjong_tournament:
|
if hanchan.event.mahjong_tournament and settings.TOURNAMENT_POINT_SYSTEM:
|
||||||
"""Für Turniere gelten andere Regeln zur Punktevergabe:
|
"""Für Turniere gelten andere Regeln zur Punktevergabe:
|
||||||
1. Platz 4 Punkte
|
1. Platz 4 Punkte
|
||||||
2. Platz 3 Punkte
|
2. Platz 3 Punkte
|
||||||
@@ -547,6 +565,7 @@ class KyuDanRanking(models.Model):
|
|||||||
hanchan.dan_points = -1
|
hanchan.dan_points = -1
|
||||||
elif hanchan.placement == 4:
|
elif hanchan.placement == 4:
|
||||||
hanchan.dan_points = -2
|
hanchan.dan_points = -2
|
||||||
|
# otherwise player must be in the kyu ranking
|
||||||
elif hanchan.game_score >= 60000:
|
elif hanchan.game_score >= 60000:
|
||||||
hanchan.kyu_points = 3
|
hanchan.kyu_points = 3
|
||||||
elif hanchan.game_score >= 30000:
|
elif hanchan.game_score >= 30000:
|
||||||
@@ -560,46 +579,47 @@ class KyuDanRanking(models.Model):
|
|||||||
if self.dan:
|
if self.dan:
|
||||||
# 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.' \
|
||||||
|
'(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.' \
|
||||||
|
'(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
|
||||||
|
|
||||||
|
|
||||||
# TODO: Merkwürdige Methode die zwar funktioniert aber nicht sehr
|
|
||||||
# aussagekräfig ist. Überarbeiten?
|
|
||||||
def update_rank(self):
|
def update_rank(self):
|
||||||
if self.dan and self.dan_points < 0:
|
# Update Dan ranking:
|
||||||
self.dan_points = 0
|
if self.dan or self.dan_points > 0:
|
||||||
self.dan = 1
|
if settings.DAN_ALLOW_DROP_DOWN:
|
||||||
elif self.dan or self.dan_points > 0:
|
self.dan = max((dan for min_points, dan in settings.DAN_RANKS
|
||||||
old_dan = self.dan
|
if self.dan_points > min_points))
|
||||||
for min_points, dan_rank in settings.DAN_RANKS:
|
else:
|
||||||
if self.dan_points > min_points:
|
self.max_dan_points = max(self.max_dan_points, self.dan_points)
|
||||||
self.dan = dan_rank
|
self.dan = max((dan for min_points, dan in settings.DAN_RANKS
|
||||||
break
|
if self.max_dan_points > min_points))
|
||||||
if old_dan is None or self.dan > old_dan:
|
|
||||||
self.wins_in_a_row = 0
|
# jump from Kyu to Dan
|
||||||
elif self.kyu_points < 1:
|
|
||||||
self.kyu_points = 0
|
|
||||||
self.kyu = 10
|
|
||||||
elif self.kyu_points > 50:
|
elif self.kyu_points > 50:
|
||||||
self.dan = 1
|
self.dan = 1
|
||||||
self.kyu = 0
|
|
||||||
self.dan_points = 0
|
self.dan_points = 0
|
||||||
|
self.kyu = None
|
||||||
self.kyu_points = 0
|
self.kyu_points = 0
|
||||||
|
self.wins_in_a_row = 0
|
||||||
|
# update Kyu ranking_
|
||||||
else:
|
else:
|
||||||
for min_points, kyu_rank in settings.KYU_RANKS:
|
self.kyu = min((kyu for min_points, kyu in settings.KYU_RANKS
|
||||||
if self.kyu_points > min_points:
|
if self.kyu_points > min_points))
|
||||||
self.kyu = kyu_rank
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
class SeasonRanking(models.Model):
|
class SeasonRanking(models.Model):
|
||||||
user = models.ForeignKey(settings.AUTH_USER_MODEL)
|
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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
120
src/mahjong_ranking/templates/mahjong_ranking/eventhanchan_form.html
Executable file
120
src/mahjong_ranking/templates/mahjong_ranking/eventhanchan_form.html
Executable file
@@ -0,0 +1,120 @@
|
|||||||
|
{% extends "events/event_detail.html" %}{% load i18n humanize thumbnail %}
|
||||||
|
|
||||||
|
{% block title %}Hanchans: {{ event.name }}{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_head %}
|
||||||
|
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.min.js"></script>
|
||||||
|
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.formset.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block maincontent %}
|
||||||
|
<h2 class="grid_12">{% trans 'Edit Hanchans' %}</h2>
|
||||||
|
|
||||||
|
<form method="post" action="" id="eventhanchan_form">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ formset.management_form }}
|
||||||
|
|
||||||
|
{% for form in formset %}
|
||||||
|
<fieldset class="hanchan">
|
||||||
|
{% for hidden in form.hidden_fields %} {{ hidden }} {% endfor %}
|
||||||
|
<p>
|
||||||
|
<label for="{{ form.start.if_for_label }}" class="field_name {{ form.start.css_classes }}">{{ form.start.label }}:</label>
|
||||||
|
{{ form.start }}
|
||||||
|
{{ form.start.errors }}
|
||||||
|
</p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{{form.player1.label}}</th>
|
||||||
|
<th>{{form.player2.label}}</th>
|
||||||
|
<th>{{form.player3.label}}</th>
|
||||||
|
<th>{{form.player4.label}}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tr>
|
||||||
|
<td>{{ form.player1 }}</td>
|
||||||
|
<td>{{ form.player2 }}</td>
|
||||||
|
<td>{{ form.player3 }}</td>
|
||||||
|
<td>{{ form.player4 }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ form.player1_input_score }}</td>
|
||||||
|
<td>{{ form.player2_input_score }}</td>
|
||||||
|
<td>{{ form.player3_input_score }}</td>
|
||||||
|
<td>{{ form.player4_input_score }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label class="field_name {{ form.comment.css_classes }}">{% trans 'Total' %}:</label>
|
||||||
|
<input type="number" value="0" name="total_score" disabled>
|
||||||
|
<label>{% trans 'Difference' %}:</label>
|
||||||
|
<span class="difference"></span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<p><label for="id_{{ form.comment.html_name }}" class="field_name {{ form.comment.css_classes }}">{{ form.comment.label }}:</label>
|
||||||
|
{{ form.comment }}
|
||||||
|
{{ form.comment.errors }}
|
||||||
|
</p>
|
||||||
|
{% if form.instance.pk %}
|
||||||
|
<p>
|
||||||
|
<label for="id_{{ form.DELETE.html_name }}" class="field_name {{ form.DELETE.css_classes }}">{{ form.DELETE.label }}:</label>
|
||||||
|
{{ form.DELETE }} {{form.DELETE.help_text}}
|
||||||
|
{{ form.DELETE.errors }}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
{% if form.non_field_errors %}
|
||||||
|
<p> {{ form.non_field_errors }}</p>
|
||||||
|
{% endif %}
|
||||||
|
</fieldset>
|
||||||
|
{% endfor %}
|
||||||
|
</form>
|
||||||
|
<script type="text/javascript">
|
||||||
|
function autofill(row) {
|
||||||
|
row.find("input[id$='start_0']").val('{{ event.start|date:"SHORT_DATE_FORMAT"}}');
|
||||||
|
row.find("input[id$='start_1']").val('{{ event.start|time:'TIME_FORMAT'}}');
|
||||||
|
}
|
||||||
|
|
||||||
|
function recalculate_score(element) {
|
||||||
|
var difference = 100000
|
||||||
|
var total = 0;
|
||||||
|
score_fields = $(element).closest('fieldset').find('input[name$="input_score"]')
|
||||||
|
total_field = $(element).closest('fieldset').find('input[name$="total_score"]')
|
||||||
|
difference_field = $(element).closest('fieldset').find('span[class="difference"]')
|
||||||
|
score_fields.each(function() {total += Number($(this).val());});
|
||||||
|
total_field.val(total)
|
||||||
|
|
||||||
|
difference = 100000 - total
|
||||||
|
if (difference > 0) {
|
||||||
|
differnence_text = difference + ' offen'
|
||||||
|
} else if (difference < 0) {
|
||||||
|
differnence_text = (0 - difference) + ' zu viel'
|
||||||
|
} else {
|
||||||
|
differnence_text = 'Ok'
|
||||||
|
}
|
||||||
|
difference_field.text(differnence_text)
|
||||||
|
}
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$('.hanchan').formset({
|
||||||
|
prefix: '{{ formset.prefix }}',
|
||||||
|
added: autofill,
|
||||||
|
addText: '<span class="fa fa-plus-circle"></span> {% trans 'Add Hanchan' %}',
|
||||||
|
addCssClass: 'button',
|
||||||
|
deleteText:'<span class="fa fa-trash"></span> {% trans 'Delete Hanchan' %}',
|
||||||
|
deleteCssClass: 'button'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
$('input[name$="_input_score"]').change(function() {recalculate_score(this);});
|
||||||
|
$('input[name$="_input_score"]').keyup(function() {recalculate_score(this);});
|
||||||
|
$('input[name$="total_score"]').each(function() {recalculate_score(this);});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block comments %}{% endblock %}
|
||||||
|
|
||||||
|
{% block buttonbar %}
|
||||||
|
<a class="button" href="{% url 'event-hanchan-list' event.pk %}"><span class="fa fa-undo"></span> {% trans 'back' %}</a>
|
||||||
|
<button type="submit" form="eventhanchan_form"><span class="fa fa-hdd-o"></span> {% trans 'save' %}</button>
|
||||||
|
{% endblock %}
|
||||||
@@ -52,6 +52,7 @@
|
|||||||
{% block buttonbar %}
|
{% block buttonbar %}
|
||||||
{% if perms.mahjong_ranking.add_hanchan %}
|
{% if perms.mahjong_ranking.add_hanchan %}
|
||||||
<a class="button" href="{{event.get_edit_url}}"><span class="fa fa-pencil"></span> {% trans 'Edit Event' %}</a>
|
<a class="button" href="{{event.get_edit_url}}"><span class="fa fa-pencil"></span> {% trans 'Edit Event' %}</a>
|
||||||
|
<a class="button" href="{% url 'event-hanchan-form' event.id %}"><span class="fa fa-pencil"></span> {% trans 'Edit Hanchans' %}</a>
|
||||||
<a class="button" href="{% url 'add-hanchan-form' event.id %}"><span class="fa fa-plus-circle"></span> {% trans 'Add Hanchan' %}</a>
|
<a class="button" href="{% url 'add-hanchan-form' event.id %}"><span class="fa fa-plus-circle"></span> {% trans 'Add Hanchan' %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -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>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user