From 7cfd477d4070ba403b407f4822924fceaa3f64ee Mon Sep 17 00:00:00 2001 From: Roman Date: Sun, 31 Aug 2025 23:41:11 +0200 Subject: [PATCH] refactor ExternView into separate class-based views with BaseApplicationView --- input/views.py | 271 ++++++++++++++++++++++++++++--------------------- 1 file changed, 158 insertions(+), 113 deletions(-) diff --git a/input/views.py b/input/views.py index 46c92c7..6c92635 100755 --- a/input/views.py +++ b/input/views.py @@ -8,6 +8,7 @@ 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.edit import FormView from .forms import ( ExternForm, @@ -29,6 +30,30 @@ LIBRARY_FORMS = { TYPE_SOFT: SoftwareForm, } +HELP_TEXTS = { + "IFG": { + "notes": ( + "Bitte gib an, wie die gewonnenen Informationen den
" + "Wikimedia-Projekten zugute kommen sollen." + ) + }, + "MAIL": { + "domain": ( + "Mit welcher Domain, bzw. für welches Wikimedia-Projekt,
" + "möchtest du eine Mailadresse beantragen?" + ) + }, + "LIT": { + "notes": "Bitte gib an, wofür du die Literatur verwenden möchtest." + }, + "LIST": { + "domain": ( + "Mit welcher Domain, bzw. für welches Wikimedia-Projekt,
" + "möchtest du eine Mailingliste beantragen?" + ) + }, +} + def auth_deny(choice, pk, auth): if choice not in MODELS: @@ -66,146 +91,166 @@ def deny(request, choice, pk): def done(request): - return HttpResponse("Deine Anfrage wurde gesendet. Du erhältst in Kürze eine E-Mail-Benachrichtigung mit deinen Angaben. Für alle Fragen kontaktiere bitte das Team Communitys und Engagement unter community@wikimedia.de.") + return HttpResponse( + "Deine Anfrage wurde gesendet. Du erhältst in Kürze eine E-Mail-Benachrichtigung mit deinen Angaben. Für alle Fragen kontaktiere bitte das Team Communitys und Engagement unter community@wikimedia.de.") def index(request): return render(request, 'input/index.html') -class ExternView(CookieWizardView): - '''This View is for Volunteers''' +class BaseApplicationView(FormView): + """ + Base view for all application types. - template_name = "input/extern.html" - form_list = [ExternForm, LibraryForm] - - def get_form(self, step=None, data=None, files=None): - '''this function determines which part of the multipart form is - displayed next''' - - if step is None: - step = self.steps.current - print ("get_form() step " + step) - - if step == '1': - prev_data = self.get_cleaned_data_for_step('0') - choice = prev_data.get('choice') - print(f'choice detection in ExternView: {TYPE_CHOICES[choice]}') - if choice == 'IFG': - form = IFGForm(data) - form.fields['notes'].help_text = mark_safe("Bitte gib an, wie die gewonnenen Informationen den
Wikimedia-Projekten zugute kommen sollen.") - elif choice in LIBRARY_FORMS: - form = LIBRARY_FORMS[choice](data) - elif choice == 'MAIL': - form = EmailForm(data) - form.fields['domain'].help_text = mark_safe("Mit welcher Domain, bzw. für welches Wikimedia-Projekt,
möchtest du eine Mailadresse beantragen?") - elif choice == 'LIT': - form = LiteratureForm(data) - form.fields['notes'].help_text = "Bitte gib an, wofür du die Literatur verwenden möchtest." - elif choice == 'VIS': - form = BusinessCardForm(data) - elif choice == 'LIST': - form = ListForm(data) - form.fields['domain'].help_text = mark_safe("Mit welcher Domain, bzw. für welches Wikimedia-Projekt,
möchtest du eine Mailingliste beantragen?") - elif choice == 'TRAV': - form = TravelForm(data) - else: # pragma: no cover - raise RuntimeError(f'ERROR! UNKNOWN FORMTYPE {choice} in ExternView') - self.choice = choice - else: - form = super().get_form(step, data, files) - return form + - 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. + - Applies optional help_text overrides for certain fields. + - Sends confirmation mail to the applicant. + - 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 = "" def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - if hasattr(self, 'choice'): - context["choice"] = TYPE_CHOICES[self.choice] - return context + """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 - def done(self, form_list, **kwargs): - print('ExternView.done() reached') - # gather data from all forms - data = {} - for form in form_list: - data = {**data, **form.cleaned_data} + 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) - if data['choice'] == 'LIT': - if data['selfbuy'] == 'TRUE': - data['selfbuy_give_data'] = 'False' + # 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 field in form.fields: + form.fields[field].help_text = mark_safe(text) - # write data to database + return form + + def form_valid(self, form): + """ + Process a valid form submission: + - Enrich form data (e.g., set type_code, handle special rules). + - Save the model instance and related data. + - Send confirmation and notification mails. + - Return the "done" response. + """ + + # Collect cleaned data and mark the current type + data = form.cleaned_data.copy() + data["choice"] = self.type_code + + # Special rule for literature applications + if self.type_code == "LIT" and data.get("selfbuy") == "TRUE": + data["selfbuy_give_data"] = "False" + + # Save model instance modell = form.save(commit=False) - # we have to copy the data from the first form here - # this is a bit ugly code. can we copy this without explicit writing? - if data['choice'] == 'LIT': - modell.selfbuy_give_data = data['selfbuy_give_data'] + # Username from session if present + user = self.request.session.get("user") + if user: + modell.username = user.get("username") - if user := self.request.session.get('user'): - modell.username = user['username'] + # Copy common fields if provided by the form + if "realname" in data: + modell.realname = data["realname"] + if "email" in data: + modell.email = data["email"] - modell.realname = data['realname'] - modell.email = data['email'] - # write type of form in some cases - if data['choice'] in ('BIB', 'ELIT', 'SOFT'): - modell.type = data['choice'] + # Set model.type for specific request types + if self.type_code in ("BIB", "ELIT", "SOFT"): + modell.type = self.type_code - form.save() + # Literature-specific extra field + if self.type_code == "LIT" and "selfbuy_give_data" in data: + modell.selfbuy_give_data = data["selfbuy_give_data"] - # add some data to context for mail templates - data['pk'] = modell.pk - data['url_prefix'] = settings.EMAIL_URL_PREFIX - data['grant'] = ('LIT', 'SOFT', 'ELIT', 'BIB', 'IFG') - data['DOMAIN'] = ('MAIL', 'LIST') - data['typestring'] = TYPE_CHOICES[data['choice']] + modell.save() + if hasattr(form, "save_m2m"): + form.save_m2m() + + # 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) + context = {"data": data} - # we need to send the following mails here: - context = { 'data': data } try: - # - mail with entered data to the Volunteer - - txt_mail_template1 = get_template('input/ifg_volunteer_mail.txt') - html_mail_template1 = get_template('input/ifg_volunteer_mail.html') - - subject1, from_email1, to1 = 'Formular ausgefüllt', settings.IF_EMAIL, data['email'] - text_content1 = txt_mail_template1.render(context) - html_content1 = html_mail_template1.render(context) - msg1 = EmailMultiAlternatives(subject1, text_content1, from_email1, [to1]) - msg1.attach_alternative(html_content1, "text/html") + # Mail to applicant + txt1 = get_template("input/ifg_volunteer_mail.txt").render(context) + html1 = get_template("input/ifg_volunteer_mail.html").render(context) + msg1 = EmailMultiAlternatives( + "Formular ausgefüllt", txt1, settings.IF_EMAIL, [data["email"]] + ) + msg1.attach_alternative(html1, "text/html") msg1.send() - #print('ifg volunteer mail would have been sent') - #send_mail( - # 'Formular ausgefüllt', - # txt_mail_template1.render(context), - # IF_EMAIL, - # [data['email']], - # fail_silently=False) - ## - mail to IF with link to accept/decline - txt_mail_template = get_template('input/if_mail.txt') - html_mail_template = get_template('input/if_mail.html') - - subject, from_email, to = 'Formular ausgefüllt', settings.IF_EMAIL, settings.IF_EMAIL - text_content = txt_mail_template.render(context) - html_content = html_mail_template.render(context) - msg2 = EmailMultiAlternatives(subject, text_content, from_email, [to]) - msg2.attach_alternative(html_content, "text/html") + # Mail to IF + txt2 = get_template("input/if_mail.txt").render(context) + html2 = get_template("input/if_mail.html").render(context) + msg2 = EmailMultiAlternatives( + "Formular ausgefüllt", txt2, settings.IF_EMAIL, [settings.IF_EMAIL] + ) + msg2.attach_alternative(html2, "text/html") msg2.send() - #print('if mail would have been sent') - #send_mail( - # 'Formular ausgefüllt', - # txt_mail_template.render(context), - # IF_EMAIL, - # [IF_EMAIL], - # fail_silently=False) - ## raise SMTPException("testing pupose only") except BadHeaderError: modell.delete() - return HttpResponse('Invalid header found. Data not saved!') + return HttpResponse("Invalid header found. Data not saved!") except SMTPException: modell.delete() - return HttpResponse('Error in sending mails (probably wrong adress?). Data not saved!') - + 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"