2025-10-16 10:16:08 +00:00
from contextlib import suppress
2020-10-22 14:02:59 +00:00
from datetime import date
2024-01-08 13:31:18 +00:00
2025-10-16 10:16:08 +00:00
from django import forms
2025-10-15 12:46:28 +00:00
from django . contrib . contenttypes . models import ContentType
2020-09-21 12:27:16 +00:00
from django . db import models
2025-10-15 12:46:28 +00:00
from django . db . models . signals import pre_save
from django . dispatch import receiver
2025-10-16 10:16:08 +00:00
from django . forms import ModelMultipleChoiceField , CheckboxSelectMultiple
from django . utils . functional import cached_property , classproperty
2025-08-20 09:02:31 +00:00
from django . utils . html import format_html
2025-08-19 14:08:48 +00:00
from django . utils . safestring import mark_safe
2020-10-22 09:36:16 +00:00
2025-10-15 12:46:28 +00:00
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 ' ,
}
2025-08-26 10:54:29 +00:00
class TermsConsentMixin ( models . Model ) :
""" Abstract mixin to add a terms_accepted field for documenting user consent. """
terms_accepted = models . BooleanField ( default = False , verbose_name = " Nutzungsbedingungen zugestimmt " )
class Meta :
abstract = True
2023-02-27 17:09:29 +00:00
2020-09-30 12:17:38 +00:00
class Volunteer ( models . Model ) :
2025-09-28 22:51:13 +00:00
realname = models . CharField ( max_length = 200 , null = True , verbose_name = ' Realname ' ,
help_text = ' Bitte gib deinen Vornamen und deinen Nachnamen ein. ' , default = ' ' )
2020-11-17 09:56:24 +00:00
email = models . EmailField ( max_length = 200 , null = True , verbose_name = ' E-Mail-Adresse ' ,
2025-08-19 14:08:48 +00:00
help_text = mark_safe ( ' Bitte gib deine E-Mail-Adresse ein, damit dich<br>Wikimedia Deutschland bei Rückfragen oder für<br>die Zusage kontaktieren kann. ' ) )
2020-10-22 14:02:59 +00:00
# the following Fields are not supposed to be edited by users
2023-10-25 15:50:40 +00:00
2023-02-27 17:09:29 +00:00
granted = models . BooleanField ( null = True , verbose_name = ' bewilligt ' )
granted_date = models . DateField ( null = True , verbose_name = ' bewilligt am ' )
2021-04-12 11:44:45 +00:00
survey_mail_date = models . DateField ( verbose_name = ' Umfragemail wurde verschickt am ' , null = True , blank = True )
2023-02-27 17:09:29 +00:00
mail_state = models . CharField ( max_length = 6 , choices = EMAIL_STATES . items ( ) , default = ' NONE ' )
2023-02-27 17:09:29 +00:00
survey_mail_send = models . BooleanField ( default = False , verbose_name = ' Keine Umfragemail schicken ' )
2023-02-27 17:09:29 +00:00
2020-10-20 10:00:59 +00:00
@classmethod
def set_granted ( cl , key , b ) :
obj = cl . objects . get ( pk = key )
obj . granted = b
2020-10-22 14:02:59 +00:00
obj . granted_date = date . today ( )
2020-10-20 10:00:59 +00:00
obj . save ( )
2020-09-30 10:15:58 +00:00
class Meta :
abstract = True
2020-09-30 09:04:47 +00:00
2020-10-22 09:36:16 +00:00
2020-10-26 10:00:12 +00:00
class Extern ( Volunteer ) :
''' abstract basis class for all data entered by extern volunteers '''
2025-08-18 10:11:44 +00:00
2020-11-17 15:42:25 +00:00
username = models . CharField ( max_length = 200 , null = True , verbose_name = ' Benutzer_innenname ' ,
2025-09-28 22:51:13 +00:00
help_text = mark_safe ( ' Wikimedia Benutzer_innenname ' ) )
2020-10-26 10:00:12 +00:00
2020-10-29 14:05:11 +00:00
# the following Fields are not supposed to be edited by users
service_id = models . CharField ( max_length = 15 , null = True , blank = True )
2025-10-15 12:46:28 +00:00
def save ( self , * args , * * kwargs ) :
2021-10-07 08:29:04 +00:00
# we don't call save with args/kwargs to avoid UNIQUE CONSTRAINT errors
# but maybe there is a better solution?
2021-10-06 14:15:58 +00:00
super ( ) . save ( )
2020-10-29 14:05:11 +00:00
self . service_id = type ( self ) . __name__ + str ( self . pk )
2021-10-06 14:15:58 +00:00
super ( ) . save ( )
2020-10-29 14:05:11 +00:00
2020-10-26 10:00:12 +00:00
class Meta :
abstract = True
2025-10-15 12:46:28 +00:00
2021-07-06 11:00:34 +00:00
class ConcreteExtern ( Extern ) :
''' needed because we can ' t initiate abstract base classes in the view '''
pass
2025-10-15 12:46:28 +00:00
2021-07-08 06:00:28 +00:00
class Account ( models . Model ) :
2025-10-15 12:46:28 +00:00
code = models . CharField ( ' Kostenstelle ' , max_length = 5 , default = ' DEF ' , null = False , primary_key = True )
2021-11-03 12:43:40 +00:00
description = models . CharField ( ' Beschreibung ' , max_length = 60 , default = ' NO DESCRIPTION ' )
2025-09-28 22:51:13 +00:00
intern_notes = models . TextField ( max_length = 1000 , blank = True , verbose_name = ' interne Anmerkungen ' )
2025-10-15 12:46:28 +00:00
2021-07-07 13:16:39 +00:00
def __str__ ( self ) :
2025-09-28 22:51:13 +00:00
return f ' { self . code } { self . description } '
2021-07-07 13:16:39 +00:00
2025-10-16 13:38:41 +00:00
@property
def has_subaccounts ( self ) :
return self . code == ' 21111 '
2025-10-15 12:46:28 +00:00
2025-10-16 10:16:08 +00:00
class BaseProjectCategory ( models . Model ) :
OTHER : str
name = models . CharField ( ' Name ' , max_length = 200 )
order = models . PositiveIntegerField ( ' Reihenfolge ' )
class Meta :
abstract = True
ordering = [ ' order ' ]
def __str__ ( self ) :
return self . name
@cached_property
def project_count ( self ) :
return self . projects . count ( )
@classproperty
def other ( cls ) :
return cls ( id = 0 , name = cls . OTHER )
class ProjectCategory ( BaseProjectCategory ) :
OTHER = ' Sonstiges '
class Meta ( BaseProjectCategory . Meta ) :
verbose_name = ' Projektkategorie '
verbose_name_plural = ' Projektkategorien '
class WikimediaProject ( BaseProjectCategory ) :
OTHER = ' Anderes '
class Meta ( BaseProjectCategory . Meta ) :
verbose_name = ' Wikimedia Projekt '
verbose_name_plural = ' Wikimedia Projekte '
2025-10-16 13:38:41 +00:00
class ProductCategoryChoiceIterator ( ModelMultipleChoiceField . iterator ) :
2025-10-16 10:16:08 +00:00
def __iter__ ( self ) :
2025-10-16 13:38:41 +00:00
yield from ModelMultipleChoiceField . iterator . __iter__ ( self )
2025-10-16 10:16:08 +00:00
yield f ' { self . field . other . id } ' , self . field . other . name
class ProductCategoryFormField ( ModelMultipleChoiceField ) :
widget = CheckboxSelectMultiple
iterator = ProductCategoryChoiceIterator
def __init__ ( self , * , other , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . other = other
def _check_values ( self , value , * , other = False ) :
with suppress ( TypeError ) :
value = set ( value )
if other := f ' { self . other . id } ' in value :
value . remove ( f ' { self . other . id } ' )
queryset = super ( ) . _check_values ( value )
if other :
return [ * queryset , self . other ]
return list ( queryset )
class ProjectCategoryField ( models . ManyToManyField ) :
def __init__ ( self , to , * * kwargs ) :
kwargs [ ' to ' ] = to
kwargs [ ' related_name ' ] = ' projects '
super ( ) . __init__ ( * * kwargs )
self . other_field = models . CharField ( max_length = 200 , blank = True )
def contribute_to_class ( self , cls , name , * * kwargs ) :
super ( ) . contribute_to_class ( cls , name , * * kwargs )
model , other_field = self . remote_field . model , self . other_field
if not isinstance ( model , str ) :
self . verbose_name = self . _verbose_name = verbose_name = model . _meta . verbose_name_plural
other_field . verbose_name = other_field . _verbose_name = f ' { verbose_name } ( { model . OTHER } ) '
other_field . contribute_to_class ( cls , f ' { name } _other ' )
def formfield ( self , * * kwargs ) :
kwargs [ ' form_class ' ] = ProductCategoryFormField
kwargs [ ' other ' ] = self . remote_field . model . other
return super ( ) . formfield ( * * kwargs )
2020-09-30 12:17:38 +00:00
class Project ( Volunteer ) :
2023-02-27 17:09:29 +00:00
end_mail_send = models . BooleanField ( default = False , verbose_name = ' Keine Projektabschlussmail schicken ' )
2020-11-02 09:58:20 +00:00
name = models . CharField ( max_length = 200 , verbose_name = ' Name des Projekts ' )
2025-09-28 22:51:13 +00:00
description = models . CharField ( max_length = 500 , verbose_name = ' Kurzbeschreibung ' , null = True )
2021-11-03 12:47:39 +00:00
start = models . DateField ( ' Startdatum ' , null = True )
2020-10-21 11:53:39 +00:00
end = models . DateField ( ' Erwartetes Projektende ' , null = True )
2021-04-12 09:48:41 +00:00
otrs = models . URLField ( max_length = 300 , null = True , verbose_name = ' OTRS-Link ' )
2025-09-28 22:51:13 +00:00
plan = models . URLField ( max_length = 2000 , null = True , blank = True , verbose_name = ' Link zum Förderplan ' )
page = models . URLField ( max_length = 2000 , null = True , blank = True , verbose_name = ' Link zur Projektseite ' )
urls = models . CharField ( max_length = 2000 , null = True , blank = True , verbose_name = ' Weitere Links ' )
group = models . CharField ( max_length = 2000 , null = True , blank = True , verbose_name = ' Mitorganisierende ' )
location = models . CharField ( max_length = 2000 , null = True , blank = True , verbose_name = ' Ort/Adresse/Location ' )
2020-11-02 09:58:20 +00:00
participants_estimated = models . IntegerField ( blank = True , null = True , verbose_name = ' Teilnehmende angefragt ' )
participants_real = models . IntegerField ( blank = True , null = True , verbose_name = ' Teilnehmende ausgezählt ' )
2020-11-02 10:56:07 +00:00
insurance = models . BooleanField ( default = False , verbose_name = ' Haftpflichtversicherung ' )
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 ' )
2023-02-27 17:09:29 +00:00
cost = models . IntegerField ( blank = True , null = True , verbose_name = ' Kosten ' )
2025-10-15 12:46:28 +00:00
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 ' )
2025-09-28 22:51:13 +00:00
intern_notes = models . TextField ( max_length = 1000 , blank = True , verbose_name = ' interne Anmerkungen ' )
2025-08-18 10:11:44 +00:00
2025-10-16 10:16:08 +00:00
categories = ProjectCategoryField ( ProjectCategory )
wikimedia_projects = ProjectCategoryField ( WikimediaProject )
2020-10-22 14:02:59 +00:00
# the following Fields are not supposed to be edited by users
2020-10-28 14:04:53 +00:00
pid = models . CharField ( max_length = 15 , null = True , blank = True )
2023-02-27 17:09:28 +00:00
status = models . CharField ( max_length = 3 , choices = ( ( ' RUN ' , ' läuft ' ) , ( ' END ' , ' beendet ' ) , ( ' NOT ' , ' nicht stattgefunden ' ) ) , default = ' RUN ' )
2021-10-04 08:43:50 +00:00
finance_id = models . CharField ( max_length = 15 , null = True , blank = True )
2021-10-04 10:39:09 +00:00
project_of_year = models . IntegerField ( default = 0 )
2025-09-28 22:51:13 +00:00
end_quartal = models . CharField ( max_length = 15 , null = True , blank = True , verbose_name = ' Quartal Projekt Ende ' )
2020-11-02 10:56:07 +00:00
2025-10-16 13:38:41 +00:00
class Meta :
verbose_name = ' Projekt '
verbose_name_plural = ' Projekte '
2025-02-18 14:34:05 +00:00
2025-10-16 13:38:41 +00:00
def __str__ ( self ) :
return f ' { self . pid or self . id } { self . name } '
2025-02-18 14:34:05 +00:00
2025-10-16 13:38:41 +00:00
def save ( self , * , using = None , * * kwargs ) :
kwargs [ ' using ' ] = using
2024-01-08 13:31:18 +00:00
2025-10-16 13:38:41 +00:00
if self . end :
self . end_quartal = f ' Q { self . end . month / / 4 + 1 } '
2025-02-18 14:34:05 +00:00
else :
2025-10-16 13:38:41 +00:00
self . end_quartal = ' '
if not self . account :
self . finance_id = ' '
self . project_of_year = 0
return super ( ) . save ( * * kwargs )
if self . should_generate_finance_id ( ) :
self . generate_finance_id ( )
super ( ) . save ( * * kwargs )
2025-02-18 14:34:05 +00:00
if not self . pid :
2025-10-16 13:41:22 +00:00
self . pid = f ' { self . account . code } { self . id : 08d } '
2025-10-16 13:38:41 +00:00
super ( ) . save ( update_fields = [ ' pid ' ] , using = using )
2025-02-18 14:34:05 +00:00
2025-10-16 13:38:41 +00:00
def should_generate_finance_id ( self ) :
if self . id is None :
return True
2025-02-18 14:34:05 +00:00
2025-10-16 13:38:41 +00:00
if not self . finance_id :
return True
2020-10-06 13:15:05 +00:00
2025-10-16 13:38:41 +00:00
start , account_id = type ( self ) . objects . values_list ( ' start ' , ' account ' ) . get ( id = self . id )
return not ( self . start . year == start . year and self . account_id == account_id )
def generate_finance_id ( self ) :
"""
This is an improved version of the old code for generating a finance id .
There is still no protection by constraints against duplicate finance ids !
"""
queryset = Project . objects . exclude ( id = self . id ) . filter ( start__year = self . start . year )
max_project_of_year = queryset . aggregate ( max = models . Max ( ' project_of_year ' ) ) . get ( ' max ' ) or 0
self . project_of_year = project_of_year = max_project_of_year + 1
if self . account . has_subaccounts :
self . finance_id = f ' { self . account . code } - { project_of_year : 03d } '
else :
self . finance_id = self . account . code
2020-09-30 11:31:19 +00:00
2025-10-16 10:16:08 +00:00
def clean ( self ) :
if ( self . start and self . end ) and ( self . end < self . start ) :
raise forms . ValidationError ( {
' end ' : [
forms . ValidationError ( ' Das erwartete Projektende muss nach dem Startdatum liegen. ' ) ,
] ,
} )
2020-10-22 09:36:16 +00:00
2025-10-16 13:38:41 +00:00
class ProjectRequest ( Project ) :
class Meta :
proxy = True
verbose_name = ' Projekt (beantragt) '
verbose_name_plural = ' Projekte (beantragt) '
class ProjectDeclined ( Project ) :
class Meta :
proxy = True
verbose_name = ' Projekt (abgelehnt) '
verbose_name_plural = ' Projekte (abgelehnt) '
2020-10-26 10:38:56 +00:00
class Intern ( Volunteer ) :
2023-02-27 17:09:29 +00:00
''' abstract base class for data entry from /intern (except Project) '''
2021-04-12 09:48:41 +00:00
request_url = models . URLField ( max_length = 2000 , verbose_name = ' Antrag (URL) ' )
2023-02-27 17:09:29 +00:00
intern_notes = models . TextField ( max_length = 1000 , blank = True , verbose_name = ' interne Anmerkungen ' )
2025-10-15 12:46:28 +00:00
2020-10-26 10:38:56 +00:00
class Meta :
abstract = True
2025-10-15 12:46:28 +00:00
2021-07-07 07:42:51 +00:00
class ConcreteVolunteer ( Volunteer ) :
''' needed because we can ' t initiate abstract base classes in the view '''
pass
2025-10-15 12:46:28 +00:00
2020-10-26 10:38:56 +00:00
class HonoraryCertificate ( Intern ) :
''' this class is also used for accreditations '''
2020-10-26 12:15:38 +00:00
project = models . ForeignKey ( Project , null = True , blank = True , on_delete = models . SET_NULL )
2025-10-15 12:46:28 +00:00
2020-09-30 11:31:19 +00:00
def __str__ ( self ) :
2025-10-16 13:38:41 +00:00
return f ' Certificate for { self . realname } '
2020-09-30 11:46:06 +00:00
2020-10-22 09:36:16 +00:00
2025-10-15 12:46:28 +00:00
TRANSPORT_CHOICES = {
' BAHN ' : ' Bahn ' ,
' NONE ' : ' Keine Fahrtkosten ' ,
' OTHER ' : ' Sonstiges (mit Begründung) ' ,
}
2020-11-02 13:04:01 +00:00
2025-10-15 12:46:28 +00:00
PAYEDBY_CHOICES = {
' WMDE ' : ' WMDE ' ,
' REQU ' : ' Antragstellender Mensch ' ,
}
2020-11-02 13:04:01 +00:00
2025-10-15 12:46:28 +00:00
HOTEL_CHOICES = {
' TRUE ' : mark_safe ( ' Hotelzimmer benötigt ' ) ,
' FALSE ' : mark_safe ( ' Kein Hotelzimmer benötigt ' ) ,
}
2023-02-27 17:09:29 +00:00
2023-02-27 17:09:29 +00:00
2023-02-27 17:09:29 +00:00
class Travel ( Extern ) :
2023-02-27 17:09:29 +00:00
# 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
project = models . ForeignKey ( Project , on_delete = models . CASCADE , null = True , blank = True )
project_name = models . CharField ( max_length = 50 , null = True , blank = True , verbose_name = ' Projektname: ' )
transport = models . CharField ( max_length = 5 , choices = TRANSPORT_CHOICES . items ( ) , default = ' BAHN ' , verbose_name = ' Transportmittel: ' )
2020-11-02 13:04:01 +00:00
other_transport = models . CharField ( max_length = 200 , null = True , blank = True , verbose_name = ' Sonstige Transportmittel (mit Begründung) ' )
2025-09-28 22:51:13 +00:00
travelcost = models . CharField ( max_length = 10 , default = ' 0 ' , verbose_name = ' Fahrtkosten ' )
2023-02-27 17:09:29 +00:00
checkin = models . DateField ( blank = True , null = True , verbose_name = ' Anreise ' )
checkout = models . DateField ( blank = True , null = True , verbose_name = ' Abreise ' )
2020-11-02 13:04:01 +00:00
payed_for_hotel_by = models . CharField ( max_length = 4 , choices = PAYEDBY_CHOICES . items ( ) , blank = True , null = True , verbose_name = ' Kostenauslage Hotel durch ' )
payed_for_travel_by = models . CharField ( max_length = 4 , choices = PAYEDBY_CHOICES . items ( ) , blank = True , null = True , verbose_name = ' Kostenauslage Fahrt durch ' )
2023-02-27 17:09:29 +00:00
hotel = models . CharField ( max_length = 10 , choices = HOTEL_CHOICES . items ( ) , verbose_name = ' Hotelzimmer benötigt: ' )
2023-02-27 17:09:29 +00:00
notes = models . TextField ( max_length = 1000 , blank = True , verbose_name = ' Anmerkungen ' )
request_url = models . URLField ( max_length = 2000 , verbose_name = ' Antrag (URL) ' )
intern_notes = models . TextField ( max_length = 1000 , blank = True , verbose_name = ' interne Anmerkungen ' )
2023-02-27 17:09:29 +00:00
project_end = models . DateField ( blank = True , null = True , verbose_name = ' Projektende ' )
# use content type model to get the end date for the project foreign key
2025-09-28 22:51:13 +00:00
project_end_quartal = models . CharField ( max_length = 15 , null = True , blank = True , verbose_name = ' Quartal Projekt Ende ' )
2023-02-27 17:09:29 +00:00
2025-09-28 22:51:13 +00:00
@receiver ( pre_save , sender = Travel , dispatch_uid = ' get_project_end ' )
2025-10-15 12:46:28 +00:00
def get_project_end ( sender , instance , * * kwargs ) :
2023-02-27 17:09:29 +00:00
if instance . project :
instance . project_end = instance . project . end
instance . project_end_quartal = instance . project . end_quartal
2023-02-27 17:09:29 +00:00
2020-10-26 10:38:56 +00:00
2025-10-15 12:46:28 +00:00
# abstract base class for Library and IFG
2020-10-26 10:00:12 +00:00
class Grant ( Extern ) :
2020-11-18 15:03:27 +00:00
cost = models . CharField ( max_length = 10 , verbose_name = ' Kosten ' ,
2025-09-28 22:51:13 +00:00
help_text = ' Bitte gib die ungefähr zu erwartenden Kosten in Euro an. ' )
2023-02-27 17:09:28 +00:00
notes = models . TextField ( max_length = 1000 , blank = True , verbose_name = ' Anmerkungen ' ,
2025-09-28 22:51:13 +00:00
help_text = ' Bitte gib an wofür Du das Stipendium verwenden willst. ' )
2020-09-30 11:46:06 +00:00
2020-09-30 12:17:38 +00:00
class Meta :
abstract = True
2020-10-20 07:35:04 +00:00
2025-08-20 09:02:31 +00:00
def type_link ( path , label ) :
return format_html (
format_string = ' <a href= " {href} " target= " _blank " rel= " noopener " > {label} </a> ' ,
href = f ' https://de.wikipedia.org/wiki/Wikipedia:Förderung/ { path } ' ,
label = label ,
)
TYPE_BIB = ' BIB '
TYPE_ELIT = ' ELIT '
TYPE_MAIL = ' MAIL '
TYPE_IFG = ' IFG '
TYPE_LIT = ' LIT '
TYPE_LIST = ' LIST '
TYPE_TRAV = ' TRAV '
TYPE_SOFT = ' SOFT '
TYPE_VIS = ' VIS '
2025-10-16 10:16:08 +00:00
TYPE_PROJ = ' PROJ '
2025-08-20 09:02:31 +00:00
TYPE_CHOICES = {
TYPE_BIB : type_link ( ' Zugang_zu_Fachliteratur#Bibliotheksstipendium ' , ' Bibliotheksstipendium ' ) ,
TYPE_ELIT : type_link ( ' Zugang_zu_Fachliteratur#eLiteraturstipendium ' , ' eLiteraturstipendium ' ) ,
TYPE_MAIL : type_link ( ' E-Mail-Adressen_und_Visitenkarten#E-Mail-Adressen ' , ' E-Mail-Adresse ' ) ,
TYPE_IFG : type_link ( ' Gebührenerstattungen_für_Behördenanfragen ' , ' Kostenübernahme IFG-Anfrage ' ) ,
TYPE_LIT : type_link ( ' Zugang_zu_Fachliteratur#Literaturstipendium ' , ' Literaturstipendium ' ) ,
TYPE_LIST : type_link ( ' E-Mail-Adressen_und_Visitenkarten#Mailinglisten ' , ' Mailingliste ' ) ,
TYPE_TRAV : type_link ( ' Reisekostenerstattungen ' , ' Reisekosten ' ) ,
TYPE_SOFT : type_link ( ' Software-Stipendien ' , ' Softwarestipendium ' ) ,
TYPE_VIS : type_link ( ' E-Mail-Adressen_und_Visitenkarten#Visitenkarten ' , ' Visitenkarten ' ) ,
2025-10-16 10:16:08 +00:00
TYPE_PROJ : type_link ( ' Projektplanung ' , ' Projektförderung unter 1000 EUR ' ) ,
2025-08-20 09:02:31 +00:00
}
2025-08-20 09:19:05 +00:00
LIBRARY_TYPES = TYPE_BIB , TYPE_ELIT , TYPE_SOFT
LIBRARY_TYPE_CHOICES = [ ( choice , TYPE_CHOICES [ choice ] ) for choice in LIBRARY_TYPES ]
2020-10-19 12:37:51 +00:00
2025-10-15 12:46:28 +00:00
2020-10-20 07:35:04 +00:00
# same model is used for Library, ELitStip and Software!
2020-09-30 12:17:38 +00:00
class Library ( Grant ) :
2025-08-20 10:06:43 +00:00
TYPE = TYPE_BIB
LIBRARY_LABEL = ' Bibliothek '
LIBRARY_HELP_TEXT = ' Für welche Bibliothek gilt das Stipendium? '
DURATION_HELP_TEXT = mark_safe ( ' In welchem Zeitraum möchtest du recherchieren oder<br>wie lange ist der Bibliotheksausweis gültig? ' )
2020-10-19 11:54:57 +00:00
2025-08-20 10:06:43 +00:00
type = models . CharField ( max_length = 4 , choices = LIBRARY_TYPE_CHOICES , default = TYPE_BIB )
2020-09-30 12:17:38 +00:00
library = models . CharField ( max_length = 200 )
2025-09-28 22:51:13 +00:00
duration = models . CharField ( max_length = 100 , verbose_name = ' Dauer ' )
intern_notes = models . TextField ( max_length = 1000 , blank = True , verbose_name = ' interne Anmerkungen ' )
2025-08-20 10:06:43 +00:00
2020-09-30 11:46:06 +00:00
def __str__ ( self ) :
return self . library
2020-09-30 12:17:38 +00:00
2025-08-20 10:06:43 +00:00
def save ( self , * * kwargs ) :
self . type = self . TYPE
return super ( ) . save ( * * kwargs )
class ELiterature ( Library ) :
TYPE = TYPE_ELIT
LIBRARY_LABEL = ' Datenbank/Online-Ressource '
LIBRARY_HELP_TEXT = ' Für welche Datenbank/Online-Ressource gilt das Stipendium? '
DURATION_HELP_TEXT = ' Wie lange gilt der Zugang? '
class Meta :
proxy = True
class Software ( Library ) :
TYPE = TYPE_SOFT
LIBRARY_LABEL = ' Software '
LIBRARY_HELP_TEXT = ' Für welche Software gilt das Stipendium? '
DURATION_HELP_TEXT = ' Wie lange gilt die Lizenz? '
class Meta :
proxy = True
2023-02-27 17:09:28 +00:00
2025-10-15 12:46:28 +00:00
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 ' ) ,
}
2023-02-27 17:09:28 +00:00
2025-08-26 10:54:29 +00:00
class Literature ( TermsConsentMixin , Grant ) :
2020-11-18 17:02:23 +00:00
info = models . CharField ( max_length = 500 , verbose_name = ' Informationen zum Werk ' ,
2025-09-28 22:51:13 +00:00
help_text = mark_safe ( ' Bitte gib alle Informationen zum benötigten Werk an,<br> \
die eine eindeutige Identifizierung ermöglichen ( Autor , Titel , Verlag , ISBN , . . . ) ' ))
2020-11-18 17:02:23 +00:00
source = models . CharField ( max_length = 200 , verbose_name = ' Bezugsquelle ' ,
2025-09-28 22:51:13 +00:00
help_text = ' Bitte gib an, wo du das Werk kaufen möchtest. ' )
2023-02-27 17:09:28 +00:00
selfbuy = models . CharField ( max_length = 10 , verbose_name = ' Selbstkauf? ' , choices = SELFBUY_CHOICES . items ( ) , default = ' TRUE ' )
2025-08-19 14:08:48 +00:00
selfbuy_give_data = models . BooleanField ( verbose_name = mark_safe ( ' Datenweitergabe erlauben ' ) , help_text = mark_safe ( ' Ich stimme der Weitergabe meiner Daten (Name, Postadresse) an den von mir angegebenen Anbieter/Dienstleister zu. ' ) )
2023-02-27 17:09:29 +00:00
selfbuy_data = models . TextField ( max_length = 1000 , verbose_name = ' Persönliche Daten sowie Adresse ' , default = ' ' , \
2025-09-28 22:51:13 +00:00
help_text = mark_safe ( ' Bitte gib hier alle persönlichen Daten an, die wir benötigen, um das Werk<br> \
2023-02-27 17:09:29 +00:00
für dich zu kaufen und es dir anschließend zu schicken ( z . B . Vorname Nachname , Anschrift , < br > \
2025-09-28 22:51:13 +00:00
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 ' )
2020-10-22 09:36:16 +00:00
2025-10-15 12:46:28 +00:00
2020-09-30 12:17:38 +00:00
class IFG ( Grant ) :
2025-09-28 22:51:13 +00:00
url = models . URLField ( max_length = 2000 , verbose_name = ' URL ' ,
2025-10-15 12:46:28 +00:00
help_text = ' Bitte gib den Link zu deiner Anfrage bei Frag den Staat an. ' )
2025-09-28 22:51:13 +00:00
intern_notes = models . TextField ( max_length = 1000 , blank = True , verbose_name = ' interne Anmerkungen ' )
2025-08-18 10:11:44 +00:00
2020-09-30 12:17:38 +00:00
def __str__ ( self ) :
2025-09-28 22:51:13 +00:00
return ' IFG-Anfrage von ' + self . realname
2020-10-27 10:00:58 +00:00
2025-10-15 12:46:28 +00:00
DOMAIN_CHOICES = {
' PEDIA ' : ' @wikipedia.de ' ,
' BOOKS ' : ' @wikibooks.de ' ,
' QUOTE ' : ' @wikiquote.de ' ,
' SOURCE ' : ' @wikisource.de ' ,
' VERSITY ' : ' @wikiversity.de ' ,
}
2020-10-27 10:00:58 +00:00
class Domain ( Extern ) :
domain = models . CharField ( max_length = 10 ,
choices = DOMAIN_CHOICES . items ( ) ,
default = ' PEDIA ' )
class Meta :
abstract = True
2025-10-15 12:46:28 +00:00
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. ' ) ,
}
2023-02-27 17:09:29 +00:00
2025-08-26 10:54:29 +00:00
class Email ( TermsConsentMixin , Domain ) :
2020-10-27 11:37:18 +00:00
address = models . CharField ( max_length = 50 ,
2025-10-15 12:46:28 +00:00
choices = MAIL_CHOICES . items ( ) ,
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. ' ) )
2020-11-18 16:46:05 +00:00
2025-10-15 12:46:28 +00:00
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 ' )
2025-09-28 22:51:13 +00:00
intern_notes = models . TextField ( max_length = 1000 , blank = True , verbose_name = ' interne Anmerkungen ' )
2020-10-27 10:00:58 +00:00
2025-10-15 12:46:28 +00:00
2025-08-26 10:54:29 +00:00
class List ( TermsConsentMixin , Domain ) :
2020-11-18 16:46:05 +00:00
address = models . CharField ( max_length = 50 , default = ' NO_ADDRESS ' ,
2025-09-28 22:51:13 +00:00
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 ' )
2020-10-27 11:37:18 +00:00
2025-10-15 12:46:28 +00:00
PROJECT_CHOICE = {
' PEDIA ' : ' Wikipedia ' ,
' SOURCE ' : ' Wikisource ' ,
' BOOKS ' : ' Wikibooks ' ,
' QUOTE ' : ' Wikiquote ' ,
' VERSITY ' : ' Wikiversity ' ,
' VOYAGE ' : ' Wikivoyage ' ,
' DATA ' : ' Wikidata ' ,
' NEWS ' : ' Wikinews ' ,
' COMMONS ' : ' Wikimedia Commons ' ,
}
BC_VARIANT = {
' PIC ' : ' mit Bild ' ,
' NOPIC ' : ' ohne Bild ' ,
}
2020-10-27 11:37:18 +00:00
2025-08-26 10:54:29 +00:00
class BusinessCard ( TermsConsentMixin , Extern ) :
2020-10-27 11:37:18 +00:00
project = models . CharField ( max_length = 20 , choices = PROJECT_CHOICE . items ( ) ,
2020-11-18 15:24:51 +00:00
default = ' PEDIA ' , verbose_name = ' Wikimedia-Projekt ' ,
help_text = ' Für welches Wikimedia-Projekt möchtest Du Visitenkarten? ' )
data = models . TextField ( max_length = 1000 , verbose_name = ' Persönliche Daten für die Visitenkarten ' , default = ' ' ,
2025-09-28 22:51:13 +00:00
help_text = mark_safe ( ' Bitte gib hier alle persönlichen Daten an, und zwar genau so,<br> \
2020-11-18 15:24:51 +00:00
wie sie ( auch in der entsprechenden Reihenfolge ) auf den Visitenkarten stehen sollen < br > \
( z . B . Vorname Nachname , Benutzer : / Benutzerin : , Benutzer - / - innenname , Anschrift , < br > \
Telefonnummer , E - Mail - Adresse usw . ) . Trenne die einzelnen Angaben durch Zeilenumbrüche . < br > \
Hinweis : Telefonnummern bilden wir üblicherweise im internationalen Format gemäß < br > \
DIN 5008 ab . Als anzugebende E - Mail - Adresse empfehlen wir dir eine Wikimedia - Projekt - < br > \
2025-09-28 22:51:13 +00:00
Adresse , die du ebenfalls beantragen kannst , sofern du nicht bereits eine besitzt . ' ))
2020-10-27 11:37:18 +00:00
variant = models . CharField ( max_length = 5 , choices = BC_VARIANT . items ( ) ,
2020-11-18 15:31:55 +00:00
default = ' NOPIC ' , verbose_name = ' Variante ' ,
2025-08-19 14:08:48 +00:00
help_text = mark_safe ( ' so sehen die Varianten aus: <a href= " https://upload.wikimedia.org/wikipedia/commons/c/cd/Muster_Visitenkarten_WMDE_2018.jpg " > \
2025-10-15 12:46:28 +00:00
mit Bild < / a > < a href = " https://upload.wikimedia.org/wikipedia/commons/d/d3/Muster_Visitenkarte_WMDE.png " > ohne Bild < / a > ' ))
2025-08-18 10:11:44 +00:00
2025-09-28 22:51:13 +00:00
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. ' )
2023-02-27 17:09:28 +00:00
2020-11-18 15:31:55 +00:00
sent_to = models . TextField ( max_length = 1000 , verbose_name = ' Versandadresse ' ,
2025-09-28 22:51:13 +00:00
default = ' ' , help_text = ' Bitte gib den Namen und die vollständige Adresse ein, an welche die Visitenkarten geschickt werden sollen. ' )
2025-08-19 14:08:48 +00:00
send_data_to_print = models . BooleanField ( default = False , verbose_name = mark_safe ( ' Datenweitergabe erlauben ' ) , help_text = mark_safe ( ' Hiermit erlaube ich die Weitergabe meiner Daten (Name, Postadresse) an den von Wikimedia<br> Deutschland ausgewählten Dienstleister (z. B. <a href= " wir-machen-druck.de " >wir-machen-druck.de</a>) zum Zwecke des direkten <br> Versands der Druckerzeugnisse an mich. ' ) )
2025-09-28 22:51:13 +00:00
intern_notes = models . TextField ( max_length = 1000 , blank = True , verbose_name = ' interne Anmerkungen ' )
2025-08-20 10:06:43 +00:00
MODELS = {
TYPE_BIB : Library ,
TYPE_ELIT : ELiterature ,
TYPE_MAIL : Email ,
TYPE_IFG : IFG ,
TYPE_LIT : Literature ,
TYPE_LIST : List ,
TYPE_TRAV : Travel ,
TYPE_SOFT : Software ,
TYPE_VIS : BusinessCard ,
}