From a7d3df7b398dcc0630b0a1b705b2110a799a39ac Mon Sep 17 00:00:00 2001 From: Oliver Zander Date: Tue, 14 Oct 2025 11:39:58 +0200 Subject: [PATCH] simplified application views --- input/templates/input/forms/extern.html | 12 +- input/templates/input/forms/form_generic.html | 2 +- input/templates/input/if_mail.html | 2 +- input/templates/input/if_mail.txt | 2 +- input/templates/input/ifg_volunteer_mail.html | 2 +- input/templates/input/ifg_volunteer_mail.txt | 2 +- input/urls.py | 43 ++--- input/views.py | 166 ++++++++++-------- requirements.txt | 1 - 9 files changed, 115 insertions(+), 117 deletions(-) diff --git a/input/templates/input/forms/extern.html b/input/templates/input/forms/extern.html index 0566f2b..a2c0385 100755 --- a/input/templates/input/forms/extern.html +++ b/input/templates/input/forms/extern.html @@ -21,15 +21,9 @@ Serviceleistungen diff --git a/input/templates/input/forms/form_generic.html b/input/templates/input/forms/form_generic.html index b7b1f92..d92605d 100644 --- a/input/templates/input/forms/form_generic.html +++ b/input/templates/input/forms/form_generic.html @@ -5,7 +5,7 @@ {{ form.media }}
-

Du hast {{ typestring }} ausgewählt.

+

Du hast {{ type_label }} ausgewählt.

diff --git a/input/templates/input/if_mail.html b/input/templates/input/if_mail.html index 6e76f50..92107f9 100755 --- a/input/templates/input/if_mail.html +++ b/input/templates/input/if_mail.html @@ -4,7 +4,7 @@ Hallo Team Communitys und Engagement,

es gab einen neuen Antrag von {{data.realname}}.

-Der Nutzer mit dem Username {{data.username}} ({{data.email}}) fragt ein_e {{data.typestring|striptags}} an.
+Der Nutzer mit dem Username {{data.username}} ({{data.email}}) fragt ein_e {{data.type_label|striptags}} an.
{% if data.choice in data.grant %}
Vorraussichtliche Kosten: {{data.cost}}
Anmerkungen: {{data.notes}} {% endif %} {% if data.choice in data.domain %}
diff --git a/input/templates/input/if_mail.txt b/input/templates/input/if_mail.txt index afee396..2e0c287 100755 --- a/input/templates/input/if_mail.txt +++ b/input/templates/input/if_mail.txt @@ -2,7 +2,7 @@ Hallo Team Communitys und Engagement, es gab einen neuen Antrag von {{data.realname}}. -Der Nutzer mit dem Username {{data.username}} ({{data.email}}) fragt ein_e {{data.typestring|striptags}} an. +Der Nutzer mit dem Username {{data.username}} ({{data.email}}) fragt ein_e {{data.type_label|striptags}} an. {% if data.choice in data.grant %} Vorraussichtliche Kosten: {{data.cost}} Anmerkungen: {{data.notes}} {% endif %} {% if data.choice in data.domain %} diff --git a/input/templates/input/ifg_volunteer_mail.html b/input/templates/input/ifg_volunteer_mail.html index 646a653..bd7f989 100755 --- a/input/templates/input/ifg_volunteer_mail.html +++ b/input/templates/input/ifg_volunteer_mail.html @@ -2,7 +2,7 @@ Hallo {{data.realname}},

-wir haben Deine Anfrage ({{data.typestring|striptags}}) erhalten.
+wir haben Deine Anfrage ({{data.type_label|striptags}}) erhalten.
{% if data.choice in data.grant %}
Vorraussichtliche Kosten: {{data.cost}}
Anmerkungen: {{data.notes}} {% endif %} {% if data.choice in data.domain %}
diff --git a/input/templates/input/ifg_volunteer_mail.txt b/input/templates/input/ifg_volunteer_mail.txt index 0f96a90..976954d 100755 --- a/input/templates/input/ifg_volunteer_mail.txt +++ b/input/templates/input/ifg_volunteer_mail.txt @@ -1,6 +1,6 @@ Hallo {{data.realname}}, -wir haben Deine Anfrage ({{data.typestring|striptags}}) erhalten. +wir haben Deine Anfrage ({{data.type_label|striptags}}) erhalten. {% if data.choice in data.grant %} Vorraussichtliche Kosten: {{data.cost}} Anmerkungen: {{data.notes}} {% endif %} {% if data.choice in data.domain %} diff --git a/input/urls.py b/input/urls.py index 3d89297..da24dc8 100755 --- a/input/urls.py +++ b/input/urls.py @@ -1,41 +1,28 @@ -from django.urls import path -from django.views.generic import TemplateView +from django.urls import path, include from django.views.i18n import JavaScriptCatalog + from .views import ( - index, done, export, authorize, deny, - TravelApplicationView, IFGApplicationView, EmailApplicationView, - LiteratureApplicationView, ListApplicationView, BusinessCardApplicationView, - LibraryApplicationView, ELiteratureApplicationView, SoftwareApplicationView, + index, + done, + export, + authorize, + deny, + ApplicationView, + ApplicationStartView, + ProjectInfoView, ) urlpatterns = [ path('', index, name='index'), - path( - 'extern/', - TemplateView.as_view(template_name='input/forms/extern.html'), - name='extern', - ), path('saved', done, name='done'), path('export', export, name='export'), path('authorize//', authorize, name='authorize'), path('deny//', deny, name='deny'), - - # Static info page for project funding above 1000 EUR - path('extern/info/projektfoerderung-ab-1000/', - TemplateView.as_view(template_name='input/info_project_funding_gt_1000.html'), - name='info-foerderprojekt-ab-1000'), - - # New single-page application views - path('extern/reisekosten/', TravelApplicationView.as_view(), name='reisekosten'), - path('extern/ifg/', IFGApplicationView.as_view(), name='ifg'), - path('extern/email/', EmailApplicationView.as_view(), name='email'), - path('extern/literaturstipendium/', LiteratureApplicationView.as_view(), name='literatur'), - path('extern/mailingliste/', ListApplicationView.as_view(), name='mailingliste'), - path('extern/visitenkarten/', BusinessCardApplicationView.as_view(), name='visitenkarten'), - path('extern/bibliotheksstipendium/', LibraryApplicationView.as_view(), name='bibliotheksstipendium'), - path('extern/eliteraturstipendium/', ELiteratureApplicationView.as_view(), name='eliteraturstipendium'), - path('extern/softwarestipendium/', SoftwareApplicationView.as_view(), name='softwarestipendium'), - + path('extern/', include([ + path('', ApplicationStartView.as_view(), name='extern'), + path('info/projektfoerderung-ab-1000/', ProjectInfoView.as_view(), name='info-foerderprojekt-ab-1000'), + path('/', ApplicationView.as_view(), name='extern'), + ])), # JavaScript translations for date widgets, etc. path('jsi18n/', JavaScriptCatalog.as_view(), name='jsi18n'), ] diff --git a/input/views.py b/input/views.py index 04b5dc8..f76cdb6 100755 --- a/input/views.py +++ b/input/views.py @@ -1,17 +1,19 @@ from smtplib import SMTPException +from typing import NamedTuple from django.shortcuts import render -from django.http import HttpResponse +from django.http import HttpResponse, Http404 +from django.utils.functional import cached_property from django.utils.safestring import mark_safe -from formtools.wizard.views import CookieWizardView from django.core.mail import BadHeaderError, EmailMultiAlternatives from django.template.loader import get_template from django.conf import settings from django.contrib.auth.decorators import login_required +from django.views.generic import TemplateView from django.views.generic.edit import FormView from .forms import ( - ExternForm, + BaseApplicationForm, LibraryForm, ELiteratureForm, SoftwareForm, @@ -22,31 +24,38 @@ from .forms import ( ListForm, BusinessCardForm, ) -from .models import TYPE_CHOICES, MODELS, TYPE_BIB, TYPE_ELIT, TYPE_SOFT - -LIBRARY_FORMS = { - TYPE_BIB: LibraryForm, - TYPE_ELIT: ELiteratureForm, - TYPE_SOFT: SoftwareForm, -} +from .models import ( + MODELS, + LIBRARY_TYPES, + TYPE_CHOICES, + TYPE_BIB, + TYPE_ELIT, + TYPE_IFG, + TYPE_LIT, + TYPE_LIST, + TYPE_MAIL, + TYPE_SOFT, + TYPE_TRAV, + TYPE_VIS, +) HELP_TEXTS = { - 'IFG': { + TYPE_IFG: { 'notes': ( 'Bitte gib an, wie die gewonnenen Informationen den
' 'Wikimedia-Projekten zugute kommen sollen.' ) }, - 'MAIL': { + TYPE_MAIL: { 'domain': ( 'Mit welcher Domain, bzw. für welches Wikimedia-Projekt,
' 'möchtest du eine Mailadresse beantragen?' ) }, - 'LIT': { + TYPE_LIT: { 'notes': 'Bitte gib an, wofür du die Literatur verwenden möchtest.' }, - 'LIST': { + TYPE_LIST: { 'domain': ( 'Mit welcher Domain, bzw. für welches Wikimedia-Projekt,
' 'möchtest du eine Mailingliste beantragen?' @@ -55,6 +64,37 @@ HELP_TEXTS = { } +class ApplicationType(NamedTuple): + code: str + path: str + form_class: type[BaseApplicationForm] + + @property + def label(self): + return TYPE_CHOICES[self.code] + + @property + def model(self): + return MODELS[self.code] + + @property + def help_texts(self): + return HELP_TEXTS.get(self.code) + + +TYPES = [ + ApplicationType(TYPE_BIB, 'bibliotheksstipendium', LibraryForm), + ApplicationType(TYPE_ELIT, 'eliteraturstipendium', ELiteratureForm), + ApplicationType(TYPE_MAIL, 'email', EmailForm), + ApplicationType(TYPE_IFG, 'ifg', IFGForm), + ApplicationType(TYPE_LIT, 'literaturstipendium', LiteratureForm), + ApplicationType(TYPE_LIST, 'mailingliste', ListForm), + ApplicationType(TYPE_TRAV, 'reisekosten', TravelForm), + ApplicationType(TYPE_SOFT, 'softwarestipendium', SoftwareForm), + ApplicationType(TYPE_VIS, 'visitenkarten', BusinessCardForm), +] + + def auth_deny(choice, pk, auth): if choice not in MODELS: return HttpResponse(f'ERROR! UNKNOWN CHOICE TYPE! {choice}') @@ -99,11 +139,19 @@ def index(request): return render(request, 'input/index.html') -class BaseApplicationView(FormView): - """ - Base view for all application types. +class ApplicationStartView(TemplateView): + template_name = 'input/forms/extern.html' + extra_context = {'types': TYPES} + + +class ProjectInfoView(TemplateView): + template_name = 'input/info_project_funding_gt_1000.html' + + +class ApplicationView(FormView): + """ + View for all application types. - - Each application type (travel, literature, email, etc.) gets its own subclass. - Renders the generic form template. - Handles saving the submitted form to the database. - Adds extra fields from the session or request type if needed. @@ -112,22 +160,37 @@ class BaseApplicationView(FormView): - Sends notification mail to the internal IF address. - Returns the "done" response after successful processing. """ + template_name = 'input/forms/form_generic.html' - type_code: str = '' + + @cached_property + def type_info(self) -> ApplicationType: + type_path = self.kwargs['type'] + + for type_info in TYPES: + if type_path == type_info.path: + return type_info + + raise Http404(f'"{type_path}" existiert nicht.') + + @property + def type_code(self): + return self.type_info.code + + @property + def form_class(self): + return self.type_info.form_class def get_context_data(self, **kwargs): - """Add the human-readable type string (from TYPE_CHOICES) to the template context.""" - ctx = super().get_context_data(**kwargs) - ctx['typestring'] = TYPE_CHOICES.get(self.type_code, self.type_code) - return ctx + return super().get_context_data(**kwargs, type_label=self.type_info.label) def get_form(self, form_class=None): """Return the form instance and inject custom help_texts if defined for this type.""" form = super().get_form(form_class) # Apply help_text overrides if defined for this type_code - if self.type_code in HELP_TEXTS: - for field, text in HELP_TEXTS[self.type_code].items(): + if help_texts := self.type_info.help_texts: + for field, text in help_texts.items(): if field in form.fields: form.fields[field].help_text = mark_safe(text) @@ -147,7 +210,7 @@ class BaseApplicationView(FormView): data['choice'] = self.type_code # Special rule for literature applications - if self.type_code == 'LIT' and data.get('selfbuy') == 'TRUE': + if self.type_code == TYPE_LIT and data.get('selfbuy') == 'TRUE': data['selfbuy_give_data'] = 'False' # Save model instance @@ -164,11 +227,11 @@ class BaseApplicationView(FormView): modell.email = data['email'] # Set model.type for specific request types - if self.type_code in ('BIB', 'ELIT', 'SOFT'): + if self.type_code in LIBRARY_TYPES: modell.type = self.type_code # Literature-specific extra field - if self.type_code == 'LIT' and 'selfbuy_give_data' in data: + if self.type_code == TYPE_LIT and 'selfbuy_give_data' in data: modell.selfbuy_give_data = data['selfbuy_give_data'] modell.save() @@ -178,7 +241,7 @@ class BaseApplicationView(FormView): # Prepare minimal mail context and send mails data['pk'] = modell.pk data['url_prefix'] = settings.EMAIL_URL_PREFIX - data['typestring'] = TYPE_CHOICES.get(self.type_code, self.type_code) + data['type_label'] = self.type_info.label context = {'data': data} try: @@ -208,48 +271,3 @@ class BaseApplicationView(FormView): return HttpResponse('Error in sending mails (probably wrong adress?). Data not saved!') return done(self.request) - - -class TravelApplicationView(BaseApplicationView): - form_class = TravelForm - type_code = 'TRAV' - - -class LibraryApplicationView(BaseApplicationView): - form_class = LibraryForm - type_code = 'BIB' - - -class ELiteratureApplicationView(BaseApplicationView): - form_class = ELiteratureForm - type_code = 'ELIT' - - -class SoftwareApplicationView(BaseApplicationView): - form_class = SoftwareForm - type_code = 'SOFT' - - -class IFGApplicationView(BaseApplicationView): - form_class = IFGForm - type_code = 'IFG' - - -class EmailApplicationView(BaseApplicationView): - form_class = EmailForm - type_code = 'MAIL' - - -class LiteratureApplicationView(BaseApplicationView): - form_class = LiteratureForm - type_code = 'LIT' - - -class ListApplicationView(BaseApplicationView): - form_class = ListForm - type_code = 'LIST' - - -class BusinessCardApplicationView(BaseApplicationView): - form_class = BusinessCardForm - type_code = 'VIS' diff --git a/requirements.txt b/requirements.txt index 34c1b13..49c45a4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ Authlib==1.6.1 Django==5.2.5 -django-formtools==2.5.1 gunicorn==23.0.0 mysqlclient==2.2.7 python-dotenv==1.1.1