Compare commits
	
		
			1 Commits
		
	
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
									
								
								 | 
						1cc2a50df8 | 
							
								
								
									
										80
									
								
								README.md
								
								
								
								
							
							
						
						| 
						 | 
				
			
			@ -7,61 +7,73 @@ meaning "Onboarding, Change, Offboarding"
 | 
			
		|||
 | 
			
		||||
# development
 | 
			
		||||
 | 
			
		||||
- install gettext for instance via "apt install gettext" for translations
 | 
			
		||||
 | 
			
		||||
- set up a virtual environment with virtualenvwrapper or some other
 | 
			
		||||
environment managing tool
 | 
			
		||||
 | 
			
		||||
- install gettext for instance via `apt install gettext` for translations
 | 
			
		||||
- set up a virtual environment with virtualenvwrapper or some other environment managing tool
 | 
			
		||||
- use this environment and do
 | 
			
		||||
 | 
			
		||||
    pip install django django-multiselectfield django-formtools django-allauth requests whitenoise
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
pip install django django-multiselectfield django-formtools django-allauth
 | 
			
		||||
```
 | 
			
		||||
- clone this repository
 | 
			
		||||
- `ln -sr eva/settings_development.py eva/settings.py`
 | 
			
		||||
- initialise your database with `python manage.py migrate`
 | 
			
		||||
- start your development server with `python manage.py runserver`
 | 
			
		||||
 | 
			
		||||
- ln -sr eva/settings_development.py eva/settings.py
 | 
			
		||||
 | 
			
		||||
- initialise your database with
 | 
			
		||||
 | 
			
		||||
    python manage.py migrate
 | 
			
		||||
# oauth
 | 
			
		||||
 | 
			
		||||
- You need to add oauth information in the django backend via .../admin in "Social Accounts"
 | 
			
		||||
 | 
			
		||||
    python manage.py createsuperuser
 | 
			
		||||
 | 
			
		||||
- start your development server with
 | 
			
		||||
 | 
			
		||||
    python manage.py runserver
 | 
			
		||||
 | 
			
		||||
- Configure OAuth: 
 | 
			
		||||
 | 
			
		||||
   + Access the admin interface at http://127.0.0.1:8000/admin
 | 
			
		||||
   + Login with your superuser credentials  
 | 
			
		||||
   + Go to ‘WEBSITE ANZEIGEN’
 | 
			
		||||
 | 
			
		||||
# production
 | 
			
		||||
 | 
			
		||||
- you can use gunicorn as server for example instead of the django development server.
 | 
			
		||||
 | 
			
		||||
- we use whitenoise for serving static files
 | 
			
		||||
 | 
			
		||||
- we still use the development SQLITE database from django
 | 
			
		||||
 | 
			
		||||
do the following in the project main directory:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
  ln -sr eva/settings_production.py eva/settings.py
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
edit /secrets.json to contain something similar to
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
  {
 | 
			
		||||
    "SECRET_KEY": "THIS IS ANOTHER SECRET!"
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
run the following commands:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
  python3 manage.py migrate
 | 
			
		||||
  python3 manage.py collectstatic
 | 
			
		||||
  django-admin compilemessages
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
server starts with
 | 
			
		||||
As root create a file `/etc/systemd/system` (it's already deployed by puppet when the corresponding manifest is applied):
 | 
			
		||||
 | 
			
		||||
  export PYTHONUNBUFFERED=TRUE; nohup gunicorn --forwarded-allow-ips="*" -b '0:8000' eva.wsgi &> logfile &
 | 
			
		||||
```
 | 
			
		||||
# /etc/systemd/system/eva.service
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=gunicorn EVA daemon
 | 
			
		||||
After=network.target
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
Type=simple
 | 
			
		||||
User=eva
 | 
			
		||||
Group=eva
 | 
			
		||||
RuntimeDirectory=eva
 | 
			
		||||
WorkingDirectory=/home/eva/eva
 | 
			
		||||
Environment=PYTHONUNBUFFERED=TRUE
 | 
			
		||||
ExecStart=/usr/bin/gunicorn  --forwarded-allow-ips='*' -b '0:8000' eva.wsgi
 | 
			
		||||
ExecReload=/bin/kill -s HUP $MAINPID
 | 
			
		||||
KillMode=mixed
 | 
			
		||||
TimeoutStopSec=5
 | 
			
		||||
PrivateTmp=true
 | 
			
		||||
 | 
			
		||||
[Install]
 | 
			
		||||
WantedBy=multi-user.target
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Adapt the paths in the file accordingly. Then, still as root, run:
 | 
			
		||||
```
 | 
			
		||||
systemctl enable --now eva.service
 | 
			
		||||
```
 | 
			
		||||
This enables the service to start at boot time and starts it immediately now. The daemon logs to the journal. You can see the last 10 lines by running `systemctl status eva.service` or the whole log by running `journalctl -u eva.service`. The usual switches to manipulate that output are available.
 | 
			
		||||
| 
						 | 
				
			
			@ -1,24 +0,0 @@
 | 
			
		|||
# Generated by Django 4.2.4 on 2023-08-11 10:28
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
import multiselectfield.db.fields
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('austritt', '0003_auto_20220208_0955'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='accounts',
 | 
			
		||||
            field=multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('OTRSWMDE', 'OTRS Ticketsystem'), ('CIVIC1', 'Civic CRM (allgemein)'), ('CIVIC2', 'Civic CRM (Mailings, impliziert allgemein)'), ('WEB', 'www.wikimedia.de (edit)'), ('BLOG', 'blog.wikimedia.de (edit)'), ('FORUM', 'forum.wikimedia.de')], max_length=10, null=True, verbose_name='Zusätzliche Accounts'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='department',
 | 
			
		||||
            field=models.CharField(choices=[('COENG', 'Communitys & Engagement'), ('SOFT', 'Softwareentwicklung'), ('CENT', 'Central'), ('KOMAD', 'Kommunikation & Advocacy'), ('VOR', 'Vorstand')], max_length=5, verbose_name='Bereich'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,32 +0,0 @@
 | 
			
		|||
# Generated by Django 4.2.16 on 2024-11-26 09:10
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('austritt', '0005_alter_employee_accounts'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.RemoveField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='vendor',
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='framework',
 | 
			
		||||
            field=models.CharField(blank=True, max_length=300, null=True, verbose_name='Möchten Sie vom Standard des Frameworks abweichen, und wenn ja, warum?'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='add_to_wikimediade',
 | 
			
		||||
            field=models.CharField(choices=[('NEIN', 'Nein'), ('JA', 'Ja')], default=True, max_length=5, verbose_name='Soll die Person bei Ansprechpartner*innen auf der WMDE-Webseite mit aufgenommen werden?'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='os',
 | 
			
		||||
            field=models.CharField(choices=[('FED', 'Fedora (Standard)'), ('WIN', 'Windows (bitte Begründung angeben)'), ('MOS', 'Mac OS (nur wenn Mac gewählt)'), ('UBU', 'Ubuntu')], default='FED', max_length=3, verbose_name='Betriebssystem'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,32 +0,0 @@
 | 
			
		|||
# Generated by Django 4.2.17 on 2024-12-17 14:44
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('austritt', '0006_remove_employee_vendor_employee_framework_and_more'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.RemoveField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='screen',
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='add_to_wikimediade',
 | 
			
		||||
            field=models.CharField(choices=[('NEIN', 'Nein'), ('JA', 'Ja')], max_length=5, verbose_name='Soll die Person bei Ansprechpartner*innen auf der WMDE-Webseite mit aufgenommen werden?'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='framework',
 | 
			
		||||
            field=models.CharField(blank=True, max_length=300, null=True, verbose_name='Möchten Sie vom Standard des Frameworks (Laptop) abweichen, und wenn ja, warum?'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='id',
 | 
			
		||||
            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,87 +0,0 @@
 | 
			
		|||
from django.db import models
 | 
			
		||||
from multiselectfield import MultiSelectField
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from eintritt.models import Employee
 | 
			
		||||
 | 
			
		||||
# ATTENTION!!!
 | 
			
		||||
# No key should be used twice in any of these dicts because of the
 | 
			
		||||
# suboptimal implementation in views.EvaFormView.beautify_data()
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
DEPARTMENT_CHOICES = {'COENG': _('Communitys & Engagement'),
 | 
			
		||||
                      'SOFT': _('Softwareentwicklung'),
 | 
			
		||||
                      'CENT': 'Central',
 | 
			
		||||
                      'KOMAD': _('Kommunikation & Advocacy'),
 | 
			
		||||
                      'VOR': _('Vorstand'),}
 | 
			
		||||
 | 
			
		||||
#VENDOR_CHOICES = {'STANDARD': 'Dell Latitude',
 | 
			
		||||
#                'LENOVO': 'Lenovo Thinkpad',
 | 
			
		||||
#                'MAC': _('Mac (nur für Grafiker_innen)')}
 | 
			
		||||
 | 
			
		||||
OS_CHOICES = {'FED': 'Fedora (Standard)',
 | 
			
		||||
              'WIN': _('Windows (bitte Begründung angeben)'),
 | 
			
		||||
              'MOS': _('Mac OS (nur wenn Mac gewählt)'),
 | 
			
		||||
              'UBU': _('Ubuntu')}
 | 
			
		||||
 | 
			
		||||
LANG_CHOICES = {'GER': 'Deutsch',
 | 
			
		||||
                'ENG': 'English',}
 | 
			
		||||
 | 
			
		||||
KEYBOARD_CHOICES = {'DE': 'Deutsch',
 | 
			
		||||
                    'US': 'USA',
 | 
			
		||||
                    'OT': _('Anderes (Bitte unten angeben)')}
 | 
			
		||||
 | 
			
		||||
ACCOUNT_CHOICES = {'OTRSWMDE': 'OTRS Ticketsystem',
 | 
			
		||||
                   'CIVIC1': _('Civic CRM (allgemein)'),
 | 
			
		||||
                   'CIVIC2': _("Civic CRM (Mailings, impliziert allgemein)"),
 | 
			
		||||
                   'WEB': 'www.wikimedia.de (edit)',
 | 
			
		||||
                   'BLOG': 'blog.wikimedia.de (edit)',
 | 
			
		||||
                   'FORUM': 'forum.wikimedia.de',
 | 
			
		||||
                   }
 | 
			
		||||
 | 
			
		||||
TRANSPONDER_CHOICES = {'NORM': _('Allgemeiner Transponder'),
 | 
			
		||||
                       'SPECIAL': _('Besondere Schließungen (bitte angeben)'),
 | 
			
		||||
                       'NOTRANS': _('Kein Transponder'),}
 | 
			
		||||
 | 
			
		||||
JANEIN_CHOICES = {'NEIN': ('Nein'),
 | 
			
		||||
           'JA': _('Ja'),}
 | 
			
		||||
 | 
			
		||||
class Employee(models.Model):
 | 
			
		||||
 | 
			
		||||
    # email adress of user. should not be necessary if we use openauth one day
 | 
			
		||||
    # usermail = models.EmailField(max_length=50, verbose_name="Deine Mailadresse (Ansprechpartner_in)", default='bestechefin@wikimedia.de')
 | 
			
		||||
 | 
			
		||||
    # personal data
 | 
			
		||||
    firstname = models.CharField(max_length=50, verbose_name=_("Vorname"))
 | 
			
		||||
    lastname = models.CharField(max_length=50, verbose_name=_("Nachname"))
 | 
			
		||||
    # intern = models.BooleanField(verbose_name='Interne_r Mitarbeiter_in?', default=True)
 | 
			
		||||
    department = models.CharField(max_length=5, choices=DEPARTMENT_CHOICES.items(), verbose_name=_('Bereich'))
 | 
			
		||||
    team = models.CharField(max_length=50, null=True, blank=True) # TODO? better with choices?
 | 
			
		||||
    add_to_wikimediade = models.CharField(max_length=5, choices=JANEIN_CHOICES.items(), verbose_name=_("Soll die Person bei Ansprechpartner*innen auf der WMDE-Webseite mit aufgenommen werden?"))
 | 
			
		||||
 | 
			
		||||
    # general work related stuff
 | 
			
		||||
    firstdate_employment = models.DateField(null=True, verbose_name=_("Erster Arbeitstag"))
 | 
			
		||||
    firstdate_presence = models.DateField(null=True, verbose_name=_("Erster Tag der Anwesenheit in der Geschäftsstelle"))
 | 
			
		||||
    jobdescription_german = models.CharField(null=True, max_length=100, verbose_name="Stellenbezeichnung(deutsch)")
 | 
			
		||||
    jobdescription_english = models.CharField(null=True, max_length=100, verbose_name="Job description(english)")
 | 
			
		||||
    works_in_gs = models.BooleanField(verbose_name=_('Braucht Arbeitsplatz in der Geschäftsstelle?)'), default=True)
 | 
			
		||||
    desk = models.CharField(max_length=100, null=True, blank=True, verbose_name=_("Wo soll der Arbeitsplatz sein?"))
 | 
			
		||||
 | 
			
		||||
    # IT related stuff
 | 
			
		||||
    #vendor = models.CharField(max_length=8, choices=VENDOR_CHOICES.items(), default='STANDARD', verbose_name=_('Hersteller'))
 | 
			
		||||
    framework = models.CharField(max_length=300, null=True, blank=True, verbose_name=_("Möchten Sie vom Standard des Frameworks (Laptop) abweichen, und wenn ja, warum?"))
 | 
			
		||||
    os = models.CharField(max_length=3, choices=OS_CHOICES.items(), default='FED', verbose_name=_('Betriebssystem'))
 | 
			
		||||
    #screen = models.BooleanField(default=False, verbose_name=_('Zusätzlicher Monitor? Einer ist standard.'))
 | 
			
		||||
    mobile = models.BooleanField(max_length=6, default=False, verbose_name=_('Diensttelefon (Handy)'))
 | 
			
		||||
    landline = models.BooleanField(default = False, verbose_name=_('Festnetznummer (Sipgate)'))
 | 
			
		||||
    # sim = models.BooleanField(default=False, verbose_name="Mobilfunkvertrag")
 | 
			
		||||
    keyboard = models.CharField(max_length=2, choices=KEYBOARD_CHOICES.items(), default='DE', verbose_name=_("Tastaturlayout"))
 | 
			
		||||
    comment = models.TextField(max_length=500, null=True, blank=True, verbose_name=_("zusätzliche IT-Anforderungen"))
 | 
			
		||||
    language = models.CharField(max_length=3, choices=LANG_CHOICES.items(), default="GER", verbose_name=_("Sprache für Onboarding"))
 | 
			
		||||
    accounts = MultiSelectField(choices=ACCOUNT_CHOICES.items(), max_length=40, null=True, blank=True, verbose_name=_("Zusätzliche Accounts"))
 | 
			
		||||
    lists = models.CharField(max_length=100, null=True, blank=True, verbose_name=_("Zusätzliche Mailinglisten"))
 | 
			
		||||
    rebu2go = models.BooleanField(verbose_name=_("Rebu2Go-Zugang benötigt?"), default=False)
 | 
			
		||||
 | 
			
		||||
    # office related stuff
 | 
			
		||||
    transponder = models.CharField(max_length=7, choices=TRANSPONDER_CHOICES.items(), default='NORM')
 | 
			
		||||
    special = models.TextField(max_length=500, null=True, blank=True, verbose_name=_("Besondere Schließungen hier eintragen"))
 | 
			
		||||
    post_office_box = models.BooleanField(default=True, verbose_name=_('Postfach am Empfang benötigt?'))
 | 
			
		||||
| 
						 | 
				
			
			@ -1,61 +0,0 @@
 | 
			
		|||
# temporary setting while change and exit is not yet fully implemented
 | 
			
		||||
ONLY_ONBOARDING = True
 | 
			
		||||
 | 
			
		||||
# sender mail adress also used for MAILTEST mode
 | 
			
		||||
EVA_MAIL = 'it-support@wikimedia.de'
 | 
			
		||||
 | 
			
		||||
# these Fields should be included in every mail
 | 
			
		||||
BASIC_DATA = ['firstname', 'lastname', 'firstdate_employment', 'firstdate_presence',
 | 
			
		||||
              'jobdescription_german', 'jobdescription_english',]
 | 
			
		||||
 | 
			
		||||
# for every department: 'MAIL' => mail adress, 'DATA': additional fields to include
 | 
			
		||||
# also one copy with all fields to the person filling the form.
 | 
			
		||||
MAILS = {
 | 
			
		||||
            'IT': {
 | 
			
		||||
                'MAIL': 'wmde-it@wikimedia.de',
 | 
			
		||||
                'DATA': [
 | 
			
		||||
                    'laptop', 'os', 'comment', 'email', 'landline', 'lists', 'mobile',
 | 
			
		||||
                    'department', 'accounts', 'language', 'screen', 'works_in_gs', 'desk',
 | 
			
		||||
                    'keyboard',
 | 
			
		||||
                ],
 | 
			
		||||
            },
 | 
			
		||||
            'OFFICE': {
 | 
			
		||||
                'MAIL': 'office@wikimedia.de',
 | 
			
		||||
                'DATA': [
 | 
			
		||||
                    'transponder', 'special', 'post_office_box', 'mobile',
 | 
			
		||||
                    'works_in_gs', 'desk',
 | 
			
		||||
                ],
 | 
			
		||||
            },
 | 
			
		||||
            'KOMM': {
 | 
			
		||||
                'MAIL': 'presse@wikimedia.de',
 | 
			
		||||
                'DATA': [
 | 
			
		||||
                    'department', 'team', 'add_to_wikimediade'
 | 
			
		||||
                ],
 | 
			
		||||
            },
 | 
			
		||||
            'CENTRAL': {
 | 
			
		||||
                'MAIL': 'anna.noelte@wikimedia.de',
 | 
			
		||||
                'DATA': [
 | 
			
		||||
                    'department', 'team', 'language', 'mobile', 'rebu2go'
 | 
			
		||||
                ],
 | 
			
		||||
            },
 | 
			
		||||
            'HR': {
 | 
			
		||||
                'MAIL': 'personal@wikimedia.de',
 | 
			
		||||
                'DATA': [
 | 
			
		||||
                    'department', 'team', 'language',
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
            'FINANCE': {
 | 
			
		||||
                'MAIL': 'claudia.langrock@wikimedia.de',
 | 
			
		||||
                'DATA': [
 | 
			
		||||
                    'rebu2go'
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
            'SUBMITTER': {
 | 
			
		||||
                'MAIL': 'submitter@wikimedia.de',
 | 
			
		||||
                'DATA': [
 | 
			
		||||
                    'department', 'team', 'add_to_wikimediade', 'remote', 'desk', 'vendor',
 | 
			
		||||
                    'os', 'screen', 'mobile', 'landline', 'keyboard', 'comment', 'language',
 | 
			
		||||
                    'accounts', 'lists', 'rebu2go', 'transponder', 'special', 'post_office_box'
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -1,110 +0,0 @@
 | 
			
		|||
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% load static  %}
 | 
			
		||||
 | 
			
		||||
<script type="text/javascript" src="{% static 'admin/js/core.js' %}"></script>
 | 
			
		||||
<script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script>
 | 
			
		||||
<script type="text/javascript" src="{% static 'admin/js/jquery.init.js' %}"></script>
 | 
			
		||||
 | 
			
		||||
{{ form.media }}
 | 
			
		||||
 | 
			
		||||
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/base.css' %}" />
 | 
			
		||||
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/widgets.css' %}" />
 | 
			
		||||
 | 
			
		||||
{% load socialaccount %}
 | 
			
		||||
{% if user.is_authenticated %}
 | 
			
		||||
{% block content %}
 | 
			
		||||
 | 
			
		||||
{% get_current_language as LANGUAGE_CODE %}
 | 
			
		||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
 | 
			
		||||
<center>
 | 
			
		||||
  <style>
 | 
			
		||||
    ul > li {
 | 
			
		||||
      list-style-type: none;
 | 
			
		||||
    }
 | 
			
		||||
    ul {
 | 
			
		||||
      padding-left: 10;
 | 
			
		||||
    }
 | 
			
		||||
    label.required::after {
 | 
			
		||||
      content: ' *';
 | 
			
		||||
      color: red;
 | 
			
		||||
    }
 | 
			
		||||
  </style>
 | 
			
		||||
 | 
			
		||||
  <img src="{% static 'evapp/logo.png' %}" />
 | 
			
		||||
  {% if TESTMODE %}
 | 
			
		||||
  <h1 style="background-color:red;color:white">{% translate "WARNUNG! Test-MODUS aktiviert. Es werden keine Mails verschickt!" %}</h1>
 | 
			
		||||
  {% endif %}
 | 
			
		||||
  <h1>
 | 
			
		||||
    E (V A) - Austritt, (Veränderung, Austritt)<p>
 | 
			
		||||
  </h1>{% translate "Du bist eingeloggt als" %} {{ user.email }}
 | 
			
		||||
 | 
			
		||||
  <h2>
 | 
			
		||||
  <p> {% translate "Schritt" %} {{ wizard.steps.step1 }} {% translate "von" %} {{ wizard.steps.count }}</p>
 | 
			
		||||
  <p>{% if wizard.steps.step1 == 1 %}
 | 
			
		||||
    {% translate "Angaben zur Person" %} {% endif %}
 | 
			
		||||
{% if choice == 'IN' %}
 | 
			
		||||
  {% if wizard.steps.step1 == 2 %}
 | 
			
		||||
    {% translate "Angaben zum neuen Arbeitsverhältnis" %}
 | 
			
		||||
  {% elif wizard.steps.step1 == 3 %}
 | 
			
		||||
    {% translate "IT-relevante Angaben" %}
 | 
			
		||||
  {% elif wizard.steps.step1 == 4 %}
 | 
			
		||||
    {% translate "Office-relevante Angaben" %}
 | 
			
		||||
  {% endif %}
 | 
			
		||||
{% else %}
 | 
			
		||||
  {% if wizard.steps.step1 == 2 %}
 | 
			
		||||
    Veränderungsrelevante Angaben
 | 
			
		||||
  {% endif %}
 | 
			
		||||
{% endif %}
 | 
			
		||||
{% if datatable == True %}
 | 
			
		||||
    {% translate "Bestätigungsschritt" %}
 | 
			
		||||
{% endif %}
 | 
			
		||||
  </p>
 | 
			
		||||
  </h2>
 | 
			
		||||
 | 
			
		||||
  {% if  datatable == True %}
 | 
			
		||||
    <table  id="toggle-heading">
 | 
			
		||||
    {% for key, value in data.items %}
 | 
			
		||||
      <tr><th>{{ key }}</th><th>{{ value }}</th></tr>
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
    </table>
 | 
			
		||||
  {% endif %}
 | 
			
		||||
 | 
			
		||||
  <form action="" method="post">
 | 
			
		||||
    {% csrf_token %}
 | 
			
		||||
    <table>
 | 
			
		||||
    {% comment %}
 | 
			
		||||
        comment this back in if you want to use CHANGE and EXIT process
 | 
			
		||||
        {% if wizard.steps.step1 > 1 %}
 | 
			
		||||
          Du hast den Prozess "{{choice_string}}" ausgewählt.
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    {% endcomment %}
 | 
			
		||||
    {{ wizard.management_form }}
 | 
			
		||||
    {% if wizard.form.forms %}
 | 
			
		||||
      {{ wizard.form.management_form }}
 | 
			
		||||
      {% for form in wizard.form.forms %}
 | 
			
		||||
        {{ form }}
 | 
			
		||||
      {% endfor %}
 | 
			
		||||
    {% else %}
 | 
			
		||||
      {{ wizard.form }}
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    </table>
 | 
			
		||||
    <p>
 | 
			
		||||
      <span style="color: red">*</span> {% translate "Pflichtfeld" %}
 | 
			
		||||
    <p>
 | 
			
		||||
    {% if wizard.steps.prev %}
 | 
			
		||||
    <button formnovalidate="formnovalidate" name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">{% translate "Zurück" %}</button>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    {% if datatable == True %}
 | 
			
		||||
      <button type="submit" value="{% trans "Weiter" %}">{% translate "Abschicken" %}</button>
 | 
			
		||||
    {% else %}
 | 
			
		||||
      <button type="submit" value="{% trans "Weiter" %}">{% translate "Weiter" %}</button>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
  </form>
 | 
			
		||||
  <p>
 | 
			
		||||
  <a href="{% url 'account_logout' %}">{% translate "logout" %}</a>
 | 
			
		||||
</center>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
{% else %}
 | 
			
		||||
<a href="{% provider_login_url 'nextcloud' %}">{% translate "Bitte einloggen!" %}</a>
 | 
			
		||||
{% endif %}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,217 +0,0 @@
 | 
			
		|||
from smtplib import SMTPException
 | 
			
		||||
import collections
 | 
			
		||||
 | 
			
		||||
from django.views.generic.edit import CreateView
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.http import HttpResponse, HttpResponseRedirect
 | 
			
		||||
from django.core.mail import send_mail, BadHeaderError
 | 
			
		||||
from django.template.loader import get_template
 | 
			
		||||
from formtools.wizard.views import CookieWizardView
 | 
			
		||||
from django.shortcuts import render
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.contrib.auth.mixins import LoginRequiredMixin
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from .models import Employee, DEPARTMENT_CHOICES, OS_CHOICES, \
 | 
			
		||||
                    LANG_CHOICES, ACCOUNT_CHOICES, TRANSPONDER_CHOICES, KEYBOARD_CHOICES, JANEIN_CHOICES
 | 
			
		||||
from .forms import PersonalForm, WorkingForm, ITForm, OfficeForm, DummyForm,\
 | 
			
		||||
                   ChangeForm, TYPE_CHOICES
 | 
			
		||||
from .settings import MAILS, EVA_MAIL, BASIC_DATA, ONLY_ONBOARDING
 | 
			
		||||
 | 
			
		||||
def success(request):
 | 
			
		||||
    return HttpResponse(f"Vielen Dank! Du hast E.V.A. erfolgreich ausgefüllt. Die Mails an die Abteilungen wurden versendet. Kopien gehen an {request.user.email}.")
 | 
			
		||||
 | 
			
		||||
def long_process(wizard):
 | 
			
		||||
    '''this method is called via urls.py to determine if a form is part of the IN-Process'''
 | 
			
		||||
 | 
			
		||||
    if ONLY_ONBOARDING:
 | 
			
		||||
        wizard.set_choice('IN')
 | 
			
		||||
        return True
 | 
			
		||||
    else:
 | 
			
		||||
        data = wizard.get_cleaned_data_for_step('0') or {}
 | 
			
		||||
        # print(data)
 | 
			
		||||
        if data.get('choice') != 'CHANGE':
 | 
			
		||||
            wizard.set_choice('IN')
 | 
			
		||||
            # print('PROZESS IN')
 | 
			
		||||
            return True
 | 
			
		||||
        else:
 | 
			
		||||
            wizard.set_choice('CHANGE')
 | 
			
		||||
            # print('PROZESS NOT IN')
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
def change_process(wizard):
 | 
			
		||||
    ''' this method is called via urls.py to determine if the form is part of the change process'''
 | 
			
		||||
    # print('CHANGE PROZESS')
 | 
			
		||||
    return not long_process(wizard)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EvaFormView(LoginRequiredMixin, CookieWizardView):
 | 
			
		||||
    template_name = 'austritt/employee_form.html'
 | 
			
		||||
    form_list = [PersonalForm, WorkingForm, ITForm, OfficeForm, ChangeForm, DummyForm]
 | 
			
		||||
    instance = None
 | 
			
		||||
    choice = 'IN'
 | 
			
		||||
 | 
			
		||||
    # maybe we dont need this, if *_process() would be class methods,
 | 
			
		||||
    # but unsure if this would work fine with the entries in urls.py
 | 
			
		||||
    def set_choice(self, c):
 | 
			
		||||
        self.choice = c
 | 
			
		||||
 | 
			
		||||
    def generate_email(self, data):
 | 
			
		||||
        (first, *_) = data['firstname'].split(maxsplit=1)
 | 
			
		||||
        (last, *_) = data['lastname'].split(maxsplit=1)
 | 
			
		||||
        name = first + '.' + last
 | 
			
		||||
        #if not data['intern']:
 | 
			
		||||
        #    mail = name + '_ext@wikimedia.de'
 | 
			
		||||
        #else:
 | 
			
		||||
        mail = name + '@wikimedia.de'
 | 
			
		||||
        data['email'] = mail
 | 
			
		||||
 | 
			
		||||
    def get_all_cleaned_data(self):
 | 
			
		||||
        '''this method deletes data which is only used temporary and is not in the modell,
 | 
			
		||||
        it also changes the mail adress of the employee in some circumstances'''
 | 
			
		||||
 | 
			
		||||
        data = super().get_all_cleaned_data()
 | 
			
		||||
        self.generate_email(data)
 | 
			
		||||
 | 
			
		||||
        # print("delete CHOICE FROM DATA")
 | 
			
		||||
        if 'choice' in data:
 | 
			
		||||
            del data['choice']
 | 
			
		||||
        return data
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, form, **kwargs):
 | 
			
		||||
        '''this method is called to give context data to the template'''
 | 
			
		||||
 | 
			
		||||
        #print('GETCONTEXT')
 | 
			
		||||
        context = super().get_context_data(form=form, **kwargs)
 | 
			
		||||
        testmode = settings.DEBUG or settings.MAILTEST
 | 
			
		||||
        context.update({'choice': self.choice,
 | 
			
		||||
                        'choice_string': TYPE_CHOICES[self.choice],
 | 
			
		||||
                        'TESTMODE': testmode})
 | 
			
		||||
 | 
			
		||||
        # deliver context for forms if we are in the last step
 | 
			
		||||
        if (self.steps.step1 == 5 or (self.choice != 'IN' and self.steps.step1 == 3)):
 | 
			
		||||
            context.update({'data': self.beautify_data(self.get_all_cleaned_data()),
 | 
			
		||||
                            'datatable': True,})
 | 
			
		||||
        return context
 | 
			
		||||
 | 
			
		||||
    def get_form_instance(self,step):
 | 
			
		||||
        ''' this method assures, that we use the same model instance for all steps'''
 | 
			
		||||
 | 
			
		||||
        if self.instance == None:
 | 
			
		||||
            self.instance = Employee()
 | 
			
		||||
        return self.instance
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def done(self, form_list, **kwargs):
 | 
			
		||||
        '''this method is called from CookieWizardView after all forms are filled'''
 | 
			
		||||
 | 
			
		||||
        print ('INSTANCE_DICT')
 | 
			
		||||
        print(self.instance_dict)
 | 
			
		||||
 | 
			
		||||
        # save data to database
 | 
			
		||||
        for form in form_list:
 | 
			
		||||
            form.save()
 | 
			
		||||
 | 
			
		||||
        # send data to departments
 | 
			
		||||
        for dep in MAILS:
 | 
			
		||||
            response = self.send_mail_to_department(dep)
 | 
			
		||||
 | 
			
		||||
        if not settings.DEBUG:
 | 
			
		||||
            self.instance.delete()
 | 
			
		||||
 | 
			
		||||
        if response:
 | 
			
		||||
            return response
 | 
			
		||||
        else:
 | 
			
		||||
            return HttpResponseRedirect('success')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def send_mail_to_department(self, department):
 | 
			
		||||
        'send a mail to the given department with the nececcary notifications'
 | 
			
		||||
 | 
			
		||||
        print(f'send mail to department {department}...')
 | 
			
		||||
 | 
			
		||||
        contact = self.request.user.email
 | 
			
		||||
        data = self.get_all_cleaned_data()
 | 
			
		||||
        # some data should be in every mail
 | 
			
		||||
        newdata = {k: v for k, v in data.items() if (k in BASIC_DATA)}
 | 
			
		||||
        # only the relevant data should be in the context
 | 
			
		||||
        newdata.update({k: v for k, v in data.items() if (k in MAILS[department]['DATA'])})
 | 
			
		||||
 | 
			
		||||
        context = {'data': self.beautify_data(newdata), 'contact': contact}
 | 
			
		||||
        firstname = data['firstname']
 | 
			
		||||
        lastname = data['lastname']
 | 
			
		||||
        firstday = data['firstdate_employment']
 | 
			
		||||
        try:
 | 
			
		||||
             mail_template = get_template(f'evapp/department_mail.txt')
 | 
			
		||||
             if settings.MAILTEST:
 | 
			
		||||
                 send_mail(
 | 
			
		||||
                 f'EVA: Neuzugang {firstname} {lastname} {firstday} (MAILTEST)',
 | 
			
		||||
                 mail_template.render(context),
 | 
			
		||||
                 EVA_MAIL,
 | 
			
		||||
                 [EVA_MAIL],
 | 
			
		||||
                 fail_silently=False)
 | 
			
		||||
             elif department != "SUBMITTER":
 | 
			
		||||
                 send_mail(
 | 
			
		||||
                 f'EVA: Neuzugang {firstname} {lastname} {firstday}',
 | 
			
		||||
                 mail_template.render(context),
 | 
			
		||||
                 EVA_MAIL,
 | 
			
		||||
                 [MAILS[department]['MAIL']],
 | 
			
		||||
                 fail_silently=False)
 | 
			
		||||
             else:
 | 
			
		||||
                 send_mail(
 | 
			
		||||
                 f'EVA: Neuzugang {firstname} {lastname} {firstday}',
 | 
			
		||||
                 mail_template.render(context),
 | 
			
		||||
                 EVA_MAIL,
 | 
			
		||||
                 [contact],
 | 
			
		||||
                 fail_silently=False)
 | 
			
		||||
        except BadHeaderError as error:
 | 
			
		||||
            print(error)
 | 
			
		||||
            self.instance.delete()
 | 
			
		||||
            return HttpResponse(f'{error}<p>Invalid header found. Data not saved!')
 | 
			
		||||
        except SMTPException as error:
 | 
			
		||||
            print(error)
 | 
			
		||||
            self.instance.delete()
 | 
			
		||||
            return HttpResponse(f'{error}<p>Error in sending mails (propably wrong adress?). Data not saved!')
 | 
			
		||||
        except Exception as error:
 | 
			
		||||
            print(error)
 | 
			
		||||
            # self.instance.delete()
 | 
			
		||||
            return HttpResponse(f'{error}<p>Error in sending mails. Data not saved! Please contact ' + EVA_MAIL)
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def beautify_data(self, data):
 | 
			
		||||
        '''  # use long form for contextdata instead of short form if available
 | 
			
		||||
            #
 | 
			
		||||
            # ATTENTION!
 | 
			
		||||
            # This implementation works only for unique keys over all of these dicts from model.py
 | 
			
		||||
            #
 | 
			
		||||
        '''
 | 
			
		||||
 | 
			
		||||
        # update values in data dictionary with keys from *_CHOICES if present there
 | 
			
		||||
        choices = {**DEPARTMENT_CHOICES, **TRANSPONDER_CHOICES,
 | 
			
		||||
                   **OS_CHOICES, **LANG_CHOICES, **KEYBOARD_CHOICES}
 | 
			
		||||
        data.update({k:choices[v] for k,v in data.items() \
 | 
			
		||||
            if isinstance(v,collections.abc.Hashable) \
 | 
			
		||||
            and v in choices})
 | 
			
		||||
 | 
			
		||||
        # replace values in accounts array from *_CHOICES
 | 
			
		||||
        if 'accounts' in data:
 | 
			
		||||
            data['accounts'] = [ACCOUNT_CHOICES[c] for c in data['accounts']]
 | 
			
		||||
 | 
			
		||||
        # replace keys in data dictionary with verbose_name
 | 
			
		||||
        # a bit ugly workaround here: we need to store 'email' away, because it es not in the modell
 | 
			
		||||
        mail = ''
 | 
			
		||||
        if 'email' in data:
 | 
			
		||||
            mail = data.pop('email')
 | 
			
		||||
        newdata = {self.instance._meta.get_field(k).verbose_name.title() : v for k,v in data.items()}
 | 
			
		||||
        if mail:
 | 
			
		||||
            newdata['Email'] = mail
 | 
			
		||||
 | 
			
		||||
        # translate booleans
 | 
			
		||||
        newdata.update({k:'Ja' for k,v in newdata.items() if isinstance(v,bool) and v == True})
 | 
			
		||||
        newdata.update({k:'Nein' for k,v in newdata.items() if isinstance(v,bool) and v == False})
 | 
			
		||||
        # handle some special data types
 | 
			
		||||
        newdata.update({k:'' for k,v in newdata.items() if v == None})
 | 
			
		||||
        newdata.update({k:'' for k,v in newdata.items() if v == []})
 | 
			
		||||
 | 
			
		||||
        return newdata
 | 
			
		||||
| 
						 | 
				
			
			@ -1,10 +0,0 @@
 | 
			
		|||
# import Django´s admin interface module
 | 
			
		||||
from django.contrib import admin
 | 
			
		||||
# import the Employee model from the current app
 | 
			
		||||
from .models import Employee
 | 
			
		||||
 | 
			
		||||
# Register the Employee model with the Django admin site
 | 
			
		||||
# This makes the Employee model manageable through the admin interface
 | 
			
		||||
admin.site.register([
 | 
			
		||||
                     Employee,
 | 
			
		||||
                     ])
 | 
			
		||||
| 
						 | 
				
			
			@ -1,7 +0,0 @@
 | 
			
		|||
# import django´s base AppConfig class
 | 
			
		||||
from django.apps import AppConfig
 | 
			
		||||
 | 
			
		||||
# define the configuration class for the 'evapp' application
 | 
			
		||||
class EintrittConfig(AppConfig):
 | 
			
		||||
    # give the app a name 
 | 
			
		||||
    name = 'eintritt'
 | 
			
		||||
| 
						 | 
				
			
			@ -1,65 +0,0 @@
 | 
			
		|||
from django.db import models
 | 
			
		||||
from django.forms import ModelForm, DateInput, Form, ChoiceField, RadioSelect
 | 
			
		||||
from django.core.exceptions import ValidationError
 | 
			
		||||
 | 
			
		||||
from .models import Employee
 | 
			
		||||
 | 
			
		||||
# class EmployeeForm(ModelForm):
 | 
			
		||||
#     class Meta:
 | 
			
		||||
#         model = Employee
 | 
			
		||||
#         fields = '__all__'
 | 
			
		||||
#         widgets = {'firstdate_employment': DateInput(attrs={'type': 'date'}),
 | 
			
		||||
#                    'firstdate_presence': DateInput(attrs={'type': 'date'}),}
 | 
			
		||||
 | 
			
		||||
class DummyForm(ModelForm):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Employee
 | 
			
		||||
        fields = []
 | 
			
		||||
 | 
			
		||||
class EvaForm(ModelForm):
 | 
			
		||||
    '''this base class provides the required css class for all forms'''
 | 
			
		||||
    required_css_class = 'required'
 | 
			
		||||
 | 
			
		||||
TYPE_CHOICES = {'IN': 'Eintritt', 'CHANGE': 'Veränderung', 'OUT': 'Austritt'}
 | 
			
		||||
 | 
			
		||||
# Form to capture basic personal and department-related information
 | 
			
		||||
class PersonalForm(EvaForm):
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Employee
 | 
			
		||||
        fields = ['firstname', 'lastname', 'department', 'team', 'add_to_wikimediade',]
 | 
			
		||||
# Form to capture working conditions and job description
 | 
			
		||||
class WorkingForm(EvaForm):
 | 
			
		||||
 | 
			
		||||
    def clean(self):
 | 
			
		||||
        data = self.cleaned_data
 | 
			
		||||
        if data['works_in_gs'] and data['desk'] is None:
 | 
			
		||||
            raise ValidationError('Wer nicht remote arbeitet braucht einen Schreibtisch!')
 | 
			
		||||
        return data
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Employee
 | 
			
		||||
        fields = ['firstdate_employment', 'firstdate_presence', 'jobdescription_german',
 | 
			
		||||
                  'jobdescription_english', 'works_in_gs', 'desk',]
 | 
			
		||||
        widgets = {'firstdate_employment': DateInput(attrs={'type': 'date'}),
 | 
			
		||||
                   'firstdate_presence': DateInput(attrs={'type': 'date'}),}
 | 
			
		||||
 | 
			
		||||
class ITForm(EvaForm):
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Employee
 | 
			
		||||
        fields = [
 | 
			
		||||
            'framework', 'os', 'keyboard', 'mobile', 'landline',
 | 
			
		||||
            'comment', 'language', 'accounts', 'lists', 'rebu2go' ]
 | 
			
		||||
 | 
			
		||||
class OfficeForm(EvaForm):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Employee
 | 
			
		||||
        fields = ['transponder', 'special', 'post_office_box',]
 | 
			
		||||
 | 
			
		||||
class ChangeForm(EvaForm):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Employee
 | 
			
		||||
        fields = ['firstdate_employment', 'jobdescription_german', 'jobdescription_english',
 | 
			
		||||
                  'desk', 'comment', 'accounts', 'lists', 'transponder']
 | 
			
		||||
        widgets = {'firstdate_employment': DateInput(attrs={'type': 'date'}),}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,46 +0,0 @@
 | 
			
		|||
# Generated by Django 3.1.4 on 2021-09-13 12:41
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
import multiselectfield.db.fields
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    initial = True
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name='Employee',
 | 
			
		||||
            fields=[
 | 
			
		||||
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
			
		||||
                ('firstname', models.CharField(max_length=50, verbose_name='Vorname')),
 | 
			
		||||
                ('lastname', models.CharField(max_length=50, verbose_name='Nachname')),
 | 
			
		||||
                ('department', models.CharField(choices=[('PROG', 'Programme'), ('SOFT', 'Softwareentwicklung'), ('CENT', 'Central'), ('VOR', 'Vorstand')], max_length=5, verbose_name='Bereich')),
 | 
			
		||||
                ('team', models.CharField(blank=True, max_length=20, null=True)),
 | 
			
		||||
                ('add_to_wikimediade', models.BooleanField(default=False, verbose_name='Soll auf wikimedia.de irgendwo stehen?')),
 | 
			
		||||
                ('firstdate_employment', models.DateField(null=True, verbose_name='Erster Arbeitstag')),
 | 
			
		||||
                ('firstdate_presence', models.DateField(null=True, verbose_name='Erster Tag der Anwesenheit in der Geschäftsstelle')),
 | 
			
		||||
                ('jobdescription_german', models.CharField(max_length=100, null=True, verbose_name='Stellenbezeichnung(deutsch)')),
 | 
			
		||||
                ('jobdescription_english', models.CharField(max_length=100, null=True, verbose_name='Job description(english)')),
 | 
			
		||||
                ('remote', models.BooleanField(default=True, verbose_name='Braucht Arbeitsplatz in der Geschäftsstelle?')),
 | 
			
		||||
                ('desk', models.CharField(blank=True, max_length=100, null=True, verbose_name='Wo soll der Arbeitsplatz sein?')),
 | 
			
		||||
                ('vendor', models.CharField(choices=[('STANDARD', 'Dell Latitude'), ('LENOVO', 'Lenovo Thinkpad'), ('MAC', 'Mac (nur für Grafiker_innen)')], default='STANDARD', max_length=8, verbose_name='Hersteller')),
 | 
			
		||||
                ('os', models.CharField(choices=[('UBU', 'Ubuntu (Standard)'), ('WIN', 'Windows (bitte Begründung angeben)'), ('MOS', 'Mac OS (nur wenn Mac gewählt)')], default='UBU', max_length=3, verbose_name='Betriebssystem')),
 | 
			
		||||
                ('screen', models.BooleanField(default=False, verbose_name='Zusätzlicher Monitor? Einer ist standard.')),
 | 
			
		||||
                ('mobile', models.BooleanField(default=False, max_length=6, verbose_name='Diensttelefon (Handy)')),
 | 
			
		||||
                ('landline', models.BooleanField(default=False, verbose_name='Festnetznummer (Sipgate)')),
 | 
			
		||||
                ('keyboard', models.CharField(choices=[('DE', 'Deutsch'), ('US', 'USA'), ('OT', 'Anderes (Bitte unten angeben)')], default='DE', max_length=2, verbose_name='Tastaturlayout')),
 | 
			
		||||
                ('comment', models.TextField(blank=True, max_length=500, null=True, verbose_name='zusätzliche IT-Anforderungen')),
 | 
			
		||||
                ('language', models.CharField(choices=[('GER', 'Deutsch'), ('ENG', 'English')], default='GER', max_length=3, verbose_name='Sprache für Onboarding')),
 | 
			
		||||
                ('accounts', multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('OTRSWMDE', 'OTRS Ticketsystem'), ('CIVIC1', 'Civic CRM (allgemein)'), ('CIVIC2', 'Civic CRM (Mailings, impliziert allgemein)'), ('WEB', 'www.wikimedia.de (edit)'), ('BLOG', 'blog.wikimedia.de (edit)'), ('FORUM', 'forum.wikimedia.de')], max_length=37, null=True, verbose_name='Zusätzliche Accounts')),
 | 
			
		||||
                ('lists', models.CharField(blank=True, max_length=100, null=True, verbose_name='Zusätzliche Mailinglisten')),
 | 
			
		||||
                ('rebu2go', models.BooleanField(default=False, verbose_name='Rebu2Go-Zugang benötigt?')),
 | 
			
		||||
                ('transponder', models.CharField(choices=[('NORM', 'Allgemeiner Transponder'), ('SPECIAL', 'Besondere Schließungen (bitte angeben)'), ('NOTRANS', 'Kein Transponder')], default='NORM', max_length=7)),
 | 
			
		||||
                ('special', models.TextField(blank=True, max_length=500, null=True, verbose_name='Besondere Schließungen hier eintragen')),
 | 
			
		||||
                ('post_office_box', models.BooleanField(default=True, verbose_name='Postfach am Empfang benötigt?')),
 | 
			
		||||
            ],
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,18 +0,0 @@
 | 
			
		|||
# Generated by Django 3.1.4 on 2021-09-14 10:55
 | 
			
		||||
 | 
			
		||||
from django.db import migrations
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('eintritt', '0001_initial'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.RenameField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            old_name='remote',
 | 
			
		||||
            new_name='works_in_gs',
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,28 +0,0 @@
 | 
			
		|||
# Generated by Django 3.1.4 on 2022-02-08 09:55
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('eintritt', '0002_auto_20210914_1055'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='department',
 | 
			
		||||
            field=models.CharField(choices=[('PROG', 'Programme'), ('SOFT', 'Softwareentwicklung'), ('CENT', 'Central'), ('KOMEV', 'Kommunikation und Events'), ('VOR', 'Vorstand')], max_length=5, verbose_name='Bereich'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='team',
 | 
			
		||||
            field=models.CharField(blank=True, max_length=50, null=True),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='works_in_gs',
 | 
			
		||||
            field=models.BooleanField(default=True, verbose_name='Braucht Arbeitsplatz in der Geschäftsstelle?)'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,19 +0,0 @@
 | 
			
		|||
# Generated by Django 4.2.4 on 2023-08-17 11:08
 | 
			
		||||
 | 
			
		||||
from django.db import migrations
 | 
			
		||||
import multiselectfield.db.fields
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('eintritt', '0004_alter_employee_accounts_alter_employee_department'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='accounts',
 | 
			
		||||
            field=multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('OTRSWMDE', 'OTRS Ticketsystem'), ('CIVIC1', 'Civic CRM (allgemein)'), ('CIVIC2', 'Civic CRM (Mailings, impliziert allgemein)'), ('WEB', 'www.wikimedia.de (edit)'), ('BLOG', 'blog.wikimedia.de (edit)'), ('FORUM', 'forum.wikimedia.de')], max_length=40, null=True, verbose_name='Zusätzliche Accounts'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,32 +0,0 @@
 | 
			
		|||
# Generated by Django 4.2.16 on 2024-11-26 09:10
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('eintritt', '0005_alter_employee_accounts'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.RemoveField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='vendor',
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='framework',
 | 
			
		||||
            field=models.CharField(blank=True, max_length=300, null=True, verbose_name='Möchten Sie vom Standard des Frameworks abweichen, und wenn ja, warum?'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='add_to_wikimediade',
 | 
			
		||||
            field=models.CharField(choices=[('NEIN', 'Nein'), ('JA', 'Ja')], default=True, max_length=5, verbose_name='Soll die Person bei Ansprechpartner*innen auf der WMDE-Webseite mit aufgenommen werden?'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='os',
 | 
			
		||||
            field=models.CharField(choices=[('FED', 'Fedora (Standard)'), ('WIN', 'Windows (bitte Begründung angeben)'), ('MOS', 'Mac OS (nur wenn Mac gewählt)'), ('UBU', 'Ubuntu')], default='FED', max_length=3, verbose_name='Betriebssystem'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,32 +0,0 @@
 | 
			
		|||
# Generated by Django 4.2.17 on 2024-12-17 14:44
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('eintritt', '0006_remove_employee_vendor_employee_framework_and_more'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.RemoveField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='screen',
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='add_to_wikimediade',
 | 
			
		||||
            field=models.CharField(choices=[('NEIN', 'Nein'), ('JA', 'Ja')], max_length=5, verbose_name='Soll die Person bei Ansprechpartner*innen auf der WMDE-Webseite mit aufgenommen werden?'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='framework',
 | 
			
		||||
            field=models.CharField(blank=True, max_length=300, null=True, verbose_name='Möchten Sie vom Standard des Frameworks (Laptop) abweichen, und wenn ja, warum?'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='id',
 | 
			
		||||
            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 99 KiB  | 
| 
		 Before Width: | Height: | Size: 17 KiB  | 
| 
		 Before Width: | Height: | Size: 5.7 KiB  | 
| 
		 Before Width: | Height: | Size: 23 KiB  | 
| 
						 | 
				
			
			@ -1,37 +0,0 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
{% load static  %}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<!-- Load core JavaScript files needed for admin functionality -->
 | 
			
		||||
<script type="text/javascript" src="{% static 'admin/js/core.js' %}"></script>
 | 
			
		||||
<script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script>
 | 
			
		||||
<script type="text/javascript" src="{% static 'admin/js/jquery.init.js' %}"></script>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<!-- Render any JavaScript or CSS required by form widgets -->
 | 
			
		||||
{{ form.media }}
 | 
			
		||||
 | 
			
		||||
<!-- Load admin base and widgets stylesheets -->
 | 
			
		||||
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/base.css' %}" />
 | 
			
		||||
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/widgets.css' %}" />
 | 
			
		||||
 | 
			
		||||
{% load account socialaccount %}
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
<center>
 | 
			
		||||
  <!-- Display logo image -->
 | 
			
		||||
  <img src="{% static 'evapp/logo.png' %}" />
 | 
			
		||||
 | 
			
		||||
  <!-- Main heading for the page -->
 | 
			
		||||
  <h1>
 | 
			
		||||
    E (V A) - Eintritt, (Veränderung, Austritt)<p>
 | 
			
		||||
  </h1>
 | 
			
		||||
 | 
			
		||||
  <!-- Instruction text for users to log in via cloud (social login) -->
 | 
			
		||||
Bitte via Wolke einloggen:
 | 
			
		||||
 <!-- Include the social login providers list  -->
 | 
			
		||||
{% include "socialaccount/snippets/provider_list.html" with process="login" %}
 | 
			
		||||
 | 
			
		||||
</center>
 | 
			
		||||
 | 
			
		||||
{% endblock %}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +0,0 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
{% autoescape off %}
 | 
			
		||||
{% for key, value in data.items %}{% if key == 'laptop' %} {{ key }}: {{ value | safe}}{% else %}
 | 
			
		||||
{% trans key %}: {{ value }}{% endif %}{% endfor %}
 | 
			
		||||
{% endautoescape %}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,31 +0,0 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
 | 
			
		||||
(english below)
 | 
			
		||||
 | 
			
		||||
Hallo!
 | 
			
		||||
 | 
			
		||||
Es gibt einen Neuzugang bei Wikimedia! Hier ( https://wiki.wikimedia.de/wiki/Onboarding ) kannst Du nachsehen,
 | 
			
		||||
welche Schritte jetzt für Deine Abteilung nötig werden. Im Folgenden alle Daten,
 | 
			
		||||
die Du dafür brauchst:
 | 
			
		||||
 | 
			
		||||
{% include 'evapp/dataloop.txt' %}
 | 
			
		||||
 | 
			
		||||
Wenn Du Fragen hast, melde Dich bei {{contact}}.
 | 
			
		||||
 | 
			
		||||
Grüße, Deine E.V.A.
 | 
			
		||||
 | 
			
		||||
-------------------------
 | 
			
		||||
{% language 'en' %}
 | 
			
		||||
Hi!
 | 
			
		||||
 | 
			
		||||
There is a new employee at Wikimedia! Here ( https://wiki.wikimedia.de/wiki/Onboarding ) you can see, which
 | 
			
		||||
steps are now necessary for your department.
 | 
			
		||||
 | 
			
		||||
All Data you need for this:
 | 
			
		||||
 | 
			
		||||
{% include 'evapp/dataloop.txt' %}
 | 
			
		||||
 | 
			
		||||
If you have any questions please write to {{contact}}.
 | 
			
		||||
 | 
			
		||||
Regards, Your E.V.A.
 | 
			
		||||
{% endlanguage %}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,35 +0,0 @@
 | 
			
		|||
{% block content %}
 | 
			
		||||
 | 
			
		||||
  {% if form.errors %}
 | 
			
		||||
    <p>Your username and password didn't match. Please try again.</p>
 | 
			
		||||
  {% endif %}
 | 
			
		||||
 | 
			
		||||
  {% if next %}
 | 
			
		||||
    {% if user.is_authenticated %}
 | 
			
		||||
      <p>Your account doesn't have access to this page. To proceed,
 | 
			
		||||
      please login with an account that has access.</p>
 | 
			
		||||
    {% else %}
 | 
			
		||||
      <p>Please login to see this page.</p>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
  {% endif %}
 | 
			
		||||
 | 
			
		||||
  <form method="post" action="{% url 'login' %}">
 | 
			
		||||
    {% csrf_token %}
 | 
			
		||||
    <table>
 | 
			
		||||
      <tr>
 | 
			
		||||
        <td>{{ form.username.label_tag }}</td>
 | 
			
		||||
        <td>{{ form.username }}</td>
 | 
			
		||||
      </tr>
 | 
			
		||||
      <tr>
 | 
			
		||||
        <td>{{ form.password.label_tag }}</td>
 | 
			
		||||
        <td>{{ form.password }}</td>
 | 
			
		||||
      </tr>
 | 
			
		||||
    </table>
 | 
			
		||||
    <input type="submit" value="login" />
 | 
			
		||||
    <input type="hidden" name="next" value="{{ next }}" />
 | 
			
		||||
  </form>
 | 
			
		||||
 | 
			
		||||
  {# Assumes you setup the password_reset view in your URLconf #}
 | 
			
		||||
  <p><a href="{% url 'password_reset' %}">Lost password?</a></p>
 | 
			
		||||
 | 
			
		||||
{% endblock %}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,127 +0,0 @@
 | 
			
		|||
from django.test import TestCase
 | 
			
		||||
from django.test import Client
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.http import HttpResponse
 | 
			
		||||
from django.core import mail
 | 
			
		||||
from django.utils import translation
 | 
			
		||||
 | 
			
		||||
from .forms import ITForm, WorkingForm, OfficeForm, DummyForm
 | 
			
		||||
 | 
			
		||||
class LoginTestCase(TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.client = Client()
 | 
			
		||||
        self.user = User.objects.create_user('vladimir', 'vladimir@reiherzehe.com', 'reiherzehe')
 | 
			
		||||
        self.client.login(username='vladimir', password='reiherzehe')
 | 
			
		||||
        self.response = self.client.get('/')
 | 
			
		||||
 | 
			
		||||
    def testLogin(self):
 | 
			
		||||
        self.assertContains(self.response, 'Du bist eingeloggt als vladimir@reiherzehe.com', status_code=200)
 | 
			
		||||
        response_en = self.client.get('/', HTTP_ACCEPT_LANGUAGE='en-us')
 | 
			
		||||
        self.assertContains(response_en, 'You are logged in as vladimir@reiherzehe.com', status_code=200)
 | 
			
		||||
        self.assertContains(response_en, 'Firstname', status_code=200)
 | 
			
		||||
        response_en = self.client.get('/', HTTP_ACCEPT_LANGUAGE='en')
 | 
			
		||||
        self.assertContains(response_en, 'You are logged in as vladimir@reiherzehe.com', status_code=200)
 | 
			
		||||
        self.assertContains(response_en, 'Firstname', status_code=200)
 | 
			
		||||
 | 
			
		||||
    def testDebugWarning(self):
 | 
			
		||||
        with self.settings(DEBUG=True):
 | 
			
		||||
            self.response = self.client.get('/') # we need to do it again with DEBUG = True
 | 
			
		||||
            self.assertContains(self.response, "WARNUNG! Test-MODUS aktiviert. Es werden keine Mails verschickt!", status_code=200)
 | 
			
		||||
        with self.settings(DEBUG=False) and self.settings(MAILTEST=False):
 | 
			
		||||
            self.response = self.client.get('/') # we need to do it again with DEBUG = False
 | 
			
		||||
            self.assertNotContains(self.response, "WARNUNG! Test-MODUS aktiviert. Es werden keine Mails verschickt!", status_code=200)
 | 
			
		||||
 | 
			
		||||
    def _postform(self, data, expected_form):
 | 
			
		||||
        '''helper function to manage the Wizzard'''
 | 
			
		||||
        response = self.client.post('/', data, follow=True)
 | 
			
		||||
        # print(type(response))
 | 
			
		||||
        self.assertEqual(200, self.response.status_code)
 | 
			
		||||
        if not type(response) == HttpResponse:
 | 
			
		||||
            if 'form' in response.context:
 | 
			
		||||
                self.assertFalse(response.context['form'].errors)
 | 
			
		||||
            else:
 | 
			
		||||
                raise "NO FORM FOUND"
 | 
			
		||||
            self.assertEqual(
 | 
			
		||||
                 type(response.context['wizard']['form']),
 | 
			
		||||
                 expected_form
 | 
			
		||||
            )
 | 
			
		||||
        return response
 | 
			
		||||
 | 
			
		||||
    def test_department(self):
 | 
			
		||||
        self.assertContains(self.response, 'Programme', status_code=200)
 | 
			
		||||
        self.assertContains(self.response, 'Kommunikation und Events', status_code=200)
 | 
			
		||||
 | 
			
		||||
    def test_wizzard_in(self):
 | 
			
		||||
        ''' this test goes through the whole onboarding process of the EvaFormView from start to end '''
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(200, self.response.status_code)
 | 
			
		||||
 | 
			
		||||
        response = self._postform({
 | 
			
		||||
                    'eva_form_view-current_step': '0',
 | 
			
		||||
                    '0-firstname': 'Ara',
 | 
			
		||||
                    '0-lastname': 'Seva',
 | 
			
		||||
                    '0-department': 'CENT',
 | 
			
		||||
                    '0-team': 'Community Communications',
 | 
			
		||||
                    '0-choice': 'IN',
 | 
			
		||||
                }, WorkingForm)
 | 
			
		||||
 | 
			
		||||
        response = self._postform({
 | 
			
		||||
                    'eva_form_view-current_step': '1',
 | 
			
		||||
                    '1-firstdate_employment': '2021-01-01',
 | 
			
		||||
                    '1-firstdate_presence': '2021-01-01',
 | 
			
		||||
                    '1-jobdescription_german': 'hau drauf',
 | 
			
		||||
                    '1-jobdescription_english': 'und schluss',
 | 
			
		||||
                    '1-works_in_gs': False
 | 
			
		||||
                }, ITForm)
 | 
			
		||||
 | 
			
		||||
        response = self._postform({
 | 
			
		||||
                    'eva_form_view-current_step': '2',
 | 
			
		||||
                    '2-vendor': 'STANDARD',
 | 
			
		||||
                    '2-os': 'UBU',
 | 
			
		||||
                    '2-keyboard': 'DE',
 | 
			
		||||
                    '2-language': 'GER'
 | 
			
		||||
                }, OfficeForm)
 | 
			
		||||
 | 
			
		||||
        response = self._postform({
 | 
			
		||||
                    'eva_form_view-current_step': '3',
 | 
			
		||||
                    '3-transponder': 'NORM'
 | 
			
		||||
                    }, DummyForm)
 | 
			
		||||
 | 
			
		||||
        response = self._postform({
 | 
			
		||||
                    'eva_form_view-current_step': '5',
 | 
			
		||||
        }, DummyForm)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def test_mail(self):
 | 
			
		||||
        self.test_wizzard_in()
 | 
			
		||||
        # print(mail.outbox[0].body)
 | 
			
		||||
        self.assertGreater(len(mail.outbox), 2)
 | 
			
		||||
        self.assertIn("Vorname", mail.outbox[0].body)
 | 
			
		||||
        self.assertIn("Firstname", mail.outbox[0].body)
 | 
			
		||||
        for i in (0,1,3):
 | 
			
		||||
            self.assertIn("Handy", mail.outbox[i].body)
 | 
			
		||||
        self.assertIn("Ara Seva", mail.outbox[0].subject)
 | 
			
		||||
 | 
			
		||||
class NoLoginTestCase(TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.client = Client()
 | 
			
		||||
 | 
			
		||||
    def test_details(self):
 | 
			
		||||
        response = self.client.get('/')
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
        response2 = self.client.get(response.url)
 | 
			
		||||
        self.assertContains( response2, 'Bitte via Wolke einloggen:', status_code=200)
 | 
			
		||||
 | 
			
		||||
class ITFORMTestCase(TestCase):
 | 
			
		||||
    def test_mac(self):
 | 
			
		||||
        form = ITForm(data={"vendor": 'MAC', 'os': 'UBU'})
 | 
			
		||||
        #print (form.errors)
 | 
			
		||||
        self.assertEqual(form.non_field_errors(), ['Ein MAC sollte Mac OS installiert haben'])
 | 
			
		||||
 | 
			
		||||
    def test_ubu(self):
 | 
			
		||||
        form = ITForm(data={"vendor": 'STANDARD', 'os': 'UBU'})
 | 
			
		||||
        #print (form.errors)
 | 
			
		||||
        self.assertNotEqual(form.non_field_errors(), ['Ein MAC sollte Mac OS installiert haben'])
 | 
			
		||||
| 
						 | 
				
			
			@ -1,13 +0,0 @@
 | 
			
		|||
from django.urls import path
 | 
			
		||||
 | 
			
		||||
from .views import EvaFormView, success, long_process, change_process
 | 
			
		||||
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
    path('', EvaFormView.as_view(condition_dict = {'1': long_process,
 | 
			
		||||
                                                   '2': long_process,
 | 
			
		||||
                                                   '3': long_process,
 | 
			
		||||
                                                   
 | 
			
		||||
                                                   '4': change_process,}),
 | 
			
		||||
         name='evaform'),
 | 
			
		||||
    path('success', success, name='success')
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -39,10 +39,7 @@ ALLOWED_HOSTS = ['*']
 | 
			
		|||
# Application definition
 | 
			
		||||
 | 
			
		||||
INSTALLED_APPS = [
 | 
			
		||||
    'home',
 | 
			
		||||
    'austritt',
 | 
			
		||||
    'veraenderung',
 | 
			
		||||
    'eintritt.apps.EintrittConfig',
 | 
			
		||||
    'evapp.apps.EvappConfig',
 | 
			
		||||
    'django.contrib.admin',
 | 
			
		||||
    'django.contrib.auth',
 | 
			
		||||
    'django.contrib.contenttypes',
 | 
			
		||||
| 
						 | 
				
			
			@ -68,8 +65,7 @@ MIDDLEWARE = [
 | 
			
		|||
    'django.contrib.auth.middleware.AuthenticationMiddleware',
 | 
			
		||||
    'django.contrib.messages.middleware.MessageMiddleware',
 | 
			
		||||
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
 | 
			
		||||
    'allauth.account.middleware.AccountMiddleware'
 | 
			
		||||
    ]
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
ROOT_URLCONF = 'eva.urls'
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -163,4 +159,3 @@ ACCOUNT_EMAIL_VERIFICATION = 'none'
 | 
			
		|||
# ACCOUNT_EMAIL_REQUIRED = True
 | 
			
		||||
LOGIN_REDIRECT_URL = 'home'
 | 
			
		||||
ACCOUNT_LOGOUT_ON_GET = True
 | 
			
		||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,10 +17,7 @@ from django.contrib import admin
 | 
			
		|||
from django.urls import path, include
 | 
			
		||||
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
    path('eintritt/', include('eintritt.urls')),  
 | 
			
		||||
    path('', include("home.urls")),
 | 
			
		||||
    path('austritt/', include("austritt.urls")),
 | 
			
		||||
    path('veraenderung/', include("veraenderung.urls")),
 | 
			
		||||
    path('', include("evapp.urls")),
 | 
			
		||||
    path('admin/', admin.site.urls),
 | 
			
		||||
    path('accounts/', include('allauth.urls')),
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,4 +2,4 @@ from django.apps import AppConfig
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class EvappConfig(AppConfig):
 | 
			
		||||
    name = 'austritt'
 | 
			
		||||
    name = 'evapp'
 | 
			
		||||
| 
						 | 
				
			
			@ -29,7 +29,7 @@ class PersonalForm(EvaForm):
 | 
			
		|||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Employee
 | 
			
		||||
        fields = ['firstname', 'lastname', 'department', 'team', 'add_to_wikimediade',]
 | 
			
		||||
        fields = ['firstname', 'lastname', 'department', 'team', ]
 | 
			
		||||
 | 
			
		||||
class WorkingForm(EvaForm):
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -48,11 +48,17 @@ class WorkingForm(EvaForm):
 | 
			
		|||
 | 
			
		||||
class ITForm(EvaForm):
 | 
			
		||||
 | 
			
		||||
    def clean(self):
 | 
			
		||||
        data = self.cleaned_data
 | 
			
		||||
        if data['vendor'] == 'MAC' and data['os'] != 'MOS':
 | 
			
		||||
            raise ValidationError('Ein MAC sollte Mac OS installiert haben')
 | 
			
		||||
        return data
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Employee
 | 
			
		||||
        fields = [
 | 
			
		||||
            'framework', 'os', 'keyboard', 'mobile', 'landline',
 | 
			
		||||
            'comment', 'language', 'accounts', 'lists', 'rebu2go' ]
 | 
			
		||||
            'vendor', 'os', 'keyboard', 'screen', 'mobile', 'comment',
 | 
			
		||||
            'language', 'accounts', 'lists', 'rebu2go' ]
 | 
			
		||||
 | 
			
		||||
class OfficeForm(EvaForm):
 | 
			
		||||
    class Meta:
 | 
			
		||||
| 
						 | 
				
			
			@ -20,7 +20,6 @@ class Migration(migrations.Migration):
 | 
			
		|||
                ('lastname', models.CharField(max_length=50, verbose_name='Nachname')),
 | 
			
		||||
                ('department', models.CharField(choices=[('PROG', 'Programme'), ('SOFT', 'Softwareentwicklung'), ('CENT', 'Central'), ('VOR', 'Vorstand')], max_length=5, verbose_name='Bereich')),
 | 
			
		||||
                ('team', models.CharField(blank=True, max_length=20, null=True)),
 | 
			
		||||
                ('add_to_wikimediade', models.BooleanField(default=False, verbose_name='Soll auf wikimedia.de irgendwo stehen?')),
 | 
			
		||||
                ('firstdate_employment', models.DateField(null=True, verbose_name='Erster Arbeitstag')),
 | 
			
		||||
                ('firstdate_presence', models.DateField(null=True, verbose_name='Erster Tag der Anwesenheit in der Geschäftsstelle')),
 | 
			
		||||
                ('jobdescription_german', models.CharField(max_length=100, null=True, verbose_name='Stellenbezeichnung(deutsch)')),
 | 
			
		||||
| 
						 | 
				
			
			@ -31,10 +30,9 @@ class Migration(migrations.Migration):
 | 
			
		|||
                ('os', models.CharField(choices=[('UBU', 'Ubuntu (Standard)'), ('WIN', 'Windows (bitte Begründung angeben)'), ('MOS', 'Mac OS (nur wenn Mac gewählt)')], default='UBU', max_length=3, verbose_name='Betriebssystem')),
 | 
			
		||||
                ('screen', models.BooleanField(default=False, verbose_name='Zusätzlicher Monitor? Einer ist standard.')),
 | 
			
		||||
                ('mobile', models.BooleanField(default=False, max_length=6, verbose_name='Diensttelefon (Handy)')),
 | 
			
		||||
                ('landline', models.BooleanField(default=False, verbose_name='Festnetznummer (Sipgate)')),
 | 
			
		||||
                ('keyboard', models.CharField(choices=[('DE', 'Deutsch'), ('US', 'USA'), ('OT', 'Anderes (Bitte unten angeben)')], default='DE', max_length=2, verbose_name='Tastaturlayout')),
 | 
			
		||||
                ('comment', models.TextField(blank=True, max_length=500, null=True, verbose_name='zusätzliche IT-Anforderungen')),
 | 
			
		||||
                ('language', models.CharField(choices=[('GER', 'Deutsch'), ('ENG', 'English')], default='GER', max_length=3, verbose_name='Sprache für Onboarding')),
 | 
			
		||||
                ('language', models.CharField(choices=[('GER', 'Deutsch'), ('ENG', 'English')], default='GER', max_length=3, verbose_name='Sprache')),
 | 
			
		||||
                ('accounts', multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('OTRSWMDE', 'OTRS Ticketsystem'), ('CIVIC1', 'Civic CRM (allgemein)'), ('CIVIC2', 'Civic CRM (Mailings, impliziert allgemein)'), ('WEB', 'www.wikimedia.de (edit)'), ('BLOG', 'blog.wikimedia.de (edit)'), ('FORUM', 'forum.wikimedia.de')], max_length=37, null=True, verbose_name='Zusätzliche Accounts')),
 | 
			
		||||
                ('lists', models.CharField(blank=True, max_length=100, null=True, verbose_name='Zusätzliche Mailinglisten')),
 | 
			
		||||
                ('rebu2go', models.BooleanField(default=False, verbose_name='Rebu2Go-Zugang benötigt?')),
 | 
			
		||||
| 
						 | 
				
			
			@ -6,7 +6,7 @@ from django.db import migrations
 | 
			
		|||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('austritt', '0001_initial'),
 | 
			
		||||
        ('evapp', '0001_initial'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
| 
						 | 
				
			
			@ -6,7 +6,7 @@ from django.db import migrations, models
 | 
			
		|||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('austritt', '0002_auto_20210914_1055'),
 | 
			
		||||
        ('evapp', '0002_auto_20210914_1055'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
| 
						 | 
				
			
			@ -7,7 +7,7 @@ import multiselectfield.db.fields
 | 
			
		|||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('eintritt', '0003_auto_20220208_0955'),
 | 
			
		||||
        ('evapp', '0003_auto_20220208_0955'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
| 
						 | 
				
			
			@ -7,7 +7,7 @@ import multiselectfield.db.fields
 | 
			
		|||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('austritt', '0004_alter_employee_accounts_alter_employee_department'),
 | 
			
		||||
        ('evapp', '0004_alter_employee_accounts_alter_employee_department'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
| 
						 | 
				
			
			@ -13,14 +13,14 @@ DEPARTMENT_CHOICES = {'COENG': _('Communitys & Engagement'),
 | 
			
		|||
                      'KOMAD': _('Kommunikation & Advocacy'),
 | 
			
		||||
                      'VOR': _('Vorstand'),}
 | 
			
		||||
 | 
			
		||||
#VENDOR_CHOICES = {'STANDARD': 'Dell Latitude',
 | 
			
		||||
#                'LENOVO': 'Lenovo Thinkpad',
 | 
			
		||||
#                'MAC': _('Mac (nur für Grafiker_innen)')}
 | 
			
		||||
VENDOR_CHOICES = {'STANDARD': 'Dell Latitude',
 | 
			
		||||
                 'LENOVO': 'Lenovo Thinkpad',
 | 
			
		||||
                 'MAC': _('Mac (nur für Grafiker_innen)')}
 | 
			
		||||
 | 
			
		||||
OS_CHOICES = {'FED': 'Fedora (Standard)',
 | 
			
		||||
OS_CHOICES = {'UBU': 'Ubuntu (Standard)',
 | 
			
		||||
              'WIN': _('Windows (bitte Begründung angeben)'),
 | 
			
		||||
              'MOS': _('Mac OS (nur wenn Mac gewählt)'),
 | 
			
		||||
              'UBU': _('Ubuntu')}
 | 
			
		||||
              'MOS': _('Mac OS (nur wenn Mac gewählt)')}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LANG_CHOICES = {'GER': 'Deutsch',
 | 
			
		||||
                'ENG': 'English',}
 | 
			
		||||
| 
						 | 
				
			
			@ -41,9 +41,6 @@ TRANSPONDER_CHOICES = {'NORM': _('Allgemeiner Transponder'),
 | 
			
		|||
                       'SPECIAL': _('Besondere Schließungen (bitte angeben)'),
 | 
			
		||||
                       'NOTRANS': _('Kein Transponder'),}
 | 
			
		||||
 | 
			
		||||
JANEIN_CHOICES = {'NEIN': ('Nein'),
 | 
			
		||||
           'JA': _('Ja'),}
 | 
			
		||||
 | 
			
		||||
class Employee(models.Model):
 | 
			
		||||
 | 
			
		||||
    # email adress of user. should not be necessary if we use openauth one day
 | 
			
		||||
| 
						 | 
				
			
			@ -55,7 +52,6 @@ class Employee(models.Model):
 | 
			
		|||
    # intern = models.BooleanField(verbose_name='Interne_r Mitarbeiter_in?', default=True)
 | 
			
		||||
    department = models.CharField(max_length=5, choices=DEPARTMENT_CHOICES.items(), verbose_name=_('Bereich'))
 | 
			
		||||
    team = models.CharField(max_length=50, null=True, blank=True) # TODO? better with choices?
 | 
			
		||||
    add_to_wikimediade = models.CharField(max_length=5, choices=JANEIN_CHOICES.items(), verbose_name=_("Soll die Person bei Ansprechpartner*innen auf der WMDE-Webseite mit aufgenommen werden?"))
 | 
			
		||||
 | 
			
		||||
    # general work related stuff
 | 
			
		||||
    firstdate_employment = models.DateField(null=True, verbose_name=_("Erster Arbeitstag"))
 | 
			
		||||
| 
						 | 
				
			
			@ -66,16 +62,14 @@ class Employee(models.Model):
 | 
			
		|||
    desk = models.CharField(max_length=100, null=True, blank=True, verbose_name=_("Wo soll der Arbeitsplatz sein?"))
 | 
			
		||||
 | 
			
		||||
    # IT related stuff
 | 
			
		||||
    #vendor = models.CharField(max_length=8, choices=VENDOR_CHOICES.items(), default='STANDARD', verbose_name=_('Hersteller'))
 | 
			
		||||
    framework = models.CharField(max_length=300, null=True, blank=True, verbose_name=_("Möchten Sie vom Standard des Frameworks (Laptop) abweichen, und wenn ja, warum?"))
 | 
			
		||||
    os = models.CharField(max_length=3, choices=OS_CHOICES.items(), default='FED', verbose_name=_('Betriebssystem'))
 | 
			
		||||
    #screen = models.BooleanField(default=False, verbose_name=_('Zusätzlicher Monitor? Einer ist standard.'))
 | 
			
		||||
    vendor = models.CharField(max_length=8, choices=VENDOR_CHOICES.items(), default='STANDARD', verbose_name=_('Hersteller'))
 | 
			
		||||
    os = models.CharField(max_length=3, choices=OS_CHOICES.items(), default='UBU', verbose_name=_('Betriebssystem'))
 | 
			
		||||
    screen = models.BooleanField(default=False, verbose_name=_('Zusätzlicher Monitor? Einer ist standard.'))
 | 
			
		||||
    mobile = models.BooleanField(max_length=6, default=False, verbose_name=_('Diensttelefon (Handy)'))
 | 
			
		||||
    landline = models.BooleanField(default = False, verbose_name=_('Festnetznummer (Sipgate)'))
 | 
			
		||||
    # sim = models.BooleanField(default=False, verbose_name="Mobilfunkvertrag")
 | 
			
		||||
    keyboard = models.CharField(max_length=2, choices=KEYBOARD_CHOICES.items(), default='DE', verbose_name=_("Tastaturlayout"))
 | 
			
		||||
    comment = models.TextField(max_length=500, null=True, blank=True, verbose_name=_("zusätzliche IT-Anforderungen"))
 | 
			
		||||
    language = models.CharField(max_length=3, choices=LANG_CHOICES.items(), default="GER", verbose_name=_("Sprache für Onboarding"))
 | 
			
		||||
    language = models.CharField(max_length=3, choices=LANG_CHOICES.items(), default="GER", verbose_name=_("Sprache"))
 | 
			
		||||
    accounts = MultiSelectField(choices=ACCOUNT_CHOICES.items(), max_length=40, null=True, blank=True, verbose_name=_("Zusätzliche Accounts"))
 | 
			
		||||
    lists = models.CharField(max_length=100, null=True, blank=True, verbose_name=_("Zusätzliche Mailinglisten"))
 | 
			
		||||
    rebu2go = models.BooleanField(verbose_name=_("Rebu2Go-Zugang benötigt?"), default=False)
 | 
			
		||||
| 
						 | 
				
			
			@ -9,7 +9,6 @@ BASIC_DATA = ['firstname', 'lastname', 'firstdate_employment', 'firstdate_presen
 | 
			
		|||
              'jobdescription_german', 'jobdescription_english',]
 | 
			
		||||
 | 
			
		||||
# for every department: 'MAIL' => mail adress, 'DATA': additional fields to include
 | 
			
		||||
# also one copy with all fields to the person filling the form.
 | 
			
		||||
MAILS = {
 | 
			
		||||
            'IT': {
 | 
			
		||||
                'MAIL': 'wmde-it@wikimedia.de',
 | 
			
		||||
| 
						 | 
				
			
			@ -29,7 +28,7 @@ MAILS = {
 | 
			
		|||
            'KOMM': {
 | 
			
		||||
                'MAIL': 'presse@wikimedia.de',
 | 
			
		||||
                'DATA': [
 | 
			
		||||
                    'department', 'team', 'add_to_wikimediade'
 | 
			
		||||
                    'department', 'team',
 | 
			
		||||
                ],
 | 
			
		||||
            },
 | 
			
		||||
            'CENTRAL': {
 | 
			
		||||
| 
						 | 
				
			
			@ -44,18 +43,16 @@ MAILS = {
 | 
			
		|||
                    'department', 'team', 'language',
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
            'DIRECTORAT': {
 | 
			
		||||
                'MAIL': 'ricarda.busse@wikimedia.de',
 | 
			
		||||
                'DATA': [
 | 
			
		||||
                    'team', 'department', 'language',
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
            'FINANCE': {
 | 
			
		||||
                'MAIL': 'claudia.langrock@wikimedia.de',
 | 
			
		||||
                'DATA': [
 | 
			
		||||
                    'rebu2go'
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
            'SUBMITTER': {
 | 
			
		||||
                'MAIL': 'submitter@wikimedia.de',
 | 
			
		||||
                'DATA': [
 | 
			
		||||
                    'department', 'team', 'add_to_wikimediade', 'remote', 'desk', 'vendor',
 | 
			
		||||
                    'os', 'screen', 'mobile', 'landline', 'keyboard', 'comment', 'language',
 | 
			
		||||
                    'accounts', 'lists', 'rebu2go', 'transponder', 'special', 'post_office_box'
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB  | 
| 
						 | 
				
			
			@ -38,7 +38,6 @@
 | 
			
		|||
  <h1>
 | 
			
		||||
    E (V A) - Eintritt, (Veränderung, Austritt)<p>
 | 
			
		||||
  </h1>{% translate "Du bist eingeloggt als" %} {{ user.email }}
 | 
			
		||||
 | 
			
		||||
  <h2>
 | 
			
		||||
  <p> {% translate "Schritt" %} {{ wizard.steps.step1 }} {% translate "von" %} {{ wizard.steps.count }}</p>
 | 
			
		||||
  <p>{% if wizard.steps.step1 == 1 %}
 | 
			
		||||
| 
						 | 
				
			
			@ -61,15 +60,13 @@
 | 
			
		|||
{% endif %}
 | 
			
		||||
  </p>
 | 
			
		||||
  </h2>
 | 
			
		||||
 | 
			
		||||
  {% if  datatable == True %}
 | 
			
		||||
    <table  id="toggle-heading">
 | 
			
		||||
    <table>
 | 
			
		||||
    {% for key, value in data.items %}
 | 
			
		||||
      <tr><th>{{ key }}</th><th>{{ value }}</th></tr>
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
    </table>
 | 
			
		||||
  {% endif %}
 | 
			
		||||
 | 
			
		||||
  <form action="" method="post">
 | 
			
		||||
    {% csrf_token %}
 | 
			
		||||
    <table>
 | 
			
		||||
| 
						 | 
				
			
			@ -12,8 +12,8 @@ from django.conf import settings
 | 
			
		|||
from django.contrib.auth.mixins import LoginRequiredMixin
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from .models import Employee, DEPARTMENT_CHOICES, OS_CHOICES, \
 | 
			
		||||
                    LANG_CHOICES, ACCOUNT_CHOICES, TRANSPONDER_CHOICES, KEYBOARD_CHOICES, JANEIN_CHOICES
 | 
			
		||||
from .models import Employee, DEPARTMENT_CHOICES, OS_CHOICES, VENDOR_CHOICES, \
 | 
			
		||||
                    LANG_CHOICES, ACCOUNT_CHOICES, TRANSPONDER_CHOICES, KEYBOARD_CHOICES
 | 
			
		||||
from .forms import PersonalForm, WorkingForm, ITForm, OfficeForm, DummyForm,\
 | 
			
		||||
                   ChangeForm, TYPE_CHOICES
 | 
			
		||||
from .settings import MAILS, EVA_MAIL, BASIC_DATA, ONLY_ONBOARDING
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +46,7 @@ def change_process(wizard):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class EvaFormView(LoginRequiredMixin, CookieWizardView):
 | 
			
		||||
    template_name = 'eintritt/employee_form.html'
 | 
			
		||||
    template_name = 'evapp/employee_form.html'
 | 
			
		||||
    form_list = [PersonalForm, WorkingForm, ITForm, OfficeForm, ChangeForm, DummyForm]
 | 
			
		||||
    instance = None
 | 
			
		||||
    choice = 'IN'
 | 
			
		||||
| 
						 | 
				
			
			@ -141,29 +141,21 @@ class EvaFormView(LoginRequiredMixin, CookieWizardView):
 | 
			
		|||
        context = {'data': self.beautify_data(newdata), 'contact': contact}
 | 
			
		||||
        firstname = data['firstname']
 | 
			
		||||
        lastname = data['lastname']
 | 
			
		||||
        firstday = data['firstdate_employment']
 | 
			
		||||
        try:
 | 
			
		||||
             mail_template = get_template(f'evapp/department_mail.txt')
 | 
			
		||||
             if settings.MAILTEST:
 | 
			
		||||
                 send_mail(
 | 
			
		||||
                 f'EVA: Neuzugang {firstname} {lastname} {firstday} (MAILTEST)',
 | 
			
		||||
                 f'EVA: Neuzugang {firstname} {lastname} (MAILTEST)',
 | 
			
		||||
                 mail_template.render(context),
 | 
			
		||||
                 EVA_MAIL,
 | 
			
		||||
                 [EVA_MAIL],
 | 
			
		||||
                 fail_silently=False)
 | 
			
		||||
             elif department != "SUBMITTER":
 | 
			
		||||
                 send_mail(
 | 
			
		||||
                 f'EVA: Neuzugang {firstname} {lastname} {firstday}',
 | 
			
		||||
                 mail_template.render(context),
 | 
			
		||||
                 EVA_MAIL,
 | 
			
		||||
                 [MAILS[department]['MAIL']],
 | 
			
		||||
                 [EVA_MAIL, contact],
 | 
			
		||||
                 fail_silently=False)
 | 
			
		||||
             else:
 | 
			
		||||
                 send_mail(
 | 
			
		||||
                 f'EVA: Neuzugang {firstname} {lastname} {firstday}',
 | 
			
		||||
                 f'EVA: Neuzugang {firstname} {lastname}',
 | 
			
		||||
                 mail_template.render(context),
 | 
			
		||||
                 EVA_MAIL,
 | 
			
		||||
                 [contact],
 | 
			
		||||
                 [MAILS[department]['MAIL'], contact],
 | 
			
		||||
                 fail_silently=False)
 | 
			
		||||
        except BadHeaderError as error:
 | 
			
		||||
            print(error)
 | 
			
		||||
| 
						 | 
				
			
			@ -189,7 +181,7 @@ class EvaFormView(LoginRequiredMixin, CookieWizardView):
 | 
			
		|||
 | 
			
		||||
        # update values in data dictionary with keys from *_CHOICES if present there
 | 
			
		||||
        choices = {**DEPARTMENT_CHOICES, **TRANSPONDER_CHOICES,
 | 
			
		||||
                   **OS_CHOICES, **LANG_CHOICES, **KEYBOARD_CHOICES}
 | 
			
		||||
                   **OS_CHOICES, **LANG_CHOICES, **VENDOR_CHOICES, **KEYBOARD_CHOICES}
 | 
			
		||||
        data.update({k:choices[v] for k,v in data.items() \
 | 
			
		||||
            if isinstance(v,collections.abc.Hashable) \
 | 
			
		||||
            and v in choices})
 | 
			
		||||
| 
						 | 
				
			
			@ -1,3 +0,0 @@
 | 
			
		|||
from django.contrib import admin
 | 
			
		||||
 | 
			
		||||
# Register your models here.
 | 
			
		||||
| 
						 | 
				
			
			@ -1,9 +0,0 @@
 | 
			
		|||
# import django´s base Appconfig class
 | 
			
		||||
from django.apps import AppConfig
 | 
			
		||||
 | 
			
		||||
# configuration class for the 'home' application
 | 
			
		||||
class HomeConfig(AppConfig):
 | 
			
		||||
    # set the default type of primary key field for models in this app
 | 
			
		||||
    default_auto_field = 'django.db.models.BigAutoField'
 | 
			
		||||
    # give the application a name 
 | 
			
		||||
    name = 'home'
 | 
			
		||||
| 
						 | 
				
			
			@ -1,3 +0,0 @@
 | 
			
		|||
from django.db import models
 | 
			
		||||
 | 
			
		||||
# Create your models here.
 | 
			
		||||
| 
						 | 
				
			
			@ -1,97 +0,0 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% load static %}
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="UTF-8"> <!-- Set character encoding -->
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
    <title>Button Navigation</title>
 | 
			
		||||
    <style>
 | 
			
		||||
 | 
			
		||||
        /* Flex container to align items in a row */
 | 
			
		||||
 | 
			
		||||
        .container-wrapper {
 | 
			
		||||
            display: flex;
 | 
			
		||||
            justify-content: center; /* Centers the images horizontally */
 | 
			
		||||
            align-items: flex-start; /* Aligns items to the top */
 | 
			
		||||
            gap: 20px; /* Adds spacing between images */
 | 
			
		||||
            margin-top: 50px; /* Adjust margin */
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Container needed to position the button. Adjust the width as needed */
 | 
			
		||||
        .container {
 | 
			
		||||
          position: relative;
 | 
			
		||||
          width: 30%;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        /* Make the image responsive */
 | 
			
		||||
        .container img {
 | 
			
		||||
          width: 50%;
 | 
			
		||||
          height: auto;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        /* Style the button and place it in the middle of the container/image */
 | 
			
		||||
        .container .btn {
 | 
			
		||||
          position: absolute;
 | 
			
		||||
          top: 120%;
 | 
			
		||||
          left: 50%;
 | 
			
		||||
          transform: translate(-50%, -50%);
 | 
			
		||||
          -ms-transform: translate(-50%, -50%);
 | 
			
		||||
          background-color: #555;
 | 
			
		||||
          color: white;
 | 
			
		||||
          font-size: 16px;
 | 
			
		||||
          padding: 12px 24px;
 | 
			
		||||
          border: none;
 | 
			
		||||
          cursor: pointer;
 | 
			
		||||
          border-radius: 5px;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        .container .btn:hover {
 | 
			
		||||
          background-color: black;
 | 
			
		||||
        } 
 | 
			
		||||
 | 
			
		||||
        body {
 | 
			
		||||
            font-family: Arial, sans-serif;
 | 
			
		||||
            text-align: center;
 | 
			
		||||
            margin-top: 175px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    </style>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
        <h1>Wählen Sie Ihren nächsten Schritt</h1>
 | 
			
		||||
<div class="container-wrapper">
 | 
			
		||||
    <!-- the first container with image and button for onboarding -->
 | 
			
		||||
    <div class="container">
 | 
			
		||||
        <img src="{% static 'evapp/eintritt.png' %}"/>
 | 
			
		||||
        <button class="btn" onclick="window.location.href='/eintritt';">Eintritt</button>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
    <!-- the second container with image and button for onboarding -->
 | 
			
		||||
    <div class="container"> 
 | 
			
		||||
        <img src="{% static 'evapp/veraenderung.png' %}"/>
 | 
			
		||||
        <button class="btn" onclick="window.location.href='/veraenderung';">Veränderung</button>
 | 
			
		||||
    </div>
 | 
			
		||||
    <!-- the third container with image and button for onboarding -->
 | 
			
		||||
    <div class="container"> 
 | 
			
		||||
        <img src="{% static 'evapp/austritt.png' %}"/>
 | 
			
		||||
        <button class="btn" onclick="window.location.href='/austritt';">Austritt</button>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
   <!-- 
 | 
			
		||||
 | 
			
		||||
    <div>
 | 
			
		||||
    <img src="{% static 'evapp/onboarding1.png' %}" width="100" height="100" align="right" />
 | 
			
		||||
    <img src="{% static 'evapp/offboarding.png' %}" width="100" height="100" align="right" />
 | 
			
		||||
    <img src="{% static 'evapp/veränderung.png' %}" width="100" height="100" align="right" />
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <button onclick="window.location.href='/onboarding';">Onboarding</button>
 | 
			
		||||
    <button onclick="window.location.href='/offboarding';">Offboarding</button>
 | 
			
		||||
    <button onclick="window.location.href='/veränderung';">Veränderung</button>-->
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,3 +0,0 @@
 | 
			
		|||
from django.test import TestCase
 | 
			
		||||
 | 
			
		||||
# Create your tests here.
 | 
			
		||||
| 
						 | 
				
			
			@ -1,9 +0,0 @@
 | 
			
		|||
# home/urls.py
 | 
			
		||||
 | 
			
		||||
from django.urls import path
 | 
			
		||||
from . import views # import views from the current app
 | 
			
		||||
 | 
			
		||||
# Define the URL patterns for the 'home' app
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
    path('', views.index, name='home-index'), # Map the URL '' to the index view with the name 'home-index'
 | 
			
		||||
]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,15 +0,0 @@
 | 
			
		|||
from django.shortcuts import render
 | 
			
		||||
 | 
			
		||||
# Create your views here.
 | 
			
		||||
# Import the render function from Django to render templates
 | 
			
		||||
from django.shortcuts import render
 | 
			
		||||
 | 
			
		||||
def index(request):
 | 
			
		||||
    # Context dictionary to pass dynamic data to the template
 | 
			
		||||
    context = {
 | 
			
		||||
        'title': 'Welcome to Pycouse!',
 | 
			
		||||
        'author': 'Brian',
 | 
			
		||||
    }
 | 
			
		||||
    # Render the 'index.html' template located in the 'home' directory
 | 
			
		||||
    # Pass the context dictionary to the template for dynamic content
 | 
			
		||||
    return render(request, 'home/index.html', context)
 | 
			
		||||
| 
						 | 
				
			
			@ -1 +0,0 @@
 | 
			
		|||
settings_development.py
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +0,0 @@
 | 
			
		|||
from django.contrib import admin
 | 
			
		||||
from .models import Employee
 | 
			
		||||
 | 
			
		||||
admin.site.register([
 | 
			
		||||
                     Employee,
 | 
			
		||||
                     ])
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +0,0 @@
 | 
			
		|||
from django.apps import AppConfig
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EvappConfig(AppConfig):
 | 
			
		||||
    name = 'veraenderung'
 | 
			
		||||
| 
						 | 
				
			
			@ -1,67 +0,0 @@
 | 
			
		|||
from django.db import models
 | 
			
		||||
from django.forms import ModelForm, DateInput, Form, ChoiceField, RadioSelect
 | 
			
		||||
from django.core.exceptions import ValidationError
 | 
			
		||||
 | 
			
		||||
from .models import Employee
 | 
			
		||||
 | 
			
		||||
# class EmployeeForm(ModelForm):
 | 
			
		||||
#     class Meta:
 | 
			
		||||
#         model = Employee
 | 
			
		||||
#         fields = '__all__'
 | 
			
		||||
#         widgets = {'firstdate_employment': DateInput(attrs={'type': 'date'}),
 | 
			
		||||
#                    'firstdate_presence': DateInput(attrs={'type': 'date'}),}
 | 
			
		||||
 | 
			
		||||
class DummyForm(ModelForm):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Employee
 | 
			
		||||
        fields = []
 | 
			
		||||
 | 
			
		||||
class EvaForm(ModelForm):
 | 
			
		||||
    '''this base class provides the required css class for all forms'''
 | 
			
		||||
    required_css_class = 'required'
 | 
			
		||||
 | 
			
		||||
TYPE_CHOICES = {'IN': 'Eintritt', 'CHANGE': 'veraenderung', 'OUT': 'Austritt'}
 | 
			
		||||
 | 
			
		||||
class PersonalForm(EvaForm):
 | 
			
		||||
    # TODO: comment this back in to use implementation of change or exit process
 | 
			
		||||
    #    choice = ChoiceField(choices=TYPE_CHOICES.items(), widget=RadioSelect,
 | 
			
		||||
    #                         label='Welcher Prozess soll angestoßen werden?')
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Employee
 | 
			
		||||
        fields = ['firstname', 'lastname', 'department', 'team', 'add_to_wikimediade',]
 | 
			
		||||
 | 
			
		||||
class WorkingForm(EvaForm):
 | 
			
		||||
 | 
			
		||||
    def clean(self):
 | 
			
		||||
        data = self.cleaned_data
 | 
			
		||||
        if data['works_in_gs'] and data['desk'] is None:
 | 
			
		||||
            raise ValidationError('Wer nicht remote arbeitet braucht einen Schreibtisch!')
 | 
			
		||||
        return data
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Employee
 | 
			
		||||
        fields = ['firstdate_employment', 'firstdate_presence', 'jobdescription_german',
 | 
			
		||||
                  'jobdescription_english', 'works_in_gs', 'desk',]
 | 
			
		||||
        widgets = {'firstdate_employment': DateInput(attrs={'type': 'date'}),
 | 
			
		||||
                   'firstdate_presence': DateInput(attrs={'type': 'date'}),}
 | 
			
		||||
 | 
			
		||||
class ITForm(EvaForm):
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Employee
 | 
			
		||||
        fields = [
 | 
			
		||||
            'framework', 'os', 'keyboard', 'mobile', 'landline',
 | 
			
		||||
            'comment', 'language', 'accounts', 'lists', 'rebu2go' ]
 | 
			
		||||
 | 
			
		||||
class OfficeForm(EvaForm):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Employee
 | 
			
		||||
        fields = ['transponder', 'special', 'post_office_box',]
 | 
			
		||||
 | 
			
		||||
class ChangeForm(EvaForm):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Employee
 | 
			
		||||
        fields = ['firstdate_employment', 'jobdescription_german', 'jobdescription_english',
 | 
			
		||||
                  'desk', 'comment', 'accounts', 'lists', 'transponder']
 | 
			
		||||
        widgets = {'firstdate_employment': DateInput(attrs={'type': 'date'}),}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,46 +0,0 @@
 | 
			
		|||
# Generated by Django 3.1.4 on 2021-09-13 12:41
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
import multiselectfield.db.fields
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    initial = True
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name='Employee',
 | 
			
		||||
            fields=[
 | 
			
		||||
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
			
		||||
                ('firstname', models.CharField(max_length=50, verbose_name='Vorname')),
 | 
			
		||||
                ('lastname', models.CharField(max_length=50, verbose_name='Nachname')),
 | 
			
		||||
                ('department', models.CharField(choices=[('PROG', 'Programme'), ('SOFT', 'Softwareentwicklung'), ('CENT', 'Central'), ('VOR', 'Vorstand')], max_length=5, verbose_name='Bereich')),
 | 
			
		||||
                ('team', models.CharField(blank=True, max_length=20, null=True)),
 | 
			
		||||
                ('add_to_wikimediade', models.BooleanField(default=False, verbose_name='Soll auf wikimedia.de irgendwo stehen?')),
 | 
			
		||||
                ('firstdate_employment', models.DateField(null=True, verbose_name='Erster Arbeitstag')),
 | 
			
		||||
                ('firstdate_presence', models.DateField(null=True, verbose_name='Erster Tag der Anwesenheit in der Geschäftsstelle')),
 | 
			
		||||
                ('jobdescription_german', models.CharField(max_length=100, null=True, verbose_name='Stellenbezeichnung(deutsch)')),
 | 
			
		||||
                ('jobdescription_english', models.CharField(max_length=100, null=True, verbose_name='Job description(english)')),
 | 
			
		||||
                ('remote', models.BooleanField(default=True, verbose_name='Braucht Arbeitsplatz in der Geschäftsstelle?')),
 | 
			
		||||
                ('desk', models.CharField(blank=True, max_length=100, null=True, verbose_name='Wo soll der Arbeitsplatz sein?')),
 | 
			
		||||
                ('vendor', models.CharField(choices=[('STANDARD', 'Dell Latitude'), ('LENOVO', 'Lenovo Thinkpad'), ('MAC', 'Mac (nur für Grafiker_innen)')], default='STANDARD', max_length=8, verbose_name='Hersteller')),
 | 
			
		||||
                ('os', models.CharField(choices=[('UBU', 'Ubuntu (Standard)'), ('WIN', 'Windows (bitte Begründung angeben)'), ('MOS', 'Mac OS (nur wenn Mac gewählt)')], default='UBU', max_length=3, verbose_name='Betriebssystem')),
 | 
			
		||||
                ('screen', models.BooleanField(default=False, verbose_name='Zusätzlicher Monitor? Einer ist standard.')),
 | 
			
		||||
                ('mobile', models.BooleanField(default=False, max_length=6, verbose_name='Diensttelefon (Handy)')),
 | 
			
		||||
                ('landline', models.BooleanField(default=False, verbose_name='Festnetznummer (Sipgate)')),
 | 
			
		||||
                ('keyboard', models.CharField(choices=[('DE', 'Deutsch'), ('US', 'USA'), ('OT', 'Anderes (Bitte unten angeben)')], default='DE', max_length=2, verbose_name='Tastaturlayout')),
 | 
			
		||||
                ('comment', models.TextField(blank=True, max_length=500, null=True, verbose_name='zusätzliche IT-Anforderungen')),
 | 
			
		||||
                ('language', models.CharField(choices=[('GER', 'Deutsch'), ('ENG', 'English')], default='GER', max_length=3, verbose_name='Sprache für Onboarding')),
 | 
			
		||||
                ('accounts', multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('OTRSWMDE', 'OTRS Ticketsystem'), ('CIVIC1', 'Civic CRM (allgemein)'), ('CIVIC2', 'Civic CRM (Mailings, impliziert allgemein)'), ('WEB', 'www.wikimedia.de (edit)'), ('BLOG', 'blog.wikimedia.de (edit)'), ('FORUM', 'forum.wikimedia.de')], max_length=37, null=True, verbose_name='Zusätzliche Accounts')),
 | 
			
		||||
                ('lists', models.CharField(blank=True, max_length=100, null=True, verbose_name='Zusätzliche Mailinglisten')),
 | 
			
		||||
                ('rebu2go', models.BooleanField(default=False, verbose_name='Rebu2Go-Zugang benötigt?')),
 | 
			
		||||
                ('transponder', models.CharField(choices=[('NORM', 'Allgemeiner Transponder'), ('SPECIAL', 'Besondere Schließungen (bitte angeben)'), ('NOTRANS', 'Kein Transponder')], default='NORM', max_length=7)),
 | 
			
		||||
                ('special', models.TextField(blank=True, max_length=500, null=True, verbose_name='Besondere Schließungen hier eintragen')),
 | 
			
		||||
                ('post_office_box', models.BooleanField(default=True, verbose_name='Postfach am Empfang benötigt?')),
 | 
			
		||||
            ],
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,18 +0,0 @@
 | 
			
		|||
# Generated by Django 3.1.4 on 2021-09-14 10:55
 | 
			
		||||
 | 
			
		||||
from django.db import migrations
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('veraenderung', '0001_initial'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.RenameField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            old_name='remote',
 | 
			
		||||
            new_name='works_in_gs',
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,28 +0,0 @@
 | 
			
		|||
# Generated by Django 3.1.4 on 2022-02-08 09:55
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('veraenderung', '0002_auto_20210914_1055'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='department',
 | 
			
		||||
            field=models.CharField(choices=[('PROG', 'Programme'), ('SOFT', 'Softwareentwicklung'), ('CENT', 'Central'), ('KOMEV', 'Kommunikation und Events'), ('VOR', 'Vorstand')], max_length=5, verbose_name='Bereich'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='team',
 | 
			
		||||
            field=models.CharField(blank=True, max_length=50, null=True),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='works_in_gs',
 | 
			
		||||
            field=models.BooleanField(default=True, verbose_name='Braucht Arbeitsplatz in der Geschäftsstelle?)'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,24 +0,0 @@
 | 
			
		|||
# Generated by Django 4.2.4 on 2023-08-11 10:28
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
import multiselectfield.db.fields
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('veraenderung', '0003_auto_20220208_0955'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='accounts',
 | 
			
		||||
            field=multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('OTRSWMDE', 'OTRS Ticketsystem'), ('CIVIC1', 'Civic CRM (allgemein)'), ('CIVIC2', 'Civic CRM (Mailings, impliziert allgemein)'), ('WEB', 'www.wikimedia.de (edit)'), ('BLOG', 'blog.wikimedia.de (edit)'), ('FORUM', 'forum.wikimedia.de')], max_length=10, null=True, verbose_name='Zusätzliche Accounts'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='department',
 | 
			
		||||
            field=models.CharField(choices=[('COENG', 'Communitys & Engagement'), ('SOFT', 'Softwareentwicklung'), ('CENT', 'Central'), ('KOMAD', 'Kommunikation & Advocacy'), ('VOR', 'Vorstand')], max_length=5, verbose_name='Bereich'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,19 +0,0 @@
 | 
			
		|||
# Generated by Django 4.2.4 on 2023-08-17 11:08
 | 
			
		||||
 | 
			
		||||
from django.db import migrations
 | 
			
		||||
import multiselectfield.db.fields
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('veraenderung', '0004_alter_employee_accounts_alter_employee_department'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='accounts',
 | 
			
		||||
            field=multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('OTRSWMDE', 'OTRS Ticketsystem'), ('CIVIC1', 'Civic CRM (allgemein)'), ('CIVIC2', 'Civic CRM (Mailings, impliziert allgemein)'), ('WEB', 'www.wikimedia.de (edit)'), ('BLOG', 'blog.wikimedia.de (edit)'), ('FORUM', 'forum.wikimedia.de')], max_length=40, null=True, verbose_name='Zusätzliche Accounts'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,32 +0,0 @@
 | 
			
		|||
# Generated by Django 4.2.16 on 2024-11-26 09:10
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('veraenderung', '0005_alter_employee_accounts'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.RemoveField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='vendor',
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='framework',
 | 
			
		||||
            field=models.CharField(blank=True, max_length=300, null=True, verbose_name='Möchten Sie vom Standard des Frameworks abweichen, und wenn ja, warum?'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='add_to_wikimediade',
 | 
			
		||||
            field=models.CharField(choices=[('NEIN', 'Nein'), ('JA', 'Ja')], default=True, max_length=5, verbose_name='Soll die Person bei Ansprechpartner*innen auf der WMDE-Webseite mit aufgenommen werden?'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='os',
 | 
			
		||||
            field=models.CharField(choices=[('FED', 'Fedora (Standard)'), ('WIN', 'Windows (bitte Begründung angeben)'), ('MOS', 'Mac OS (nur wenn Mac gewählt)'), ('UBU', 'Ubuntu')], default='FED', max_length=3, verbose_name='Betriebssystem'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,32 +0,0 @@
 | 
			
		|||
# Generated by Django 4.2.17 on 2024-12-17 14:44
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('veraenderung', '0006_remove_employee_vendor_employee_framework_and_more'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.RemoveField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='screen',
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='add_to_wikimediade',
 | 
			
		||||
            field=models.CharField(choices=[('NEIN', 'Nein'), ('JA', 'Ja')], max_length=5, verbose_name='Soll die Person bei Ansprechpartner*innen auf der WMDE-Webseite mit aufgenommen werden?'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='framework',
 | 
			
		||||
            field=models.CharField(blank=True, max_length=300, null=True, verbose_name='Möchten Sie vom Standard des Frameworks (Laptop) abweichen, und wenn ja, warum?'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='employee',
 | 
			
		||||
            name='id',
 | 
			
		||||
            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,87 +0,0 @@
 | 
			
		|||
from django.db import models
 | 
			
		||||
from multiselectfield import MultiSelectField
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from eintritt.models import Employee
 | 
			
		||||
 | 
			
		||||
# ATTENTION!!!
 | 
			
		||||
# No key should be used twice in any of these dicts because of the
 | 
			
		||||
# suboptimal implementation in views.EvaFormView.beautify_data()
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
DEPARTMENT_CHOICES = {'COENG': _('Communitys & Engagement'),
 | 
			
		||||
                      'SOFT': _('Softwareentwicklung'),
 | 
			
		||||
                      'CENT': 'Central',
 | 
			
		||||
                      'KOMAD': _('Kommunikation & Advocacy'),
 | 
			
		||||
                      'VOR': _('Vorstand'),}
 | 
			
		||||
 | 
			
		||||
#VENDOR_CHOICES = {'STANDARD': 'Dell Latitude',
 | 
			
		||||
#                'LENOVO': 'Lenovo Thinkpad',
 | 
			
		||||
#                'MAC': _('Mac (nur für Grafiker_innen)')}
 | 
			
		||||
 | 
			
		||||
OS_CHOICES = {'FED': 'Fedora (Standard)',
 | 
			
		||||
              'WIN': _('Windows (bitte Begründung angeben)'),
 | 
			
		||||
              'MOS': _('Mac OS (nur wenn Mac gewählt)'),
 | 
			
		||||
              'UBU': _('Ubuntu')}
 | 
			
		||||
 | 
			
		||||
LANG_CHOICES = {'GER': 'Deutsch',
 | 
			
		||||
                'ENG': 'English',}
 | 
			
		||||
 | 
			
		||||
KEYBOARD_CHOICES = {'DE': 'Deutsch',
 | 
			
		||||
                    'US': 'USA',
 | 
			
		||||
                    'OT': _('Anderes (Bitte unten angeben)')}
 | 
			
		||||
 | 
			
		||||
ACCOUNT_CHOICES = {'OTRSWMDE': 'OTRS Ticketsystem',
 | 
			
		||||
                   'CIVIC1': _('Civic CRM (allgemein)'),
 | 
			
		||||
                   'CIVIC2': _("Civic CRM (Mailings, impliziert allgemein)"),
 | 
			
		||||
                   'WEB': 'www.wikimedia.de (edit)',
 | 
			
		||||
                   'BLOG': 'blog.wikimedia.de (edit)',
 | 
			
		||||
                   'FORUM': 'forum.wikimedia.de',
 | 
			
		||||
                   }
 | 
			
		||||
 | 
			
		||||
TRANSPONDER_CHOICES = {'NORM': _('Allgemeiner Transponder'),
 | 
			
		||||
                       'SPECIAL': _('Besondere Schließungen (bitte angeben)'),
 | 
			
		||||
                       'NOTRANS': _('Kein Transponder'),}
 | 
			
		||||
 | 
			
		||||
JANEIN_CHOICES = {'NEIN': ('Nein'),
 | 
			
		||||
           'JA': _('Ja'),}
 | 
			
		||||
 | 
			
		||||
class Employee(models.Model):
 | 
			
		||||
 | 
			
		||||
    # email adress of user. should not be necessary if we use openauth one day
 | 
			
		||||
    # usermail = models.EmailField(max_length=50, verbose_name="Deine Mailadresse (Ansprechpartner_in)", default='bestechefin@wikimedia.de')
 | 
			
		||||
 | 
			
		||||
    # personal data
 | 
			
		||||
    firstname = models.CharField(max_length=50, verbose_name=_("Vorname"))
 | 
			
		||||
    lastname = models.CharField(max_length=50, verbose_name=_("Nachname"))
 | 
			
		||||
    # intern = models.BooleanField(verbose_name='Interne_r Mitarbeiter_in?', default=True)
 | 
			
		||||
    department = models.CharField(max_length=5, choices=DEPARTMENT_CHOICES.items(), verbose_name=_('Bereich'))
 | 
			
		||||
    team = models.CharField(max_length=50, null=True, blank=True) # TODO? better with choices?
 | 
			
		||||
    add_to_wikimediade = models.CharField(max_length=5, choices=JANEIN_CHOICES.items(), verbose_name=_("Soll die Person bei Ansprechpartner*innen auf der WMDE-Webseite mit aufgenommen werden?"))
 | 
			
		||||
 | 
			
		||||
    # general work related stuff
 | 
			
		||||
    firstdate_employment = models.DateField(null=True, verbose_name=_("Erster Arbeitstag"))
 | 
			
		||||
    firstdate_presence = models.DateField(null=True, verbose_name=_("Erster Tag der Anwesenheit in der Geschäftsstelle"))
 | 
			
		||||
    jobdescription_german = models.CharField(null=True, max_length=100, verbose_name="Stellenbezeichnung(deutsch)")
 | 
			
		||||
    jobdescription_english = models.CharField(null=True, max_length=100, verbose_name="Job description(english)")
 | 
			
		||||
    works_in_gs = models.BooleanField(verbose_name=_('Braucht Arbeitsplatz in der Geschäftsstelle?)'), default=True)
 | 
			
		||||
    desk = models.CharField(max_length=100, null=True, blank=True, verbose_name=_("Wo soll der Arbeitsplatz sein?"))
 | 
			
		||||
 | 
			
		||||
    # IT related stuff
 | 
			
		||||
    #vendor = models.CharField(max_length=8, choices=VENDOR_CHOICES.items(), default='STANDARD', verbose_name=_('Hersteller'))
 | 
			
		||||
    framework = models.CharField(max_length=300, null=True, blank=True, verbose_name=_("Möchten Sie vom Standard des Frameworks (Laptop) abweichen, und wenn ja, warum?"))
 | 
			
		||||
    os = models.CharField(max_length=3, choices=OS_CHOICES.items(), default='FED', verbose_name=_('Betriebssystem'))
 | 
			
		||||
    #screen = models.BooleanField(default=False, verbose_name=_('Zusätzlicher Monitor? Einer ist standard.'))
 | 
			
		||||
    mobile = models.BooleanField(max_length=6, default=False, verbose_name=_('Diensttelefon (Handy)'))
 | 
			
		||||
    landline = models.BooleanField(default = False, verbose_name=_('Festnetznummer (Sipgate)'))
 | 
			
		||||
    # sim = models.BooleanField(default=False, verbose_name="Mobilfunkvertrag")
 | 
			
		||||
    keyboard = models.CharField(max_length=2, choices=KEYBOARD_CHOICES.items(), default='DE', verbose_name=_("Tastaturlayout"))
 | 
			
		||||
    comment = models.TextField(max_length=500, null=True, blank=True, verbose_name=_("zusätzliche IT-Anforderungen"))
 | 
			
		||||
    language = models.CharField(max_length=3, choices=LANG_CHOICES.items(), default="GER", verbose_name=_("Sprache für Onboarding"))
 | 
			
		||||
    accounts = MultiSelectField(choices=ACCOUNT_CHOICES.items(), max_length=40, null=True, blank=True, verbose_name=_("Zusätzliche Accounts"))
 | 
			
		||||
    lists = models.CharField(max_length=100, null=True, blank=True, verbose_name=_("Zusätzliche Mailinglisten"))
 | 
			
		||||
    rebu2go = models.BooleanField(verbose_name=_("Rebu2Go-Zugang benötigt?"), default=False)
 | 
			
		||||
 | 
			
		||||
    # office related stuff
 | 
			
		||||
    transponder = models.CharField(max_length=7, choices=TRANSPONDER_CHOICES.items(), default='NORM')
 | 
			
		||||
    special = models.TextField(max_length=500, null=True, blank=True, verbose_name=_("Besondere Schließungen hier eintragen"))
 | 
			
		||||
    post_office_box = models.BooleanField(default=True, verbose_name=_('Postfach am Empfang benötigt?'))
 | 
			
		||||
| 
						 | 
				
			
			@ -1,61 +0,0 @@
 | 
			
		|||
# temporary setting while change and exit is not yet fully implemented
 | 
			
		||||
ONLY_ONBOARDING = True
 | 
			
		||||
 | 
			
		||||
# sender mail adress also used for MAILTEST mode
 | 
			
		||||
EVA_MAIL = 'it-support@wikimedia.de'
 | 
			
		||||
 | 
			
		||||
# these Fields should be included in every mail
 | 
			
		||||
BASIC_DATA = ['firstname', 'lastname', 'firstdate_employment', 'firstdate_presence',
 | 
			
		||||
              'jobdescription_german', 'jobdescription_english',]
 | 
			
		||||
 | 
			
		||||
# for every department: 'MAIL' => mail adress, 'DATA': additional fields to include
 | 
			
		||||
# also one copy with all fields to the person filling the form.
 | 
			
		||||
MAILS = {
 | 
			
		||||
            'IT': {
 | 
			
		||||
                'MAIL': 'wmde-it@wikimedia.de',
 | 
			
		||||
                'DATA': [
 | 
			
		||||
                    'laptop', 'os', 'comment', 'email', 'landline', 'lists', 'mobile',
 | 
			
		||||
                    'department', 'accounts', 'language', 'screen', 'works_in_gs', 'desk',
 | 
			
		||||
                    'keyboard',
 | 
			
		||||
                ],
 | 
			
		||||
            },
 | 
			
		||||
            'OFFICE': {
 | 
			
		||||
                'MAIL': 'office@wikimedia.de',
 | 
			
		||||
                'DATA': [
 | 
			
		||||
                    'transponder', 'special', 'post_office_box', 'mobile',
 | 
			
		||||
                    'works_in_gs', 'desk',
 | 
			
		||||
                ],
 | 
			
		||||
            },
 | 
			
		||||
            'KOMM': {
 | 
			
		||||
                'MAIL': 'presse@wikimedia.de',
 | 
			
		||||
                'DATA': [
 | 
			
		||||
                    'department', 'team', 'add_to_wikimediade'
 | 
			
		||||
                ],
 | 
			
		||||
            },
 | 
			
		||||
            'CENTRAL': {
 | 
			
		||||
                'MAIL': 'anna.noelte@wikimedia.de',
 | 
			
		||||
                'DATA': [
 | 
			
		||||
                    'department', 'team', 'language', 'mobile', 'rebu2go'
 | 
			
		||||
                ],
 | 
			
		||||
            },
 | 
			
		||||
            'HR': {
 | 
			
		||||
                'MAIL': 'personal@wikimedia.de',
 | 
			
		||||
                'DATA': [
 | 
			
		||||
                    'department', 'team', 'language',
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
            'FINANCE': {
 | 
			
		||||
                'MAIL': 'claudia.langrock@wikimedia.de',
 | 
			
		||||
                'DATA': [
 | 
			
		||||
                    'rebu2go'
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
            'SUBMITTER': {
 | 
			
		||||
                'MAIL': 'submitter@wikimedia.de',
 | 
			
		||||
                'DATA': [
 | 
			
		||||
                    'department', 'team', 'add_to_wikimediade', 'remote', 'desk', 'vendor',
 | 
			
		||||
                    'os', 'screen', 'mobile', 'landline', 'keyboard', 'comment', 'language',
 | 
			
		||||
                    'accounts', 'lists', 'rebu2go', 'transponder', 'special', 'post_office_box'
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 5.7 KiB  | 
| 
						 | 
				
			
			@ -1,27 +0,0 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
{% load static  %}
 | 
			
		||||
 | 
			
		||||
<script type="text/javascript" src="{% static 'admin/js/core.js' %}"></script>
 | 
			
		||||
<script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script>
 | 
			
		||||
<script type="text/javascript" src="{% static 'admin/js/jquery.init.js' %}"></script>
 | 
			
		||||
 | 
			
		||||
{{ form.media }}
 | 
			
		||||
 | 
			
		||||
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/base.css' %}" />
 | 
			
		||||
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/widgets.css' %}" />
 | 
			
		||||
 | 
			
		||||
{% load account socialaccount %}
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
<center>
 | 
			
		||||
  <img src="{% static 'evapp/logo.png' %}" />
 | 
			
		||||
  <h1>
 | 
			
		||||
    E (V A) - Eintritt, (Veränderung, Austritt)<p>
 | 
			
		||||
  </h1>
 | 
			
		||||
 | 
			
		||||
Bitte via Wolke einloggen:
 | 
			
		||||
{% include "socialaccount/snippets/provider_list.html" with process="login" %}
 | 
			
		||||
 | 
			
		||||
</center>
 | 
			
		||||
 | 
			
		||||
{% endblock %}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,35 +0,0 @@
 | 
			
		|||
{% block content %}
 | 
			
		||||
 | 
			
		||||
  {% if form.errors %}
 | 
			
		||||
    <p>Your username and password didn't match. Please try again.</p>
 | 
			
		||||
  {% endif %}
 | 
			
		||||
 | 
			
		||||
  {% if next %}
 | 
			
		||||
    {% if user.is_authenticated %}
 | 
			
		||||
      <p>Your account doesn't have access to this page. To proceed,
 | 
			
		||||
      please login with an account that has access.</p>
 | 
			
		||||
    {% else %}
 | 
			
		||||
      <p>Please login to see this page.</p>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
  {% endif %}
 | 
			
		||||
 | 
			
		||||
  <form method="post" action="{% url 'login' %}">
 | 
			
		||||
    {% csrf_token %}
 | 
			
		||||
    <table>
 | 
			
		||||
      <tr>
 | 
			
		||||
        <td>{{ form.username.label_tag }}</td>
 | 
			
		||||
        <td>{{ form.username }}</td>
 | 
			
		||||
      </tr>
 | 
			
		||||
      <tr>
 | 
			
		||||
        <td>{{ form.password.label_tag }}</td>
 | 
			
		||||
        <td>{{ form.password }}</td>
 | 
			
		||||
      </tr>
 | 
			
		||||
    </table>
 | 
			
		||||
    <input type="submit" value="login" />
 | 
			
		||||
    <input type="hidden" name="next" value="{{ next }}" />
 | 
			
		||||
  </form>
 | 
			
		||||
 | 
			
		||||
  {# Assumes you setup the password_reset view in your URLconf #}
 | 
			
		||||
  <p><a href="{% url 'password_reset' %}">Lost password?</a></p>
 | 
			
		||||
 | 
			
		||||
{% endblock %}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +0,0 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
{% autoescape off %}
 | 
			
		||||
{% for key, value in data.items %}{% if key == 'laptop' %} {{ key }}: {{ value | safe}}{% else %}
 | 
			
		||||
{% trans key %}: {{ value }}{% endif %}{% endfor %}
 | 
			
		||||
{% endautoescape %}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,31 +0,0 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
 | 
			
		||||
(english below)
 | 
			
		||||
 | 
			
		||||
Hallo!
 | 
			
		||||
 | 
			
		||||
Es gibt einen Neuzugang bei Wikimedia! Hier ( https://wiki.wikimedia.de/wiki/Onboarding ) kannst Du nachsehen,
 | 
			
		||||
welche Schritte jetzt für Deine Abteilung nötig werden. Im Folgenden alle Daten,
 | 
			
		||||
die Du dafür brauchst:
 | 
			
		||||
 | 
			
		||||
{% include 'evapp/dataloop.txt' %}
 | 
			
		||||
 | 
			
		||||
Wenn Du Fragen hast, melde Dich bei {{contact}}.
 | 
			
		||||
 | 
			
		||||
Grüße, Deine E.V.A.
 | 
			
		||||
 | 
			
		||||
-------------------------
 | 
			
		||||
{% language 'en' %}
 | 
			
		||||
Hi!
 | 
			
		||||
 | 
			
		||||
There is a new employee at Wikimedia! Here ( https://wiki.wikimedia.de/wiki/Onboarding ) you can see, which
 | 
			
		||||
steps are now necessary for your department.
 | 
			
		||||
 | 
			
		||||
All Data you need for this:
 | 
			
		||||
 | 
			
		||||
{% include 'evapp/dataloop.txt' %}
 | 
			
		||||
 | 
			
		||||
If you have any questions please write to {{contact}}.
 | 
			
		||||
 | 
			
		||||
Regards, Your E.V.A.
 | 
			
		||||
{% endlanguage %}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,110 +0,0 @@
 | 
			
		|||
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% load static  %}
 | 
			
		||||
 | 
			
		||||
<script type="text/javascript" src="{% static 'admin/js/core.js' %}"></script>
 | 
			
		||||
<script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script>
 | 
			
		||||
<script type="text/javascript" src="{% static 'admin/js/jquery.init.js' %}"></script>
 | 
			
		||||
 | 
			
		||||
{{ form.media }}
 | 
			
		||||
 | 
			
		||||
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/base.css' %}" />
 | 
			
		||||
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/widgets.css' %}" />
 | 
			
		||||
 | 
			
		||||
{% load socialaccount %}
 | 
			
		||||
{% if user.is_authenticated %}
 | 
			
		||||
{% block content %}
 | 
			
		||||
 | 
			
		||||
{% get_current_language as LANGUAGE_CODE %}
 | 
			
		||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
 | 
			
		||||
<center>
 | 
			
		||||
  <style>
 | 
			
		||||
    ul > li {
 | 
			
		||||
      list-style-type: none;
 | 
			
		||||
    }
 | 
			
		||||
    ul {
 | 
			
		||||
      padding-left: 10;
 | 
			
		||||
    }
 | 
			
		||||
    label.required::after {
 | 
			
		||||
      content: ' *';
 | 
			
		||||
      color: red;
 | 
			
		||||
    }
 | 
			
		||||
  </style>
 | 
			
		||||
 | 
			
		||||
  <img src="{% static 'evapp/logo.png' %}" />
 | 
			
		||||
  {% if TESTMODE %}
 | 
			
		||||
  <h1 style="background-color:red;color:white">{% translate "WARNUNG! Test-MODUS aktiviert. Es werden keine Mails verschickt!" %}</h1>
 | 
			
		||||
  {% endif %}
 | 
			
		||||
  <h1>
 | 
			
		||||
    E (V A) - Veränderung, (Veränderung, Austritt)<p>
 | 
			
		||||
  </h1>{% translate "Du bist eingeloggt als" %} {{ user.email }}
 | 
			
		||||
 | 
			
		||||
  <h2>
 | 
			
		||||
  <p> {% translate "Schritt" %} {{ wizard.steps.step1 }} {% translate "von" %} {{ wizard.steps.count }}</p>
 | 
			
		||||
  <p>{% if wizard.steps.step1 == 1 %}
 | 
			
		||||
    {% translate "Angaben zur Person" %} {% endif %}
 | 
			
		||||
{% if choice == 'IN' %}
 | 
			
		||||
  {% if wizard.steps.step1 == 2 %}
 | 
			
		||||
    {% translate "Angaben zum neuen Arbeitsverhältnis" %}
 | 
			
		||||
  {% elif wizard.steps.step1 == 3 %}
 | 
			
		||||
    {% translate "IT-relevante Angaben" %}
 | 
			
		||||
  {% elif wizard.steps.step1 == 4 %}
 | 
			
		||||
    {% translate "Office-relevante Angaben" %}
 | 
			
		||||
  {% endif %}
 | 
			
		||||
{% else %}
 | 
			
		||||
  {% if wizard.steps.step1 == 2 %}
 | 
			
		||||
    Veränderungsrelevante Angaben
 | 
			
		||||
  {% endif %}
 | 
			
		||||
{% endif %}
 | 
			
		||||
{% if datatable == True %}
 | 
			
		||||
    {% translate "Bestätigungsschritt" %}
 | 
			
		||||
{% endif %}
 | 
			
		||||
  </p>
 | 
			
		||||
  </h2>
 | 
			
		||||
 | 
			
		||||
  {% if  datatable == True %}
 | 
			
		||||
    <table  id="toggle-heading">
 | 
			
		||||
    {% for key, value in data.items %}
 | 
			
		||||
      <tr><th>{{ key }}</th><th>{{ value }}</th></tr>
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
    </table>
 | 
			
		||||
  {% endif %}
 | 
			
		||||
 | 
			
		||||
  <form action="" method="post">
 | 
			
		||||
    {% csrf_token %}
 | 
			
		||||
    <table>
 | 
			
		||||
    {% comment %}
 | 
			
		||||
        comment this back in if you want to use CHANGE and EXIT process
 | 
			
		||||
        {% if wizard.steps.step1 > 1 %}
 | 
			
		||||
          Du hast den Prozess "{{choice_string}}" ausgewählt.
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    {% endcomment %}
 | 
			
		||||
    {{ wizard.management_form }}
 | 
			
		||||
    {% if wizard.form.forms %}
 | 
			
		||||
      {{ wizard.form.management_form }}
 | 
			
		||||
      {% for form in wizard.form.forms %}
 | 
			
		||||
        {{ form }}
 | 
			
		||||
      {% endfor %}
 | 
			
		||||
    {% else %}
 | 
			
		||||
      {{ wizard.form }}
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    </table>
 | 
			
		||||
    <p>
 | 
			
		||||
      <span style="color: red">*</span> {% translate "Pflichtfeld" %}
 | 
			
		||||
    <p>
 | 
			
		||||
    {% if wizard.steps.prev %}
 | 
			
		||||
    <button formnovalidate="formnovalidate" name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">{% translate "Zurück" %}</button>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    {% if datatable == True %}
 | 
			
		||||
      <button type="submit" value="{% trans "Weiter" %}">{% translate "Abschicken" %}</button>
 | 
			
		||||
    {% else %}
 | 
			
		||||
      <button type="submit" value="{% trans "Weiter" %}">{% translate "Weiter" %}</button>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
  </form>
 | 
			
		||||
  <p>
 | 
			
		||||
  <a href="{% url 'account_logout' %}">{% translate "logout" %}</a>
 | 
			
		||||
</center>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
{% else %}
 | 
			
		||||
<a href="{% provider_login_url 'nextcloud' %}">{% translate "Bitte einloggen!" %}</a>
 | 
			
		||||
{% endif %}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,127 +0,0 @@
 | 
			
		|||
from django.test import TestCase
 | 
			
		||||
from django.test import Client
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.http import HttpResponse
 | 
			
		||||
from django.core import mail
 | 
			
		||||
from django.utils import translation
 | 
			
		||||
 | 
			
		||||
from .forms import ITForm, WorkingForm, OfficeForm, DummyForm
 | 
			
		||||
 | 
			
		||||
class LoginTestCase(TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.client = Client()
 | 
			
		||||
        self.user = User.objects.create_user('vladimir', 'vladimir@reiherzehe.com', 'reiherzehe')
 | 
			
		||||
        self.client.login(username='vladimir', password='reiherzehe')
 | 
			
		||||
        self.response = self.client.get('/')
 | 
			
		||||
 | 
			
		||||
    def testLogin(self):
 | 
			
		||||
        self.assertContains(self.response, 'Du bist eingeloggt als vladimir@reiherzehe.com', status_code=200)
 | 
			
		||||
        response_en = self.client.get('/', HTTP_ACCEPT_LANGUAGE='en-us')
 | 
			
		||||
        self.assertContains(response_en, 'You are logged in as vladimir@reiherzehe.com', status_code=200)
 | 
			
		||||
        self.assertContains(response_en, 'Firstname', status_code=200)
 | 
			
		||||
        response_en = self.client.get('/', HTTP_ACCEPT_LANGUAGE='en')
 | 
			
		||||
        self.assertContains(response_en, 'You are logged in as vladimir@reiherzehe.com', status_code=200)
 | 
			
		||||
        self.assertContains(response_en, 'Firstname', status_code=200)
 | 
			
		||||
 | 
			
		||||
    def testDebugWarning(self):
 | 
			
		||||
        with self.settings(DEBUG=True):
 | 
			
		||||
            self.response = self.client.get('/') # we need to do it again with DEBUG = True
 | 
			
		||||
            self.assertContains(self.response, "WARNUNG! Test-MODUS aktiviert. Es werden keine Mails verschickt!", status_code=200)
 | 
			
		||||
        with self.settings(DEBUG=False) and self.settings(MAILTEST=False):
 | 
			
		||||
            self.response = self.client.get('/') # we need to do it again with DEBUG = False
 | 
			
		||||
            self.assertNotContains(self.response, "WARNUNG! Test-MODUS aktiviert. Es werden keine Mails verschickt!", status_code=200)
 | 
			
		||||
 | 
			
		||||
    def _postform(self, data, expected_form):
 | 
			
		||||
        '''helper function to manage the Wizzard'''
 | 
			
		||||
        response = self.client.post('/', data, follow=True)
 | 
			
		||||
        # print(type(response))
 | 
			
		||||
        self.assertEqual(200, self.response.status_code)
 | 
			
		||||
        if not type(response) == HttpResponse:
 | 
			
		||||
            if 'form' in response.context:
 | 
			
		||||
                self.assertFalse(response.context['form'].errors)
 | 
			
		||||
            else:
 | 
			
		||||
                raise "NO FORM FOUND"
 | 
			
		||||
            self.assertEqual(
 | 
			
		||||
                 type(response.context['wizard']['form']),
 | 
			
		||||
                 expected_form
 | 
			
		||||
            )
 | 
			
		||||
        return response
 | 
			
		||||
 | 
			
		||||
    def test_department(self):
 | 
			
		||||
        self.assertContains(self.response, 'Programme', status_code=200)
 | 
			
		||||
        self.assertContains(self.response, 'Kommunikation und Events', status_code=200)
 | 
			
		||||
 | 
			
		||||
    def test_wizzard_in(self):
 | 
			
		||||
        ''' this test goes through the whole onboarding process of the EvaFormView from start to end '''
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(200, self.response.status_code)
 | 
			
		||||
 | 
			
		||||
        response = self._postform({
 | 
			
		||||
                    'eva_form_view-current_step': '0',
 | 
			
		||||
                    '0-firstname': 'Ara',
 | 
			
		||||
                    '0-lastname': 'Seva',
 | 
			
		||||
                    '0-department': 'CENT',
 | 
			
		||||
                    '0-team': 'Community Communications',
 | 
			
		||||
                    '0-choice': 'IN',
 | 
			
		||||
                }, WorkingForm)
 | 
			
		||||
 | 
			
		||||
        response = self._postform({
 | 
			
		||||
                    'eva_form_view-current_step': '1',
 | 
			
		||||
                    '1-firstdate_employment': '2021-01-01',
 | 
			
		||||
                    '1-firstdate_presence': '2021-01-01',
 | 
			
		||||
                    '1-jobdescription_german': 'hau drauf',
 | 
			
		||||
                    '1-jobdescription_english': 'und schluss',
 | 
			
		||||
                    '1-works_in_gs': False
 | 
			
		||||
                }, ITForm)
 | 
			
		||||
 | 
			
		||||
        response = self._postform({
 | 
			
		||||
                    'eva_form_view-current_step': '2',
 | 
			
		||||
                    '2-vendor': 'STANDARD',
 | 
			
		||||
                    '2-os': 'UBU',
 | 
			
		||||
                    '2-keyboard': 'DE',
 | 
			
		||||
                    '2-language': 'GER'
 | 
			
		||||
                }, OfficeForm)
 | 
			
		||||
 | 
			
		||||
        response = self._postform({
 | 
			
		||||
                    'eva_form_view-current_step': '3',
 | 
			
		||||
                    '3-transponder': 'NORM'
 | 
			
		||||
                    }, DummyForm)
 | 
			
		||||
 | 
			
		||||
        response = self._postform({
 | 
			
		||||
                    'eva_form_view-current_step': '5',
 | 
			
		||||
        }, DummyForm)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def test_mail(self):
 | 
			
		||||
        self.test_wizzard_in()
 | 
			
		||||
        # print(mail.outbox[0].body)
 | 
			
		||||
        self.assertGreater(len(mail.outbox), 2)
 | 
			
		||||
        self.assertIn("Vorname", mail.outbox[0].body)
 | 
			
		||||
        self.assertIn("Firstname", mail.outbox[0].body)
 | 
			
		||||
        for i in (0,1,3):
 | 
			
		||||
            self.assertIn("Handy", mail.outbox[i].body)
 | 
			
		||||
        self.assertIn("Ara Seva", mail.outbox[0].subject)
 | 
			
		||||
 | 
			
		||||
class NoLoginTestCase(TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.client = Client()
 | 
			
		||||
 | 
			
		||||
    def test_details(self):
 | 
			
		||||
        response = self.client.get('/')
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
        response2 = self.client.get(response.url)
 | 
			
		||||
        self.assertContains( response2, 'Bitte via Wolke einloggen:', status_code=200)
 | 
			
		||||
 | 
			
		||||
class ITFORMTestCase(TestCase):
 | 
			
		||||
    def test_mac(self):
 | 
			
		||||
        form = ITForm(data={"vendor": 'MAC', 'os': 'UBU'})
 | 
			
		||||
        #print (form.errors)
 | 
			
		||||
        self.assertEqual(form.non_field_errors(), ['Ein MAC sollte Mac OS installiert haben'])
 | 
			
		||||
 | 
			
		||||
    def test_ubu(self):
 | 
			
		||||
        form = ITForm(data={"vendor": 'STANDARD', 'os': 'UBU'})
 | 
			
		||||
        #print (form.errors)
 | 
			
		||||
        self.assertNotEqual(form.non_field_errors(), ['Ein MAC sollte Mac OS installiert haben'])
 | 
			
		||||
| 
						 | 
				
			
			@ -1,13 +0,0 @@
 | 
			
		|||
from django.urls import path
 | 
			
		||||
 | 
			
		||||
from .views import EvaFormView, success, long_process, change_process
 | 
			
		||||
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
    path('', EvaFormView.as_view(condition_dict = {'1': long_process,
 | 
			
		||||
                                                   '2': long_process,
 | 
			
		||||
                                                   '3': long_process,
 | 
			
		||||
                                                   
 | 
			
		||||
                                                   '4': change_process,}),
 | 
			
		||||
         name='evaform'),
 | 
			
		||||
    path('success', success, name='success')
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,217 +0,0 @@
 | 
			
		|||
from smtplib import SMTPException
 | 
			
		||||
import collections
 | 
			
		||||
 | 
			
		||||
from django.views.generic.edit import CreateView
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.http import HttpResponse, HttpResponseRedirect
 | 
			
		||||
from django.core.mail import send_mail, BadHeaderError
 | 
			
		||||
from django.template.loader import get_template
 | 
			
		||||
from formtools.wizard.views import CookieWizardView
 | 
			
		||||
from django.shortcuts import render
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.contrib.auth.mixins import LoginRequiredMixin
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from .models import Employee, DEPARTMENT_CHOICES, OS_CHOICES, \
 | 
			
		||||
                    LANG_CHOICES, ACCOUNT_CHOICES, TRANSPONDER_CHOICES, KEYBOARD_CHOICES, JANEIN_CHOICES
 | 
			
		||||
from .forms import PersonalForm, WorkingForm, ITForm, OfficeForm, DummyForm,\
 | 
			
		||||
                   ChangeForm, TYPE_CHOICES
 | 
			
		||||
from .settings import MAILS, EVA_MAIL, BASIC_DATA, ONLY_ONBOARDING
 | 
			
		||||
 | 
			
		||||
def success(request):
 | 
			
		||||
    return HttpResponse(f"Vielen Dank! Du hast E.V.A. erfolgreich ausgefüllt. Die Mails an die Abteilungen wurden versendet. Kopien gehen an {request.user.email}.")
 | 
			
		||||
 | 
			
		||||
def long_process(wizard):
 | 
			
		||||
    '''this method is called via urls.py to determine if a form is part of the IN-Process'''
 | 
			
		||||
 | 
			
		||||
    if ONLY_ONBOARDING:
 | 
			
		||||
        wizard.set_choice('IN')
 | 
			
		||||
        return True
 | 
			
		||||
    else:
 | 
			
		||||
        data = wizard.get_cleaned_data_for_step('0') or {}
 | 
			
		||||
        # print(data)
 | 
			
		||||
        if data.get('choice') != 'CHANGE':
 | 
			
		||||
            wizard.set_choice('IN')
 | 
			
		||||
            # print('PROZESS IN')
 | 
			
		||||
            return True
 | 
			
		||||
        else:
 | 
			
		||||
            wizard.set_choice('CHANGE')
 | 
			
		||||
            # print('PROZESS NOT IN')
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
def change_process(wizard):
 | 
			
		||||
    ''' this method is called via urls.py to determine if the form is part of the change process'''
 | 
			
		||||
    # print('CHANGE PROZESS')
 | 
			
		||||
    return not long_process(wizard)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EvaFormView(LoginRequiredMixin, CookieWizardView):
 | 
			
		||||
    template_name = 'veraenderung/employee_form.html'
 | 
			
		||||
    form_list = [PersonalForm, WorkingForm, ITForm, OfficeForm, ChangeForm, DummyForm]
 | 
			
		||||
    instance = None
 | 
			
		||||
    choice = 'IN'
 | 
			
		||||
 | 
			
		||||
    # maybe we dont need this, if *_process() would be class methods,
 | 
			
		||||
    # but unsure if this would work fine with the entries in urls.py
 | 
			
		||||
    def set_choice(self, c):
 | 
			
		||||
        self.choice = c
 | 
			
		||||
 | 
			
		||||
    def generate_email(self, data):
 | 
			
		||||
        (first, *_) = data['firstname'].split(maxsplit=1)
 | 
			
		||||
        (last, *_) = data['lastname'].split(maxsplit=1)
 | 
			
		||||
        name = first + '.' + last
 | 
			
		||||
        #if not data['intern']:
 | 
			
		||||
        #    mail = name + '_ext@wikimedia.de'
 | 
			
		||||
        #else:
 | 
			
		||||
        mail = name + '@wikimedia.de'
 | 
			
		||||
        data['email'] = mail
 | 
			
		||||
 | 
			
		||||
    def get_all_cleaned_data(self):
 | 
			
		||||
        '''this method deletes data which is only used temporary and is not in the modell,
 | 
			
		||||
        it also changes the mail adress of the employee in some circumstances'''
 | 
			
		||||
 | 
			
		||||
        data = super().get_all_cleaned_data()
 | 
			
		||||
        self.generate_email(data)
 | 
			
		||||
 | 
			
		||||
        # print("delete CHOICE FROM DATA")
 | 
			
		||||
        if 'choice' in data:
 | 
			
		||||
            del data['choice']
 | 
			
		||||
        return data
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, form, **kwargs):
 | 
			
		||||
        '''this method is called to give context data to the template'''
 | 
			
		||||
 | 
			
		||||
        #print('GETCONTEXT')
 | 
			
		||||
        context = super().get_context_data(form=form, **kwargs)
 | 
			
		||||
        testmode = settings.DEBUG or settings.MAILTEST
 | 
			
		||||
        context.update({'choice': self.choice,
 | 
			
		||||
                        'choice_string': TYPE_CHOICES[self.choice],
 | 
			
		||||
                        'TESTMODE': testmode})
 | 
			
		||||
 | 
			
		||||
        # deliver context for forms if we are in the last step
 | 
			
		||||
        if (self.steps.step1 == 5 or (self.choice != 'IN' and self.steps.step1 == 3)):
 | 
			
		||||
            context.update({'data': self.beautify_data(self.get_all_cleaned_data()),
 | 
			
		||||
                            'datatable': True,})
 | 
			
		||||
        return context
 | 
			
		||||
 | 
			
		||||
    def get_form_instance(self,step):
 | 
			
		||||
        ''' this method assures, that we use the same model instance for all steps'''
 | 
			
		||||
 | 
			
		||||
        if self.instance == None:
 | 
			
		||||
            self.instance = Employee()
 | 
			
		||||
        return self.instance
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def done(self, form_list, **kwargs):
 | 
			
		||||
        '''this method is called from CookieWizardView after all forms are filled'''
 | 
			
		||||
 | 
			
		||||
        print ('INSTANCE_DICT')
 | 
			
		||||
        print(self.instance_dict)
 | 
			
		||||
 | 
			
		||||
        # save data to database
 | 
			
		||||
        for form in form_list:
 | 
			
		||||
            form.save()
 | 
			
		||||
 | 
			
		||||
        # send data to departments
 | 
			
		||||
        for dep in MAILS:
 | 
			
		||||
            response = self.send_mail_to_department(dep)
 | 
			
		||||
 | 
			
		||||
        if not settings.DEBUG:
 | 
			
		||||
            self.instance.delete()
 | 
			
		||||
 | 
			
		||||
        if response:
 | 
			
		||||
            return response
 | 
			
		||||
        else:
 | 
			
		||||
            return HttpResponseRedirect('success')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def send_mail_to_department(self, department):
 | 
			
		||||
        'send a mail to the given department with the nececcary notifications'
 | 
			
		||||
 | 
			
		||||
        print(f'send mail to department {department}...')
 | 
			
		||||
 | 
			
		||||
        contact = self.request.user.email
 | 
			
		||||
        data = self.get_all_cleaned_data()
 | 
			
		||||
        # some data should be in every mail
 | 
			
		||||
        newdata = {k: v for k, v in data.items() if (k in BASIC_DATA)}
 | 
			
		||||
        # only the relevant data should be in the context
 | 
			
		||||
        newdata.update({k: v for k, v in data.items() if (k in MAILS[department]['DATA'])})
 | 
			
		||||
 | 
			
		||||
        context = {'data': self.beautify_data(newdata), 'contact': contact}
 | 
			
		||||
        firstname = data['firstname']
 | 
			
		||||
        lastname = data['lastname']
 | 
			
		||||
        firstday = data['firstdate_employment']
 | 
			
		||||
        try:
 | 
			
		||||
             mail_template = get_template(f'evapp/department_mail.txt')
 | 
			
		||||
             if settings.MAILTEST:
 | 
			
		||||
                 send_mail(
 | 
			
		||||
                 f'EVA: Neuzugang {firstname} {lastname} {firstday} (MAILTEST)',
 | 
			
		||||
                 mail_template.render(context),
 | 
			
		||||
                 EVA_MAIL,
 | 
			
		||||
                 [EVA_MAIL],
 | 
			
		||||
                 fail_silently=False)
 | 
			
		||||
             elif department != "SUBMITTER":
 | 
			
		||||
                 send_mail(
 | 
			
		||||
                 f'EVA: Neuzugang {firstname} {lastname} {firstday}',
 | 
			
		||||
                 mail_template.render(context),
 | 
			
		||||
                 EVA_MAIL,
 | 
			
		||||
                 [MAILS[department]['MAIL']],
 | 
			
		||||
                 fail_silently=False)
 | 
			
		||||
             else:
 | 
			
		||||
                 send_mail(
 | 
			
		||||
                 f'EVA: Neuzugang {firstname} {lastname} {firstday}',
 | 
			
		||||
                 mail_template.render(context),
 | 
			
		||||
                 EVA_MAIL,
 | 
			
		||||
                 [contact],
 | 
			
		||||
                 fail_silently=False)
 | 
			
		||||
        except BadHeaderError as error:
 | 
			
		||||
            print(error)
 | 
			
		||||
            self.instance.delete()
 | 
			
		||||
            return HttpResponse(f'{error}<p>Invalid header found. Data not saved!')
 | 
			
		||||
        except SMTPException as error:
 | 
			
		||||
            print(error)
 | 
			
		||||
            self.instance.delete()
 | 
			
		||||
            return HttpResponse(f'{error}<p>Error in sending mails (propably wrong adress?). Data not saved!')
 | 
			
		||||
        except Exception as error:
 | 
			
		||||
            print(error)
 | 
			
		||||
            # self.instance.delete()
 | 
			
		||||
            return HttpResponse(f'{error}<p>Error in sending mails. Data not saved! Please contact ' + EVA_MAIL)
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def beautify_data(self, data):
 | 
			
		||||
        '''  # use long form for contextdata instead of short form if available
 | 
			
		||||
            #
 | 
			
		||||
            # ATTENTION!
 | 
			
		||||
            # This implementation works only for unique keys over all of these dicts from model.py
 | 
			
		||||
            #
 | 
			
		||||
        '''
 | 
			
		||||
 | 
			
		||||
        # update values in data dictionary with keys from *_CHOICES if present there
 | 
			
		||||
        choices = {**DEPARTMENT_CHOICES, **TRANSPONDER_CHOICES,
 | 
			
		||||
                   **OS_CHOICES, **LANG_CHOICES, **KEYBOARD_CHOICES}
 | 
			
		||||
        data.update({k:choices[v] for k,v in data.items() \
 | 
			
		||||
            if isinstance(v,collections.abc.Hashable) \
 | 
			
		||||
            and v in choices})
 | 
			
		||||
 | 
			
		||||
        # replace values in accounts array from *_CHOICES
 | 
			
		||||
        if 'accounts' in data:
 | 
			
		||||
            data['accounts'] = [ACCOUNT_CHOICES[c] for c in data['accounts']]
 | 
			
		||||
 | 
			
		||||
        # replace keys in data dictionary with verbose_name
 | 
			
		||||
        # a bit ugly workaround here: we need to store 'email' away, because it es not in the modell
 | 
			
		||||
        mail = ''
 | 
			
		||||
        if 'email' in data:
 | 
			
		||||
            mail = data.pop('email')
 | 
			
		||||
        newdata = {self.instance._meta.get_field(k).verbose_name.title() : v for k,v in data.items()}
 | 
			
		||||
        if mail:
 | 
			
		||||
            newdata['Email'] = mail
 | 
			
		||||
 | 
			
		||||
        # translate booleans
 | 
			
		||||
        newdata.update({k:'Ja' for k,v in newdata.items() if isinstance(v,bool) and v == True})
 | 
			
		||||
        newdata.update({k:'Nein' for k,v in newdata.items() if isinstance(v,bool) and v == False})
 | 
			
		||||
        # handle some special data types
 | 
			
		||||
        newdata.update({k:'' for k,v in newdata.items() if v == None})
 | 
			
		||||
        newdata.update({k:'' for k,v in newdata.items() if v == []})
 | 
			
		||||
 | 
			
		||||
        return newdata
 | 
			
		||||