fixed spacing & imports

This commit is contained in:
Oliver Zander 2025-10-15 14:46:28 +02:00
parent 7d1511bb93
commit cf81a45231
2 changed files with 141 additions and 114 deletions

View File

@ -1,10 +1,11 @@
from django import forms
from django.conf import settings
from django.forms import ModelForm, ChoiceField, RadioSelect, BooleanField, CharField, EmailField
from django.contrib.admin.widgets import AdminDateWidget
from django.forms import ModelForm, ChoiceField, RadioSelect, BooleanField, CharField, EmailField
from django.forms.renderers import DjangoTemplates
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django import forms
from .models import ProjectRequest, PROJECT_CATEGORIES, WIKIMEDIA_CHOICES
from .models import (
@ -90,7 +91,6 @@ class CommonOrderMixin(forms.Form):
class ExternForm(FdbForm):
choice = ChoiceField(choices=TYPE_CHOICES.items(), widget=RadioSelect,
label='Was möchtest Du beantragen?')
@ -103,24 +103,22 @@ class ExternForm(FdbForm):
exclude = ('username', 'granted', 'granted_date', 'survey_mail_send', 'service_id', 'survey_mail_date', 'mail_state')
INTERN_CHOICES = {'PRO': 'Projektsteckbrief',
INTERN_CHOICES = {
'PRO': 'Projektsteckbrief',
'HON': 'Ehrenamtsbescheinigung, Akkreditierung oder Redaktionsbestätigung',
'TRAV': 'Reisekostenerstattung'}
'TRAV': 'Reisekostenerstattung',
}
class InternForm(FdbForm):
choice = ChoiceField(choices = INTERN_CHOICES.items(), widget=RadioSelect,
label = 'Was möchtest Du eingeben?')
choice = ChoiceField(choices=INTERN_CHOICES.items(), widget=RadioSelect,
label='Was möchtest Du eingeben?')
class Meta:
model = ConcreteVolunteer
exclude = ('granted', 'granted_date', 'survey_mail_send', 'survey_mail_date', 'mail_state')
HOTEL_CHOICES = {'TRUE': mark_safe('Hotelzimmer benötigt'),
'FALSE': mark_safe('Kein Hotelzimmer benötigt')
}
class BaseApplicationForm(FdbForm):
"""
Base form for all external applications.
@ -147,9 +145,17 @@ class BaseApplicationForm(FdbForm):
settings.DATAPROTECTION, settings.FOERDERRICHTLINIEN))
HOTEL_CHOICES = {
'TRUE': mark_safe('Hotelzimmer benötigt'),
'FALSE': mark_safe('Kein Hotelzimmer benötigt'),
}
class TravelForm(BaseApplicationForm, CommonOrderMixin):
# TODO: add some javascript to show/hide other-field
hotel = ChoiceField(label='Hotelzimmer benötigt:', choices=HOTEL_CHOICES.items(), widget=RadioSelect())
# this is the code, to change required to false if needed
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -162,11 +168,13 @@ class TravelForm(BaseApplicationForm, CommonOrderMixin):
class Meta:
model = Travel
exclude = ('granted', 'granted_date', 'survey_mail_send', 'realname', 'email', 'survey_mail_date', 'project', 'request_url', 'payed_for_hotel_by', 'payed_for_travel_by', 'intern_notes', 'mail_state' )
widgets = {'checkin': AdminDateWidget(),
'checkout': AdminDateWidget(),}
fields = ['project_name', 'transport', 'travelcost', 'checkin', 'checkout', 'hotel', 'notes']
hotel = ChoiceField(label='Hotelzimmer benötigt:', choices=HOTEL_CHOICES.items(), widget=RadioSelect())
exclude = ('granted', 'granted_date', 'survey_mail_send', 'realname', 'email', 'survey_mail_date', 'project',
'request_url', 'payed_for_hotel_by', 'payed_for_travel_by', 'intern_notes', 'mail_state')
widgets = {
'checkin': AdminDateWidget,
'checkout': AdminDateWidget,
}
class Media:
js = ('dropdown/js/otrs_link.js',)
@ -208,6 +216,7 @@ class HonoraryCertificateForm(FdbForm):
model = HonoraryCertificate
fields = ['request_url', 'project']
exclude = ['intern_notes']
class Media:
js = ('dropdown/js/otrs_link.js',)
@ -242,20 +251,23 @@ class LiteratureForm(BaseApplicationForm, CommonOrderMixin):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['selfbuy_give_data'].required = True
class Meta:
model = Literature
fields = ['cost', 'info', 'source', 'notes', 'selfbuy', 'selfbuy_data', 'selfbuy_give_data', 'terms_accepted']
exclude = ['intern_notes', 'survey_mail_send', 'mail_state']
class Media:
js = ('dropdown/js/literature.js',)
ADULT_CHOICES = {'TRUE': mark_safe('Ich bin volljährig.'),
'FALSE': mark_safe('Ich bin noch nicht volljährig.')
}
ADULT_CHOICES = {
'TRUE': mark_safe('Ich bin volljährig.'),
'FALSE': mark_safe('Ich bin noch nicht volljährig.'),
}
class EmailForm(BaseApplicationForm, CommonOrderMixin):
termstoaccept = settings.NUTZUNGSBEDINGUNGEN_EMAIL_SERVICE
# this is the code, to change required to false if needed
@ -266,20 +278,19 @@ class EmailForm(BaseApplicationForm, CommonOrderMixin):
adult = ChoiceField(label='Volljährigkeit', choices=ADULT_CHOICES.items(), widget=RadioSelect())
# TODO: add some javascript to show/hide other-field
class Meta:
model = Email
fields = ['domain', 'address', 'other', 'adult', 'terms_accepted']
exclude = ['intern_notes', 'survey_mail_send', 'mail_state']
class Media:
js = ('dropdown/js/mail.js',)
class BusinessCardForm(BaseApplicationForm, CommonOrderMixin):
termstoaccept = settings.NUTZUNGSBEDINGUNGEN_VISITENKARTEN
# this is the code, to change required to false if needed
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -290,16 +301,18 @@ class BusinessCardForm(BaseApplicationForm, CommonOrderMixin):
model = BusinessCard
exclude = ['intern_notes', 'survey_mail_send', 'mail_state']
fields = ['project', 'data', 'variant', 'url_of_pic', 'send_data_to_print', 'sent_to', 'terms_accepted']
class Media:
js = ('dropdown/js/businessCard.js',)
class ListForm(BaseApplicationForm, CommonOrderMixin):
termstoaccept = settings.NUTZUNGSBEDINGUNGEN_MAILINGLISTEN
class Meta:
model = List
fields = ['domain', 'address', 'terms_accepted']
exclude = ['intern_notes', 'survey_mail_send','mail_state']
exclude = ['intern_notes', 'survey_mail_send', 'mail_state']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

View File

@ -1,15 +1,20 @@
from datetime import date
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator
from django.db import models
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator, MaxValueValidator
EMAIL_STATES = {'NONE': 'noch keine Mail versendet',
EMAIL_STATES = {
'NONE': 'noch keine Mail versendet',
'INF': 'die Benachrichtigung zur Projektabschlussmail wurde versendet',
'CLOSE': 'die Projektabschlussmail wurde versendet',
'END': 'alle automatischen Mails, auch surveyMail, wurden versendet'}
'END': 'alle automatischen Mails, auch surveyMail, wurden versendet',
}
class TermsConsentMixin(models.Model):
@ -35,7 +40,6 @@ class Volunteer(models.Model):
mail_state = models.CharField(max_length=6, choices=EMAIL_STATES.items(), default='NONE')
survey_mail_send = models.BooleanField(default=False, verbose_name='Keine Umfragemail schicken')
@classmethod
def set_granted(cl, key, b):
obj = cl.objects.get(pk=key)
@ -47,7 +51,6 @@ class Volunteer(models.Model):
abstract = True
class Extern(Volunteer):
''' abstract basis class for all data entered by extern volunteers '''
@ -57,7 +60,7 @@ class Extern(Volunteer):
# the following Fields are not supposed to be edited by users
service_id = models.CharField(max_length=15, null=True, blank=True)
def save(self,*args,**kwargs):
def save(self, *args, **kwargs):
# we don't call save with args/kwargs to avoid UNIQUE CONSTRAINT errors
# but maybe there is a better solution?
super().save()
@ -67,18 +70,21 @@ class Extern(Volunteer):
class Meta:
abstract = True
class ConcreteExtern(Extern):
''' needed because we can't initiate abstract base classes in the view'''
pass
class Account(models.Model):
code = models.CharField('Kostenstelle', max_length=5, default='DEF',
null=False, primary_key = True)
code = models.CharField('Kostenstelle', max_length=5, default='DEF', null=False, primary_key=True)
description = models.CharField('Beschreibung', max_length=60, default='NO DESCRIPTION')
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name='interne Anmerkungen')
def __str__(self):
return f'{self.code} {self.description}'
class Project(Volunteer):
end_mail_send = models.BooleanField(default=False, verbose_name='Keine Projektabschlussmail schicken')
name = models.CharField(max_length=200, verbose_name='Name des Projekts')
@ -97,12 +103,11 @@ class Project(Volunteer):
insurance_technic = models.BooleanField(default=False, verbose_name='Technikversicherung Ausland')
support = models.CharField(max_length=300, blank=True, null=True, verbose_name='Betreuungsperson und Vertretung')
cost = models.IntegerField(blank=True, null=True, verbose_name='Kosten')
account = models.ForeignKey('Account', on_delete=models.CASCADE, null=True, to_field='code', db_constraint = False)
granted_from = models.CharField(max_length=100,null=True,verbose_name='Bewilligt von')
notes = models.TextField(max_length=1000,null=True,blank=True,verbose_name='Anmerkungen')
account = models.ForeignKey('Account', on_delete=models.CASCADE, null=True, to_field='code', db_constraint=False)
granted_from = models.CharField(max_length=100, null=True, verbose_name='Bewilligt von')
notes = models.TextField(max_length=1000, null=True, blank=True, verbose_name='Anmerkungen')
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name='interne Anmerkungen')
# the following Fields are not supposed to be edited by users
pid = models.CharField(max_length=15, null=True, blank=True)
status = models.CharField(max_length=3,choices=(('RUN', 'läuft'),('END','beendet'),('NOT','nicht stattgefunden')),default='RUN')
@ -110,66 +115,59 @@ class Project(Volunteer):
project_of_year = models.IntegerField(default=0)
end_quartal = models.CharField(max_length=15, null=True, blank=True, verbose_name='Quartal Projekt Ende')
def save(self, *args, **kwargs):
def save(self,*args,**kwargs):
generate_finance_id=False
generate_finance_id = False
'''we generate the autogenerated fields here'''
# we don't call save with args/kwargs to avoid UNIQUE CONSTRAINT errors
# but maybe there is a better solution?
if not self.pk:
print ('NO PK THERE');
generate_finance_id=True
print('NO PK THERE')
generate_finance_id = True
super().save()
else:
orig = type(self).objects.get(pk=self.pk) # Originaldaten aus der DB abrufen
if orig.start.year != self.start.year:
generate_finance_id=True
generate_finance_id = True
if orig.account.code != self.account.code:
if str(self.account.code) == '21111':
generate_finance_id=True
generate_finance_id = True
else:
self.finance_id = str(self.account.code)
if generate_finance_id:
print ('MUST GENERATE FINANCE ID')
print('MUST GENERATE FINANCE ID')
year = self.start.year
projects = Project.objects.filter(start__year=year)
if not projects:
self.project_of_year = 1
#self.pid = str(self.start.year) + '-' + str(self.account.code) + str(self.project_of_year).zfill(3)
# self.pid = str(self.start.year) + '-' + str(self.account.code) + str(self.project_of_year).zfill(3)
else:
# get the project of year number of latest entry
projects = projects.order_by('-project_of_year')[0]
# add one to value of latest entry
self.project_of_year = int(projects.project_of_year) + 1
# self.pid = str(self.start.year) + '-' + str(self.account.code) + str(self.project_of_year).zfill(3)
# self.pid = str(self.start.year) + '-' + str(self.account.code) + str(self.project_of_year).zfill(3)
if str(self.account.code) == '21111':
self.finance_id = str(self.account.code) + '-' + str(self.project_of_year).zfill(3)
else:
self.finance_id = str(self.account.code)
# print (('Current PID',self.pid))
# print (('Current PID',self.pid))
if not self.pid:
self.pid = str(self.account.code) + str(self.pk).zfill(8)
# self.pid = str(self.account.code) + str(self.pk).zfill(3)
print (('Hallo Leute! Ich save jetzt mal MIT PID DANN!!!',self.pid))
print(('Hallo Leute! Ich save jetzt mal MIT PID DANN!!!', self.pid))
if self.end:
self.end_quartal = f'Q{self.end.month // 4 + 1}'
super().save()
def __str__(self):
return f'{self.pid} {self.name}'
@ -178,33 +176,41 @@ class Intern(Volunteer):
'''abstract base class for data entry from /intern (except Project)'''
request_url = models.URLField(max_length=2000, verbose_name='Antrag (URL)')
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name='interne Anmerkungen')
class Meta:
abstract = True
class ConcreteVolunteer(Volunteer):
''' needed because we can't initiate abstract base classes in the view'''
pass
class HonoraryCertificate(Intern):
''' this class is also used for accreditations '''
project = models.ForeignKey(Project, null=True, blank=True, on_delete=models.SET_NULL)
def __str__(self):
return 'Certificate for ' + self.realname
TRANSPORT_CHOICES = {'BAHN': 'Bahn',
TRANSPORT_CHOICES = {
'BAHN': 'Bahn',
'NONE': 'Keine Fahrtkosten',
'OTHER': 'Sonstiges (mit Begründung)'}
'OTHER': 'Sonstiges (mit Begründung)',
}
PAYEDBY_CHOICES = {'WMDE': 'WMDE',
'REQU': 'Antragstellender Mensch'}
PAYEDBY_CHOICES = {
'WMDE': 'WMDE',
'REQU': 'Antragstellender Mensch',
}
HOTEL_CHOICES = {'TRUE': mark_safe('Hotelzimmer benötigt'),
'FALSE': mark_safe('Kein Hotelzimmer benötigt')
}
HOTEL_CHOICES = {
'TRUE': mark_safe('Hotelzimmer benötigt'),
'FALSE': mark_safe('Kein Hotelzimmer benötigt'),
}
from django.contrib.contenttypes.models import ContentType
class Travel(Extern):
# project variable is now null true and blank true, which means it can be saved without project id to be later on filled out by admins
@ -225,27 +231,15 @@ class Travel(Extern):
# use content type model to get the end date for the project foreign key
project_end_quartal = models.CharField(max_length=15, null=True, blank=True, verbose_name='Quartal Projekt Ende')
from django.db.models.signals import pre_save
from django.dispatch import receiver
@receiver(pre_save, sender=Travel, dispatch_uid='get_project_end')
def getProjectEnd(sender, instance, **kwargs):
#instance.project_end = instance.project.end
def get_project_end(sender, instance, **kwargs):
if instance.project:
instance.project_end = instance.project.end
instance.project_end_quartal = instance.project.end_quartal
# using pre save instead
# def save(self,*args,**kwargs):
# '''we generate the autogenerated fields here'''
# # we don't call save with args/kwargs to avoid UNIQUE CONSTRAINT errors
# # but maybe there is a better solution?
# intern_notes
# project_end = self.checkout
# super(Travel, self).save(*args,**kwargs)
#abstract base class for Library and IFG
# abstract base class for Library and IFG
class Grant(Extern):
cost = models.CharField(max_length=10, verbose_name='Kosten',
help_text='Bitte gib die ungefähr zu erwartenden Kosten in Euro an.')
@ -291,6 +285,7 @@ TYPE_CHOICES = {
LIBRARY_TYPES = TYPE_BIB, TYPE_ELIT, TYPE_SOFT
LIBRARY_TYPE_CHOICES = [(choice, TYPE_CHOICES[choice]) for choice in LIBRARY_TYPES]
# same model is used for Library, ELitStip and Software!
class Library(Grant):
TYPE = TYPE_BIB
@ -332,9 +327,10 @@ class Software(Library):
proxy = True
SELFBUY_CHOICES = {'TRUE': mark_safe('Ich möchte das Werk selbst kaufen und per Kostenerstattung bei Wikimedia Deutschland abrechnen.'),
SELFBUY_CHOICES = {
'TRUE': mark_safe('Ich möchte das Werk selbst kaufen und per Kostenerstattung bei Wikimedia Deutschland abrechnen.'),
'FALSE': mark_safe('Ich möchte, dass Wikimedia Deutschland das Werk für mich kauft'),
}
}
class Literature(TermsConsentMixin, Grant):
@ -351,6 +347,7 @@ class Literature(TermsConsentMixin, Grant):
Telefonnummer, E-Mail-Adresse usw.). Trenne die einzelnen Angaben durch Zeilenumbrüche.'))
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name='interne Anmerkungen')
class IFG(Grant):
url = models.URLField(max_length=2000, verbose_name='URL',
help_text='Bitte gib den Link zu deiner Anfrage bei Frag den Staat an.')
@ -359,11 +356,15 @@ class IFG(Grant):
def __str__(self):
return 'IFG-Anfrage von ' + self.realname
DOMAIN_CHOICES = {'PEDIA': '@wikipedia.de',
DOMAIN_CHOICES = {
'PEDIA': '@wikipedia.de',
'BOOKS': '@wikibooks.de',
'QUOTE': '@wikiquote.de',
'SOURCE': '@wikisource.de',
'VERSITY': '@wikiversity.de',}
'VERSITY': '@wikiversity.de',
}
class Domain(Extern):
domain = models.CharField(max_length=10,
@ -373,13 +374,18 @@ class Domain(Extern):
class Meta:
abstract = True
MAIL_CHOICES = {'REALNAME': 'Vorname.Nachname',
'USERNAME': 'Username',
'OTHER': 'Sonstiges:'}
ADULT_CHOICES = {'TRUE': mark_safe('Ich bin volljährig.'),
'FALSE': mark_safe('Ich bin noch nicht volljährig.')
}
MAIL_CHOICES = {
'REALNAME': 'Vorname.Nachname',
'USERNAME': 'Username',
'OTHER': 'Sonstiges:',
}
ADULT_CHOICES = {
'TRUE': mark_safe('Ich bin volljährig.'),
'FALSE': mark_safe('Ich bin noch nicht volljährig.'),
}
class Email(TermsConsentMixin, Domain):
address = models.CharField(max_length=50,
@ -387,17 +393,20 @@ class Email(TermsConsentMixin, Domain):
default='USERNAME', verbose_name='Adressbestandteil',
help_text=mark_safe('Bitte gib hier den gewünschten Adressbestandteil an,<br>der sich vor der Domain befinden soll.'))
other = models.CharField(max_length=50,blank=True,null=True, verbose_name='Sonstiges')
adult = models.CharField( max_length=10, verbose_name='Volljährigkeit', choices=ADULT_CHOICES.items(), default='FALSE')
other = models.CharField(max_length=50, blank=True, null=True, verbose_name='Sonstiges')
adult = models.CharField(max_length=10, verbose_name='Volljährigkeit', choices=ADULT_CHOICES.items(), default='FALSE')
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name='interne Anmerkungen')
class List(TermsConsentMixin, Domain):
address = models.CharField(max_length=50, default='NO_ADDRESS',
verbose_name='Adressbestandteil für Projektmailingliste',
help_text=mark_safe('Bitte gib hier den gewünschten Adressbestandteil an,<br>der sich vor der Domain befinden soll.'))
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name='interne Anmerkungen')
PROJECT_CHOICE = {'PEDIA': 'Wikipedia',
PROJECT_CHOICE = {
'PEDIA': 'Wikipedia',
'SOURCE': 'Wikisource',
'BOOKS': 'Wikibooks',
'QUOTE': 'Wikiquote',
@ -405,10 +414,14 @@ PROJECT_CHOICE = {'PEDIA': 'Wikipedia',
'VOYAGE': 'Wikivoyage',
'DATA': 'Wikidata',
'NEWS': 'Wikinews',
'COMMONS': 'Wikimedia Commons'}
'COMMONS': 'Wikimedia Commons',
}
BC_VARIANT = {
'PIC': 'mit Bild',
'NOPIC': 'ohne Bild',
}
BC_VARIANT = {'PIC': 'mit Bild',
'NOPIC': 'ohne Bild'}
class BusinessCard(TermsConsentMixin, Extern):
project = models.CharField(max_length=20, choices=PROJECT_CHOICE.items(),
@ -426,7 +439,7 @@ class BusinessCard(TermsConsentMixin, Extern):
variant = models.CharField(max_length=5, choices=BC_VARIANT.items(),
default='NOPIC', verbose_name='Variante',
help_text=mark_safe('so sehen die Varianten aus: <a href="https://upload.wikimedia.org/wikipedia/commons/c/cd/Muster_Visitenkarten_WMDE_2018.jpg">\
mit Bild</a> <a href="https://upload.wikimedia.org/wikipedia/commons/d/d3/Muster_Visitenkarte_WMDE.png">ohne Bild</a>' ))
mit Bild</a> <a href="https://upload.wikimedia.org/wikipedia/commons/d/d3/Muster_Visitenkarte_WMDE.png">ohne Bild</a>'))
url_of_pic = models.CharField(max_length=200, verbose_name='Url des Bildes', default='', help_text='Bitte gib die Wikimedia-Commons-URL des Bildes an.')
@ -467,6 +480,7 @@ validate_cost = MaxValueValidator(
),
)
# Application for project funding < 1000 EUR
class ProjectRequest(Volunteer):
name = models.CharField('Name des Projekts', max_length=200)