diff --git a/TODO.txt b/TODO.txt index 686b3d5..d634df8 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,6 +1,5 @@ - beim Erstellen einer neuen Veranstaltung wird nach event.trainer_email = user.username gesucht, evtl. wäre event.trainer_email = user.email besser? -- echte config nicht im repository verwalten. - Anzeige Bahnfahrt - ApproachForm aufteilen - Anmeldungstext @@ -11,7 +10,7 @@ - Save as Draft - Copy Event - Tourenreferent managed Gruppen der Subreferenten -- Initialtexte und Placeholder von forms in config +- Initialtexte und Placeholder von forms in config/app_config - Passwort vergessen Funktion - Besserer Zurück-Button in Formulare diff --git a/dav_events/apps.py b/dav_events/apps.py index 922574a..0f75e69 100644 --- a/dav_events/apps.py +++ b/dav_events/apps.py @@ -1,6 +1,25 @@ -from django.apps import AppConfig as _AppConfig +from django.core.exceptions import ImproperlyConfigured + +from .config import AppConfig as _AppConfig, DefaultSetting + +DEFAULT_SETTINGS = ( + DefaultSetting('enable_email_notifications', False), + DefaultSetting('email_sender', None), + DefaultSetting('email_base_url', None), + DefaultSetting('manage_all_group', None), + DefaultSetting('manage_w_group', None), + DefaultSetting('manage_s_group', None), + DefaultSetting('manage_m_group', None), + DefaultSetting('manage_k_group', None), + DefaultSetting('manage_b_group', None), + DefaultSetting('publisher_group', None), + DefaultSetting('incremental_publisher_group', None), + DefaultSetting('development_init_forms', False), + DefaultSetting('fee_matrix', ImproperlyConfigured), +) class AppConfig(_AppConfig): name = 'dav_events' verbose_name = u'DAV Veranstaltungen' + default_settings = DEFAULT_SETTINGS diff --git a/dav_events/config.py b/dav_events/config.py index 942e2df..9712de1 100644 --- a/dav_events/config.py +++ b/dav_events/config.py @@ -1,31 +1,14 @@ -# -*- coding: utf-8 -*- -from django.utils.translation import ugettext_lazy as _ +import importlib +import re +from django.apps import AppConfig as _AppConfig +from django.core.exceptions import ImproperlyConfigured -# E-Mails -ENABLE_EMAIL_NOTIFICATIONS = True -EMAIL_SENDER = 'Jens Kleineheismann ' -EMAIL_BASE_URL = 'http://localhost:8000' - -# Auth Config -MANAGE_ALL_GROUP = 'Tourenreferenten' -MANAGE_W_GROUP = 'Wanderreferenten' -MANAGE_S_GROUP = 'Skireferenten' -MANAGE_M_GROUP = 'MTBReferenten' -MANAGE_K_GROUP = 'Kletterreferenten' -MANAGE_B_GROUP = 'Bergsteigerreferenten' -PUBLISHER_GROUP = 'Redaktion' -INCREMENTAL_PUBLISHER_GROUP = 'OnlineRedaktion' - -# Form Config +# Form and Model Field Config COMMON_CHAR_FIELD_LENGTH = 250 - NUMBER_MAX_LENGTH = 12 - TITLE_MAX_LENGTH = COMMON_CHAR_FIELD_LENGTH - TRAINER_NAME_MAX_LENGTH = COMMON_CHAR_FIELD_LENGTH PHONE_NUMBER_MAX_LENGTH = COMMON_CHAR_FIELD_LENGTH - LOCATION_MAX_LENGTH = COMMON_CHAR_FIELD_LENGTH TRANSPORT_OTHER_MAX_LENGTH = COMMON_CHAR_FIELD_LENGTH MEETING_POINT_OTHER_MAX_LENGTH = COMMON_CHAR_FIELD_LENGTH @@ -34,89 +17,66 @@ ACCOMMODATION_OTHER_MAX_LENGTH = COMMON_CHAR_FIELD_LENGTH MEALS_OTHER_MAX_LENGTH = COMMON_CHAR_FIELD_LENGTH ADDITIONAL_COSTS_MAX_LENGTH = COMMON_CHAR_FIELD_LENGTH -FEE_MATRIX = { - '0': {'description': _(u'Keiner / direkte Abrechnung (Tageswanderung)'), - 'trainer_fee': 0, - 'pre_meeting_fee': 0, - 'trainer_day_fee': 0, - 'participant_fee': 0, - 'participant_day_fee': 0, - }, - 'A': {'description': _(u'A (Mehrtageswanderung Mittelgebirge'), - 'trainer_fee': 40, - 'trainer_day_fee': 50, - 'participant_fee': 10, - 'participant_day_fee': 10, - 'pre_meeting_fee': 0, - }, - 'B': {'description': _(u'B (Alpine Mehrtageswanderung)'), - 'trainer_fee': 50, - 'trainer_day_fee': 75, - 'participant_fee': 10, - 'participant_day_fee': 20, - 'pre_meeting_fee': 0, - }, - 'C': {'description': _(u'C (Tour/Kurs ohne Übernachtung)'), - 'trainer_fee': 30, - 'trainer_day_fee': 60, - 'participant_fee': 10, - 'participant_day_fee': 30, - 'pre_meeting_fee': 0, - }, - 'D': {'description': _(u'D (Tour/Kurs Mittelgebirge)'), - 'trainer_fee': 50, - 'trainer_day_fee': 75, - 'participant_fee': 20, - 'participant_day_fee': 25, - 'pre_meeting_fee': 0, - }, - 'E': {'description': _(u'E (Alpine Klettertour DE/AU)'), - 'trainer_fee': 80, - 'trainer_day_fee': 75, - 'participant_fee': 40, - 'participant_day_fee': 40, - 'pre_meeting_fee': 0, - }, - 'F': {'description': _(u'F (Alpine Klettertour CH/FR/IT)'), - 'trainer_fee': 80, - 'trainer_day_fee': 85, - 'participant_fee': 40, - 'participant_day_fee': 45, - 'pre_meeting_fee': 0, - }, - 'G': {'description': _(u'G (Alpiner Kurs DE/AU)'), - 'trainer_fee': 100, - 'trainer_day_fee': 75, - 'participant_fee': 35, - 'participant_day_fee': 30, - 'pre_meeting_fee': 0, - }, - 'H': {'description': _(u'H (Alpiner Kurs CH/FR/IT/..)'), - 'trainer_fee': 100, - 'trainer_day_fee': 85, - 'participant_fee': 35, - 'participant_day_fee': 30, - 'pre_meeting_fee': 0, - }, - 'I': {'description': _(u'I (Alpine MTB/Ski-Tour DE/AU)'), - 'trainer_fee': 80, - 'trainer_day_fee': 75, - 'participant_fee': 25, - 'participant_day_fee': 25, - 'pre_meeting_fee': 0, - }, - 'J': {'description': _(u'J (Alpine MTB/Ski-Tour CH/FR/IT/..)'), - 'trainer_fee': 80, - 'trainer_day_fee': 85, - 'participant_fee': 25, - 'participant_day_fee': 25, - 'pre_meeting_fee': 0, - }, - 'K': {'description': _(u'K (Ski-Tour/-Kurs mit Liftbenutzung)'), - 'trainer_fee': 80, - 'trainer_day_fee': 130, - 'participant_fee': 40, - 'participant_day_fee': 40, - 'pre_meeting_fee': 0, - }, -} \ No newline at end of file + +class DefaultSetting(object): + def __init__(self, name, value, key_name=None, validator=None): + self.name = name + self.value = value + if key_name is None: + self.key_name = self.name.upper() + else: + self.key_name = key_name + self.validator = validator + + def validate(self, value): + if hasattr(self, 'validator') and self.validator is not None: + if callable(self.validator): + if not self.validator(value): + raise ImproperlyConfigured("Validator callback {clb} returned False".format(clb=self.validator)) + else: + if not re.search(self.validator, value): + raise ImproperlyConfigured("Does not match /{re}/".format(re=self.validator)) + + +class AppSettings(object): + def __init__(self, app_name, defaults): + settings_name = 'main.settings-' + app_name + + try: + settings_module = importlib.import_module(settings_name) + except ImportError: + settings_module = None + + for default in defaults: + if hasattr(settings_module, default.key_name): + value = getattr(settings_module, default.key_name) + try: + default.validate(value) + except ImproperlyConfigured as e: + msg = 'Invalid value of {key} in {module}: {cause}'.format(key=default.key_name, + module=settings_name, + cause=e.message) + raise ImproperlyConfigured(msg) + setattr(self, default.name, value) + elif isinstance(default.value, ImproperlyConfigured): + raise default.value + else: + try: + is_impconf = issubclass(default.value, ImproperlyConfigured) + except TypeError: + is_impconf = False + + if is_impconf: + msg = '{key} must be defined in {module}'.format(key=default.key_name, + module=settings_name) + raise default.value(msg) + else: + setattr(self, default.name, default.value) + + +class AppConfig(_AppConfig): + default_settings = () + + def __init__(self, app_name, app_module): + super(AppConfig, self).__init__(app_name, app_module) + self.settings = AppSettings(app_name, self.default_settings) diff --git a/dav_events/console_scripts/Resources/django.main.additional_settings.py b/dav_events/console_scripts/Resources/django.main.additional_settings.py index b839745..aea53c3 100644 --- a/dav_events/console_scripts/Resources/django.main.additional_settings.py +++ b/dav_events/console_scripts/Resources/django.main.additional_settings.py @@ -38,7 +38,9 @@ LOGGING = { 'format': '%(asctime)s - %(name)s - %(levelname)s: %(message)s' }, 'verbose': { - 'format': '%(asctime)s -- %(processName)s[%(process)d]:%(threadName)s[%(thread)d] -- %(name)s -- %(module)s...%(funcName)s() -- %(pathname)s:%(lineno)d -- %(levelname)s: %(message)s' + 'format': ('%(asctime)s -- %(processName)s[%(process)d]:%(threadName)s[%(thread)d] -- ' + '%(name)s -- %(module)s...%(funcName)s() -- %(pathname)s:%(lineno)d -- ' + '%(levelname)s: %(message)s') }, }, 'filters': { diff --git a/dav_events/console_scripts/Resources/django.main.settings-dav_events.py b/dav_events/console_scripts/Resources/django.main.settings-dav_events.py new file mode 100644 index 0000000..f4dd196 --- /dev/null +++ b/dav_events/console_scripts/Resources/django.main.settings-dav_events.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +from django.utils.translation import ugettext_lazy as _ + +# E-Mails +ENABLE_EMAIL_NOTIFICATIONS = False +EMAIL_SENDER = 'Jens Kleineheismann ' +EMAIL_BASE_URL = 'http://localhost:8000' + +# Auth Config +MANAGE_ALL_GROUP = 'Tourenreferenten' +MANAGE_W_GROUP = 'Wanderreferenten' +MANAGE_S_GROUP = 'Skireferenten' +MANAGE_M_GROUP = 'MTBReferenten' +MANAGE_K_GROUP = 'Kletterreferenten' +MANAGE_B_GROUP = 'Bergsteigerreferenten' +PUBLISHER_GROUP = 'Redaktion' +INCREMENTAL_PUBLISHER_GROUP = 'OnlineRedaktion' + +# EventCreateForm +# DEVELOPMENT_INIT_FORMS = False +FEE_MATRIX = { + '0': {'description': _(u'Keiner / direkte Abrechnung (Tageswanderung)'), + 'trainer_fee': 0, + 'pre_meeting_fee': 0, + 'trainer_day_fee': 0, + 'participant_fee': 0, + 'participant_day_fee': 0, + }, + 'A': {'description': _(u'A (Mehrtageswanderung Mittelgebirge'), + 'trainer_fee': 40, + 'trainer_day_fee': 50, + 'participant_fee': 10, + 'participant_day_fee': 10, + 'pre_meeting_fee': 0, + }, + 'B': {'description': _(u'B (Alpine Mehrtageswanderung)'), + 'trainer_fee': 50, + 'trainer_day_fee': 75, + 'participant_fee': 10, + 'participant_day_fee': 20, + 'pre_meeting_fee': 0, + }, + 'C': {'description': _(u'C (Tour/Kurs ohne Übernachtung)'), + 'trainer_fee': 30, + 'trainer_day_fee': 60, + 'participant_fee': 10, + 'participant_day_fee': 30, + 'pre_meeting_fee': 0, + }, + 'D': {'description': _(u'D (Tour/Kurs Mittelgebirge)'), + 'trainer_fee': 50, + 'trainer_day_fee': 75, + 'participant_fee': 20, + 'participant_day_fee': 25, + 'pre_meeting_fee': 0, + }, + 'E': {'description': _(u'E (Alpine Klettertour DE/AU)'), + 'trainer_fee': 80, + 'trainer_day_fee': 75, + 'participant_fee': 40, + 'participant_day_fee': 40, + 'pre_meeting_fee': 0, + }, + 'F': {'description': _(u'F (Alpine Klettertour CH/FR/IT)'), + 'trainer_fee': 80, + 'trainer_day_fee': 85, + 'participant_fee': 40, + 'participant_day_fee': 45, + 'pre_meeting_fee': 0, + }, + 'G': {'description': _(u'G (Alpiner Kurs DE/AU)'), + 'trainer_fee': 100, + 'trainer_day_fee': 75, + 'participant_fee': 35, + 'participant_day_fee': 30, + 'pre_meeting_fee': 0, + }, + 'H': {'description': _(u'H (Alpiner Kurs CH/FR/IT/..)'), + 'trainer_fee': 100, + 'trainer_day_fee': 85, + 'participant_fee': 35, + 'participant_day_fee': 30, + 'pre_meeting_fee': 0, + }, + 'I': {'description': _(u'I (Alpine MTB/Ski-Tour DE/AU)'), + 'trainer_fee': 80, + 'trainer_day_fee': 75, + 'participant_fee': 25, + 'participant_day_fee': 25, + 'pre_meeting_fee': 0, + }, + 'J': {'description': _(u'J (Alpine MTB/Ski-Tour CH/FR/IT/..)'), + 'trainer_fee': 80, + 'trainer_day_fee': 85, + 'participant_fee': 25, + 'participant_day_fee': 25, + 'pre_meeting_fee': 0, + }, + 'K': {'description': _(u'K (Ski-Tour/-Kurs mit Liftbenutzung)'), + 'trainer_fee': 80, + 'trainer_day_fee': 130, + 'participant_fee': 40, + 'participant_day_fee': 40, + 'pre_meeting_fee': 0, + }, +} diff --git a/dav_events/console_scripts/admin.py b/dav_events/console_scripts/admin.py index 4a8ae74..1afba9b 100644 --- a/dav_events/console_scripts/admin.py +++ b/dav_events/console_scripts/admin.py @@ -78,6 +78,11 @@ class AdminCommand(object): with open(output_file, 'w') as f: f.write(pkg_resources.resource_string(__package__, input_file)) + input_file = os.path.join('Resources', 'django.main.settings-dav_events.py') + output_file = os.path.join(django_project_path, django_project_name, 'settings-dav_events.py') + with open(output_file, 'w') as f: + f.write(pkg_resources.resource_string(__package__, input_file)) + return posix.EX_OK def __init__(self): diff --git a/dav_events/emails.py b/dav_events/emails.py index 0cf5932..de7a6f5 100644 --- a/dav_events/emails.py +++ b/dav_events/emails.py @@ -1,32 +1,33 @@ # -*- coding: utf-8 -*- import logging +from django.apps import apps from django.core.exceptions import ImproperlyConfigured from django.core.mail import EmailMessage from django.template.loader import get_template -from . import config from .utils import get_group_members +app_config = apps.get_containing_app_config(__package__) logger = logging.getLogger(__name__) def get_recipients(task, sport=None): users = [] if task == 'accept': - group_var_name = 'MANAGE_ALL_GROUP' - group_name = getattr(config, group_var_name, None) + group_var_name = 'manage_all_group' + group_name = getattr(app_config.settings, group_var_name, None) if group_name: users.extend(get_group_members(group_name, ignore_missing=True)) if sport: - group_var_name = 'MANAGE_{}_GROUP'.format(sport) - group_name = getattr(config, group_var_name, None) + group_var_name = 'manage_{}_group'.format(sport.lower()) + group_name = getattr(app_config.settings, group_var_name, None) if group_name: users.extend(get_group_members(group_name, ignore_missing=True)) elif task == 'publish': - group_var_name = 'INCREMENTAL_PUBLISHER_GROUP' - group_name = getattr(config, group_var_name, None) + group_var_name = 'incremental_publisher_group' + group_name = getattr(app_config.settings, group_var_name, None) if group_name: users.extend(get_group_members(group_name, ignore_missing=True)) else: @@ -36,7 +37,7 @@ def get_recipients(task, sport=None): class AbstractMail(object): - _sender = config.EMAIL_SENDER + _sender = app_config.settings.email_sender _subject = 'Generic Mail' _template_name = None @@ -50,7 +51,7 @@ class AbstractMail(object): def _get_context_data(self, extra_context=None): context = { - 'base_url': config.EMAIL_BASE_URL, + 'base_url': app_config.settings.email_base_url, } if extra_context: context.update(extra_context) @@ -65,7 +66,7 @@ class AbstractMail(object): raise NotImplementedError() def send(self): - if not config.ENABLE_EMAIL_NOTIFICATIONS: + if not app_config.settings.enable_email_notifications: return None subject = self._get_subject() body = self._get_body() diff --git a/dav_events/forms/events.py b/dav_events/forms/events.py index 6d19562..f74ff1a 100644 --- a/dav_events/forms/events.py +++ b/dav_events/forms/events.py @@ -4,6 +4,7 @@ import datetime import logging from babel.dates import format_date from django import forms +from django.apps import apps from django.utils.translation import get_language, ugettext, ugettext_lazy as _ from django_countries.fields import LazyTypedChoiceField from datetimewidget.widgets import DateWidget, TimeWidget, DateTimeWidget @@ -13,8 +14,8 @@ from .. import config from .. import models from .generic import ChainedForm +app_config = apps.get_containing_app_config(__package__) logger = logging.getLogger(__name__) -DEVELOPMENT_INIT_FORMS = False class EventUpdateForm(forms.ModelForm): @@ -207,7 +208,7 @@ class LocationForm(EventCreateForm): elif sport == 'W': self.fields['location'].widget.attrs['placeholder'] = u'Maikammer, Pfalz' - if DEVELOPMENT_INIT_FORMS: + if app_config.settings.development_init_forms: self.fields['location'].initial = self.fields['location'].widget.attrs['placeholder'] @@ -478,7 +479,7 @@ class DescriptionForm(EventCreateForm): elif sport == 'W' and not last_day: title_prefix += u'%s ' % ugettext(u'Tageswanderung') - if DEVELOPMENT_INIT_FORMS: + if app_config.settings.development_init_forms: if not title_prefix: title_prefix = u'%s' % choices.SPORT_CHOICES.get_label(sport) @@ -609,7 +610,7 @@ class TrainerForm(EventCreateForm): self.fields['trainer_firstname'].initial = self._request.user.first_name self.fields['trainer_familyname'].initial = self._request.user.last_name self.fields['trainer_email'].initial = self._request.user.email - elif DEVELOPMENT_INIT_FORMS: + elif app_config.settings.development_init_forms: self.fields['trainer_firstname'].initial = self.fields['trainer_firstname'].widget.attrs['placeholder'] self.fields['trainer_familyname'].initial = self.fields['trainer_familyname'].widget.attrs['placeholder'] self.fields['trainer_email'].initial = self.fields['trainer_email'].widget.attrs['placeholder'] @@ -712,7 +713,7 @@ class ChargesForm(EventCreateForm): else: n_pre_meetings = 0 - fees = config.FEE_MATRIX[charge_key] + fees = app_config.settings.fee_matrix[charge_key] trainer_reward = ( fees['trainer_fee'] diff --git a/dav_events/views/events.py b/dav_events/views/events.py index 8fb09c7..c93a963 100644 --- a/dav_events/views/events.py +++ b/dav_events/views/events.py @@ -1,5 +1,6 @@ import logging import os +from django.apps import apps from django.contrib import messages from django.contrib.auth import login from django.contrib.auth.decorators import login_required @@ -11,10 +12,10 @@ from django.utils.decorators import method_decorator from django.utils.translation import ugettext as _ from django.views import generic -from .. import config from .. import forms from .. import models +app_config = apps.get_containing_app_config(__package__) logger = logging.getLogger(__name__) @@ -25,13 +26,13 @@ class EventListView(generic.ListView): user = self.request.user if user.is_superuser: qs = self.model.objects.all() - elif user.groups.filter(name=config.MANAGE_ALL_GROUP).count(): + elif user.groups.filter(name=app_config.settings.manage_all_group).count(): qs = self.model.objects.all() else: user_sports_list = list() for k in ('W', 'S', 'M', 'K', 'B'): - group_name_var = 'MANAGE_{}_GROUP'.format(k) - group_name = getattr(config, group_name_var, None) + group_name_var = 'manage_{}_group'.format(k.lower()) + group_name = getattr(app_config.settings, group_name_var, None) if group_name and user.groups.filter(name=group_name).count(): user_sports_list.append(k) qs = self.model.objects.filter(Q(owner=user) | Q(sport__in=user_sports_list)) @@ -55,17 +56,17 @@ class EventPermissionMixin(object): if permission == 'view': if obj.owner == user: return True - if user.groups.filter(name=config.MANAGE_ALL_GROUP).count(): + if user.groups.filter(name=app_config.settings.manage_all_group).count(): return True - group_name_var = 'MANAGE_{}_GROUP'.format(obj.sport) - group_name = getattr(config, group_name_var, None) + group_name_var = 'manage_{}_group'.format(obj.sport.lower()) + group_name = getattr(app_config.settings, group_name_var, None) if group_name and user.groups.filter(name=group_name).count(): return True elif permission in ('update', 'accept'): - if user.groups.filter(name=config.MANAGE_ALL_GROUP).count(): + if user.groups.filter(name=app_config.settings.manage_all_group).count(): return True - group_name_var = 'MANAGE_{}_GROUP'.format(obj.sport) - group_name = getattr(config, group_name_var, None) + group_name_var = 'manage_{}_group'.format(obj.sport) + group_name = getattr(app_config.settings, group_name_var, None) if group_name and user.groups.filter(name=group_name).count(): return True