Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f1f2473d41 | |||
| dacd6b68c0 | |||
| 4c00211432 | |||
| c62d2ee010 | |||
| ef00fc25f7 | |||
| fbfabb982f | |||
| 3efc4335bb | |||
|
|
e3daccafb1 | ||
|
|
1f4b978fd2 | ||
| 18d7f8a926 | |||
| 2885ef9d5c | |||
| 3f403b3f52 |
8
Makefile
8
Makefile
@@ -1,4 +1,4 @@
|
|||||||
ASSESTS=requirements static
|
ASSETS=requirements static
|
||||||
DEV_REQUIREMENTS=requirements/development.txt
|
DEV_REQUIREMENTS=requirements/development.txt
|
||||||
DJANGO_SETTINGS_MODULE="kasu.settings"
|
DJANGO_SETTINGS_MODULE="kasu.settings"
|
||||||
EXCLUDE_FILES=*.pyc
|
EXCLUDE_FILES=*.pyc
|
||||||
@@ -16,7 +16,7 @@ all: cleanup migrate testserver
|
|||||||
|
|
||||||
venv: $(VENV_PATH)/bin/activate
|
venv: $(VENV_PATH)/bin/activate
|
||||||
$(VENV_PATH)/bin/activate:
|
$(VENV_PATH)/bin/activate:
|
||||||
@test -d $(VENV_PATH) || python3 -m venv --clear $(VENV_PATH)
|
@test -d $(VENV_PATH) || python3 -m venv --clear --system-site-packages $(VENV_PATH)
|
||||||
|
|
||||||
dev-requirements: venv ${DEV_REQUIREMENTS}
|
dev-requirements: venv ${DEV_REQUIREMENTS}
|
||||||
${PYTHON} -m pip install -qU pip
|
${PYTHON} -m pip install -qU pip
|
||||||
@@ -35,7 +35,7 @@ requirements_remote:
|
|||||||
|
|
||||||
sync_assets:
|
sync_assets:
|
||||||
@echo "Syncing project assets ..."
|
@echo "Syncing project assets ..."
|
||||||
rsync -qr --copy-links --delete ${ASSESTS} ${SSH_LOGIN}:~/
|
rsync -qr --copy-links --delete ${ASSETS} ${SSH_LOGIN}:~/
|
||||||
|
|
||||||
sync_src:
|
sync_src:
|
||||||
@echo "Syncing Sourcecode ..."
|
@echo "Syncing Sourcecode ..."
|
||||||
@@ -55,7 +55,7 @@ testserver: venv
|
|||||||
messages: venv
|
messages: venv
|
||||||
@echo "aktualisiere Übersetzungen..."
|
@echo "aktualisiere Übersetzungen..."
|
||||||
@(for d in ${SRC_PATH}/*; do \
|
@(for d in ${SRC_PATH}/*; do \
|
||||||
test -d $$d/locale && cd $$d && ${VENV_PATH}/bin/django-admin.py makemessages -l de;\
|
test -d $$d/locale && cd $$d && ${VENV_PATH}/bin/django-admin makemessages -l de;\
|
||||||
done)
|
done)
|
||||||
${MANAGE_PY} compilemessages -v0
|
${MANAGE_PY} compilemessages -v0
|
||||||
|
|
||||||
|
|||||||
42
pyproject.toml
Normal file
42
pyproject.toml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
[project]
|
||||||
|
name = "kasu"
|
||||||
|
version = "4.230918"
|
||||||
|
description = "Homepage CMS for Kasu.at"
|
||||||
|
authors = [
|
||||||
|
{ name = "Christian Berg", email = "xeniac@xendynastie.at" }
|
||||||
|
]
|
||||||
|
requires-python = ">=3.8"
|
||||||
|
dependencies = ["beautifulsoup4",
|
||||||
|
"django < 5.0",
|
||||||
|
"django-appconf",
|
||||||
|
"django-ckeditor",
|
||||||
|
"django-contrib-comments",
|
||||||
|
"django-csp",
|
||||||
|
"django-compressor",
|
||||||
|
"django-extra-views",
|
||||||
|
"django-markdown",
|
||||||
|
"django-recaptcha",
|
||||||
|
"easy_thumbnails[svg]",
|
||||||
|
"icalendar",
|
||||||
|
"openpyxl",
|
||||||
|
"markdown",
|
||||||
|
"pillow",
|
||||||
|
"psycopg2-binary",
|
||||||
|
"PyJWT",
|
||||||
|
"pytz",
|
||||||
|
"requests",
|
||||||
|
"requests-oauthlib"
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
dev = [
|
||||||
|
"django-debug-toolbar",
|
||||||
|
"django-rosetta",
|
||||||
|
"sqlparse",
|
||||||
|
"pylint>=2.0",
|
||||||
|
"pylint-django"
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.setuptools.packages.find]
|
||||||
|
where = ["src"]
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
beautifulsoup4
|
beautifulsoup4
|
||||||
django < 3.0
|
django < 5.0
|
||||||
django-appconf
|
django-appconf
|
||||||
django-ckeditor
|
django-ckeditor
|
||||||
django-contrib-comments
|
django-contrib-comments
|
||||||
@@ -8,7 +8,7 @@ django-compressor
|
|||||||
django-extra-views
|
django-extra-views
|
||||||
django-markdown
|
django-markdown
|
||||||
django-recaptcha
|
django-recaptcha
|
||||||
git+https://github.com/SmileyChris/easy-thumbnails.git
|
easy_thumbnails[svg]
|
||||||
icalendar
|
icalendar
|
||||||
openpyxl
|
openpyxl
|
||||||
markdown
|
markdown
|
||||||
|
|||||||
@@ -19,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 = models.Page()
|
current_page: models.Page = models.Page.objects.get(slug='index')
|
||||||
current_top_page = models.Page()
|
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')
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: kasu.content\n"
|
"Project-Id-Version: kasu.content\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2019-12-13 23:38+0100\n"
|
"POT-Creation-Date: 2023-07-27 00:05+0200\n"
|
||||||
"PO-Revision-Date: 2018-01-12 15:25+0105\n"
|
"PO-Revision-Date: 2018-01-12 15:25+0105\n"
|
||||||
"Last-Translator: b'Christian Berg <kasu@xendynastie.at>'\n"
|
"Last-Translator: b'Christian Berg <kasu@xendynastie.at>'\n"
|
||||||
"Language-Team: Deutsch <>\n"
|
"Language-Team: Deutsch <>\n"
|
||||||
@@ -206,25 +206,27 @@ msgstr "Erstellt am"
|
|||||||
msgid "share on"
|
msgid "share on"
|
||||||
msgstr "Teile auf"
|
msgstr "Teile auf"
|
||||||
|
|
||||||
#: templates/content/article_detail.html:48 views.py:156
|
#: templates/content/article_detail.html:48 views.py:159
|
||||||
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"
|
||||||
|
|
||||||
@@ -249,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:160
|
||||||
msgid "Create Article"
|
msgid "Create Article"
|
||||||
msgstr "Artikel erstellen"
|
msgstr "Artikel erstellen"
|
||||||
|
|
||||||
#: views.py:237
|
#: views.py:240
|
||||||
#, 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:266
|
#: views.py:269
|
||||||
#, 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."
|
||||||
|
|||||||
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),
|
||||||
|
),
|
||||||
|
]
|
||||||
19
src/content/migrations/0010_alter_page_parent.py
Normal file
19
src/content/migrations/0010_alter_page_parent.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# 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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
src/content/migrations/0011_alter_page_id.py
Normal file
18
src/content/migrations/0011_alter_page_id.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# 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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -8,7 +8,7 @@ from django.template.defaultfilters import slugify
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import get_language, ugettext as _
|
from django.utils.translation import get_language, gettext as _
|
||||||
|
|
||||||
from utils import STATUS_CHOICES, STATUS_WAITING, STATUS_PUBLISHED, CLEANER
|
from utils import STATUS_CHOICES, STATUS_WAITING, STATUS_PUBLISHED, CLEANER
|
||||||
|
|
||||||
@@ -111,6 +111,7 @@ class Article(models.Model):
|
|||||||
"""Returns the headline of this article."""
|
"""Returns the headline of this article."""
|
||||||
return self.headline
|
return self.headline
|
||||||
|
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
@property
|
@property
|
||||||
def get_image(self):
|
def get_image(self):
|
||||||
"""Return the article image, or the category image if unset."""
|
"""Return the article image, or the category image if unset."""
|
||||||
@@ -169,6 +170,7 @@ class Category(models.Model):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
class Page(models.Model):
|
class Page(models.Model):
|
||||||
"""A page on this homepage. It can have a "static" HTML page, the URL of a
|
"""A page on this homepage. It can have a "static" HTML page, the URL of a
|
||||||
dynamic Django view, or a PDF document.
|
dynamic Django view, or a PDF document.
|
||||||
@@ -216,7 +218,7 @@ class Page(models.Model):
|
|||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
related_name='subpages',
|
related_name='subpages',
|
||||||
on_delete=models.SET_NULL
|
on_delete=models.CASCADE
|
||||||
)
|
)
|
||||||
position = models.PositiveSmallIntegerField(
|
position = models.PositiveSmallIntegerField(
|
||||||
blank=True,
|
blank=True,
|
||||||
@@ -317,12 +319,12 @@ class Page(models.Model):
|
|||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_(u'Please upload a PDF-File to this PDF-Page.'))
|
_(u'Please upload a PDF-File to this PDF-Page.'))
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self) -> str:
|
||||||
"""Return the path with an extension that matches the content type.
|
"""Return the path with an extension that matches the content type.
|
||||||
It's useful for an user to match the URL to the contenttype.
|
It's useful for a user to match the URL to the contenttype.
|
||||||
|
|
||||||
:return: sting with the absolute URL of this page."""
|
:return: string with the absolute URL of this page."""
|
||||||
return '/' + self.path + CONTENT_TYPE_EXTENSIONS[self.content_type]
|
return '/' + str(self.path) + CONTENT_TYPE_EXTENSIONS[self.content_type]
|
||||||
|
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
"""Set default ordering and an unique priamry key to avoid dupes."""
|
"""Set default ordering and an unique priamry key to avoid dupes."""
|
||||||
|
|||||||
@@ -4,26 +4,19 @@ Created on 03.10.2011
|
|||||||
|
|
||||||
@author: christian
|
@author: christian
|
||||||
"""
|
"""
|
||||||
from django.conf.urls import url
|
from django.urls import path
|
||||||
|
|
||||||
from .views import ArticleArchiveIndex, ArticleForm, ArticleYearArchive, \
|
from .views import ArticleArchiveIndex, ArticleForm, ArticleYearArchive, \
|
||||||
ArticleMonthArchive, ArticleDetail
|
ArticleMonthArchive, ArticleDetail
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', ArticleArchiveIndex.as_view(), name='article-archive'),
|
path("", ArticleArchiveIndex.as_view(), name='article-archive'),
|
||||||
url(r'^add/$', ArticleForm.as_view(), name='add-article'),
|
path('add/', ArticleForm.as_view(), name='add-article'),
|
||||||
url(r'^edit/(?P<pk>[\d]+)/$', ArticleForm.as_view(), name='edit-article'),
|
path('edit/<int:pk>/', ArticleForm.as_view(), name='edit-article'),
|
||||||
url(r'^(?P<year>[\d]{4})/$', ArticleYearArchive.as_view(),
|
path('<int:year>/', ArticleYearArchive.as_view(), name='article-archive'),
|
||||||
name='article-archive'),
|
path('<int:year>/<int:month>/', ArticleMonthArchive.as_view(), name='article-archive'),
|
||||||
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/$', ArticleMonthArchive.as_view(),
|
path('<int:year>/<int:month>/<slug:slug>/', ArticleDetail.as_view(), name='show-article'),
|
||||||
name='article-archive'),
|
path('<slug:category>/', ArticleArchiveIndex.as_view(), name='article-archive'),
|
||||||
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/(?P<slug>[\-\d\w]+)/$',
|
path('<slug:category>/<int:year>/', ArticleYearArchive.as_view(), name='article-archive'),
|
||||||
ArticleDetail.as_view(), name='show-article'),
|
path('<slug:category>/<int:year>/<int:month>/', ArticleMonthArchive.as_view(), name='article-archive'),
|
||||||
url(r'^(?P<category>[\-\d\w]+)/$', ArticleArchiveIndex.as_view(),
|
|
||||||
name='article-archive'),
|
|
||||||
url(r'^(?P<category>[\-\d\w]+)/(?P<year>[\d]{4})/$',
|
|
||||||
ArticleYearArchive.as_view(), name='article-archive'),
|
|
||||||
url(r'^(?P<category>[\-\d\w]+)/(?P<year>[\d]{4})/(?P<month>[\d]+)/$',
|
|
||||||
ArticleMonthArchive.as_view(), name='article-archive'),
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -9,51 +9,39 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block maincontent %}
|
{% block maincontent %}
|
||||||
|
{% get_fieldset "category, image" from form as fieldset_common %}
|
||||||
|
{% get_fieldset "headline_de" from form as fieldset_de %}
|
||||||
|
{% get_fieldset "headline_en" from form as fieldset_en %}
|
||||||
|
|
||||||
<form action="" method="post" enctype="multipart/form-data">
|
<form action="" method="post" enctype="multipart/form-data">
|
||||||
{{ form.media }} {% csrf_token %}
|
{{ form.media }} {% csrf_token %}
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<div>
|
{% with fieldset_common as form %}{% include "form.html" %}{% endwith %}
|
||||||
<label for="id_category" class="field_name {{ form.category.css_classes }}">{{ form.category.label}}</label>
|
|
||||||
{{ form.category }}
|
|
||||||
{% if form.category.help_text %}<p class="help_text">{{form.category.help_text}}</p>{% endif %}
|
|
||||||
{% if form.category.errors %}{{ form.category.errors }}{% endif %}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="id_image" class="field_name {{ form.image.css_classes }}">{{ form.image.label}}</label>
|
|
||||||
{{ form.image }}
|
|
||||||
{% if form.image.help_text %}<p class="help_text">{{form.image.help_text}}</p>{% endif %}
|
|
||||||
{% if form.image.errors %}{{ form.image.errors }}{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<ul class="tabs grid_12">
|
<ul class="tabs">
|
||||||
<li><a href="#de">{% trans "German" %}</a></li>
|
<li><a href="#de">{% trans "German" %}</a></li>
|
||||||
<li><a href="#en">{% trans "English" %}</a></li>
|
<li><a href="#en">{% trans "English" %}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<label for="id_{{ form.headline_de.html_name }}" class="fieldname {{ form.headline_de.css_classes }}">
|
|
||||||
{{ form.headline_de.label }}</label>
|
|
||||||
<p class="grid_10">{{ form.headline_de }}</p>
|
|
||||||
|
|
||||||
<div class="tab_container">
|
<div class="tab_container">
|
||||||
<div id="de" class="tab_content">
|
<section id="de" class="tab_content">
|
||||||
<fieldset>
|
<fieldset class="grid_12">
|
||||||
<legend>Deutsch</legend>
|
<legend>{% trans "German" %}</legend>
|
||||||
<label for="id_{{ form.headline_de.html_name }}" class="fieldname {{ form.headline_de.css_classes }}">
|
{% with fieldset_de as form %}{% include "form.html" %}{% endwith %}
|
||||||
{{ form.headline_de.label }}</label>
|
|
||||||
<p class="grid_10">{{ form.headline_de }}</p>
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{{ form.content_de }}
|
<br>
|
||||||
</div>
|
{{form.content_de}}
|
||||||
<div id="en" class="tab_content">
|
</section>
|
||||||
<h3 class="grid_12">English</h3>
|
<section id="en" class="tab_content">
|
||||||
<label for="id_{{ form.headline_en.html_name }}" class="grid_2 {{ form.headline_en.css_classes }}">
|
<fieldset class="grid_12">
|
||||||
{{ form.headline_en.label }}</label>
|
<legend>{% trans "English" %}</legend>
|
||||||
<p class="grid_10">{{ form.headline_en }}</p>
|
{% with fieldset_en as form %}{% include "form.html" %}{% endwith %}
|
||||||
<p> </p>
|
</fieldset>
|
||||||
{{ form.content_en }}
|
<br>
|
||||||
</div>
|
{{form.content_en}}
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<p class="buttonbar">
|
<p class="buttonbar">
|
||||||
<button type="reset"><span class="fa fa-undo"></span> {% trans 'reset' %}</button>
|
<button type="reset"><span class="fa fa-undo"></span> {% trans 'reset' %}</button>
|
||||||
|
|||||||
@@ -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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
queryset = models.Article.objects.filter(status=models.STATUS_PUBLISHED)
|
queryset = models.Article.objects.filter(status=models.STATUS_PUBLISHED)
|
||||||
|
queryset = queryset.filter(date_created__year=self.kwargs['year'])
|
||||||
|
queryset = queryset.filter(date_created__month=self.kwargs['month'])
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class ArticleForm(WYSIWYGEditorMixin, generic.UpdateView):
|
class ArticleForm(WYSIWYGEditorMixin, generic.UpdateView):
|
||||||
@@ -170,7 +174,7 @@ class ArticleForm(WYSIWYGEditorMixin, generic.UpdateView):
|
|||||||
|
|
||||||
|
|
||||||
class PageAddForm(WYSIWYGEditorMixin, generic.CreateView):
|
class PageAddForm(WYSIWYGEditorMixin, generic.CreateView):
|
||||||
""" Renders an Form to create a new page for users with conforming
|
""" Renders a Form to create a new page for users with conforming
|
||||||
permissions."""
|
permissions."""
|
||||||
|
|
||||||
form_class = forms.PageForm
|
form_class = forms.PageForm
|
||||||
@@ -192,7 +196,7 @@ class PageAddForm(WYSIWYGEditorMixin, generic.CreateView):
|
|||||||
|
|
||||||
|
|
||||||
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
|
||||||
@@ -201,7 +205,7 @@ 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.
|
||||||
@@ -270,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:
|
||||||
@@ -282,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):
|
||||||
@@ -291,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: If an event is running at this moment, the corresponding event object.
|
||||||
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,12 @@
|
|||||||
""" 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'),
|
path('<int:event>/upload/', views.EventPhotoUpload.as_view(), name='event-photo-upload'),
|
||||||
url(r'^(?P<event>[\d]+)/upload/$', views.EventPhotoUpload.as_view(),
|
path('<int:event>/<int:pk>/', views.EventPhoto.as_view(), name='event-photo'),
|
||||||
name='event-photo-upload'),
|
path('delete/<int:pk>/', views.DeleteEventPhoto.as_view(), name='delete-event-photo'),
|
||||||
url(r'^(?P<event>[\d]+)/(?P<pk>[\d]+)/$', views.EventPhoto.as_view(),
|
path('upload/', views.EventPhotoUpload.as_view(), name='event-photo-upload'),
|
||||||
name='event-photo'),
|
|
||||||
url(r'^delete/(?P<pk>[\d]+)/$', views.DeleteEventPhoto.as_view(),
|
|
||||||
name='delete-event-photo'),
|
|
||||||
url(r'^upload/$', views.EventPhotoUpload.as_view(),
|
|
||||||
name='event-photo-upload'),
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: kasu.events\n"
|
"Project-Id-Version: kasu.events\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2019-12-13 23:38+0100\n"
|
"POT-Creation-Date: 2023-07-26 18:31+0200\n"
|
||||||
"PO-Revision-Date: 2018-01-12 15:25+0105\n"
|
"PO-Revision-Date: 2018-01-12 15:25+0105\n"
|
||||||
"Last-Translator: b'Christian Berg <kasu@xendynastie.at>'\n"
|
"Last-Translator: b'Christian Berg <kasu@xendynastie.at>'\n"
|
||||||
"Language-Team: Kasu <verein@kasu.at>\n"
|
"Language-Team: Kasu <verein@kasu.at>\n"
|
||||||
@@ -23,19 +23,19 @@ msgstr ""
|
|||||||
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:76
|
#: mixins.py:87
|
||||||
msgid "Event does not exist"
|
msgid "Event does not exist"
|
||||||
msgstr "Veranstaltung gibt es nicht"
|
msgstr "Veranstaltung gibt es nicht"
|
||||||
|
|
||||||
@@ -242,7 +242,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:63
|
||||||
msgid "Edit Event"
|
msgid "Edit Event"
|
||||||
msgstr "Termin bearbeiten"
|
msgstr "Termin bearbeiten"
|
||||||
|
|
||||||
@@ -250,7 +250,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:65
|
||||||
msgid "Add Event"
|
msgid "Add Event"
|
||||||
msgstr "Neuer Termin"
|
msgstr "Neuer Termin"
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ 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):
|
||||||
@@ -23,7 +23,7 @@ class Migration(migrations.Migration):
|
|||||||
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(
|
||||||
@@ -36,7 +36,7 @@ class Migration(migrations.Migration):
|
|||||||
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(
|
||||||
@@ -49,7 +49,7 @@ class Migration(migrations.Migration):
|
|||||||
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,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
"""Mixins for Events."""
|
"""Mixins for Events."""
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
|
|
||||||
@@ -32,19 +33,18 @@ 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
|
"""Add this event or the related event of the given object to the template context.
|
||||||
context.
|
|
||||||
|
|
||||||
:return: TemplateContext object"""
|
:return: TemplateContext object"""
|
||||||
context = super(EventDetailMixin, self).get_context_data(**kwargs)
|
context = super(EventDetailMixin, self).get_context_data(**kwargs)
|
||||||
if hasattr(self, 'event'):
|
if getattr(self, 'event'):
|
||||||
context['event'] = self.event
|
context['event'] = self.event
|
||||||
elif hasattr(self, 'object') and isinstance(self.object, models.Event):
|
elif isinstance(getattr(self, 'object'), models.Event):
|
||||||
context['event'] = self.object
|
context['event'] = self.object
|
||||||
elif hasattr(self, 'object') and hasattr(self.object, 'event'):
|
elif getattr(getattr(self, 'object'), 'event'):
|
||||||
context['event'] = self.object.event
|
context['event'] = self.object.event
|
||||||
else:
|
else:
|
||||||
print("No Event in Context!")
|
raise ImproperlyConfigured("No Event in Context!")
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
@@ -55,11 +55,21 @@ class EventDetailMixin(object):
|
|||||||
"""
|
"""
|
||||||
if self.model == models.Event:
|
if self.model == models.Event:
|
||||||
self.event = get_object_or_404(models.Event, pk=self.kwargs['pk'])
|
self.event = get_object_or_404(models.Event, pk=self.kwargs['pk'])
|
||||||
queryset = self.model.objects.all()
|
queryset = self.model._default_manager.all()
|
||||||
else:
|
elif self.kwargs.get('event'):
|
||||||
self.event = get_object_or_404(models.Event,
|
self.event = get_object_or_404(
|
||||||
|
models.Event,
|
||||||
pk=self.kwargs['event'])
|
pk=self.kwargs['event'])
|
||||||
queryset = self.model.objects.filter(event=self.event)
|
queryset = self.model.objects.filter(event=self.event)
|
||||||
|
elif self.model:
|
||||||
|
queryset = self.model._default_manager.all()
|
||||||
|
else:
|
||||||
|
raise ImproperlyConfigured(
|
||||||
|
"%(cls)s is missing a QuerySet. Define "
|
||||||
|
"%(cls)s.model, %(cls)s.queryset, or override "
|
||||||
|
"%(cls)s.get_queryset()." % {
|
||||||
|
'cls': self.__class__.__name__
|
||||||
|
})
|
||||||
return queryset.prefetch_related()
|
return queryset.prefetch_related()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Models to solitary events, events series with an location and photos."""
|
"""Models to solitary events, events series with a location and photos."""
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from ckeditor.fields import RichTextField
|
from ckeditor.fields import RichTextField
|
||||||
@@ -9,21 +9,22 @@ from django.db.models import Q
|
|||||||
from django.template.defaultfilters import slugify
|
from django.template.defaultfilters import slugify
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import gettext as _
|
||||||
from easy_thumbnails.fields import ThumbnailerImageField
|
from easy_thumbnails.fields import ThumbnailerImageField
|
||||||
|
|
||||||
from utils import COUNTRIES, OverwriteStorage
|
from utils import COUNTRIES, OverwriteStorage
|
||||||
from .managers import EventManager
|
from .managers import EventManager
|
||||||
|
|
||||||
|
|
||||||
def get_upload_path(instance, filename):
|
def get_upload_path(instance: models.Model, filename: str) -> str:
|
||||||
"""
|
"""
|
||||||
Generates the desired file path and filename for an uploaded Image.
|
Generates the desired file path and filename for an uploaded Image.
|
||||||
With this function Django can save the uploaded images to subfolders that
|
With this function Django can save the uploaded images to a subfolder that
|
||||||
also have a meaning for humans.
|
also have a meaning for humans.
|
||||||
|
|
||||||
@param instance: an Django Object for which the Image has been uploaded.
|
@return: String: path for the image.
|
||||||
@type instance: a instace of an models.Model sub-class.
|
@param instance: a Django Object for which the Image has been uploaded.
|
||||||
|
@type instance: an instance of a models.Model subclass.
|
||||||
@param filename: The filename of the uploaded image.
|
@param filename: The filename of the uploaded image.
|
||||||
@type filename: String
|
@type filename: String
|
||||||
"""
|
"""
|
||||||
@@ -48,7 +49,7 @@ def get_upload_path(instance, filename):
|
|||||||
|
|
||||||
|
|
||||||
class Event(models.Model):
|
class Event(models.Model):
|
||||||
"""An Event that could be a tournament, a game session, or an convention."""
|
"""An Event that could be a tournament, a game session, or a convention."""
|
||||||
name = models.CharField(_('Name'), max_length=255)
|
name = models.CharField(_('Name'), max_length=255)
|
||||||
description = RichTextField(_("Description"), blank=True)
|
description = RichTextField(_("Description"), blank=True)
|
||||||
location = models.ForeignKey('Location', on_delete=models.PROTECT)
|
location = models.ForeignKey('Location', on_delete=models.PROTECT)
|
||||||
@@ -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 = {
|
||||||
@@ -164,10 +165,10 @@ class Event(models.Model):
|
|||||||
self.location = master_event.location
|
self.location = master_event.location
|
||||||
self.url = master_event.url
|
self.url = master_event.url
|
||||||
self.image = self.image or master_event.image
|
self.image = self.image or master_event.image
|
||||||
self.photo_count = self.photo_set.count()
|
self.photo_count = self.photo_set.count() if self.pk else 0
|
||||||
super(Event, self).save(**kwargs)
|
super(Event, self).save(**kwargs)
|
||||||
|
|
||||||
# Update the Hanchans if necesery:
|
# Update the Hanchans if necessary:
|
||||||
for hanchan in self.hanchan_set.all():
|
for hanchan in self.hanchan_set.all():
|
||||||
hanchan.save()
|
hanchan.save()
|
||||||
|
|
||||||
@@ -267,7 +268,7 @@ class Photo(models.Model):
|
|||||||
return os.path.basename(self.image.name)
|
return os.path.basename(self.image.name)
|
||||||
|
|
||||||
def rotate(self, rotate):
|
def rotate(self, rotate):
|
||||||
# TODO: Eine vernüftigte Methode ohne viele Abhängigkeiten finden um
|
# TODO: Eine vernünftige Methode ohne viele Abhängigkeiten finden um
|
||||||
# die Bilder bei Bedarf zu drehen.
|
# die Bilder bei Bedarf zu drehen.
|
||||||
if rotate == 'clockwise':
|
if rotate == 'clockwise':
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
"""To geneate a Sitemap with all events."""
|
|
||||||
from kasu.sitemaps import GenericSitemap
|
from kasu.sitemaps import GenericSitemap
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from .models import Event
|
from .models import Event
|
||||||
@@ -6,11 +5,13 @@ 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()
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
<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 %}
|
||||||
|
|||||||
@@ -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,16 @@
|
|||||||
"""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(), name='event-archive'),
|
||||||
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/$',
|
path('<int:year>/<int:month>/<int:pk>/', views.EventDetail.as_view(), name='event-detail'),
|
||||||
views.EventArchiveMonth.as_view(),
|
path('<int:year>/<int:month>/<int:pk>/add_dates/', views.EventSeriesForm.as_view(), name='event-series-form'),
|
||||||
name='event-archive'),
|
path('<int:year>/<int:month>/<int:pk>/edit/', views.EventForm.as_view(), name='event-form'),
|
||||||
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/(?P<pk>[\d]+)/$',
|
path('add/', views.EventForm.as_view(), name='event-form'),
|
||||||
views.EventDetail.as_view(), name='event-detail'),
|
path('archive/', views.EventArchiveIndex.as_view(), name='event-archive'),
|
||||||
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/(?P<pk>[\d]+)/add_dates/$',
|
path('upcoming/', views.UpcomingEvents.as_view(), name='upcoming-events'),
|
||||||
views.EventSeriesForm.as_view(), name='eventseries-form'),
|
|
||||||
url(r'^(?P<year>[\d]{4})/(?P<month>[\d]+)/(?P<pk>[\d]+)/edit/$',
|
|
||||||
views.EventForm.as_view(), name='event-form'),
|
|
||||||
url(r'^add/$', views.EventForm.as_view(), name='event-form'),
|
|
||||||
url(r'^archive/$', views.EventArchiveIndex.as_view(), name='event-archive'),
|
|
||||||
url(r'^upcoming/$', views.UpcomingEvents.as_view(), name='upcoming-events'),
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ from django.urls import reverse
|
|||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect, get_object_or_404
|
||||||
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
|
||||||
@@ -50,19 +50,15 @@ 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
|
"""set the title to add or edit Event, depending on the fact if an event ID was given."""
|
||||||
event ID was given, or not."""
|
|
||||||
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):
|
||||||
@@ -74,7 +70,7 @@ class EventForm(PermissionRequiredMixin, mixins.EventDetailMixin,
|
|||||||
|
|
||||||
|
|
||||||
class EventGallery(generic.ListView):
|
class EventGallery(generic.ListView):
|
||||||
"""Display a overview of all event photo albums."""
|
"""Display an overview of all event photo albums."""
|
||||||
template_name = 'events/photo_gallery.html'
|
template_name = 'events/photo_gallery.html'
|
||||||
paginate_by = 24
|
paginate_by = 24
|
||||||
|
|
||||||
@@ -89,7 +85,7 @@ class EventGallery(generic.ListView):
|
|||||||
|
|
||||||
|
|
||||||
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."""
|
||||||
@@ -132,7 +128,7 @@ class EventPhoto(mixins.EventPhotoMixin, generic.UpdateView):
|
|||||||
|
|
||||||
|
|
||||||
class EventPhotoList(mixins.EventPhotoMixin, 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
|
||||||
@@ -188,7 +184,7 @@ class EventSeriesForm(mixins.EventDetailMixin, PermissionRequiredMixin,
|
|||||||
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
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ 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: 2019-12-13 23:38+0100\n"
|
"POT-Creation-Date: 2023-07-26 18:31+0200\n"
|
||||||
"PO-Revision-Date: 2018-12-30 11:14+0105\n"
|
"PO-Revision-Date: 2018-12-30 11:14+0105\n"
|
||||||
"Last-Translator: b' <kasu@xendynastie.at>'\n"
|
"Last-Translator: b' <kasu@xendynastie.at>'\n"
|
||||||
"Language-Team: Kasu <verein@kasu.at>\n"
|
"Language-Team: Kasu <verein@kasu.at>\n"
|
||||||
@@ -19,11 +19,11 @@ msgstr ""
|
|||||||
"X-Generator: Poedit 1.8.9\n"
|
"X-Generator: Poedit 1.8.9\n"
|
||||||
"X-Translated-Using: django-rosetta 0.9.0\n"
|
"X-Translated-Using: django-rosetta 0.9.0\n"
|
||||||
|
|
||||||
#: settings.py:140
|
#: settings.py:144
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr "Deutsch"
|
msgstr "Deutsch"
|
||||||
|
|
||||||
#: settings.py:140
|
#: settings.py:144
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr "Englisch"
|
msgstr "Englisch"
|
||||||
|
|
||||||
@@ -192,8 +192,8 @@ msgid ""
|
|||||||
" 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|date:'Y-m-d\\TH:i:sO')s\">"
|
" <time "
|
||||||
"%(since)s</time>\n"
|
"datetime=\"%(submit_date|date:'Y-m-d\\TH:i:sO')s\">%(since)s</time>\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
@@ -215,11 +215,11 @@ 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:7
|
||||||
msgid "Previous"
|
msgid "Previous"
|
||||||
msgstr "Vorherige"
|
msgstr "Vorherige"
|
||||||
|
|
||||||
#: templates/paginator.html:20
|
#: templates/paginator.html:18
|
||||||
msgid "Next"
|
msgid "Next"
|
||||||
msgstr "Nächste"
|
msgstr "Nächste"
|
||||||
|
|
||||||
|
|||||||
@@ -133,6 +133,10 @@ LOGIN_URL = '/membership/login/'
|
|||||||
LOGIN_ERROR_URL = '/membership/login/error/'
|
LOGIN_ERROR_URL = '/membership/login/error/'
|
||||||
LOGIN_REDIRECT_URL = '/users/'
|
LOGIN_REDIRECT_URL = '/users/'
|
||||||
|
|
||||||
|
|
||||||
|
# Set the primarykey handing to old django style
|
||||||
|
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
||||||
|
|
||||||
# Localization
|
# Localization
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
USE_L10N = True
|
USE_L10N = True
|
||||||
|
|||||||
@@ -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 %}
|
||||||
@@ -116,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 %}
|
||||||
@@ -142,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
|
||||||
@@ -169,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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
<input type="{{ type|default:'text' }}" name="{{ widget.name }}"{% if widget.value != None %} value="{{ widget.value|stringformat:'s' }}"{% endif %}{% include "django/forms/widgets/attrs.html" %} />
|
<input type="{{ type|default:'text' }}" name="{{ widget.name }}"
|
||||||
|
value="{{ widget.value|stringformat:'s' }}" "django/forms/widgets/attrs.html" %} />
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
{% for hidden in form.hidden_fields %}{{ hidden }}{% endfor %}
|
{% for hidden in form.hidden_fields %}{{ hidden }}{% endfor %}
|
||||||
{% for field in form.visible_fields %}
|
{% for field in form.visible_fields %}
|
||||||
<div>
|
<div>
|
||||||
<label {% if field.html_name != 'recaptcha' %} for="id_{{ field.html_name}}" {% endif %}
|
<label for="id_{{ field.html_name}}"
|
||||||
class="field_name {{ field.css_classes }}">{{ field.label}}</label>
|
class="field_name {{ field.css_classes }}">{{ field.label}}</label>
|
||||||
{{ field }}
|
{{ field }}
|
||||||
{% if field.field.widget.input_type == 'checkbox' %}
|
{% if field.field.widget.input_type == 'checkbox' %}
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
<nav class="grid_12 pagination">
|
<nav class="grid_12 pagination">
|
||||||
<a {% if page_obj.has_previous %}
|
{% if page_obj.has_previous %}
|
||||||
class="previous" href="?page={{ page_obj.previous_page_number }}"
|
<a class="previous" href="?page={{ page_obj.previous_page_number }}"><span class="fa fa-arrow-left"></span>
|
||||||
|
{% trans "Previous" %}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
class="previous disabled"
|
<a class="previous disabled"><span class="fa fa-arrow-left"></span>{% trans "Previous" %}</a>
|
||||||
{% endif %}>
|
{% endif %}
|
||||||
<span class="fa fa-arrow-left"></span>{% trans "Previous" %}
|
|
||||||
</a>
|
|
||||||
|
|
||||||
{% for page in paginator.page_range %}
|
{% for page in paginator.page_range %}
|
||||||
<a {% ifequal page_obj.number page %}class="active"{% else %}href="?page={{page}}"{% endifequal %}>{{page}}</a>
|
{% if page_obj.number == page %}
|
||||||
{% endfor %}
|
<a class="active">{{page}}</a>
|
||||||
|
|
||||||
<a {% if page_obj.has_next %}
|
|
||||||
class="next" href="?page={{ page_obj.next_page_number }}"
|
|
||||||
{% else %}
|
{% else %}
|
||||||
class="next disabled"
|
<a href="?page={{page}}">{{page}}</a>
|
||||||
{% endif %}>
|
{% endif %}
|
||||||
{% trans "Next" %} <span class="fa fa-arrow-right"></span>
|
{% endfor %}
|
||||||
</a>
|
{% if page_obj.has_next %}
|
||||||
|
<a class="next" href="?page={{ page_obj.next_page_number }}">{% trans "Next" %}
|
||||||
|
<span class="fa fa-arrow-right"></span></a>
|
||||||
|
{% else %}
|
||||||
|
<a class="next disabled">{% trans "Next" %} <span class="fa fa-arrow-right"></span></a>
|
||||||
|
{% endif %}
|
||||||
</nav>
|
</nav>
|
||||||
@@ -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
|
||||||
@@ -15,11 +15,12 @@ from maistar_ranking.sitemaps import *
|
|||||||
from membership.views import MembershipDetail
|
from membership.views import MembershipDetail
|
||||||
|
|
||||||
admin.autodiscover()
|
admin.autodiscover()
|
||||||
|
# register_converter('path')
|
||||||
|
|
||||||
sitemaps = {
|
sitemaps = {
|
||||||
'event_rankings': EventRankingSitemap,
|
'event_rankings': EventRankingSitemap,
|
||||||
'event_hanchans': EventHanchanSitemap,
|
'event_hanchans': EventHanchanSitemap,
|
||||||
'mahjong_seasons': MajongSeasonSitemap,
|
'mahjong_seasons': MahjongSeasonSitemap,
|
||||||
'maistar_games': MaistarGamesSitemap,
|
'maistar_games': MaistarGamesSitemap,
|
||||||
'articles': ArticleSitemap,
|
'articles': ArticleSitemap,
|
||||||
'events': EventSitemap,
|
'events': EventSitemap,
|
||||||
@@ -27,41 +28,33 @@ sitemaps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
urlpatterns = [ # Ignore PyLintBear (C0103)
|
urlpatterns = [ # Ignore PyLintBear (C0103)
|
||||||
url(r'^$', views.StartPage.as_view()),
|
path("", views.StartPage.as_view(), name="index"),
|
||||||
url(r'^404/$', TemplateView.as_view(template_name='404.html')),
|
path('index.html', views.StartPage.as_view()),
|
||||||
url(r'^add_page/(?P<path>[\+\.\-\d\w\/]*)$',
|
path('404/', TemplateView.as_view(template_name='404.html'), name="404"),
|
||||||
views.PageAddForm.as_view(), name='add-page'),
|
path('admin/doc/', include('django.contrib.admindocs.urls'), name="django-docs"),
|
||||||
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
path('admin/', admin.site.urls, name="django-admin"),
|
||||||
url(r'^admin/', admin.site.urls),
|
path('ckeditor/', include('ckeditor_uploader.urls')),
|
||||||
url(r'^ckeditor/', include('ckeditor_uploader.urls')),
|
path('comments/', include('django_comments.urls')),
|
||||||
url(r'^comments/', include('django_comments.urls')),
|
path('events/', include('events.urls')),
|
||||||
url(r'^edit_page/(?P<path>[\+\.\-\d\w\/]*)$',
|
path('events.ics', EventListIcal.as_view(), name='events-ical'),
|
||||||
views.PageEditForm.as_view(), name='edit-page'),
|
path('feeds/latest/', feeds.LatestNews(), name='feed-latest-news'),
|
||||||
url(r'^events/', include('events.urls')),
|
path('feeds/comments/', feeds.LatestComments(), name='feed-latest-comments'),
|
||||||
url(r'^events.ics$', EventListIcal.as_view(), name='events-ical'),
|
path('gallery/', include('events.gallery_urls')),
|
||||||
url(r'^feeds/latest/$', feeds.LatestNews(), name='feed-latest-news'),
|
path('google25dabc1a49a9ef03.html', TemplateView.as_view(template_name='google25dabc1a49a9ef03.html')),
|
||||||
url(r'^feeds/comments/$', feeds.LatestComments(),
|
path('i18n/', include('django.conf.urls.i18n'), name='start-page'),
|
||||||
name='feed-latest-comments'),
|
path('manifest.json', TemplateView.as_view(template_name='manifest.json')),
|
||||||
url(r'^gallery/', include('events.gallery_urls')),
|
path('membership/', include('membership.urls')),
|
||||||
url(r'^google25dabc1a49a9ef03.html$', TemplateView.as_view(
|
path('news/', include('content.news_urls')),
|
||||||
template_name='google25dabc1a49a9ef03.html')),
|
path('ranking/', include('mahjong_ranking.urls')),
|
||||||
url(r'^i18n/', include('django.conf.urls.i18n'), name='start-page'),
|
path('ranking/', include('maistar_ranking.urls')),
|
||||||
url(r'^index.html$', views.StartPage.as_view()),
|
path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'),
|
||||||
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>/', MembershipDetail.as_view(), name='membership-details'),
|
||||||
url(r'^news/', include('content.news_urls')),
|
path('add_page/<path:path>', views.PageAddForm.as_view(), name='add-page'),
|
||||||
url(r'^ranking/', include('mahjong_ranking.urls')),
|
path('edit_page/<path:path>', views.PageEditForm.as_view(), name='edit-page'),
|
||||||
url(r'^ranking/', include('maistar_ranking.urls')),
|
path('<path:path>.html', views.PageHtml.as_view(), name='view-page'),
|
||||||
url(r'^sitemap\.xml$', sitemap, {
|
path('<path:path>.pdf', views.PagePdf.as_view()),
|
||||||
'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'),
|
|
||||||
url(r'^robots.txt$', TemplateView.as_view(template_name='robots.txt')),
|
|
||||||
url(r'^users/$', MembershipDetail.as_view(), name='membership-details'),
|
|
||||||
url(r'^users/(?P<username>[\-\.\d\w]+)/$',
|
|
||||||
MembershipDetail.as_view(), name='membership-details'),
|
|
||||||
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()),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
@@ -70,8 +63,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,6 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Helper to generate XLSX Spreadsheets in an uniform way.
|
Helper to generate XLSX Spreadsheets in an uniform way.
|
||||||
"""
|
"""
|
||||||
|
import datetime
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
import openpyxl
|
import openpyxl
|
||||||
@@ -68,6 +69,8 @@ def getattr_recursive(obj, attr_string):
|
|||||||
for attr in attr_list:
|
for attr in attr_list:
|
||||||
return_value = getattr(obj, attr)
|
return_value = getattr(obj, attr)
|
||||||
obj = return_value
|
obj = return_value
|
||||||
|
if isinstance(return_value, datetime.datetime):
|
||||||
|
return_value = return_value.replace(tzinfo=None)
|
||||||
return return_value
|
return return_value
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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',)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Created on 04.10.2011
|
|||||||
"""
|
"""
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from events.models import Event
|
from events.models import Event
|
||||||
from . import models
|
from . import models
|
||||||
@@ -64,7 +64,7 @@ class HanchanAdminForm(HanchanForm):
|
|||||||
""" Extends the HanchanForm for users with admin privileges.
|
""" Extends the HanchanForm for users with admin privileges.
|
||||||
|
|
||||||
They are allowed to confirm/unconfirm Hanchans, this could be userful if
|
They are allowed to confirm/unconfirm Hanchans, this could be userful if
|
||||||
one games smells fishy and needs the opinion of an referee."""
|
one games smells fishy and needs the opinion of a referee."""
|
||||||
|
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
""" Extend the formfields to add the confirmed checkbox. """
|
""" Extend the formfields to add the confirmed checkbox. """
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: kasu.mahjong_ranking\n"
|
"Project-Id-Version: kasu.mahjong_ranking\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2019-12-13 23:38+0100\n"
|
"POT-Creation-Date: 2023-07-27 00:05+0200\n"
|
||||||
"PO-Revision-Date: 2018-05-08 00:20+0105\n"
|
"PO-Revision-Date: 2018-05-08 00:20+0105\n"
|
||||||
"Last-Translator: b'Christian Berg <kasu@xendynastie.at>'\n"
|
"Last-Translator: b'Christian Berg <kasu@xendynastie.at>'\n"
|
||||||
"Language-Team: Kasu <verein@kasu.at>\n"
|
"Language-Team: Kasu <verein@kasu.at>\n"
|
||||||
@@ -19,15 +19,15 @@ msgstr ""
|
|||||||
"X-Generator: Poedit 1.8.9\n"
|
"X-Generator: Poedit 1.8.9\n"
|
||||||
"X-Translated-Using: django-rosetta 0.8.1\n"
|
"X-Translated-Using: django-rosetta 0.8.1\n"
|
||||||
|
|
||||||
#: admin.py:24
|
#: admin.py:26
|
||||||
msgid "Recalculate"
|
msgid "Recalculate"
|
||||||
msgstr "Neuberechnen"
|
msgstr "Neuberechnen"
|
||||||
|
|
||||||
#: admin.py:34
|
#: admin.py:36
|
||||||
msgid "Confirm"
|
msgid "Confirm"
|
||||||
msgstr "Bestätigen"
|
msgstr "Bestätigen"
|
||||||
|
|
||||||
#: admin.py:44
|
#: admin.py:46
|
||||||
msgid "Set unconfirmed"
|
msgid "Set unconfirmed"
|
||||||
msgstr "Als unbestätigt markieren"
|
msgstr "Als unbestätigt markieren"
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ msgstr "Als unbestätigt markieren"
|
|||||||
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,16 +43,16 @@ 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
|
||||||
@@ -62,76 +62,76 @@ msgstr "Spieler 1"
|
|||||||
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:607 templates/mahjong_ranking/ladder_redbox.html:29
|
#: models.py:181 models.py:620 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:362
|
#: models.py:368
|
||||||
msgid "Kyū/Dan Ranking"
|
msgid "Kyū/Dan Ranking"
|
||||||
msgstr "Kyū/Dan Wertung"
|
msgstr "Kyū/Dan Wertung"
|
||||||
|
|
||||||
#: models.py:363
|
#: models.py:369
|
||||||
msgid "Kyū/Dan Rankings"
|
msgid "Kyū/Dan Rankings"
|
||||||
msgstr "Kyū/Dan Wertungen"
|
msgstr "Kyū/Dan Wertungen"
|
||||||
|
|
||||||
@@ -386,7 +386,7 @@ msgstr "%s wurde erfolgreich aktualisiert."
|
|||||||
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:219
|
#: views.py:218
|
||||||
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"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Export Mahjong Rankings as excel files."""
|
"""Export Mahjong Rankings as Excel files."""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from datetime import date, time, datetime
|
from datetime import date, time, datetime
|
||||||
|
|||||||
56
src/mahjong_ranking/management/commands/fixateranking.py
Normal file
56
src/mahjong_ranking/management/commands/fixateranking.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
"""
|
||||||
|
Recalculates all Kyu/Dan Rankings until the given date a 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):
|
||||||
|
""" Recalculates all Kyu/Dan Rankings until the given date a writes them to the legacy fields. """
|
||||||
|
|
||||||
|
help = "Recalculates all Kyu/Dan Rankings until the given date a 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()
|
||||||
@@ -11,7 +11,7 @@ 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."
|
||||||
|
|
||||||
|
|||||||
@@ -74,11 +74,12 @@ 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 +93,7 @@ class HanchanManager(models.Manager):
|
|||||||
|
|
||||||
:param user: Return Hanchans where this user participated.
|
:param user: Return Hanchans where this user participated.
|
||||||
:param since: only return Hanchans played since the given datetime
|
:param since: only return Hanchans played since the given datetime
|
||||||
|
:param until: only return hanchans played until the given date.
|
||||||
:param filter_args: To add specific arguments to the Django filter.
|
:param filter_args: To add specific arguments to the Django filter.
|
||||||
:return: a QuerySet Object
|
:return: a QuerySet Object
|
||||||
"""
|
"""
|
||||||
@@ -199,10 +201,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 +237,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()}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# 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),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -12,7 +12,8 @@ from django.core.exceptions import ValidationError
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
|
||||||
from events.models import Event
|
from events.models import Event
|
||||||
from . import DAN_RANKS_DICT, LOGGER, set_dirty
|
from . import DAN_RANKS_DICT, LOGGER, set_dirty
|
||||||
@@ -47,11 +48,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',
|
||||||
@@ -290,9 +291,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)
|
||||||
@@ -349,6 +349,7 @@ class KyuDanRanking(models.Model):
|
|||||||
legacy_date = models.DateField(blank=True, null=True)
|
legacy_date = models.DateField(blank=True, null=True)
|
||||||
legacy_dan = models.PositiveSmallIntegerField(blank=True, null=True)
|
legacy_dan = models.PositiveSmallIntegerField(blank=True, null=True)
|
||||||
legacy_dan_points = models.PositiveIntegerField(blank=True, null=True)
|
legacy_dan_points = models.PositiveIntegerField(blank=True, null=True)
|
||||||
|
legacy_max_dan_points = models.PositiveIntegerField(default=0)
|
||||||
legacy_kyu = models.PositiveSmallIntegerField(blank=True, null=True)
|
legacy_kyu = models.PositiveSmallIntegerField(blank=True, null=True)
|
||||||
legacy_kyu_points = models.PositiveIntegerField(blank=True, null=True)
|
legacy_kyu_points = models.PositiveIntegerField(blank=True, null=True)
|
||||||
legacy_hanchan_count = models.PositiveIntegerField(blank=True, null=True)
|
legacy_hanchan_count = models.PositiveIntegerField(blank=True, null=True)
|
||||||
@@ -359,7 +360,10 @@ class KyuDanRanking(models.Model):
|
|||||||
objects = managers.KyuDanRankingManager()
|
objects = managers.KyuDanRankingManager()
|
||||||
|
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
ordering = ('-dan_points', 'dan', '-kyu_points')
|
ordering = (models.F("dan").desc(nulls_last=True),
|
||||||
|
'-dan_points', '-kyu_points',
|
||||||
|
'-won_hanchans', '-good_hanchans',
|
||||||
|
'-last_hanchan_date')
|
||||||
verbose_name = _(u'Kyū/Dan Ranking')
|
verbose_name = _(u'Kyū/Dan Ranking')
|
||||||
verbose_name_plural = _(u'Kyū/Dan Rankings')
|
verbose_name_plural = _(u'Kyū/Dan Rankings')
|
||||||
|
|
||||||
@@ -418,7 +422,7 @@ class KyuDanRanking(models.Model):
|
|||||||
|
|
||||||
def append_tournament_bonuspoints(self, hanchan):
|
def append_tournament_bonuspoints(self, hanchan):
|
||||||
"""
|
"""
|
||||||
Prüft ob es die letzte Hanchan in einem Turnier war. Wenn ja werden
|
Prüft, ob es die letzte Hanchan in einem Turnier war. Wenn ja, werden
|
||||||
bei Bedarf Bonuspunkte vergeben, falls der Spieler das Turnier
|
bei Bedarf Bonuspunkte vergeben, falls der Spieler das Turnier
|
||||||
gewonnen hat.
|
gewonnen hat.
|
||||||
:param hanchan: Ein Player Objekt
|
:param hanchan: Ein Player Objekt
|
||||||
@@ -474,7 +478,7 @@ class KyuDanRanking(models.Model):
|
|||||||
# Setze alles auf die legacy Werte und berechne alles von neuem.
|
# Setze alles auf die legacy Werte und berechne alles von neuem.
|
||||||
self.dan = self.legacy_dan
|
self.dan = self.legacy_dan
|
||||||
self.dan_points = self.legacy_dan_points or 0
|
self.dan_points = self.legacy_dan_points or 0
|
||||||
self.max_dan_points = self.dan_points
|
self.max_dan_points = self.legacy_max_dan_points or 0
|
||||||
self.kyu = self.legacy_kyu
|
self.kyu = self.legacy_kyu
|
||||||
self.kyu_points = self.legacy_kyu_points or 0
|
self.kyu_points = self.legacy_kyu_points or 0
|
||||||
self.hanchan_count = self.legacy_hanchan_count or 0
|
self.hanchan_count = self.legacy_hanchan_count or 0
|
||||||
@@ -491,19 +495,24 @@ class KyuDanRanking(models.Model):
|
|||||||
since = timezone.make_aware(
|
since = timezone.make_aware(
|
||||||
datetime.combine(self.legacy_date, time(0, 0, 0))
|
datetime.combine(self.legacy_date, time(0, 0, 0))
|
||||||
)
|
)
|
||||||
LOGGER.info(
|
|
||||||
"recalculating Kyu/Dan points for %(user)s since %(since)s...",
|
|
||||||
{'user': self.user, 'since': str(since)}
|
|
||||||
)
|
|
||||||
if since:
|
if since:
|
||||||
valid_hanchans = valid_hanchans.filter(start__gt=since)
|
valid_hanchans = valid_hanchans.filter(start__gt=since)
|
||||||
|
else:
|
||||||
|
since = valid_hanchans.aggregate(since=models.Min("start"))["since"]
|
||||||
if until:
|
if until:
|
||||||
valid_hanchans = valid_hanchans.filter(start__lte=until)
|
valid_hanchans = valid_hanchans.filter(start__lte=until)
|
||||||
|
else:
|
||||||
|
until = valid_hanchans.aggregate(until=models.Max("start"))["until"]
|
||||||
|
if valid_hanchans.count() > 0:
|
||||||
|
LOGGER.info(f"recalculating Kyu/Dan points for {self.user} ({since:%Y-%m-%d} - {until:%Y-%m-%d})...")
|
||||||
|
else:
|
||||||
|
LOGGER.info(f"No new valid Hanchans for {self.user}...")
|
||||||
for hanchan in valid_hanchans:
|
for hanchan in valid_hanchans:
|
||||||
self.hanchan_count += 1
|
self.hanchan_count += 1
|
||||||
|
LOGGER.info(f"{self.user} Hanchan no. {self.hanchan_count} from {hanchan.start}")
|
||||||
hanchan.get_playerdata(self.user)
|
hanchan.get_playerdata(self.user)
|
||||||
if since and hanchan.start < since:
|
if since and hanchan.start < since:
|
||||||
LOGGER.debug(hanchan, "<", since, "no recalc")
|
LOGGER.info(hanchan, "<", since, "no recalc")
|
||||||
self.dan_points += hanchan.dan_points or 0
|
self.dan_points += hanchan.dan_points or 0
|
||||||
self.kyu_points += hanchan.kyu_points or 0
|
self.kyu_points += hanchan.kyu_points or 0
|
||||||
self.update_rank()
|
self.update_rank()
|
||||||
@@ -515,7 +524,7 @@ class KyuDanRanking(models.Model):
|
|||||||
self.append_tournament_bonuspoints(hanchan)
|
self.append_tournament_bonuspoints(hanchan)
|
||||||
self.update_rank()
|
self.update_rank()
|
||||||
self.append_3_in_a_row_bonuspoints(hanchan)
|
self.append_3_in_a_row_bonuspoints(hanchan)
|
||||||
hanchan.update_playerdata(self.user)
|
hanchan.update_player_data(self.user)
|
||||||
hanchan.save(recalculate=False)
|
hanchan.save(recalculate=False)
|
||||||
self.won_hanchans += 1 if hanchan.placement == 1 else 0
|
self.won_hanchans += 1 if hanchan.placement == 1 else 0
|
||||||
self.good_hanchans += 1 if hanchan.placement == 2 else 0
|
self.good_hanchans += 1 if hanchan.placement == 2 else 0
|
||||||
@@ -526,7 +535,7 @@ class KyuDanRanking(models.Model):
|
|||||||
"""
|
"""
|
||||||
Berechne die Kyu bzw. Dan Punkte für eine Hanchan neu.
|
Berechne die Kyu bzw. Dan Punkte für eine Hanchan neu.
|
||||||
:type hanchan: Hanchan
|
:type hanchan: Hanchan
|
||||||
:param hanchan: Das Player Objekt das neuberechnet werden soll.
|
:param hanchan: das Player-Objekt, welches neu berechnet werden soll.
|
||||||
"""
|
"""
|
||||||
hanchan.kyu_points = None
|
hanchan.kyu_points = None
|
||||||
hanchan.dan_points = None
|
hanchan.dan_points = None
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<fieldset class="hanchan">
|
<fieldset class="hanchan">
|
||||||
{% for hidden in form.hidden_fields %} {{ hidden }} {% endfor %}
|
{% for hidden in form.hidden_fields %} {{ hidden }} {% endfor %}
|
||||||
<p>
|
<p>
|
||||||
<label for="id_{{ form.start.html_name }}_0" class="field_name {{ form.start.css_classes }}">{{ form.start.label }}:</label>
|
<label for="{{ form.start.if_for_label }}" class="field_name {{ form.start.css_classes }}">{{ form.start.label }}:</label>
|
||||||
{{ form.start }}
|
{{ form.start }}
|
||||||
{{ form.start.errors }}
|
{{ form.start.errors }}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
{% for season_link in season_list%}
|
{% for season_link in season_list%}
|
||||||
<option
|
<option
|
||||||
value="{% url 'mahjong-ladder' season_link %}"
|
value="{% url 'mahjong-ladder' season_link %}"
|
||||||
{% ifequal season season_link %} selected="selected"{% endifequal %}>{{ season_link }}
|
selected="selected">{{ season_link }}
|
||||||
</option>
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
{% if kyu_dan_ranking.legacy_date %}
|
{% if kyu_dan_ranking.legacy_date %}
|
||||||
<p><strong>Frühere Dan Punkte vom {{ kyu_dan_ranking.legacy_date|date }}:</strong> {{kyu_dan_ranking.legacy_dan_points }}</p>
|
<p><strong>Frühere Dan Punkte vom {{kyu_dan_ranking.legacy_date|date}}:</strong> {{kyu_dan_ranking.legacy_dan_points}}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@
|
|||||||
<select id="season" name="season" size="1">
|
<select id="season" name="season" size="1">
|
||||||
{% for season_link in season_list%}
|
{% for season_link in season_list%}
|
||||||
<option
|
<option
|
||||||
{% ifequal season season_link %} selected="selected"{% endifequal %}
|
selected="selected"
|
||||||
value="{{ season_link }}">{{ season_link }}</option>
|
value="{{ season_link }}">{{ season_link }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
@@ -75,5 +75,5 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block buttonbar %}
|
{% block buttonbar %}
|
||||||
<a href="?download=xlsx" class="button"><span class="fa fa-table"></span> Download</a>
|
<a href="?download=xlsx{% if season %}&season={{ season }}{% endif %}" class="button"><span class="fa fa-table"></span> Download</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
{% empty %}
|
{% empty %}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="8">Leider hat es noch niemand in das Ranking geschafft.
|
<td colspan="8">Leider hat es noch niemand in das Ranking geschafft.
|
||||||
Ein Spieler wird erst ins Ranking genommen wenn er 5 Hanchans absolviert hat.
|
Spieler werden erst ins Ranking genommen, wenn sie 5 Hanchans absolviert haben.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -1,38 +1,24 @@
|
|||||||
""" URLS to display the Riichi Mahjong Rankings and the Ladder system."""
|
""" URLS to display the Riichi Mahjong Rankings and the Ladder system."""
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.urls import path
|
||||||
from django.views.generic import RedirectView
|
from django.views.generic import RedirectView
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
urlpatterns = [ # Ignore PyLintBear (C0103)
|
|
||||||
url(r'^$',
|
urlpatterns = [
|
||||||
RedirectView.as_view(url='/ranking/mahjong-ladder/', permanent=True)),
|
path("", RedirectView.as_view(url='/ranking/mahjong-ladder/', permanent=True)),
|
||||||
url(r'^event/(?P<event>[\d]+)/add-hanchan/$',
|
path('event/<int:event>/add-hanchan/', views.HanchanForm.as_view(), name="add-hanchan-form"),
|
||||||
views.HanchanForm.as_view(), name="add-hanchan-form"),
|
path('event/<int:event>/edit/', views.EventHanchanForm.as_view(), name="event-hanchan-form"),
|
||||||
url(r'^event/(?P<event>[\d]+)/edit/$',
|
path('event/<int:event>/mahjong/', views.EventHanchanList.as_view(), name="event-hanchan-list"),
|
||||||
views.EventHanchanForm.as_view(), name="event-hanchan-form"),
|
path('event/<int:event>/mahjong-ranking/', views.EventRankingList.as_view(), name="event-ranking"),
|
||||||
url(r'^event/(?P<event>[\d]+)/mahjong/$',
|
path('hanchan/<int:hanchan>/edit/', views.HanchanForm.as_view(), name="edit-hanchan"),
|
||||||
views.EventHanchanList.as_view(), name="event-hanchan-list"),
|
path('hanchan/<int:hanchan>/delete/', views.DeleteHanchan.as_view(), name="delete-hanchan"),
|
||||||
url(r'^event/(?P<event>[\d]+)/mahjong-ranking/$',
|
path('mahjong-ladder/', views.SeasonRankingList.as_view(), name="mahjong-ladder"),
|
||||||
views.EventRankingList.as_view(), name="event-ranking"),
|
path('mahjong-ladder/<int:season>/', views.SeasonRankingList.as_view(), name="mahjong-ladder"),
|
||||||
url(r'^hanchan/(?P<hanchan>[\d]+)/edit/$',
|
path('player/<slug:username>/dan/', views.PlayerDanScore.as_view(), name="player-dan-score"),
|
||||||
views.HanchanForm.as_view(), name="edit-hanchan"),
|
path('player/<slug:username>/invalid/', views.PlayerInvalidScore.as_view(), name="player-invalid-score"),
|
||||||
url(r'^hanchan/(?P<hanchan>[\d]+)/delete/$',
|
path('player/<slug:username>/kyu/', views.PlayerKyuScore.as_view(), name="player-kyu-score"),
|
||||||
views.DeleteHanchan.as_view(), name="delete-hanchan"),
|
path('player/<slug:username>/ladder/', views.PlayerLadderScore.as_view(), name="player-ladder-score"),
|
||||||
url(r'^mahjong-ladder/$', views.SeasonRankingList.as_view(),
|
path('mahjong/', views.KyuDanRankingList.as_view(), name="kyudanranking-list"),
|
||||||
name="mahjong-ladder"),
|
path('mahjong/<str:order_by>/', views.KyuDanRankingList.as_view(), name="kyudanranking-list"),
|
||||||
url(r'^mahjong-ladder/(?P<season>[\d]+)/$',
|
|
||||||
views.SeasonRankingList.as_view(), name="mahjong-ladder"),
|
|
||||||
url(r'^player/(?P<username>[\-\.\d\w]+)/dan/$',
|
|
||||||
views.PlayerDanScore.as_view(), name="player-dan-score"),
|
|
||||||
url(r'^player/(?P<username>[\-\.\d\w]+)/invalid/$',
|
|
||||||
views.PlayerInvalidScore.as_view(), name="player-invalid-score"),
|
|
||||||
url(r'^player/(?P<username>[\-\.\d\w]+)/kyu/$',
|
|
||||||
views.PlayerKyuScore.as_view(), name="player-kyu-score"),
|
|
||||||
url(r'^player/(?P<username>[\-\.\d\w]+)/ladder/$',
|
|
||||||
views.PlayerLadderScore.as_view(), name="player-ladder-score"),
|
|
||||||
url(r'^mahjong/$', views.KyuDanRankingList.as_view(),
|
|
||||||
name="kyudanranking-list"),
|
|
||||||
url(r'^mahjong/(?P<order_by>[\+\-][a-z_]+)/$',
|
|
||||||
views.KyuDanRankingList.as_view(), name="kyudanranking-list"),
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin, \
|
|||||||
PermissionRequiredMixin
|
PermissionRequiredMixin
|
||||||
from django.contrib.messages.views import SuccessMessageMixin
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.views import generic
|
from django.views import generic
|
||||||
|
|
||||||
from events.mixins import EventDetailMixin
|
from events.mixins import EventDetailMixin
|
||||||
@@ -45,7 +45,7 @@ def get_kyu_dan_ranking(user=None):
|
|||||||
|
|
||||||
class DeleteHanchan(EventDetailMixin, PermissionRequiredMixin,
|
class DeleteHanchan(EventDetailMixin, PermissionRequiredMixin,
|
||||||
generic.DeleteView):
|
generic.DeleteView):
|
||||||
"""Deletes a Hanchan if confimration has been answerd with 'yes'."""
|
"""Deletes a Hanchan if confirmation has been answered with 'yes'."""
|
||||||
form_class = forms.HanchanForm
|
form_class = forms.HanchanForm
|
||||||
model = models.Hanchan
|
model = models.Hanchan
|
||||||
permission_required = 'mahjong_ranking.delete_hanchan'
|
permission_required = 'mahjong_ranking.delete_hanchan'
|
||||||
@@ -53,7 +53,7 @@ class DeleteHanchan(EventDetailMixin, PermissionRequiredMixin,
|
|||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
"""
|
"""
|
||||||
Return to the HachanList of the event form the deleted hanchan.
|
Return to the HanchanList of the event form the deleted hanchan.
|
||||||
:return: URL of the EventHanchanList for the event
|
:return: URL of the EventHanchanList for the event
|
||||||
"""
|
"""
|
||||||
return reverse('event-hanchan-list',
|
return reverse('event-hanchan-list',
|
||||||
@@ -71,7 +71,7 @@ class HanchanForm(SuccessMessageMixin, EventDetailMixin,
|
|||||||
|
|
||||||
def get_form_class(self):
|
def get_form_class(self):
|
||||||
"""
|
"""
|
||||||
Users with hanchan edit persmission can also un-/confirm hanchans.
|
Users with hanchan edit permission can also un-/confirm a Hanchan.
|
||||||
:return: forms.HanchanForm, or forms.HanchanAdminForm
|
:return: forms.HanchanForm, or forms.HanchanAdminForm
|
||||||
"""
|
"""
|
||||||
return forms.HanchanAdminForm if self.request.user.has_perm(
|
return forms.HanchanAdminForm if self.request.user.has_perm(
|
||||||
@@ -123,27 +123,27 @@ class HanchanForm(SuccessMessageMixin, EventDetailMixin,
|
|||||||
class EventHanchanForm(EventDetailMixin, PermissionRequiredMixin,
|
class EventHanchanForm(EventDetailMixin, PermissionRequiredMixin,
|
||||||
generic.TemplateView):
|
generic.TemplateView):
|
||||||
"""Display a Formset to add and Edit Hanchans of the specific Event."""
|
"""Display a Formset to add and Edit Hanchans of the specific Event."""
|
||||||
|
formset: forms.HanchanFormset
|
||||||
permission_required = 'mahjong_ranking.add_hanchan'
|
permission_required = 'mahjong_ranking.add_hanchan'
|
||||||
template_name = 'mahjong_ranking/eventhanchan_form.html'
|
template_name = 'mahjong_ranking/eventhanchan_form.html'
|
||||||
model = models.Hanchan
|
model = models.Hanchan
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
self.event = models.Event.objects.get(pk=self.kwargs['event'])
|
self.event = models.Event.objects.get(pk=self.kwargs['event'])
|
||||||
|
self.formset = forms.HanchanFormset(
|
||||||
|
instance=self.event,
|
||||||
|
initial=[{'start': self.event.start}]
|
||||||
|
)
|
||||||
context = super(EventHanchanForm, self).get_context_data()
|
context = super(EventHanchanForm, self).get_context_data()
|
||||||
context['formset'] = self.formset
|
context['formset'] = self.formset
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
self.get_queryset()
|
self.get_queryset()
|
||||||
self.formset = forms.HanchanFormset(
|
|
||||||
instance=self.event,
|
|
||||||
initial=[{'start': self.event.start}]
|
|
||||||
)
|
|
||||||
context = self.get_context_data(**kwargs)
|
context = self.get_context_data(**kwargs)
|
||||||
return self.render_to_response(context)
|
return self.render_to_response(context)
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
print("ICH WURDE GEPOSTET!!!!")
|
|
||||||
self.get_queryset()
|
self.get_queryset()
|
||||||
self.formset = forms.HanchanFormset(
|
self.formset = forms.HanchanFormset(
|
||||||
self.request.POST,
|
self.request.POST,
|
||||||
@@ -172,9 +172,9 @@ class EventRankingList(EventDetailMixin, generic.ListView):
|
|||||||
|
|
||||||
|
|
||||||
class KyuDanRankingList(MahjongMixin, generic.ListView):
|
class KyuDanRankingList(MahjongMixin, generic.ListView):
|
||||||
"""List all Players with an Kyu or Dan score. """
|
"""List all Players with a Kyu or Dan score. """
|
||||||
order_by = None
|
order_by = None
|
||||||
paginate_by = 25
|
paginate_by = 100
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
"""Set the order_by settings, revert to default_order if necessary."""
|
"""Set the order_by settings, revert to default_order if necessary."""
|
||||||
@@ -192,7 +192,7 @@ class KyuDanRankingList(MahjongMixin, generic.ListView):
|
|||||||
|
|
||||||
class SeasonRankingList(MahjongMixin, generic.ListView):
|
class SeasonRankingList(MahjongMixin, generic.ListView):
|
||||||
model = models.SeasonRanking
|
model = models.SeasonRanking
|
||||||
paginate_by = 25
|
paginate_by = 100
|
||||||
season = None
|
season = None
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
@@ -205,18 +205,23 @@ class SeasonRankingList(MahjongMixin, generic.ListView):
|
|||||||
|
|
||||||
|
|
||||||
class PlayerScore(LoginRequiredMixin, generic.ListView):
|
class PlayerScore(LoginRequiredMixin, generic.ListView):
|
||||||
paginate_by = 25
|
paginate_by: int = 100
|
||||||
user = auth.get_user_model()
|
user = auth.get_user_model()
|
||||||
|
kyu_dan_ranking = None
|
||||||
|
season = None
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
user_model = auth.get_user_model()
|
user_model = auth.get_user_model()
|
||||||
try:
|
try:
|
||||||
self.user = user_model.objects.get(
|
self.user = user_model.objects.get(
|
||||||
username=self.kwargs.get('username'))
|
username=self.kwargs.get('username'))
|
||||||
|
self.kyu_dan_ranking = get_kyu_dan_ranking(user=self.user)
|
||||||
|
self.season = int(self.request.GET.get('season', date.today().year))
|
||||||
except user_model.DoesNotExist:
|
except user_model.DoesNotExist:
|
||||||
raise django.http.Http404(
|
raise django.http.Http404(
|
||||||
_("No user found matching the name {}").format(
|
_("No user found matching the name {}").format(
|
||||||
self.kwargs.get('username')))
|
self.kwargs.get('username')))
|
||||||
|
|
||||||
if request.GET.get('download') == 'xlsx':
|
if request.GET.get('download') == 'xlsx':
|
||||||
return self.get_xlsx(request, *args, **kwargs)
|
return self.get_xlsx(request, *args, **kwargs)
|
||||||
return super(PlayerScore, self).get(request, *args, **kwargs)
|
return super(PlayerScore, self).get(request, *args, **kwargs)
|
||||||
@@ -228,7 +233,7 @@ class PlayerScore(LoginRequiredMixin, generic.ListView):
|
|||||||
context['kyu_dan_ranking'] = models.KyuDanRanking.objects.get(
|
context['kyu_dan_ranking'] = models.KyuDanRanking.objects.get(
|
||||||
user=self.user)
|
user=self.user)
|
||||||
except models.KyuDanRanking.DoesNotExist:
|
except models.KyuDanRanking.DoesNotExist:
|
||||||
context['ranking'] = None
|
context['kyu_dan_ranking'] = None
|
||||||
try:
|
try:
|
||||||
context['ladder_ranking'] = models.SeasonRanking.objects.get(
|
context['ladder_ranking'] = models.SeasonRanking.objects.get(
|
||||||
user=self.user,
|
user=self.user,
|
||||||
@@ -238,27 +243,31 @@ class PlayerScore(LoginRequiredMixin, generic.ListView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
def get_xlsx(self, request, *args, **kwargs):
|
def get_xlsx(self, request, *args, **kwargs):
|
||||||
self.object_list = self.get_queryset()
|
|
||||||
response = django.http.HttpResponse(
|
response = django.http.HttpResponse(
|
||||||
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
|
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
|
||||||
response['Content-Disposition'] = 'attachment; ' \
|
response['Content-Disposition'] = f'attachment; filename="{self.xlsx_filename}"'
|
||||||
'filename="{xlsx_filename}"'.format(
|
xlsx_workbook = xlsx.Workbook()
|
||||||
xlsx_filename=self.xlsx_filename)
|
xlsx_workbook.generate_sheet(
|
||||||
xlxs_workbook = xlsx.Workbook()
|
|
||||||
xlxs_workbook.generate_sheet(
|
|
||||||
title=self.xlsx_filename.split('.')[0],
|
title=self.xlsx_filename.split('.')[0],
|
||||||
columns_settings=self.xlsx_columns,
|
columns_settings=self.xlsx_columns,
|
||||||
object_list=self.object_list
|
object_list=self.get_queryset()
|
||||||
)
|
)
|
||||||
xlxs_workbook.save(response)
|
xlsx_workbook.save(response)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@property
|
||||||
|
def xlsx_columns(self):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def xlsx_filename(self):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
class PlayerDanScore(PlayerScore):
|
class PlayerDanScore(PlayerScore):
|
||||||
template_name = 'mahjong_ranking/player_dan_score.html'
|
template_name = 'mahjong_ranking/player_dan_score.html'
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
self.kyu_dan_ranking = get_kyu_dan_ranking(user=self.user)
|
|
||||||
return models.Hanchan.objects.dan_hanchans(
|
return models.Hanchan.objects.dan_hanchans(
|
||||||
user=self.user,
|
user=self.user,
|
||||||
since=self.kyu_dan_ranking.legacy_date)
|
since=self.kyu_dan_ranking.legacy_date)
|
||||||
@@ -298,17 +307,20 @@ class PlayerDanScore(PlayerScore):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def xlsx_filename(self):
|
def xlsx_filename(self):
|
||||||
return "{username}_dan_score.xlsx".format(username=self.user.username)
|
return f"{self.user.username}_dan_score.xlsx"
|
||||||
|
|
||||||
|
|
||||||
class PlayerInvalidScore(PlayerScore):
|
class PlayerInvalidScore(PlayerScore):
|
||||||
template_name = 'mahjong_ranking/player_invalid_score.html'
|
template_name = 'mahjong_ranking/player_invalid_score.html'
|
||||||
|
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
self.xlsx_filename = "{username}_invalid_score.xlsx".format(
|
|
||||||
username=self.user.username)
|
|
||||||
return models.Hanchan.objects.unconfirmed(user=self.user)
|
return models.Hanchan.objects.unconfirmed(user=self.user)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def xlsx_filename(self):
|
||||||
|
return f"{self.user.username}_invalid_score.xlsx"
|
||||||
|
|
||||||
|
|
||||||
class PlayerKyuScore(PlayerScore):
|
class PlayerKyuScore(PlayerScore):
|
||||||
template_name = 'mahjong_ranking/player_kyu_score.html'
|
template_name = 'mahjong_ranking/player_kyu_score.html'
|
||||||
@@ -354,7 +366,7 @@ class PlayerKyuScore(PlayerScore):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def xlsx_filename(self):
|
def xlsx_filename(self):
|
||||||
return "{username}_kyu_score.xlsx".format(username=self.user.username)
|
return f"{self.user.username}_kyu_score.xlsx"
|
||||||
|
|
||||||
|
|
||||||
class PlayerLadderScore(PlayerScore):
|
class PlayerLadderScore(PlayerScore):
|
||||||
@@ -370,7 +382,6 @@ class PlayerLadderScore(PlayerScore):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
def get_queryset(self, **kwargs):
|
def get_queryset(self, **kwargs):
|
||||||
self.season = int(self.request.GET.get('season', date.today().year))
|
|
||||||
hanchan_list = models.Hanchan.objects.season_hanchans(
|
hanchan_list = models.Hanchan.objects.season_hanchans(
|
||||||
user=self.user,
|
user=self.user,
|
||||||
season=self.season
|
season=self.season
|
||||||
@@ -408,7 +419,4 @@ class PlayerLadderScore(PlayerScore):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def xlsx_filename(self):
|
def xlsx_filename(self):
|
||||||
return "{username}_ladder_score_{season}.xlsx".format(
|
return f"{self.user.username}_ladder_{self.season}_score.xlsx"
|
||||||
username=self.user.username,
|
|
||||||
season=self.season
|
|
||||||
)
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
""" Admin Interface to manipulate the maistar ranking """
|
""" Admin Interface to manipulate the maistar ranking """
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from . import forms, models
|
from . import forms, models
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""Django Forms to add and edit Mai-Star games."""
|
"""Django Forms to add and edit Mai-Star games."""
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: kasu.mahjong_ranking\n"
|
"Project-Id-Version: kasu.mahjong_ranking\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2019-12-13 23:38+0100\n"
|
"POT-Creation-Date: 2023-07-26 18:31+0200\n"
|
||||||
"PO-Revision-Date: 2016-09-28 00:24+0200\n"
|
"PO-Revision-Date: 2016-09-28 00:24+0200\n"
|
||||||
"Last-Translator: Christian Berg <xeniac.at@gmail.com>\n"
|
"Last-Translator: Christian Berg <xeniac.at@gmail.com>\n"
|
||||||
"Language-Team: Kasu <verein@kasu.at>\n"
|
"Language-Team: Kasu <verein@kasu.at>\n"
|
||||||
@@ -69,7 +69,7 @@ msgstr "Wurde bestätigt"
|
|||||||
msgid "the game only counts whe it has been confirmed"
|
msgid "the game only counts whe it has been confirmed"
|
||||||
msgstr "das Spiel zählt nur wenn es bestätigt wurde"
|
msgstr "das Spiel zählt nur wenn es bestätigt wurde"
|
||||||
|
|
||||||
#: models.py:70 models.py:153 templates/maistar_ranking/player_game_list.html:6
|
#: models.py:70 models.py:154 templates/maistar_ranking/player_game_list.html:6
|
||||||
#: templates/maistar_ranking/ranking_list.html:4
|
#: templates/maistar_ranking/ranking_list.html:4
|
||||||
#: templates/maistar_ranking/ranking_list.html:72
|
#: templates/maistar_ranking/ranking_list.html:72
|
||||||
msgid "Season"
|
msgid "Season"
|
||||||
@@ -79,6 +79,30 @@ msgstr "Saison"
|
|||||||
msgid "Mai-Star Game with {0} from {1:%Y-%m-%d}"
|
msgid "Mai-Star Game with {0} from {1:%Y-%m-%d}"
|
||||||
msgstr "Mai-Star Spiel mit {0} vom {1:%Y-%m-%d}"
|
msgstr "Mai-Star Spiel mit {0} vom {1:%Y-%m-%d}"
|
||||||
|
|
||||||
|
#: templates/maistar_ranking/game_confirm_delete.html:4
|
||||||
|
#: templates/maistar_ranking/game_confirm_delete.html:9
|
||||||
|
msgid "Delete game"
|
||||||
|
msgstr "Spiel löschen"
|
||||||
|
|
||||||
|
#: templates/maistar_ranking/game_confirm_delete.html:12
|
||||||
|
#: templates/maistar_ranking/game_list.html:14
|
||||||
|
msgid "Place"
|
||||||
|
msgstr "Platz"
|
||||||
|
|
||||||
|
#: templates/maistar_ranking/game_confirm_delete.html:18
|
||||||
|
#: templates/maistar_ranking/game_list.html:19
|
||||||
|
#: templates/maistar_ranking/player_game_list.html:36
|
||||||
|
msgid "Points"
|
||||||
|
msgstr "Punkte"
|
||||||
|
|
||||||
|
#: templates/maistar_ranking/game_confirm_delete.html:23
|
||||||
|
msgid "Cancel"
|
||||||
|
msgstr "Abbrechen"
|
||||||
|
|
||||||
|
#: templates/maistar_ranking/game_confirm_delete.html:25
|
||||||
|
msgid "Delete"
|
||||||
|
msgstr "Löschen"
|
||||||
|
|
||||||
#: templates/maistar_ranking/game_form.html:5
|
#: templates/maistar_ranking/game_form.html:5
|
||||||
#: templates/maistar_ranking/game_form.html:16
|
#: templates/maistar_ranking/game_form.html:16
|
||||||
#: templates/maistar_ranking/game_list.html:27
|
#: templates/maistar_ranking/game_list.html:27
|
||||||
@@ -113,15 +137,6 @@ msgstr "Gespielte Mai-Star Spiele"
|
|||||||
msgid "Game"
|
msgid "Game"
|
||||||
msgstr "Spiel"
|
msgstr "Spiel"
|
||||||
|
|
||||||
#: templates/maistar_ranking/game_list.html:14
|
|
||||||
msgid "Place"
|
|
||||||
msgstr "Platz"
|
|
||||||
|
|
||||||
#: templates/maistar_ranking/game_list.html:19
|
|
||||||
#: templates/maistar_ranking/player_game_list.html:36
|
|
||||||
msgid "Points"
|
|
||||||
msgstr "Punkte"
|
|
||||||
|
|
||||||
#: templates/maistar_ranking/game_list.html:24
|
#: templates/maistar_ranking/game_list.html:24
|
||||||
#: templates/maistar_ranking/player_game_list.html:41
|
#: templates/maistar_ranking/player_game_list.html:41
|
||||||
msgid "Delete Game"
|
msgid "Delete Game"
|
||||||
@@ -135,19 +150,6 @@ msgstr "Für diese Veranstaltung wurden noch keine Mai-Star Spiele erfasst."
|
|||||||
msgid "Edit Event"
|
msgid "Edit Event"
|
||||||
msgstr "Veranstaltung bearbeiten"
|
msgstr "Veranstaltung bearbeiten"
|
||||||
|
|
||||||
#: templates/maistar_ranking/hanchan_confirm_delete.html:4
|
|
||||||
#: templates/maistar_ranking/hanchan_confirm_delete.html:10
|
|
||||||
msgid "Delete game"
|
|
||||||
msgstr "Spiel löschen"
|
|
||||||
|
|
||||||
#: templates/maistar_ranking/hanchan_confirm_delete.html:13
|
|
||||||
msgid "Cancel"
|
|
||||||
msgstr "Abbrechen"
|
|
||||||
|
|
||||||
#: templates/maistar_ranking/hanchan_confirm_delete.html:14
|
|
||||||
msgid "Delete"
|
|
||||||
msgstr "Löschen"
|
|
||||||
|
|
||||||
#: templates/maistar_ranking/page.html:5
|
#: templates/maistar_ranking/page.html:5
|
||||||
msgid "Archive"
|
msgid "Archive"
|
||||||
msgstr "Archiv"
|
msgstr "Archiv"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from django.db import models
|
|||||||
from django.db.models.signals import post_delete, post_save
|
from django.db.models.signals import post_delete, post_save
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from events.models import Event
|
from events.models import Event
|
||||||
from . import settings, managers
|
from . import settings, managers
|
||||||
|
|||||||
30
src/maistar_ranking/templates/maistar_ranking/game_confirm_delete.html
Executable file
30
src/maistar_ranking/templates/maistar_ranking/game_confirm_delete.html
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load i18n humanize thumbnail %}
|
||||||
|
|
||||||
|
{% block meta_title %}{% trans 'Delete game' %}{% endblock %}
|
||||||
|
|
||||||
|
{% block maincontent %}
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<h2 class="grid_12">{% trans "Delete game" %}</h2>
|
||||||
|
{% for player in game.player_list %}
|
||||||
|
<div class="grid_2 center">
|
||||||
|
<h4>{{ player.placement|ordinal }} {% trans 'Place' %}</h4>
|
||||||
|
<a class="avatar player"
|
||||||
|
href="{% url 'maistar-player-games' username=player.user.username season=game.season %}"><img
|
||||||
|
src="{% thumbnail player.user.avatar|default:'unknown_profile.jpg' 'avatar' %}"
|
||||||
|
width="70" height="70" alt=""/></a>
|
||||||
|
<p><a href="{% url 'maistar-player-games' username=player.user.username season=game.season %}">{{player.user.username}}</a><br/>
|
||||||
|
{{player.score}} {% trans 'Points' %}</p>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% include 'form.html' %}
|
||||||
|
<p class="buttonbar grid_12">
|
||||||
|
<button type="button" onclick="window.history.back()"><span class="fa fa-close"></span>{% trans 'Cancel' %}
|
||||||
|
</button>
|
||||||
|
<button type="submit"><span class="fa fa-trash"></span>{% trans 'Delete' %}</button>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block buttonbar %}{% endblock %}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
{% load i18n comments %}
|
|
||||||
|
|
||||||
{% block meta_title %}{% trans 'Delete game' %}{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<form method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<fieldset>
|
|
||||||
<legend>{% trans "Delete game" %}</legend>
|
|
||||||
{% include 'form.html' %}
|
|
||||||
<p class="buttonbar">
|
|
||||||
<button type="button" onclick="window.history.back()"><span class="fa fa-close"></span>{% trans 'Cancel' %}</button>
|
|
||||||
<button type="submit"><span class="fa fa-trash"></span>{% trans 'Delete' %}</button>
|
|
||||||
</p>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block buttonbar %}{% endblock %}
|
|
||||||
@@ -4,6 +4,6 @@
|
|||||||
{% block additional_nav_elements %}
|
{% block additional_nav_elements %}
|
||||||
<a href="{% url 'season_ranking-archive' %}" class="{% if is_archive %}active{% endif %}">{% trans 'Archive' %}</a>
|
<a href="{% url 'season_ranking-archive' %}" class="{% if is_archive %}active{% endif %}">{% trans 'Archive' %}</a>
|
||||||
{% if perms.events.add_event %}
|
{% if perms.events.add_event %}
|
||||||
<a href="{% url 'event-form' %}" class="{% ifequal request.path '/events/add/' %}active{% endifequal %}">{% trans 'Add Event' %}</a>
|
<a href="{% url 'event-form' %}" class="{% if request.path == '/events/add/' %}active{% endif %}">{% trans 'Add Event' %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
<select id="season" name="season" size="1" onChange="window.location.href = document.season_select.season.options[document.season_select.season.selectedIndex].value;">
|
<select id="season" name="season" size="1" onChange="window.location.href = document.season_select.season.options[document.season_select.season.selectedIndex].value;">
|
||||||
{% for season_link in season_list%}
|
{% for season_link in season_list%}
|
||||||
<option
|
<option
|
||||||
{% ifequal season season_link %} selected="selected"{% endifequal %}
|
selected="selected"
|
||||||
value="{% url 'maistar-ranking' season=season_link %}" >{{ season_link }}</option>
|
value="{% url 'maistar-ranking' season=season_link %}" >{{ season_link }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -1,24 +1,16 @@
|
|||||||
""" Definition of the URL paths to access the views of the Maistar ranking. """
|
""" Definition of the URL paths to access the views of the Maistar ranking. """
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.urls import path
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url('^$', views.ListRankings.as_view()),
|
path("", views.ListRankings.as_view()),
|
||||||
url(r'^event/(?P<event>[\d]+)/maistar/$',
|
path('event/<int:event>/maistar/', views.ListGames.as_view(), name="maistar-game-list"),
|
||||||
views.ListGames.as_view(), name="maistar-game-list"),
|
path('event/<int:event>/maistar/add/', views.GameForm.as_view(), name="maistar-add-game"),
|
||||||
url(r'^event/(?P<event>[\d]+)/maistar/add/$',
|
path('maistar/', views.ListRankings.as_view(), name="maistar-ranking"),
|
||||||
views.GameForm.as_view(), name="maistar-add-game"),
|
path('maistar/<int:season>/', views.ListRankings.as_view(), name="maistar-ranking"),
|
||||||
url(r'^maistar/(?P<game>[\d]+)/edit/$',
|
path('maistar/<int:game>/edit/', views.GameForm.as_view(), name="maistar-edit-game"),
|
||||||
views.GameForm.as_view(), name="maistar-edit-game"),
|
path('maistar/<int:game>/delete/', views.DeleteGame.as_view(), name="maistar-delete-game"),
|
||||||
url(r'^maistar/(?P<game>[\d]+)/delete/$',
|
path('player/<slug:username>/maistar/', views.ListPlayerGames.as_view(), name="maistar-player-games"),
|
||||||
views.DeleteGame.as_view(), name="maistar-delete-game"),
|
path('player/<slug:username>/maistar/<int:season>/', views.ListPlayerGames.as_view(), name="maistar-player-games"),
|
||||||
url(r'^maistar/$',
|
|
||||||
views.ListRankings.as_view(), name="maistar-ranking"),
|
|
||||||
url(r'^maistar/(?P<season>[\d]+)/$',
|
|
||||||
views.ListRankings.as_view(), name="maistar-ranking"),
|
|
||||||
url(r'^player/(?P<username>[\-\.\d\w]+)/maistar/$',
|
|
||||||
views.ListPlayerGames.as_view(), name="maistar-player-games"),
|
|
||||||
url(r'^player/(?P<username>[\-\.\d\w]+)/maistar/(?P<season>[\d]+)/$',
|
|
||||||
views.ListPlayerGames.as_view(), name="maistar-player-games"),
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -31,11 +31,11 @@ class GameForm(EventDetailMixin, PermissionRequiredMixin, generic.UpdateView):
|
|||||||
class DeleteGame(EventDetailMixin, PermissionRequiredMixin, generic.DeleteView):
|
class DeleteGame(EventDetailMixin, PermissionRequiredMixin, generic.DeleteView):
|
||||||
"""
|
"""
|
||||||
Fragt zuerst nach, ob die Hanchan wirklich gelöscht werden soll.
|
Fragt zuerst nach, ob die Hanchan wirklich gelöscht werden soll.
|
||||||
Wir die Frage mit "Ja" beantwortet, wird die die Hanchan gelöscht.
|
Wir die Frage mit "Ja" beantwortet, wird die Hanchan gelöscht.
|
||||||
"""
|
"""
|
||||||
model = models.Game
|
model = models.Game
|
||||||
permission_required = 'maistar_ranking.delete_game'
|
permission_required = 'maistar_ranking.delete_game'
|
||||||
pk_url_kwarg = 'hanchan'
|
pk_url_kwarg = 'game'
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse(
|
return reverse(
|
||||||
@@ -46,11 +46,12 @@ class DeleteGame(EventDetailMixin, PermissionRequiredMixin, generic.DeleteView):
|
|||||||
|
|
||||||
class UpdateGame(EventDetailMixin, PermissionRequiredMixin, generic.UpdateView):
|
class UpdateGame(EventDetailMixin, PermissionRequiredMixin, generic.UpdateView):
|
||||||
"""
|
"""
|
||||||
Ein Formular um eine neues Spiel anzulegen, bzw. eine bestehendes zu
|
Ein Formular um ein neues Spiel anzulegen, bzw. eine bestehendes zu
|
||||||
bearbeiten.
|
bearbeiten.
|
||||||
"""
|
"""
|
||||||
model = models.Game
|
model = models.Game
|
||||||
permission_required = 'maistar_ranking.update_game'
|
permission_required = 'maistar_ranking.update_game'
|
||||||
|
pk_url_kwarg = 'game'
|
||||||
|
|
||||||
def get_object(self, queryset=None): # @UnusedVariable
|
def get_object(self, queryset=None): # @UnusedVariable
|
||||||
game = models.Game.objects.get(id=self.kwargs['game'])
|
game = models.Game.objects.get(id=self.kwargs['game'])
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from django.contrib import admin, messages
|
|||||||
from django.contrib.auth.admin import UserAdmin, GroupAdmin
|
from django.contrib.auth.admin import UserAdmin, GroupAdmin
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import gettext as _
|
||||||
from easy_thumbnails import fields, widgets
|
from easy_thumbnails import fields, widgets
|
||||||
from membership.models import Membership, ActivationRequest
|
from membership.models import Membership, ActivationRequest
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from django import forms
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import auth
|
from django.contrib import auth
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from utils.massmailer import MassMailer
|
from utils.massmailer import MassMailer
|
||||||
from . import models
|
from . import models
|
||||||
from content.models import Page
|
from content.models import Page
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: kasu.membership\n"
|
"Project-Id-Version: kasu.membership\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2019-12-13 23:38+0100\n"
|
"POT-Creation-Date: 2023-06-09 22:00+0200\n"
|
||||||
"PO-Revision-Date: 2018-05-08 00:19+0105\n"
|
"PO-Revision-Date: 2018-05-08 00:19+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"
|
||||||
@@ -35,38 +35,38 @@ msgstr "Ausgewählte Benutzer freischalten"
|
|||||||
msgid "Cleanup selected Activation Requests"
|
msgid "Cleanup selected Activation Requests"
|
||||||
msgstr "Ausgewählte Aktivierungsanfragen bereinigen"
|
msgstr "Ausgewählte Aktivierungsanfragen bereinigen"
|
||||||
|
|
||||||
#: admin.py:62
|
#: admin.py:65
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Can't remove personal data from active member %s."
|
msgid "Can't remove personal data from active member %s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Persönliche Daten von aktiven Mitglied %s können nicht entfernt werden."
|
"Persönliche Daten von aktiven Mitglied %s können nicht entfernt werden."
|
||||||
|
|
||||||
#: admin.py:64
|
#: admin.py:68
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Cleared %d personal data profiles."
|
msgid "Cleared %d personal data profiles."
|
||||||
msgstr "Persönliche Daten in %d Profilen entfernt."
|
msgstr "Persönliche Daten in %d Profilen entfernt."
|
||||||
|
|
||||||
#: admin.py:66
|
#: admin.py:71
|
||||||
msgid "Clear personal Data"
|
msgid "Clear personal Data"
|
||||||
msgstr "Persönliche Daten bereinigen"
|
msgstr "Persönliche Daten bereinigen"
|
||||||
|
|
||||||
#: admin.py:75
|
#: admin.py:80
|
||||||
msgid "Group"
|
msgid "Group"
|
||||||
msgstr "Gruppe"
|
msgstr "Gruppe"
|
||||||
|
|
||||||
#: admin.py:76
|
#: admin.py:81
|
||||||
msgid "Groups"
|
msgid "Groups"
|
||||||
msgstr "Gruppen"
|
msgstr "Gruppen"
|
||||||
|
|
||||||
#: admin.py:102 forms.py:73 models.py:163 models.py:218
|
#: admin.py:107 forms.py:73 models.py:163 models.py:218
|
||||||
msgid "Membership"
|
msgid "Membership"
|
||||||
msgstr "Mitgliedschaft"
|
msgstr "Mitgliedschaft"
|
||||||
|
|
||||||
#: admin.py:107
|
#: admin.py:112
|
||||||
msgid "Permissions"
|
msgid "Permissions"
|
||||||
msgstr "Berechtigung"
|
msgstr "Berechtigung"
|
||||||
|
|
||||||
#: admin.py:109
|
#: admin.py:114
|
||||||
msgid "Important dates"
|
msgid "Important dates"
|
||||||
msgstr "Wichtige Daten"
|
msgstr "Wichtige Daten"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-06-11 09:18
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('membership', '0008_auto_20190106_1954'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='membership',
|
||||||
|
name='first_name',
|
||||||
|
field=models.CharField(blank=True, max_length=150, verbose_name='first name'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -10,7 +10,7 @@ from django.contrib.auth.models import AbstractUser
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import gettext as _
|
||||||
from easy_thumbnails.fields import ThumbnailerImageField
|
from easy_thumbnails.fields import ThumbnailerImageField
|
||||||
|
|
||||||
from utils import OverwriteStorage
|
from utils import OverwriteStorage
|
||||||
|
|||||||
@@ -50,10 +50,10 @@
|
|||||||
<li><strong>{% trans "Member Since" %}:</strong> {{membership.date_joined}}</li>
|
<li><strong>{% trans "Member Since" %}:</strong> {{membership.date_joined}}</li>
|
||||||
<li><strong>{% trans "Last Login" %}:</strong> {{membership.last_login}}</li>
|
<li><strong>{% trans "Last Login" %}:</strong> {{membership.last_login}}</li>
|
||||||
</ul>
|
</ul>
|
||||||
{% ifequal membership user %}
|
{% if membership == user %}
|
||||||
<a href="{% url 'membership-edit' membership.username %}" class="button"> <span class="fa fa-pencil"></span> {% trans "Edit Profile" %} </a>
|
<a href="{% url 'membership-edit' membership.username %}" class="button"> <span class="fa fa-pencil"></span> {% trans "Edit Profile" %} </a>
|
||||||
<a href="{% url 'password_change' %}" class="button"> <span class="fa fa-key"></span> {% trans 'Change Password' %}</a>
|
<a href="{% url 'password_change' %}" class="button"> <span class="fa fa-key"></span> {% trans 'Change Password' %}</a>
|
||||||
{% endifequal %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|||||||
@@ -4,38 +4,25 @@ Created on 03.10.2011
|
|||||||
@author: christian
|
@author: christian
|
||||||
"""
|
"""
|
||||||
import django.contrib.auth.views as auth_views
|
import django.contrib.auth.views as auth_views
|
||||||
from django.conf.urls import url
|
from django.urls import path
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', views.MembershipDetail.as_view()),
|
path("", views.MembershipDetail.as_view()),
|
||||||
url(r'^activate/(?P<activation_key>[\d\w]+)/$',
|
path('activate/<str:activation_key>', views.ActivateRegistration.as_view(),
|
||||||
views.ActivateRegistration.as_view(),
|
|
||||||
name='membership-activate-registration'),
|
name='membership-activate-registration'),
|
||||||
url(r'^activation_sent/$',
|
path('activation_sent/', views.ActivationSent.as_view(), name="membership-registration-complete"),
|
||||||
views.ActivationSent.as_view(),
|
path('login/', auth_views.LoginView.as_view(), name='login'),
|
||||||
name="membership-registration-complete"),
|
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
|
||||||
url(r'^login/$', auth_views.LoginView.as_view(), name='login'),
|
path('password_change/', auth_views.PasswordChangeView.as_view(), name='password_change'),
|
||||||
url(r'^logout/$', auth_views.LogoutView.as_view(), name='logout'),
|
path('password_change/done/', auth_views.PasswordChangeDoneView.as_view(), name='password_change_done'),
|
||||||
url(r'^password_change/$', auth_views.PasswordChangeView.as_view(),
|
path('password_reset/', auth_views.PasswordResetView.as_view(), name='password_reset'),
|
||||||
name='password_change'),
|
path('password_reset/done/', auth_views.PasswordResetDoneView.as_view(), name='password_reset_done'),
|
||||||
url(r'^password_change/done/$', auth_views.PasswordChangeDoneView.as_view(),
|
path('register/', views.RegisterForm.as_view(), name="membership-register"),
|
||||||
name='password_change_done'),
|
path('reset/<uuid:uidb64>/<str:token>/', auth_views.PasswordResetConfirmView.as_view(),
|
||||||
url(r'^password_reset/$', auth_views.PasswordResetView.as_view(),
|
|
||||||
name='password_reset'),
|
|
||||||
url(r'^password_reset/done/$', auth_views.PasswordResetDoneView.as_view(),
|
|
||||||
name='password_reset_done'),
|
|
||||||
url(r'^register/$', views.RegisterForm.as_view(),
|
|
||||||
name="membership-register"),
|
|
||||||
url(
|
|
||||||
r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
|
|
||||||
auth_views.PasswordResetConfirmView.as_view(),
|
|
||||||
name='password_reset_confirm'),
|
name='password_reset_confirm'),
|
||||||
url(r'^reset/done/$', auth_views.PasswordResetCompleteView.as_view(),
|
path('reset/done/', auth_views.PasswordResetCompleteView.as_view(), name='password_reset_complete'),
|
||||||
name='password_reset_complete'),
|
path('<slug:username>/', views.MembershipDetail.as_view(), name='membership-details'),
|
||||||
url(r'^(?P<username>[\-\.\d\w]+)/$',
|
path('<slug:username>/edit/', views.EditMembership.as_view(), name="membership-edit")
|
||||||
views.MembershipDetail.as_view(), name='membership-details'),
|
|
||||||
url(r'^(?P<username>[\-\.\d\w]+)/edit/$',
|
|
||||||
views.EditMembership.as_view(), name="membership-edit")
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
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 mahjong_ranking.models import KyuDanRanking, SeasonRanking
|
from mahjong_ranking.models import KyuDanRanking, SeasonRanking
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Created on 28.09.2011
|
|||||||
@author: christian
|
@author: christian
|
||||||
"""
|
"""
|
||||||
from django.core.files.storage import FileSystemStorage
|
from django.core.files.storage import FileSystemStorage
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from .countries import COUNTRIES
|
from .countries import COUNTRIES
|
||||||
from .html_cleaner import HtmlCleaner
|
from .html_cleaner import HtmlCleaner
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"""A list of all approved countries on planet earth, i18n enabled."""
|
"""A list of all approved countries on planet earth, i18n enabled."""
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
COUNTRIES = (
|
COUNTRIES = (
|
||||||
('GB', _('United Kingdom')),
|
('GB', _('United Kingdom')),
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ 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: 2019-12-13 23:38+0100\n"
|
"POT-Creation-Date: 2023-06-09 22:00+0200\n"
|
||||||
"PO-Revision-Date: 2016-09-28 00:24+0200\n"
|
"PO-Revision-Date: 2016-09-28 00:24+0200\n"
|
||||||
"Last-Translator: Christian Berg <xeniac@posteo.at>\n"
|
"Last-Translator: Christian Berg <xeniac@posteo.at>\n"
|
||||||
"Language-Team: Kasu <verein@kasu.at>\n"
|
"Language-Team: Kasu <verein@kasu.at>\n"
|
||||||
|
|||||||
Reference in New Issue
Block a user