chore(*): reformat using ruff (#19)

mk-personal-profile
Christian Merten 2 weeks ago committed by GitHub
parent 91575eb280
commit 589527e1ac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,14 +1,12 @@
import copy
from django.contrib.auth import get_permission_codename
from django.contrib import messages
from django.contrib.auth import get_permission_codename
from django.core.exceptions import PermissionDenied
from django.contrib import admin, messages
from django.utils.translation import gettext_lazy as _
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import path, reverse
from django.db import models
from django.contrib.admin import helpers, widgets
import rules.contrib.admin
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from rules.permissions import perm_exists
@ -16,19 +14,36 @@ def decorate_admin_view(model, perm=None):
"""
Decorator for wrapping admin views.
"""
def decorator(fun):
def aux(self, request, object_id):
try:
obj = model.objects.get(pk=object_id)
except model.DoesNotExist:
messages.error(request, _('%(modelname)s not found.') % {'modelname': self.opts.verbose_name})
return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % (self.opts.app_label, self.opts.model_name)))
permitted = self.has_change_permission(request, obj) if not perm else request.user.has_perm(perm)
messages.error(
request, _("%(modelname)s not found.") % {"modelname": self.opts.verbose_name}
)
return HttpResponseRedirect(
reverse(
"admin:{}_{}_changelist".format(self.opts.app_label, self.opts.model_name)
)
)
permitted = (
self.has_change_permission(request, obj)
if not perm
else request.user.has_perm(perm)
)
if not permitted:
messages.error(request, _('Insufficient permissions.'))
return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % (self.opts.app_label, self.opts.model_name)))
messages.error(request, _("Insufficient permissions."))
return HttpResponseRedirect(
reverse(
"admin:{}_{}_changelist".format(self.opts.app_label, self.opts.model_name)
)
)
return fun(self, request, obj)
return aux
return decorator
@ -37,7 +52,7 @@ class FieldPermissionsAdminMixin:
field_view_permissions = {}
def may_view_field(self, field_desc, request, obj=None):
if not type(field_desc) is tuple:
if type(field_desc) is not tuple:
field_desc = (field_desc,)
for fd in field_desc:
if fd not in self.field_view_permissions:
@ -47,37 +62,41 @@ class FieldPermissionsAdminMixin:
return True
def get_fieldsets(self, request, obj=None):
fieldsets = super(FieldPermissionsAdminMixin, self).get_fieldsets(request, obj)
fieldsets = super().get_fieldsets(request, obj)
d = []
for title, attrs in fieldsets:
allowed = [f for f in attrs['fields'] if self.may_view_field(f, request, obj)]
allowed = [f for f in attrs["fields"] if self.may_view_field(f, request, obj)]
if len(allowed) == 0:
continue
d.append((title, dict(attrs, **{'fields': allowed})))
d.append((title, dict(attrs, **{"fields": allowed})))
return d
def get_fields(self, request, obj=None):
fields = super(FieldPermissionsAdminMixin, self).get_fields(request, obj)
fields = super().get_fields(request, obj)
return [fd for fd in fields if self.may_view_field(fd, request, obj)]
def get_readonly_fields(self, request, obj=None):
readonly_fields = super(FieldPermissionsAdminMixin, self).get_readonly_fields(request, obj)
return list(readonly_fields) +\
[fd for fd, perm in self.field_change_permissions.items() if not request.user.has_perm(perm)]
readonly_fields = super().get_readonly_fields(request, obj)
return list(readonly_fields) + [
fd
for fd, perm in self.field_change_permissions.items()
if not request.user.has_perm(perm)
]
class ChangeViewAdminMixin:
def change_view(self, request, object_id, form_url="", extra_context=None):
try:
return super(ChangeViewAdminMixin, self).change_view(request, object_id,
form_url=form_url,
extra_context=extra_context)
return super().change_view(
request, object_id, form_url=form_url, extra_context=extra_context
)
except PermissionDenied:
opts = self.opts
obj = self.model.objects.get(pk=object_id)
messages.error(request,
_("You are not allowed to view %(name)s.") % {'name': str(obj)})
return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % (opts.app_label, opts.model_name)))
messages.error(request, _("You are not allowed to view %(name)s.") % {"name": str(obj)})
return HttpResponseRedirect(
reverse("admin:{}_{}_changelist".format(opts.app_label, opts.model_name))
)
class FilteredQuerysetAdminMixin:
@ -91,28 +110,34 @@ class FilteredQuerysetAdminMixin:
if ordering:
qs = qs.order_by(*ordering)
queryset = qs
list_global_perm = '%s.list_global_%s' % (self.opts.app_label, self.opts.model_name)
list_global_perm = "{}.list_global_{}".format(self.opts.app_label, self.opts.model_name)
if request.user.has_perm(list_global_perm):
view_global_perm = '%s.view_global_%s' % (self.opts.app_label, self.opts.model_name)
view_global_perm = "{}.view_global_{}".format(self.opts.app_label, self.opts.model_name)
if request.user.has_perm(view_global_perm):
return queryset
if hasattr(request.user, 'member'):
return queryset
if hasattr(request.user, "member"):
return request.user.member.annotate_view_permission(queryset, model=self.model)
return queryset.annotate(_viewable=models.Value(False))
if not hasattr(request.user, 'member'):
if not hasattr(request.user, "member"):
return self.model.objects.none()
return request.user.member.filter_queryset_by_permissions(queryset, annotate=True, model=self.model)
return request.user.member.filter_queryset_by_permissions(
queryset, annotate=True, model=self.model
)
# class ObjectPermissionsInlineModelAdminMixin(rules.contrib.admin.ObjectPermissionsInlineModelAdminMixin):
#class ObjectPermissionsInlineModelAdminMixin(rules.contrib.admin.ObjectPermissionsInlineModelAdminMixin):
class CommonAdminMixin(FieldPermissionsAdminMixin, ChangeViewAdminMixin, FilteredQuerysetAdminMixin):
class CommonAdminMixin(
FieldPermissionsAdminMixin, ChangeViewAdminMixin, FilteredQuerysetAdminMixin
):
def has_add_permission(self, request, obj=None):
assert obj is None
opts = self.opts
codename = get_permission_codename("add_global", opts)
perm = "%s.%s" % (opts.app_label, codename)
perm = "{}.{}".format(opts.app_label, codename)
return request.user.has_perm(perm, obj)
def has_view_permission(self, request, obj=None):
@ -121,7 +146,7 @@ class CommonAdminMixin(FieldPermissionsAdminMixin, ChangeViewAdminMixin, Filtere
codename = get_permission_codename("view", opts)
else:
codename = get_permission_codename("view_obj", opts)
perm = "%s.%s" % (opts.app_label, codename)
perm = "{}.{}".format(opts.app_label, codename)
if perm_exists(perm):
return request.user.has_perm(perm, obj)
else:
@ -133,7 +158,7 @@ class CommonAdminMixin(FieldPermissionsAdminMixin, ChangeViewAdminMixin, Filtere
codename = get_permission_codename("view", opts)
else:
codename = get_permission_codename("change_obj", opts)
return request.user.has_perm("%s.%s" % (opts.app_label, codename), obj)
return request.user.has_perm("{}.{}".format(opts.app_label, codename), obj)
def has_delete_permission(self, request, obj=None):
opts = self.opts
@ -141,7 +166,7 @@ class CommonAdminMixin(FieldPermissionsAdminMixin, ChangeViewAdminMixin, Filtere
codename = get_permission_codename("delete_global", opts)
else:
codename = get_permission_codename("delete_obj", opts)
return request.user.has_perm("%s.%s" % (opts.app_label, codename), obj)
return request.user.has_perm("{}.{}".format(opts.app_label, codename), obj)
def formfield_for_dbfield(self, db_field, request, **kwargs):
"""
@ -176,7 +201,7 @@ class CommonAdminMixin(FieldPermissionsAdminMixin, ChangeViewAdminMixin, Filtere
# extra HTML -- the "add other" interface -- to the end of the
# rendered output. formfield can be None if it came from a
# OneToOneField with parent_link=True or a M2M intermediary.
#if formfield and db_field.name not in self.raw_id_fields:
# if formfield and db_field.name not in self.raw_id_fields:
# formfield.widget = widgets.RelatedFieldWidgetWrapper(
# formfield.widget,
# db_field.remote_field,
@ -198,13 +223,13 @@ class CommonAdminMixin(FieldPermissionsAdminMixin, ChangeViewAdminMixin, Filtere
class CommonAdminInlineMixin(CommonAdminMixin):
def has_add_permission(self, request, obj):
#assert obj is not None
# assert obj is not None
if obj is None:
return True
if obj.pk is None:
return True
codename = get_permission_codename("add_obj", self.opts)
return request.user.has_perm('%s.%s' % (self.opts.app_label, codename), obj)
return request.user.has_perm("{}.{}".format(self.opts.app_label, codename), obj)
def has_view_permission(self, request, obj=None): # pragma: no cover
if obj is None:
@ -216,7 +241,7 @@ class CommonAdminInlineMixin(CommonAdminMixin):
codename = get_permission_codename("view", opts)
else:
codename = get_permission_codename("view_obj", opts)
perm = "%s.%s" % (opts.app_label, codename)
perm = "{}.{}".format(opts.app_label, codename)
if perm_exists(perm):
return request.user.has_perm(perm, obj)
else:
@ -234,7 +259,7 @@ class CommonAdminInlineMixin(CommonAdminMixin):
opts = field.rel.to._meta
break
codename = get_permission_codename("change_obj", opts)
return request.user.has_perm("%s.%s" % (opts.app_label, codename), obj)
return request.user.has_perm("{}.{}".format(opts.app_label, codename), obj)
def has_delete_permission(self, request, obj=None): # pragma: no cover
if obj is None:

@ -2,5 +2,5 @@ from django.apps import AppConfig
class ContribConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'contrib'
default_auto_field = "django.db.models.BigAutoField"
name = "contrib"

@ -1,9 +1,9 @@
import os
from wsgiref.util import FileWrapper
from django import template
from django.conf import settings
from django.http import HttpResponse
from django import template
from django.template.loader import get_template
from wsgiref.util import FileWrapper
def find_template(template_name):
@ -27,12 +27,15 @@ def serve_media(filename, content_type):
"""
Serve the media file with the given `filename` as an HTTP response.
"""
with open(media_path(filename), 'rb') as f:
with open(media_path(filename), "rb") as f:
response = HttpResponse(FileWrapper(f))
response['Content-Type'] = content_type
response["Content-Type"] = content_type
# download other files than pdf, show pdfs in the browser
response['Content-Disposition'] = 'filename='+filename if content_type == 'application/pdf' else 'attachment; filename='+filename
response["Content-Disposition"] = (
"filename=" + filename
if content_type == "application/pdf"
else "attachment; filename=" + filename
)
return response

@ -1,10 +1,17 @@
from django.db import models
from rules.contrib.models import RulesModelBase, RulesModelMixin
from rules.contrib.models import RulesModelBase
from rules.contrib.models import RulesModelMixin
# Create your models here.
class CommonModel(models.Model, RulesModelMixin, metaclass=RulesModelBase):
class Meta:
abstract = True
default_permissions = (
'add_global', 'change_global', 'view_global', 'delete_global', 'list_global', 'view',
"add_global",
"change_global",
"view_global",
"delete_global",
"list_global",
"view",
)

@ -1,12 +1,12 @@
from django.contrib.auth import get_permission_codename
import rules.contrib.admin
import rules
def memberize_user(func):
def inner(user, other):
if not hasattr(user, 'member'):
if not hasattr(user, "member"):
return False
return func(user.member, other)
return inner

@ -1,28 +1,38 @@
from datetime import datetime, timedelta
from decimal import Decimal
from django.test import TestCase, RequestFactory
from django.contrib.auth import get_user_model
from datetime import timedelta
from unittest.mock import Mock
from unittest.mock import patch
from contrib.admin import CommonAdminMixin
from contrib.models import CommonModel
from contrib.rules import has_global_perm
from django.contrib import admin
from django.db import models
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.core.files.uploadedfile import SimpleUploadedFile
from django.db import models
from django.test import RequestFactory
from django.test import TestCase
from django.utils.translation import gettext_lazy as _
from unittest.mock import Mock, patch
from rules.contrib.models import RulesModelMixin, RulesModelBase
from contrib.models import CommonModel
from contrib.rules import has_global_perm
from contrib.admin import CommonAdminMixin
from utils import file_size_validator, RestrictedFileField, cvt_to_decimal, get_member, normalize_name, normalize_filename, coming_midnight, mondays_until_nth
from rules.contrib.models import RulesModelBase
from rules.contrib.models import RulesModelMixin
from utils import file_size_validator
from utils import mondays_until_nth
from utils import RestrictedFileField
User = get_user_model()
class CommonModelTestCase(TestCase):
def test_common_model_abstract_base(self):
"""Test that CommonModel provides the correct meta attributes"""
meta = CommonModel._meta
self.assertTrue(meta.abstract)
expected_permissions = (
'add_global', 'change_global', 'view_global', 'delete_global', 'list_global', 'view',
"add_global",
"change_global",
"view_global",
"delete_global",
"list_global",
"view",
)
self.assertEqual(meta.default_permissions, expected_permissions)
@ -38,15 +48,13 @@ class CommonModelTestCase(TestCase):
class GlobalPermissionRulesTestCase(TestCase):
def setUp(self):
self.user = User.objects.create_user(
username='testuser',
email='test@example.com',
password='testpass123'
username="testuser", email="test@example.com", password="testpass123"
)
def test_has_global_perm_predicate_creation(self):
"""Test that has_global_perm creates a predicate function"""
# has_global_perm is a decorator factory, not a direct predicate
predicate = has_global_perm('auth.add_user')
predicate = has_global_perm("auth.add_user")
self.assertTrue(callable(predicate))
def test_has_global_perm_with_superuser(self):
@ -54,33 +62,32 @@ class GlobalPermissionRulesTestCase(TestCase):
self.user.is_superuser = True
self.user.save()
predicate = has_global_perm('auth.add_user')
predicate = has_global_perm("auth.add_user")
result = predicate(self.user, None)
self.assertTrue(result)
def test_has_global_perm_with_regular_user(self):
"""Test that regular users don't automatically have global permissions"""
predicate = has_global_perm('auth.add_user')
predicate = has_global_perm("auth.add_user")
result = predicate(self.user, None)
self.assertFalse(result)
class CommonAdminMixinTestCase(TestCase):
def setUp(self):
self.user = User.objects.create_user(username='testuser', password='testpass')
self.user = User.objects.create_user(username="testuser", password="testpass")
def test_formfield_for_dbfield_with_formfield_overrides(self):
"""Test formfield_for_dbfield when db_field class is in formfield_overrides"""
# Create a test admin instance that inherits from Django's ModelAdmin
class TestAdmin(CommonAdminMixin, admin.ModelAdmin):
formfield_overrides = {
models.ForeignKey: {'widget': Mock()}
}
formfield_overrides = {models.ForeignKey: {"widget": Mock()}}
# Create a mock model to use with the admin
class TestModel:
_meta = Mock()
_meta.app_label = 'test'
_meta.app_label = "test"
admin_instance = TestAdmin(TestModel, admin.site)
@ -88,11 +95,11 @@ class CommonAdminMixinTestCase(TestCase):
db_field = models.ForeignKey(User, on_delete=models.CASCADE)
# Create a test request
request = RequestFactory().get('/')
request = RequestFactory().get("/")
request.user = self.user
# Call the method to test formfield_overrides usage
result = admin_instance.formfield_for_dbfield(db_field, request, help_text='Test help text')
result = admin_instance.formfield_for_dbfield(db_field, request, help_text="Test help text")
# Verify that the formfield_overrides were used
self.assertIsNotNone(result)
@ -101,9 +108,7 @@ class CommonAdminMixinTestCase(TestCase):
class UtilsTestCase(TestCase):
def setUp(self):
self.user = User.objects.create_user(
username='testuser',
email='test@example.com',
password='testpass123'
username="testuser", email="test@example.com", password="testpass123"
)
def test_file_size_validator_exceeds_limit(self):
@ -118,12 +123,14 @@ class UtilsTestCase(TestCase):
validator(mock_file)
# Check for the translated error message
expected_message = str(_('Please keep filesize under {} MiB. Current filesize: {:10.2f} MiB.').format(1, 2.00))
expected_message = str(
_("Please keep filesize under {} MiB. Current filesize: {:10.2f} MiB.").format(1, 2.00)
)
self.assertIn(expected_message, str(cm.exception))
def test_restricted_file_field_content_type_not_supported(self):
"""Test RestrictedFileField when content type is not supported"""
field = RestrictedFileField(content_types=['image/jpeg'])
field = RestrictedFileField(content_types=["image/jpeg"])
# Create mock data with unsupported content type
mock_data = Mock()
@ -131,12 +138,12 @@ class UtilsTestCase(TestCase):
mock_data.file.content_type = "text/plain"
# Mock the super().clean() to return our mock data
with patch.object(models.FileField, 'clean', return_value=mock_data):
with patch.object(models.FileField, "clean", return_value=mock_data):
with self.assertRaises(ValidationError) as cm:
field.clean("dummy")
# Check for the translated error message
expected_message = str(_('Filetype not supported.'))
expected_message = str(_("Filetype not supported."))
self.assertIn(expected_message, str(cm.exception))
def test_restricted_file_field_size_exceeds_limit(self):
@ -150,12 +157,14 @@ class UtilsTestCase(TestCase):
mock_data.file._size = 2 # 2 bytes, exceeds limit
# Mock the super().clean() to return our mock data
with patch.object(models.FileField, 'clean', return_value=mock_data):
with patch.object(models.FileField, "clean", return_value=mock_data):
with self.assertRaises(ValidationError) as cm:
field.clean("dummy")
# Check for the translated error message
expected_message = str(_('Please keep filesize under {}. Current filesize: {}').format(1, 2))
expected_message = str(
_("Please keep filesize under {}. Current filesize: {}").format(1, 2)
)
self.assertIn(expected_message, str(cm.exception))
def test_mondays_until_nth(self):

@ -1,10 +1,16 @@
from django.utils.translation import gettext_lazy as _
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin, GroupAdmin as BaseAuthGroupAdmin
from django.contrib.auth.models import User as BaseUser, Group as BaseAuthGroup
from .models import AuthGroup, LoginDatum, RegistrationPassword
from django.contrib.auth.admin import GroupAdmin as BaseAuthGroupAdmin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import Group as BaseAuthGroup
from django.contrib.auth.models import User as BaseUser
from django.utils.translation import gettext_lazy as _
from members.models import Member
from .models import AuthGroup
from .models import LoginDatum
from .models import RegistrationPassword
# Register your models here.
class AuthGroupAdmin(BaseAuthGroupAdmin):
pass
@ -17,8 +23,8 @@ class UserInline(admin.StackedInline):
class LoginDatumAdmin(BaseUserAdmin):
list_display = ('username', 'is_superuser')
#inlines = [UserInline]
list_display = ("username", "is_superuser")
# inlines = [UserInline]
fieldsets = (
(None, {"fields": ("username", "password")}),
(
@ -45,6 +51,7 @@ class LoginDatumAdmin(BaseUserAdmin):
),
)
admin.site.unregister(BaseUser)
admin.site.unregister(BaseAuthGroup)
admin.site.register(LoginDatum, LoginDatumAdmin)

@ -3,6 +3,6 @@ from django.utils.translation import gettext_lazy as _
class LoginDataConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'logindata'
verbose_name = _('Authentication')
default_auto_field = "django.db.models.BigAutoField"
name = "logindata"
verbose_name = _("Authentication")

@ -1,39 +1,41 @@
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import Group as BaseAuthGroup
from django.contrib.auth.models import User as BaseUser
from django.db import models
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin, GroupAdmin as BaseAuthGroupAdmin
from django.contrib.auth.models import User as BaseUser, Group as BaseAuthGroup
from django.utils.translation import gettext_lazy as _
class AuthGroup(BaseAuthGroup):
class Meta:
proxy = True
verbose_name = _('Permission group')
verbose_name_plural = _('Permission groups')
verbose_name = _("Permission group")
verbose_name_plural = _("Permission groups")
class LoginDatum(BaseUser):
class Meta:
proxy = True
verbose_name = _('Login Datum')
verbose_name_plural = _('Login Data')
verbose_name = _("Login Datum")
verbose_name_plural = _("Login Data")
class RegistrationPassword(models.Model):
"""
A password that can be used to register after inviting a member.
"""
password = models.CharField(max_length=100, verbose_name=_('Password'))
password = models.CharField(max_length=100, verbose_name=_("Password"))
def __str__(self):
return self.password
class Meta:
verbose_name = _('Active registration password')
verbose_name_plural = _('Active registration passwords')
verbose_name = _("Active registration password")
verbose_name_plural = _("Active registration passwords")
def initial_user_setup(user, member):
try:
standard_group = AuthGroup.objects.get(name='Standard')
standard_group = AuthGroup.objects.get(name="Standard")
except AuthGroup.DoesNotExist:
return False
@ -41,6 +43,6 @@ def initial_user_setup(user, member):
user.save()
user.groups.add(standard_group)
member.user = user
member.invite_as_user_key = ''
member.invite_as_user_key = ""
member.save()
return True

@ -7,7 +7,7 @@ class CustomOAuth2Validator(OAuth2Validator):
def get_additional_claims(self, request):
if request.user.member:
context = {'email': request.user.member.email}
context = {"email": request.user.member.email}
else:
context = {}
return dict(context, preferred_username=request.user.username)

@ -4,5 +4,5 @@ from . import views
app_name = "logindata"
urlpatterns = [
re_path(r'^register', views.register , name='register'),
re_path(r"^register", views.register, name="register"),
]

@ -1,44 +1,46 @@
from django import forms
from django.shortcuts import render
from django.contrib.auth.forms import UserCreationForm
from django.http import HttpResponseRedirect
from django.utils.translation import gettext_lazy as _
from django.shortcuts import render
from django.urls import reverse
from django.contrib.auth.forms import UserCreationForm
from django.utils.translation import gettext_lazy as _
from members.models import Member
from .models import initial_user_setup, RegistrationPassword
from .models import initial_user_setup
from .models import RegistrationPassword
def render_register_password(request, key, member, error_message=''):
return render(request, 'logindata/register_password.html',
context={'key': key,
'member': member,
'error_message': error_message})
def render_register_password(request, key, member, error_message=""):
return render(
request,
"logindata/register_password.html",
context={"key": key, "member": member, "error_message": error_message},
)
def render_register_failed(request):
return render(request, 'logindata/register_failed.html')
return render(request, "logindata/register_failed.html")
def render_register_form(request, key, password, member, form):
return render(request, 'logindata/register_form.html',
context={'key': key,
'password': password,
'member': member,
'form': form})
return render(
request,
"logindata/register_form.html",
context={"key": key, "password": password, "member": member, "form": form},
)
def render_register_success(request):
return render(request, 'logindata/register_success.html')
return render(request, "logindata/register_success.html")
# Create your views here.
def register(request):
if request.method == 'GET' and 'key' not in request.GET:
return HttpResponseRedirect(reverse('startpage:index'))
if request.method == 'POST' and 'key' not in request.POST:
return HttpResponseRedirect(reverse('startpage:index'))
if request.method == "GET" and "key" not in request.GET:
return HttpResponseRedirect(reverse("startpage:index"))
if request.method == "POST" and "key" not in request.POST:
return HttpResponseRedirect(reverse("startpage:index"))
key = request.GET['key'] if request.method == 'GET' else request.POST['key']
key = request.GET["key"] if request.method == "GET" else request.POST["key"]
if not key:
return render_register_failed(request)
try:
@ -46,17 +48,19 @@ def register(request):
except (Member.DoesNotExist, Member.MultipleObjectsReturned):
return render_register_failed(request)
if request.method == 'GET':
return render_register_password(request, request.GET['key'], member)
if request.method == "GET":
return render_register_password(request, request.GET["key"], member)
if 'password' not in request.POST:
if "password" not in request.POST:
return render_register_failed(request)
password = request.POST['password']
password = request.POST["password"]
# check if the entered password is one of the active registration passwords
if RegistrationPassword.objects.filter(password=password).count() == 0:
return render_register_password(request, key, member, error_message=_('You entered a wrong password.'))
return render_register_password(
request, key, member, error_message=_("You entered a wrong password.")
)
if "save" in request.POST:
form = UserCreationForm(request.POST)
@ -70,6 +74,6 @@ def register(request):
else:
return render_register_failed(request)
else:
prefill = {'username': member.suggested_username()}
prefill = {"username": member.suggested_username()}
form = UserCreationForm(initial=prefill)
return render_register_form(request, key, password, member, form)

@ -1,26 +1,23 @@
import os
import xlsxwriter
from contrib.media import ensure_media_dir
from contrib.media import media_path
from contrib.media import serve_media
from django.contrib import admin
from wsgiref.util import FileWrapper
from django.http import HttpResponse
from django.conf import settings
from .models import Termin
from contrib.media import media_path, serve_media, ensure_media_dir
import xlsxwriter
from .models import Termin
class TerminAdmin(admin.ModelAdmin):
list_display = ('title','start_date', 'end_date', 'group', 'category', 'responsible')
list_filter = ('group',)
ordering = ('start_date','end_date')
actions = ['make_overview']
list_display = ("title", "start_date", "end_date", "group", "category", "responsible")
list_filter = ("group",)
ordering = ("start_date", "end_date")
actions = ["make_overview"]
def make_overview(self, request, queryset):
ensure_media_dir()
filename = 'termine.xlsx'
filename = "termine.xlsx"
workbook = xlsxwriter.Workbook(media_path(filename))
bold = workbook.add_format({'bold': True})
bold = workbook.add_format({"bold": True})
worksheet = workbook.add_worksheet()
worksheet.write(0, 0, "Titel", bold)
worksheet.write(0, 1, "Untertitel", bold)
@ -44,30 +41,32 @@ class TerminAdmin(admin.ModelAdmin):
worksheet.write(0, 19, "Telefonnummer", bold)
worksheet.write(0, 20, "Emailadresse", bold)
for row, termin in enumerate(queryset):
worksheet.write(row+2, 0, termin.title)
worksheet.write(row+2, 1, termin.subtitle)
worksheet.write(row+2, 2, termin.start_date.strftime('%d.%m.%Y'))
worksheet.write(row+2, 3, termin.end_date.strftime('%d.%m.%Y'))
worksheet.write(row+2, 4, termin.group)
worksheet.write(row+2, 5, termin.category)
worksheet.write(row+2, 6, termin.technik)
worksheet.write(row+2, 7, termin.condition)
worksheet.write(row+2, 8, termin.saison)
worksheet.write(row+2, 9, termin.eventart)
worksheet.write(row+2, 10, termin.klassifizierung)
worksheet.write(row+2, 11, termin.anforderung_hoehe)
worksheet.write(row+2, 12, termin.anforderung_strecke)
worksheet.write(row+2, 13, termin.anforderung_dauer)
worksheet.write(row+2, 14, termin.voraussetzungen)
worksheet.write(row+2, 15, termin.description)
worksheet.write(row+2, 16, termin.equipment)
worksheet.write(row+2, 17, termin.max_participants)
worksheet.write(row+2, 18, termin.responsible)
worksheet.write(row+2, 19, termin.phone)
worksheet.write(row+2, 20, termin.email)
worksheet.write(row + 2, 0, termin.title)
worksheet.write(row + 2, 1, termin.subtitle)
worksheet.write(row + 2, 2, termin.start_date.strftime("%d.%m.%Y"))
worksheet.write(row + 2, 3, termin.end_date.strftime("%d.%m.%Y"))
worksheet.write(row + 2, 4, termin.group)
worksheet.write(row + 2, 5, termin.category)
worksheet.write(row + 2, 6, termin.technik)
worksheet.write(row + 2, 7, termin.condition)
worksheet.write(row + 2, 8, termin.saison)
worksheet.write(row + 2, 9, termin.eventart)
worksheet.write(row + 2, 10, termin.klassifizierung)
worksheet.write(row + 2, 11, termin.anforderung_hoehe)
worksheet.write(row + 2, 12, termin.anforderung_strecke)
worksheet.write(row + 2, 13, termin.anforderung_dauer)
worksheet.write(row + 2, 14, termin.voraussetzungen)
worksheet.write(row + 2, 15, termin.description)
worksheet.write(row + 2, 16, termin.equipment)
worksheet.write(row + 2, 17, termin.max_participants)
worksheet.write(row + 2, 18, termin.responsible)
worksheet.write(row + 2, 19, termin.phone)
worksheet.write(row + 2, 20, termin.email)
workbook.close()
return serve_media(filename, 'application/xlsx')
return serve_media(filename, "application/xlsx")
make_overview.short_description = "Termine in Excel Liste überführen"
# Register your models here.
admin.site.register(Termin, TerminAdmin)

@ -2,4 +2,4 @@ from django.apps import AppConfig
class LudwigsburgalpinConfig(AppConfig):
name = 'ludwigsburgalpin'
name = "ludwigsburgalpin"

@ -1,121 +1,128 @@
from django.db import models
from django.core.validators import MinValueValidator
from django.db import models
GRUPPE = [
('ASG', 'Alpinsportgruppe'),
('OGB', 'Ortsgruppe Bietigheim'),
('OGV', 'Ortsgruppe Vaihingen'),
('JUG', 'Jugend'),
('FAM', 'Familie'),
('Ü30', 'Ü30'),
('MTB', 'Mountainbike'),
('RA', 'RegioAktiv'),
('SEK', 'Sektion'),
("ASG", "Alpinsportgruppe"),
("OGB", "Ortsgruppe Bietigheim"),
("OGV", "Ortsgruppe Vaihingen"),
("JUG", "Jugend"),
("FAM", "Familie"),
("Ü30", "Ü30"),
("MTB", "Mountainbike"),
("RA", "RegioAktiv"),
("SEK", "Sektion"),
]
KATEGORIE = [
('WAN', 'Wandern'),
('BW', 'Bergwandern'),
('KST', 'Klettersteig'),
('KL', 'Klettern'),
('SKI', 'Piste, Loipe'),
('SCH', 'Schneeschuhgehen'),
('ST', 'Skitour'),
('STH', 'Skihochtour'),
('HT', 'Hochtour'),
('MTB', 'Montainbike'),
('AUS', 'Ausbildung'),
('SON', 'Sonstiges z.B. Treffen')
("WAN", "Wandern"),
("BW", "Bergwandern"),
("KST", "Klettersteig"),
("KL", "Klettern"),
("SKI", "Piste, Loipe"),
("SCH", "Schneeschuhgehen"),
("ST", "Skitour"),
("STH", "Skihochtour"),
("HT", "Hochtour"),
("MTB", "Montainbike"),
("AUS", "Ausbildung"),
("SON", "Sonstiges z.B. Treffen"),
]
KONDITION = [
('gering', 'gering'),
('mittel', 'mittel'),
('groß', 'groß'),
('sehr groß', 'sehr groß'),
("gering", "gering"),
("mittel", "mittel"),
("groß", "groß"),
("sehr groß", "sehr groß"),
]
TECHNIK = [
('leicht', 'leicht'),
('mittel', 'mittel'),
('schwer', 'schwer'),
('sehr schwer', 'sehr schwer'),
("leicht", "leicht"),
("mittel", "mittel"),
("schwer", "schwer"),
("sehr schwer", "sehr schwer"),
]
SAISON = [
('ganzjährig','ganzjährig'),
('Indoor', 'Indoor'),
('Sommer', 'Sommer'),
('Winter', 'Winter'),
("ganzjährig", "ganzjährig"),
("Indoor", "Indoor"),
("Sommer", "Sommer"),
("Winter", "Winter"),
]
EVENTART = [
('Einzeltermin', 'Einzeltermin',),
('Mehrtagesevent', 'Mehrtagesevent',),
('Regelmäßiges Event/Training', 'Regelmäßiges Event/Training',),
('Tagesevent', 'Tagesevent',),
('Wochenendevent', 'Wochenendevent',),
(
"Einzeltermin",
"Einzeltermin",
),
(
"Mehrtagesevent",
"Mehrtagesevent",
),
(
"Regelmäßiges Event/Training",
"Regelmäßiges Event/Training",
),
(
"Tagesevent",
"Tagesevent",
),
(
"Wochenendevent",
"Wochenendevent",
),
]
KLASSIFIZIERUNG = [
('Gemeinschaftstour', 'Gemeinschaftstour'),
('Ausbildung', 'Ausbildung'),
("Gemeinschaftstour", "Gemeinschaftstour"),
("Ausbildung", "Ausbildung"),
]
# Create your models here.
class Termin(models.Model):
title = models.CharField('Titel', max_length=100)
subtitle = models.CharField('Untertitel', max_length=100, blank=True)
start_date = models.DateField('Von')
end_date = models.DateField('Bis')
group = models.CharField('Gruppe',
choices=GRUPPE,
max_length=100)
responsible = models.CharField('Organisator', max_length=100, blank=False)
phone = models.CharField(max_length=20, verbose_name='Telefonnumer', blank=True)
email = models.EmailField(max_length=100, verbose_name='Email', blank=False)
category = models.CharField('Kategorie', blank=False, choices=KATEGORIE, max_length=100,
default='SON')
condition = models.CharField('Kondition', blank=False, choices=KONDITION, max_length=100,
default='mittel')
technik = models.CharField('Technik', blank=False, choices=TECHNIK, max_length=100,
default='mittel')
saison = models.CharField('Saison', blank=False, choices=SAISON, max_length=100,
default='ganzjährig')
eventart = models.CharField('Eventart', blank=False, choices=EVENTART, max_length=100,
default='Einzeltermin')
klassifizierung = models.CharField('Klassifizierung', blank=False, choices=KLASSIFIZIERUNG,
max_length=100,
default='Gemeinschaftstour')
equipment = models.TextField('Ausrüstung',
blank=True)
voraussetzungen = models.TextField('Voraussetzungen',
blank=True)
description = models.TextField('Beschreibung',
blank=True)
max_participants = models.IntegerField('Max. Teilnehmerzahl',
blank=False,
validators=[
MinValueValidator(1)
],
default=10)
anforderung_hoehe = models.IntegerField('Höhenmeter in Meter',
blank=True,
validators=[
MinValueValidator(0)
],
default=0)
anforderung_strecke = models.IntegerField('Strecke in Kilometer',
blank=True,
validators=[
MinValueValidator(0)
],
default=0)
anforderung_dauer = models.IntegerField('Etappendauer in Stunden',
blank=True,
validators=[
MinValueValidator(0)
],
default=0)
title = models.CharField("Titel", max_length=100)
subtitle = models.CharField("Untertitel", max_length=100, blank=True)
start_date = models.DateField("Von")
end_date = models.DateField("Bis")
group = models.CharField("Gruppe", choices=GRUPPE, max_length=100)
responsible = models.CharField("Organisator", max_length=100, blank=False)
phone = models.CharField(max_length=20, verbose_name="Telefonnumer", blank=True)
email = models.EmailField(max_length=100, verbose_name="Email", blank=False)
category = models.CharField(
"Kategorie", blank=False, choices=KATEGORIE, max_length=100, default="SON"
)
condition = models.CharField(
"Kondition", blank=False, choices=KONDITION, max_length=100, default="mittel"
)
technik = models.CharField(
"Technik", blank=False, choices=TECHNIK, max_length=100, default="mittel"
)
saison = models.CharField(
"Saison", blank=False, choices=SAISON, max_length=100, default="ganzjährig"
)
eventart = models.CharField(
"Eventart", blank=False, choices=EVENTART, max_length=100, default="Einzeltermin"
)
klassifizierung = models.CharField(
"Klassifizierung",
blank=False,
choices=KLASSIFIZIERUNG,
max_length=100,
default="Gemeinschaftstour",
)
equipment = models.TextField("Ausrüstung", blank=True)
voraussetzungen = models.TextField("Voraussetzungen", blank=True)
description = models.TextField("Beschreibung", blank=True)
max_participants = models.IntegerField(
"Max. Teilnehmerzahl", blank=False, validators=[MinValueValidator(1)], default=10
)
anforderung_hoehe = models.IntegerField(
"Höhenmeter in Meter", blank=True, validators=[MinValueValidator(0)], default=0
)
anforderung_strecke = models.IntegerField(
"Strecke in Kilometer", blank=True, validators=[MinValueValidator(0)], default=0
)
anforderung_dauer = models.IntegerField(
"Etappendauer in Stunden", blank=True, validators=[MinValueValidator(0)], default=0
)
def __str__(self):
return "{} {}".format(self.title, str(self.group))
class Meta:
verbose_name = 'Termin'
verbose_name_plural = 'Termine'
verbose_name = "Termin"
verbose_name_plural = "Termine"

@ -1,13 +1,21 @@
from http import HTTPStatus
from django.test import TestCase, RequestFactory
from django.utils import timezone
from django.conf import settings
from django.contrib.admin.sites import AdminSite
from django.test import RequestFactory
from django.test import TestCase
from django.urls import reverse
from django.conf import settings
from .models import Termin, GRUPPE, KATEGORIE, KONDITION, TECHNIK, SAISON,\
EVENTART, KLASSIFIZIERUNG
from django.utils import timezone
from .admin import TerminAdmin
from .models import EVENTART
from .models import GRUPPE
from .models import KATEGORIE
from .models import KLASSIFIZIERUNG
from .models import KONDITION
from .models import SAISON
from .models import TECHNIK
from .models import Termin
class BasicTerminTestCase(TestCase):
@ -15,68 +23,80 @@ class BasicTerminTestCase(TestCase):
def setUp(self):
for i in range(BasicTerminTestCase.TERMIN_NO):
Termin.objects.create(title='Foo {}'.format(i),
start_date=timezone.now().date(),
end_date=timezone.now().date(),
group=GRUPPE[0][0],
email=settings.TEST_MAIL,
category=KATEGORIE[0][0],
technik=TECHNIK[0][0],
max_participants=42,
anforderung_hoehe=10)
Termin.objects.create(
title="Foo {}".format(i),
start_date=timezone.now().date(),
end_date=timezone.now().date(),
group=GRUPPE[0][0],
email=settings.TEST_MAIL,
category=KATEGORIE[0][0],
technik=TECHNIK[0][0],
max_participants=42,
anforderung_hoehe=10,
)
class TerminAdminTestCase(BasicTerminTestCase):
def test_str(self):
t = Termin.objects.all()[0]
self.assertEqual(str(t), '{} {}'.format(t.title, str(t.group)))
self.assertEqual(str(t), "{} {}".format(t.title, str(t.group)))
def test_make_overview(self):
factory = RequestFactory()
admin = TerminAdmin(Termin, AdminSite())
url = reverse('admin:ludwigsburgalpin_termin_changelist')
url = reverse("admin:ludwigsburgalpin_termin_changelist")
request = factory.get(url)
response = admin.make_overview(request, Termin.objects.all())
self.assertEqual(response['Content-Type'], 'application/xlsx',
'The content-type of the generated overview should be an .xlsx file.')
self.assertEqual(
response["Content-Type"],
"application/xlsx",
"The content-type of the generated overview should be an .xlsx file.",
)
class ViewTestCase(BasicTerminTestCase):
def test_get_index(self):
url = reverse('ludwigsburgalpin:index')
url = reverse("ludwigsburgalpin:index")
response = self.client.get(url)
self.assertEqual(response.status_code, HTTPStatus.OK)
def test_submit_termin(self):
url = reverse('ludwigsburgalpin:index')
response = self.client.post(url, data={
'title': 'My Title',
'subtitle': 'My Subtitle',
'start_date': '2024-01-01',
'end_date': '2024-02-01',
'group': GRUPPE[0][0],
'category': KATEGORIE[0][0],
'condition': KONDITION[0][0],
'technik': TECHNIK[0][0],
'saison': SAISON[0][0],
'eventart': EVENTART[0][0],
'klassifizierung': KLASSIFIZIERUNG[0][0],
'anforderung_hoehe': 10,
'anforderung_strecke': 10,
'anforderung_dauer': 10,
'max_participants': 100,
})
t = Termin.objects.get(title='My Title')
url = reverse("ludwigsburgalpin:index")
response = self.client.post(
url,
data={
"title": "My Title",
"subtitle": "My Subtitle",
"start_date": "2024-01-01",
"end_date": "2024-02-01",
"group": GRUPPE[0][0],
"category": KATEGORIE[0][0],
"condition": KONDITION[0][0],
"technik": TECHNIK[0][0],
"saison": SAISON[0][0],
"eventart": EVENTART[0][0],
"klassifizierung": KLASSIFIZIERUNG[0][0],
"anforderung_hoehe": 10,
"anforderung_strecke": 10,
"anforderung_dauer": 10,
"max_participants": 100,
},
)
t = Termin.objects.get(title="My Title")
self.assertEqual(t.group, GRUPPE[0][0])
self.assertEqual(response.status_code, HTTPStatus.OK)
self.assertContains(response, "Termin erfolgreich eingereicht", html=True)
def test_submit_termin_invalid(self):
url = reverse('ludwigsburgalpin:index')
url = reverse("ludwigsburgalpin:index")
# many required fields are missing
response = self.client.post(url, data={
'title': 'My Title',
})
response = self.client.post(
url,
data={
"title": "My Title",
},
)
self.assertEqual(response.status_code, HTTPStatus.OK)
self.assertContains(response, "Dieses Feld ist zwingend erforderlich.", html=True)

@ -4,6 +4,6 @@ from . import views
app_name = "ludwigsburgalpin"
urlpatterns = [
re_path(r'^$', views.index, name='index')
re_path(r"^$", views.index, name="index")
# re_path(r'^subscribe', views.subscribe, name='subscribe'),
]

@ -1,100 +1,89 @@
from django.shortcuts import render
from django import forms
from django.http import HttpResponseRedirect
from django.contrib.admin import widgets
from django.core.validators import MinValueValidator
from .models import Termin, GRUPPE, KATEGORIE, KONDITION, TECHNIK, SAISON, EVENTART, KLASSIFIZIERUNG
from django.shortcuts import render
datepicker = forms.TextInput(attrs={'class': 'datepicker'})
from .models import EVENTART
from .models import GRUPPE
from .models import KATEGORIE
from .models import KLASSIFIZIERUNG
from .models import KONDITION
from .models import SAISON
from .models import TECHNIK
from .models import Termin
datepicker = forms.TextInput(attrs={"class": "datepicker"})
class TerminForm(forms.Form):
title = forms.CharField(label='Titel')
subtitle = forms.CharField(label='Untertitel')
start_date = forms.DateField(label='Von',
widget=datepicker)
end_date = forms.DateField(label='Bis',
widget=datepicker)
group = forms.ChoiceField(label='Gruppe',
required=True,
choices=GRUPPE)
category = forms.ChoiceField(label='Kategorie', required=True, choices=KATEGORIE)
condition = forms.ChoiceField(label='Kondition', required=True, choices=KONDITION)
technik = forms.ChoiceField(label='Technik', required=True, choices=TECHNIK)
saison = forms.ChoiceField(label='Saison', required=True, choices=SAISON)
eventart = forms.ChoiceField(label='Eventart', required=True, choices=EVENTART)
klassifizierung = forms.ChoiceField(label='Klassifizierung', required=True, choices=KLASSIFIZIERUNG)
anforderung_hoehe = forms.IntegerField(label='Höhenmeter in Metern',
required=True,
validators=[
MinValueValidator(0)
])
anforderung_strecke = forms.IntegerField(label='Strecke in Kilometern',
required=True,
validators=[
MinValueValidator(0)
])
anforderung_dauer = forms.IntegerField(label='Etappendauer in Stunden',
required=True,
validators=[
MinValueValidator(0)
])
description = forms.CharField(label='Beschreibung',
widget=forms.Textarea,
required=False)
equipment = forms.CharField(label='Ausrüstung',
widget=forms.Textarea,
required=False)
voraussetzungen = forms.CharField(label='Voraussetzungen',
widget=forms.Textarea,
required=False)
max_participants = forms.IntegerField(label='Max. Teilnehmerzahl',
required=True,
validators=[
MinValueValidator(1)
])
responsible = forms.CharField(label='Organisator', max_length=100,
required=False)
phone = forms.CharField(max_length=20, label='Telefonnumer',
required=False)
email = forms.EmailField(max_length=100, label='Email',
required=False)
class TerminForm(forms.Form):
title = forms.CharField(label="Titel")
subtitle = forms.CharField(label="Untertitel")
start_date = forms.DateField(label="Von", widget=datepicker)
end_date = forms.DateField(label="Bis", widget=datepicker)
group = forms.ChoiceField(label="Gruppe", required=True, choices=GRUPPE)
category = forms.ChoiceField(label="Kategorie", required=True, choices=KATEGORIE)
condition = forms.ChoiceField(label="Kondition", required=True, choices=KONDITION)
technik = forms.ChoiceField(label="Technik", required=True, choices=TECHNIK)
saison = forms.ChoiceField(label="Saison", required=True, choices=SAISON)
eventart = forms.ChoiceField(label="Eventart", required=True, choices=EVENTART)
klassifizierung = forms.ChoiceField(
label="Klassifizierung", required=True, choices=KLASSIFIZIERUNG
)
anforderung_hoehe = forms.IntegerField(
label="Höhenmeter in Metern", required=True, validators=[MinValueValidator(0)]
)
anforderung_strecke = forms.IntegerField(
label="Strecke in Kilometern", required=True, validators=[MinValueValidator(0)]
)
anforderung_dauer = forms.IntegerField(
label="Etappendauer in Stunden", required=True, validators=[MinValueValidator(0)]
)
description = forms.CharField(label="Beschreibung", widget=forms.Textarea, required=False)
equipment = forms.CharField(label="Ausrüstung", widget=forms.Textarea, required=False)
voraussetzungen = forms.CharField(
label="Voraussetzungen", widget=forms.Textarea, required=False
)
max_participants = forms.IntegerField(
label="Max. Teilnehmerzahl", required=True, validators=[MinValueValidator(1)]
)
responsible = forms.CharField(label="Organisator", max_length=100, required=False)
phone = forms.CharField(max_length=20, label="Telefonnumer", required=False)
email = forms.EmailField(max_length=100, label="Email", required=False)
# Create your views here.
def index(request, *args):
if request.method == 'POST':
if request.method == "POST":
form = TerminForm(request.POST)
if form.is_valid():
termin = Termin(title=form.cleaned_data["title"],
subtitle=form.cleaned_data["subtitle"],
start_date=form.cleaned_data["start_date"],
end_date=form.cleaned_data["end_date"],
group=form.cleaned_data["group"],
responsible=form.cleaned_data["responsible"],
phone=form.cleaned_data["phone"],
email=form.cleaned_data["email"],
category=form.cleaned_data["category"],
condition=form.cleaned_data["condition"],
technik=form.cleaned_data["technik"],
saison=form.cleaned_data["saison"],
eventart=form.cleaned_data["eventart"],
klassifizierung=form.cleaned_data["klassifizierung"],
equipment=form.cleaned_data["equipment"],
voraussetzungen=form.cleaned_data["voraussetzungen"],
max_participants=form.cleaned_data["max_participants"],
anforderung_hoehe=form.cleaned_data["anforderung_hoehe"],
anforderung_strecke=form.cleaned_data["anforderung_strecke"],
anforderung_dauer=form.cleaned_data["anforderung_dauer"],
description=form.cleaned_data["description"])
termin = Termin(
title=form.cleaned_data["title"],
subtitle=form.cleaned_data["subtitle"],
start_date=form.cleaned_data["start_date"],
end_date=form.cleaned_data["end_date"],
group=form.cleaned_data["group"],
responsible=form.cleaned_data["responsible"],
phone=form.cleaned_data["phone"],
email=form.cleaned_data["email"],
category=form.cleaned_data["category"],
condition=form.cleaned_data["condition"],
technik=form.cleaned_data["technik"],
saison=form.cleaned_data["saison"],
eventart=form.cleaned_data["eventart"],
klassifizierung=form.cleaned_data["klassifizierung"],
equipment=form.cleaned_data["equipment"],
voraussetzungen=form.cleaned_data["voraussetzungen"],
max_participants=form.cleaned_data["max_participants"],
anforderung_hoehe=form.cleaned_data["anforderung_hoehe"],
anforderung_strecke=form.cleaned_data["anforderung_strecke"],
anforderung_dauer=form.cleaned_data["anforderung_dauer"],
description=form.cleaned_data["description"],
)
termin.save()
return published(request)
else:
form = TerminForm()
return render(request, 'ludwigsburgalpin/termine.html', {'form': form.as_table()})
return render(request, "ludwigsburgalpin/termine.html", {"form": form.as_table()})
def published(request):
return render(request, 'ludwigsburgalpin/published.html')
return render(request, "ludwigsburgalpin/published.html")

@ -1,11 +1,15 @@
from datetime import datetime, timedelta
import logging
import unicodedata
from datetime import datetime
from datetime import timedelta
from decimal import Decimal
from decimal import ROUND_HALF_DOWN
from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from decimal import Decimal, ROUND_HALF_DOWN
import unicodedata
import logging
logger = logging.getLogger(__name__)
@ -14,18 +18,20 @@ def file_size_validator(max_upload_size):
Returns a function checking if the supplied file has file size less or equal
than `max_upload_size` in MB.
"""
def check_file_size(value):
limit = max_upload_size * 1024 * 1024
if value.size > limit:
raise ValidationError(_('Please keep filesize under {} MiB. '
'Current filesize: '
'{:10.2f} MiB.').format(max_upload_size,
value.size / 1024 / 1024))
raise ValidationError(
_("Please keep filesize under {} MiB. Current filesize: {:10.2f} MiB.").format(
max_upload_size, value.size / 1024 / 1024
)
)
return check_file_size
class RestrictedFileField(models.FileField):
def __init__(self, *args, **kwargs):
if "max_upload_size" in kwargs:
self.max_upload_size = kwargs.pop("max_upload_size")
@ -36,32 +42,33 @@ class RestrictedFileField(models.FileField):
else:
self.content_types = None
super(RestrictedFileField, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.validators = [file_size_validator(self.max_upload_size)]
def clean(self, *args, **kwargs):
data = super(RestrictedFileField, self).clean(*args, **kwargs)
data = super().clean(*args, **kwargs)
f = data.file
try:
content_type = f.content_type
if self.content_types is not None and content_type not in self.content_types:
raise ValidationError(_('Filetype not supported.'))
raise ValidationError(_("Filetype not supported."))
if self.max_upload_size is not None and f._size > self.max_upload_size:
raise ValidationError(_('Please keep filesize under {}. '
'Current filesize: '
'{}').format(self.max_upload_size,
f._size))
raise ValidationError(
_("Please keep filesize under {}. Current filesize: {}").format(
self.max_upload_size, f._size
)
)
except AttributeError as e:
logger.warning(e)
return data
def cvt_to_decimal(f):
return Decimal(f).quantize(Decimal('.01'), rounding=ROUND_HALF_DOWN)
return Decimal(f).quantize(Decimal(".01"), rounding=ROUND_HALF_DOWN)
def get_member(request):
if not hasattr(request.user, 'member'):
if not hasattr(request.user, "member"):
return None
else:
return request.user.member
@ -69,10 +76,10 @@ def get_member(request):
def normalize_name(raw, nospaces=True, noumlaut=True):
if noumlaut:
raw = raw.replace('ö', 'oe').replace('ä', 'ae').replace('ü', 'ue')
raw = raw.replace("ö", "oe").replace("ä", "ae").replace("ü", "ue")
if nospaces:
raw = raw.replace(' ', '_')
return unicodedata.normalize('NFKD', raw).encode('ascii', 'ignore').decode('ascii')
raw = raw.replace(" ", "_")
return unicodedata.normalize("NFKD", raw).encode("ascii", "ignore").decode("ascii")
def normalize_filename(filename, append_date=True, date=None):
@ -80,20 +87,27 @@ def normalize_filename(filename, append_date=True, date=None):
date = datetime.today()
if date:
filename = filename + "_" + date.strftime("%d_%m_%Y")
filename = filename.replace(' ', '_').replace('&', '').replace('/', '_')
filename = filename.replace(" ", "_").replace("&", "").replace("/", "_")
# drop umlauts, accents etc.
return unicodedata.normalize('NFKD', filename).encode('ASCII', 'ignore').decode()
return unicodedata.normalize("NFKD", filename).encode("ASCII", "ignore").decode()
def coming_midnight():
base = timezone.now() + timezone.timedelta(days=1)
return timezone.datetime(year=base.year, month=base.month, day=base.day,
hour=0, minute=0, second=0, microsecond=0,
tzinfo=base.tzinfo)
return timezone.datetime(
year=base.year,
month=base.month,
day=base.day,
hour=0,
minute=0,
second=0,
microsecond=0,
tzinfo=base.tzinfo,
)
def mondays_until_nth(n):
""" Returns a list of dates for the next n Mondays, starting from the next Monday.
"""Returns a list of dates for the next n Mondays, starting from the next Monday.
This functions aids in the generation of weekly schedules or reports."""
today = datetime.today()
next_monday = today + timedelta(days=(7 - today.weekday()) % 7 or 7)

Loading…
Cancel
Save