Compare commits
8 Commits
Author | SHA1 | Date |
---|---|---|
|
d2d470442e | |
|
96dc73539f | |
|
f4e05a0799 | |
|
619b846f3a | |
|
ab42e76e82 | |
|
b0d1aa1b00 | |
|
f5f10c5414 | |
|
b184564c83 |
|
@ -39,6 +39,9 @@ ALLOWED_HOSTS = ['*']
|
|||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'home',
|
||||
'offboarding',
|
||||
'veränderung',
|
||||
'evapp.apps.EvappConfig',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
|
@ -65,6 +68,7 @@ MIDDLEWARE = [
|
|||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'allauth.account.middleware.AccountMiddleware'
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'eva.urls'
|
||||
|
@ -159,3 +163,4 @@ 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,7 +17,10 @@ from django.contrib import admin
|
|||
from django.urls import path, include
|
||||
|
||||
urlpatterns = [
|
||||
path('', include("evapp.urls")),
|
||||
path('onboarding/', include('evapp.urls')),
|
||||
path('', include("home.urls")),
|
||||
path('offboarding/', include("offboarding.urls")),
|
||||
path('veränderung/', include("veränderung.urls")),
|
||||
path('admin/', admin.site.urls),
|
||||
path('accounts/', include('allauth.urls')),
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
# 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,5 +1,7 @@
|
|||
# import django´s base AppConfig class
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
# define the configuration class for the 'evapp' application
|
||||
class EvappConfig(AppConfig):
|
||||
# give the app a name
|
||||
name = 'evapp'
|
||||
|
|
|
@ -22,15 +22,13 @@ class EvaForm(ModelForm):
|
|||
|
||||
TYPE_CHOICES = {'IN': 'Eintritt', 'CHANGE': 'Veränderung', 'OUT': 'Austritt'}
|
||||
|
||||
# Form to capture basic personal and department-related information
|
||||
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', ]
|
||||
|
||||
fields = ['firstname', 'lastname', 'department', 'team', 'add_to_wikimediade',]
|
||||
# Form to capture working conditions and job description
|
||||
class WorkingForm(EvaForm):
|
||||
|
||||
def clean(self):
|
||||
|
@ -48,17 +46,11 @@ 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 = [
|
||||
'vendor', 'os', 'keyboard', 'screen', 'mobile', 'comment',
|
||||
'language', 'accounts', 'lists', 'rebu2go' ]
|
||||
'framework', 'os', 'keyboard', 'mobile', 'landline',
|
||||
'comment', 'language', 'accounts', 'lists', 'rebu2go' ]
|
||||
|
||||
class OfficeForm(EvaForm):
|
||||
class Meta:
|
||||
|
|
|
@ -20,6 +20,7 @@ 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)')),
|
||||
|
@ -30,9 +31,10 @@ 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')),
|
||||
('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?')),
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
# Generated by Django 4.2.16 on 2024-11-26 09:10
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evapp', '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'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,32 @@
|
|||
# Generated by Django 4.2.17 on 2024-12-17 14:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evapp', '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'),
|
||||
),
|
||||
]
|
|
@ -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 = {'UBU': 'Ubuntu (Standard)',
|
||||
OS_CHOICES = {'FED': 'Fedora (Standard)',
|
||||
'WIN': _('Windows (bitte Begründung angeben)'),
|
||||
'MOS': _('Mac OS (nur wenn Mac gewählt)')}
|
||||
|
||||
'MOS': _('Mac OS (nur wenn Mac gewählt)'),
|
||||
'UBU': _('Ubuntu')}
|
||||
|
||||
LANG_CHOICES = {'GER': 'Deutsch',
|
||||
'ENG': 'English',}
|
||||
|
@ -41,6 +41,9 @@ 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
|
||||
|
@ -52,6 +55,7 @@ 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"))
|
||||
|
@ -62,14 +66,16 @@ 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'))
|
||||
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.'))
|
||||
#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"))
|
||||
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)
|
||||
|
|
|
@ -9,6 +9,7 @@ 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',
|
||||
|
@ -28,7 +29,7 @@ MAILS = {
|
|||
'KOMM': {
|
||||
'MAIL': 'presse@wikimedia.de',
|
||||
'DATA': [
|
||||
'department', 'team',
|
||||
'department', 'team', 'add_to_wikimediade'
|
||||
],
|
||||
},
|
||||
'CENTRAL': {
|
||||
|
@ -43,16 +44,18 @@ 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'
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 99 KiB |
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
|
@ -1,12 +1,17 @@
|
|||
{% 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' %}" />
|
||||
|
||||
|
@ -14,12 +19,17 @@
|
|||
|
||||
{% 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>
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
<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 %}
|
||||
|
@ -60,13 +61,15 @@
|
|||
{% endif %}
|
||||
</p>
|
||||
</h2>
|
||||
|
||||
{% if datatable == True %}
|
||||
<table>
|
||||
<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>
|
||||
|
|
|
@ -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, VENDOR_CHOICES, \
|
||||
LANG_CHOICES, ACCOUNT_CHOICES, TRANSPONDER_CHOICES, KEYBOARD_CHOICES
|
||||
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
|
||||
|
@ -141,21 +141,29 @@ 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} (MAILTEST)',
|
||||
f'EVA: Neuzugang {firstname} {lastname} {firstday} (MAILTEST)',
|
||||
mail_template.render(context),
|
||||
EVA_MAIL,
|
||||
[EVA_MAIL, contact],
|
||||
[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}',
|
||||
f'EVA: Neuzugang {firstname} {lastname} {firstday}',
|
||||
mail_template.render(context),
|
||||
EVA_MAIL,
|
||||
[MAILS[department]['MAIL'], contact],
|
||||
[contact],
|
||||
fail_silently=False)
|
||||
except BadHeaderError as error:
|
||||
print(error)
|
||||
|
@ -181,7 +189,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, **VENDOR_CHOICES, **KEYBOARD_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})
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
|
@ -0,0 +1,9 @@
|
|||
# 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'
|
|
@ -0,0 +1,3 @@
|
|||
from django.db import models
|
||||
|
||||
# Create your models here.
|
|
@ -0,0 +1,97 @@
|
|||
<!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>Choose Your Destination</h1>
|
||||
<div class="container-wrapper">
|
||||
<!-- the first container with image and button for onboarding -->
|
||||
<div class="container">
|
||||
<img src="{% static 'evapp/onboarding1.png' %}"/>
|
||||
<button class="btn" onclick="window.location.href='/onboarding';">Eintritt</button>
|
||||
|
||||
</div>
|
||||
<!-- the second container with image and button for onboarding -->
|
||||
<div class="container">
|
||||
<img src="{% static 'evapp/veränderung.png' %}"/>
|
||||
<button class="btn" onclick="window.location.href='/veränderung';">Veränderung</button>
|
||||
</div>
|
||||
<!-- the third container with image and button for onboarding -->
|
||||
<div class="container">
|
||||
<img src="{% static 'evapp/offboarding.png' %}"/>
|
||||
<button class="btn" onclick="window.location.href='/offboarding';">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>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
|
@ -0,0 +1,9 @@
|
|||
# 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'
|
||||
]
|
|
@ -0,0 +1,15 @@
|
|||
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)
|
|
@ -0,0 +1,6 @@
|
|||
from django.contrib import admin
|
||||
from .models import Employee
|
||||
|
||||
admin.site.register([
|
||||
Employee,
|
||||
])
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class EvappConfig(AppConfig):
|
||||
name = 'offboarding'
|
|
@ -0,0 +1,67 @@
|
|||
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'}
|
||||
|
||||
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'}),}
|
|
@ -0,0 +1,46 @@
|
|||
# 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?')),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.1.4 on 2021-09-14 10:55
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evapp', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='employee',
|
||||
old_name='remote',
|
||||
new_name='works_in_gs',
|
||||
),
|
||||
]
|
|
@ -0,0 +1,28 @@
|
|||
# Generated by Django 3.1.4 on 2022-02-08 09:55
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evapp', '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?)'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,24 @@
|
|||
# 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 = [
|
||||
('evapp', '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'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,19 @@
|
|||
# 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 = [
|
||||
('evapp', '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'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,32 @@
|
|||
# Generated by Django 4.2.16 on 2024-11-26 09:10
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evapp', '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'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,32 @@
|
|||
# Generated by Django 4.2.17 on 2024-12-17 14:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evapp', '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'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,86 @@
|
|||
from django.db import models
|
||||
from multiselectfield import MultiSelectField
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
# 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?'))
|
|
@ -0,0 +1,61 @@
|
|||
# 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'
|
||||
]
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
|
@ -0,0 +1,27 @@
|
|||
{% 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 %}
|
|
@ -0,0 +1,5 @@
|
|||
{% load i18n %}
|
||||
{% autoescape off %}
|
||||
{% for key, value in data.items %}{% if key == 'laptop' %} {{ key }}: {{ value | safe}}{% else %}
|
||||
{% trans key %}: {{ value }}{% endif %}{% endfor %}
|
||||
{% endautoescape %}
|
|
@ -0,0 +1,31 @@
|
|||
{% 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 %}
|
|
@ -0,0 +1,110 @@
|
|||
|
||||
{% 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 %}
|
|
@ -0,0 +1,35 @@
|
|||
{% 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 %}
|
|
@ -0,0 +1,127 @@
|
|||
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'])
|
|
@ -0,0 +1,13 @@
|
|||
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')
|
||||
]
|
|
@ -0,0 +1,217 @@
|
|||
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 = 'offboarding/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
|
|
@ -0,0 +1 @@
|
|||
settings_development.py
|
|
@ -0,0 +1,6 @@
|
|||
from django.contrib import admin
|
||||
from .models import Employee
|
||||
|
||||
admin.site.register([
|
||||
Employee,
|
||||
])
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class EvappConfig(AppConfig):
|
||||
name = 'veränderung'
|
|
@ -0,0 +1,67 @@
|
|||
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'}
|
||||
|
||||
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'}),}
|
|
@ -0,0 +1,46 @@
|
|||
# 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?')),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.1.4 on 2021-09-14 10:55
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evapp', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='employee',
|
||||
old_name='remote',
|
||||
new_name='works_in_gs',
|
||||
),
|
||||
]
|
|
@ -0,0 +1,28 @@
|
|||
# Generated by Django 3.1.4 on 2022-02-08 09:55
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evapp', '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?)'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,24 @@
|
|||
# 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 = [
|
||||
('evapp', '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'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,19 @@
|
|||
# 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 = [
|
||||
('evapp', '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'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,32 @@
|
|||
# Generated by Django 4.2.16 on 2024-11-26 09:10
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evapp', '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'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,32 @@
|
|||
# Generated by Django 4.2.17 on 2024-12-17 14:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evapp', '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'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,86 @@
|
|||
from django.db import models
|
||||
from multiselectfield import MultiSelectField
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
# 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?'))
|
|
@ -0,0 +1,61 @@
|
|||
# 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'
|
||||
]
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
|
@ -0,0 +1,27 @@
|
|||
{% 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 %}
|
|
@ -0,0 +1,35 @@
|
|||
{% 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 %}
|
|
@ -0,0 +1,5 @@
|
|||
{% load i18n %}
|
||||
{% autoescape off %}
|
||||
{% for key, value in data.items %}{% if key == 'laptop' %} {{ key }}: {{ value | safe}}{% else %}
|
||||
{% trans key %}: {{ value }}{% endif %}{% endfor %}
|
||||
{% endautoescape %}
|
|
@ -0,0 +1,31 @@
|
|||
{% 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 %}
|
|
@ -0,0 +1,110 @@
|
|||
|
||||
{% 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 %}
|
|
@ -0,0 +1,127 @@
|
|||
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'])
|
|
@ -0,0 +1,13 @@
|
|||
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')
|
||||
]
|
|
@ -0,0 +1,217 @@
|
|||
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 = 'veränderung/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
|
Loading…
Reference in New Issue