WMDE
/
eva
forked from tohe/eva
7
1
Fork 0

Compare commits

..

8 Commits

92 changed files with 304 additions and 2780 deletions

View File

@ -7,14 +7,12 @@ meaning "Onboarding, Change, Offboarding"
# development # development
- install gettext for instance via "apt install gettext" for translations
- set up a virtual environment with virtualenvwrapper or some other - set up a virtual environment with virtualenvwrapper or some other
environment managing tool environment managing tool
- use this environment and do - use this environment and do
pip install django django-multiselectfield django-formtools django-allauth requests whitenoise pip install django django-multiselectfield django-formtools django-allauth
- clone this repository - clone this repository
@ -24,19 +22,13 @@ environment managing tool
python manage.py migrate python manage.py migrate
- You need to add oauth information in the django backend via .../admin in "Social Accounts"
python manage.py createsuperuser
- start your development server with - start your development server with
python manage.py runserver python manage.py runserver
- Configure OAuth: # oauth
+ Access the admin interface at http://127.0.0.1:8000/admin - You need to add oauth information in the django backend via .../admin in "Social Accounts"
+ Login with your superuser credentials
+ Go to WEBSITE ANZEIGEN
# production # production
@ -60,7 +52,6 @@ run the following commands:
python3 manage.py migrate python3 manage.py migrate
python3 manage.py collectstatic python3 manage.py collectstatic
django-admin compilemessages
server starts with server starts with

5
TODO
View File

@ -1,8 +1,3 @@
* switch to django 4
minor stuff:
* remove dot before "Nextcloud" at login page. * remove dot before "Nextcloud" at login page.
* add Testmode warning at login screen * add Testmode warning at login screen

View File

@ -1,67 +0,0 @@
from django.db import models
from django.forms import ModelForm, DateInput, Form, ChoiceField, RadioSelect
from django.core.exceptions import ValidationError
from .models import Employee
# class EmployeeForm(ModelForm):
# class Meta:
# model = Employee
# fields = '__all__'
# widgets = {'firstdate_employment': DateInput(attrs={'type': 'date'}),
# 'firstdate_presence': DateInput(attrs={'type': 'date'}),}
class DummyForm(ModelForm):
class Meta:
model = Employee
fields = []
class EvaForm(ModelForm):
'''this base class provides the required css class for all forms'''
required_css_class = 'required'
TYPE_CHOICES = {'IN': 'Eintritt', 'CHANGE': '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'}),}

View File

@ -1,28 +0,0 @@
# Generated by Django 3.1.4 on 2022-02-08 09:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('austritt', '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?)'),
),
]

View File

@ -1,24 +0,0 @@
# Generated by Django 4.2.4 on 2023-08-11 10:28
from django.db import migrations, models
import multiselectfield.db.fields
class Migration(migrations.Migration):
dependencies = [
('austritt', '0003_auto_20220208_0955'),
]
operations = [
migrations.AlterField(
model_name='employee',
name='accounts',
field=multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('OTRSWMDE', 'OTRS Ticketsystem'), ('CIVIC1', 'Civic CRM (allgemein)'), ('CIVIC2', 'Civic CRM (Mailings, impliziert allgemein)'), ('WEB', 'www.wikimedia.de (edit)'), ('BLOG', 'blog.wikimedia.de (edit)'), ('FORUM', 'forum.wikimedia.de')], max_length=10, null=True, verbose_name='Zusätzliche Accounts'),
),
migrations.AlterField(
model_name='employee',
name='department',
field=models.CharField(choices=[('COENG', 'Communitys & Engagement'), ('SOFT', 'Softwareentwicklung'), ('CENT', 'Central'), ('KOMAD', 'Kommunikation & Advocacy'), ('VOR', 'Vorstand')], max_length=5, verbose_name='Bereich'),
),
]

View File

@ -1,19 +0,0 @@
# Generated by Django 4.2.4 on 2023-08-17 11:08
from django.db import migrations
import multiselectfield.db.fields
class Migration(migrations.Migration):
dependencies = [
('austritt', '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'),
),
]

View File

@ -1,32 +0,0 @@
# Generated by Django 4.2.16 on 2024-11-26 09:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('austritt', '0005_alter_employee_accounts'),
]
operations = [
migrations.RemoveField(
model_name='employee',
name='vendor',
),
migrations.AddField(
model_name='employee',
name='framework',
field=models.CharField(blank=True, max_length=300, null=True, verbose_name='Möchten Sie vom Standard des Frameworks abweichen, und wenn ja, warum?'),
),
migrations.AlterField(
model_name='employee',
name='add_to_wikimediade',
field=models.CharField(choices=[('NEIN', 'Nein'), ('JA', 'Ja')], default=True, max_length=5, verbose_name='Soll die Person bei Ansprechpartner*innen auf der WMDE-Webseite mit aufgenommen werden?'),
),
migrations.AlterField(
model_name='employee',
name='os',
field=models.CharField(choices=[('FED', 'Fedora (Standard)'), ('WIN', 'Windows (bitte Begründung angeben)'), ('MOS', 'Mac OS (nur wenn Mac gewählt)'), ('UBU', 'Ubuntu')], default='FED', max_length=3, verbose_name='Betriebssystem'),
),
]

View File

@ -1,32 +0,0 @@
# Generated by Django 4.2.17 on 2024-12-17 14:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('austritt', '0006_remove_employee_vendor_employee_framework_and_more'),
]
operations = [
migrations.RemoveField(
model_name='employee',
name='screen',
),
migrations.AlterField(
model_name='employee',
name='add_to_wikimediade',
field=models.CharField(choices=[('NEIN', 'Nein'), ('JA', 'Ja')], max_length=5, verbose_name='Soll die Person bei Ansprechpartner*innen auf der WMDE-Webseite mit aufgenommen werden?'),
),
migrations.AlterField(
model_name='employee',
name='framework',
field=models.CharField(blank=True, max_length=300, null=True, verbose_name='Möchten Sie vom Standard des Frameworks (Laptop) abweichen, und wenn ja, warum?'),
),
migrations.AlterField(
model_name='employee',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@ -1,87 +0,0 @@
from django.db import models
from multiselectfield import MultiSelectField
from django.utils.translation import gettext_lazy as _
from eintritt.models import Employee
# ATTENTION!!!
# No key should be used twice in any of these dicts because of the
# suboptimal implementation in views.EvaFormView.beautify_data()
#
DEPARTMENT_CHOICES = {'COENG': _('Communitys & Engagement'),
'SOFT': _('Softwareentwicklung'),
'CENT': 'Central',
'KOMAD': _('Kommunikation & Advocacy'),
'VOR': _('Vorstand'),}
#VENDOR_CHOICES = {'STANDARD': 'Dell Latitude',
# 'LENOVO': 'Lenovo Thinkpad',
# 'MAC': _('Mac (nur für Grafiker_innen)')}
OS_CHOICES = {'FED': 'Fedora (Standard)',
'WIN': _('Windows (bitte Begründung angeben)'),
'MOS': _('Mac OS (nur wenn Mac gewählt)'),
'UBU': _('Ubuntu')}
LANG_CHOICES = {'GER': 'Deutsch',
'ENG': 'English',}
KEYBOARD_CHOICES = {'DE': 'Deutsch',
'US': 'USA',
'OT': _('Anderes (Bitte unten angeben)')}
ACCOUNT_CHOICES = {'OTRSWMDE': 'OTRS Ticketsystem',
'CIVIC1': _('Civic CRM (allgemein)'),
'CIVIC2': _("Civic CRM (Mailings, impliziert allgemein)"),
'WEB': 'www.wikimedia.de (edit)',
'BLOG': 'blog.wikimedia.de (edit)',
'FORUM': 'forum.wikimedia.de',
}
TRANSPONDER_CHOICES = {'NORM': _('Allgemeiner Transponder'),
'SPECIAL': _('Besondere Schließungen (bitte angeben)'),
'NOTRANS': _('Kein Transponder'),}
JANEIN_CHOICES = {'NEIN': ('Nein'),
'JA': _('Ja'),}
class Employee(models.Model):
# email adress of user. should not be necessary if we use openauth one day
# usermail = models.EmailField(max_length=50, verbose_name="Deine Mailadresse (Ansprechpartner_in)", default='bestechefin@wikimedia.de')
# personal data
firstname = models.CharField(max_length=50, verbose_name=_("Vorname"))
lastname = models.CharField(max_length=50, verbose_name=_("Nachname"))
# intern = models.BooleanField(verbose_name='Interne_r Mitarbeiter_in?', default=True)
department = models.CharField(max_length=5, choices=DEPARTMENT_CHOICES.items(), verbose_name=_('Bereich'))
team = models.CharField(max_length=50, null=True, blank=True) # TODO? better with choices?
add_to_wikimediade = models.CharField(max_length=5, choices=JANEIN_CHOICES.items(), verbose_name=_("Soll die Person bei Ansprechpartner*innen auf der WMDE-Webseite mit aufgenommen werden?"))
# general work related stuff
firstdate_employment = models.DateField(null=True, verbose_name=_("Erster Arbeitstag"))
firstdate_presence = models.DateField(null=True, verbose_name=_("Erster Tag der Anwesenheit in der Geschäftsstelle"))
jobdescription_german = models.CharField(null=True, max_length=100, verbose_name="Stellenbezeichnung(deutsch)")
jobdescription_english = models.CharField(null=True, max_length=100, verbose_name="Job description(english)")
works_in_gs = models.BooleanField(verbose_name=_('Braucht Arbeitsplatz in der Geschäftsstelle?)'), default=True)
desk = models.CharField(max_length=100, null=True, blank=True, verbose_name=_("Wo soll der Arbeitsplatz sein?"))
# IT related stuff
#vendor = models.CharField(max_length=8, choices=VENDOR_CHOICES.items(), default='STANDARD', verbose_name=_('Hersteller'))
framework = models.CharField(max_length=300, null=True, blank=True, verbose_name=_("Möchten Sie vom Standard des Frameworks (Laptop) abweichen, und wenn ja, warum?"))
os = models.CharField(max_length=3, choices=OS_CHOICES.items(), default='FED', verbose_name=_('Betriebssystem'))
#screen = models.BooleanField(default=False, verbose_name=_('Zusätzlicher Monitor? Einer ist standard.'))
mobile = models.BooleanField(max_length=6, default=False, verbose_name=_('Diensttelefon (Handy)'))
landline = models.BooleanField(default = False, verbose_name=_('Festnetznummer (Sipgate)'))
# sim = models.BooleanField(default=False, verbose_name="Mobilfunkvertrag")
keyboard = models.CharField(max_length=2, choices=KEYBOARD_CHOICES.items(), default='DE', verbose_name=_("Tastaturlayout"))
comment = models.TextField(max_length=500, null=True, blank=True, verbose_name=_("zusätzliche IT-Anforderungen"))
language = models.CharField(max_length=3, choices=LANG_CHOICES.items(), default="GER", verbose_name=_("Sprache für Onboarding"))
accounts = MultiSelectField(choices=ACCOUNT_CHOICES.items(), max_length=40, null=True, blank=True, verbose_name=_("Zusätzliche Accounts"))
lists = models.CharField(max_length=100, null=True, blank=True, verbose_name=_("Zusätzliche Mailinglisten"))
rebu2go = models.BooleanField(verbose_name=_("Rebu2Go-Zugang benötigt?"), default=False)
# office related stuff
transponder = models.CharField(max_length=7, choices=TRANSPONDER_CHOICES.items(), default='NORM')
special = models.TextField(max_length=500, null=True, blank=True, verbose_name=_("Besondere Schließungen hier eintragen"))
post_office_box = models.BooleanField(default=True, verbose_name=_('Postfach am Empfang benötigt?'))

View File

@ -1,31 +0,0 @@
{% load i18n %}
(english below)
Hallo!
Es gibt einen Neuzugang bei Wikimedia! Hier ( https://wiki.wikimedia.de/wiki/Onboarding ) kannst Du nachsehen,
welche Schritte jetzt für Deine Abteilung nötig werden. Im Folgenden alle Daten,
die Du dafür brauchst:
{% include 'evapp/dataloop.txt' %}
Wenn Du Fragen hast, melde Dich bei {{contact}}.
Grüße, Deine E.V.A.
-------------------------
{% language 'en' %}
Hi!
There is a new employee at Wikimedia! Here ( https://wiki.wikimedia.de/wiki/Onboarding ) you can see, which
steps are now necessary for your department.
All Data you need for this:
{% include 'evapp/dataloop.txt' %}
If you have any questions please write to {{contact}}.
Regards, Your E.V.A.
{% endlanguage %}

View File

@ -1,110 +0,0 @@
{% load i18n %}
{% load static %}
<script type="text/javascript" src="{% static 'admin/js/core.js' %}"></script>
<script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script>
<script type="text/javascript" src="{% static 'admin/js/jquery.init.js' %}"></script>
{{ form.media }}
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/base.css' %}" />
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/widgets.css' %}" />
{% load socialaccount %}
{% if user.is_authenticated %}
{% block content %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<center>
<style>
ul > li {
list-style-type: none;
}
ul {
padding-left: 10;
}
label.required::after {
content: ' *';
color: red;
}
</style>
<img src="{% static 'evapp/logo.png' %}" />
{% if TESTMODE %}
<h1 style="background-color:red;color:white">{% translate "WARNUNG! Test-MODUS aktiviert. Es werden keine Mails verschickt!" %}</h1>
{% endif %}
<h1>
E (V A) - Austritt, (Veränderung, Austritt)<p>
</h1>{% translate "Du bist eingeloggt als" %} {{ user.email }}
<h2>
<p> {% translate "Schritt" %} {{ wizard.steps.step1 }} {% translate "von" %} {{ wizard.steps.count }}</p>
<p>{% if wizard.steps.step1 == 1 %}
{% translate "Angaben zur Person" %} {% endif %}
{% if choice == 'IN' %}
{% if wizard.steps.step1 == 2 %}
{% translate "Angaben zum neuen Arbeitsverhältnis" %}
{% elif wizard.steps.step1 == 3 %}
{% translate "IT-relevante Angaben" %}
{% elif wizard.steps.step1 == 4 %}
{% translate "Office-relevante Angaben" %}
{% endif %}
{% else %}
{% if wizard.steps.step1 == 2 %}
Veränderungsrelevante Angaben
{% endif %}
{% endif %}
{% if datatable == True %}
{% translate "Bestätigungsschritt" %}
{% endif %}
</p>
</h2>
{% if datatable == True %}
<table id="toggle-heading">
{% for key, value in data.items %}
<tr><th>{{ key }}</th><th>{{ value }}</th></tr>
{% endfor %}
</table>
{% endif %}
<form action="" method="post">
{% csrf_token %}
<table>
{% comment %}
comment this back in if you want to use CHANGE and EXIT process
{% if wizard.steps.step1 > 1 %}
Du hast den Prozess "{{choice_string}}" ausgewählt.
{% endif %}
{% endcomment %}
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
{{ wizard.form }}
{% endif %}
</table>
<p>
<span style="color: red">*</span> {% translate "Pflichtfeld" %}
<p>
{% if wizard.steps.prev %}
<button formnovalidate="formnovalidate" name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">{% translate "Zurück" %}</button>
{% endif %}
{% if datatable == True %}
<button type="submit" value="{% trans "Weiter" %}">{% translate "Abschicken" %}</button>
{% else %}
<button type="submit" value="{% trans "Weiter" %}">{% translate "Weiter" %}</button>
{% endif %}
</form>
<p>
<a href="{% url 'account_logout' %}">{% translate "logout" %}</a>
</center>
{% endblock %}
{% else %}
<a href="{% provider_login_url 'nextcloud' %}">{% translate "Bitte einloggen!" %}</a>
{% endif %}

View File

@ -1,127 +0,0 @@
from django.test import TestCase
from django.test import Client
from django.contrib.auth.models import User
from django.conf import settings
from django.http import HttpResponse
from django.core import mail
from django.utils import translation
from .forms import ITForm, WorkingForm, OfficeForm, DummyForm
class LoginTestCase(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user('vladimir', 'vladimir@reiherzehe.com', 'reiherzehe')
self.client.login(username='vladimir', password='reiherzehe')
self.response = self.client.get('/')
def testLogin(self):
self.assertContains(self.response, 'Du bist eingeloggt als vladimir@reiherzehe.com', status_code=200)
response_en = self.client.get('/', HTTP_ACCEPT_LANGUAGE='en-us')
self.assertContains(response_en, 'You are logged in as vladimir@reiherzehe.com', status_code=200)
self.assertContains(response_en, 'Firstname', status_code=200)
response_en = self.client.get('/', HTTP_ACCEPT_LANGUAGE='en')
self.assertContains(response_en, 'You are logged in as vladimir@reiherzehe.com', status_code=200)
self.assertContains(response_en, 'Firstname', status_code=200)
def testDebugWarning(self):
with self.settings(DEBUG=True):
self.response = self.client.get('/') # we need to do it again with DEBUG = True
self.assertContains(self.response, "WARNUNG! Test-MODUS aktiviert. Es werden keine Mails verschickt!", status_code=200)
with self.settings(DEBUG=False) and self.settings(MAILTEST=False):
self.response = self.client.get('/') # we need to do it again with DEBUG = False
self.assertNotContains(self.response, "WARNUNG! Test-MODUS aktiviert. Es werden keine Mails verschickt!", status_code=200)
def _postform(self, data, expected_form):
'''helper function to manage the Wizzard'''
response = self.client.post('/', data, follow=True)
# print(type(response))
self.assertEqual(200, self.response.status_code)
if not type(response) == HttpResponse:
if 'form' in response.context:
self.assertFalse(response.context['form'].errors)
else:
raise "NO FORM FOUND"
self.assertEqual(
type(response.context['wizard']['form']),
expected_form
)
return response
def test_department(self):
self.assertContains(self.response, 'Programme', status_code=200)
self.assertContains(self.response, 'Kommunikation und Events', status_code=200)
def test_wizzard_in(self):
''' this test goes through the whole onboarding process of the EvaFormView from start to end '''
self.assertEqual(200, self.response.status_code)
response = self._postform({
'eva_form_view-current_step': '0',
'0-firstname': 'Ara',
'0-lastname': 'Seva',
'0-department': 'CENT',
'0-team': 'Community Communications',
'0-choice': 'IN',
}, WorkingForm)
response = self._postform({
'eva_form_view-current_step': '1',
'1-firstdate_employment': '2021-01-01',
'1-firstdate_presence': '2021-01-01',
'1-jobdescription_german': 'hau drauf',
'1-jobdescription_english': 'und schluss',
'1-works_in_gs': False
}, ITForm)
response = self._postform({
'eva_form_view-current_step': '2',
'2-vendor': 'STANDARD',
'2-os': 'UBU',
'2-keyboard': 'DE',
'2-language': 'GER'
}, OfficeForm)
response = self._postform({
'eva_form_view-current_step': '3',
'3-transponder': 'NORM'
}, DummyForm)
response = self._postform({
'eva_form_view-current_step': '5',
}, DummyForm)
def test_mail(self):
self.test_wizzard_in()
# print(mail.outbox[0].body)
self.assertGreater(len(mail.outbox), 2)
self.assertIn("Vorname", mail.outbox[0].body)
self.assertIn("Firstname", mail.outbox[0].body)
for i in (0,1,3):
self.assertIn("Handy", mail.outbox[i].body)
self.assertIn("Ara Seva", mail.outbox[0].subject)
class NoLoginTestCase(TestCase):
def setUp(self):
self.client = Client()
def test_details(self):
response = self.client.get('/')
self.assertEqual(response.status_code, 302)
response2 = self.client.get(response.url)
self.assertContains( response2, 'Bitte via Wolke einloggen:', status_code=200)
class ITFORMTestCase(TestCase):
def test_mac(self):
form = ITForm(data={"vendor": 'MAC', 'os': 'UBU'})
#print (form.errors)
self.assertEqual(form.non_field_errors(), ['Ein MAC sollte Mac OS installiert haben'])
def test_ubu(self):
form = ITForm(data={"vendor": 'STANDARD', 'os': 'UBU'})
#print (form.errors)
self.assertNotEqual(form.non_field_errors(), ['Ein MAC sollte Mac OS installiert haben'])

View File

@ -1,13 +0,0 @@
from django.urls import path
from .views import EvaFormView, success, long_process, change_process
urlpatterns = [
path('', EvaFormView.as_view(condition_dict = {'1': long_process,
'2': long_process,
'3': long_process,
'4': change_process,}),
name='evaform'),
path('success', success, name='success')
]

View File

@ -1,217 +0,0 @@
from smtplib import SMTPException
import collections
from django.views.generic.edit import CreateView
from django.urls import reverse
from django.http import HttpResponse, HttpResponseRedirect
from django.core.mail import send_mail, BadHeaderError
from django.template.loader import get_template
from formtools.wizard.views import CookieWizardView
from django.shortcuts import render
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
from django.utils.translation import gettext_lazy as _
from .models import Employee, DEPARTMENT_CHOICES, OS_CHOICES, \
LANG_CHOICES, ACCOUNT_CHOICES, TRANSPONDER_CHOICES, KEYBOARD_CHOICES, JANEIN_CHOICES
from .forms import PersonalForm, WorkingForm, ITForm, OfficeForm, DummyForm,\
ChangeForm, TYPE_CHOICES
from .settings import MAILS, EVA_MAIL, BASIC_DATA, ONLY_ONBOARDING
def success(request):
return HttpResponse(f"Vielen Dank! Du hast E.V.A. erfolgreich ausgefüllt. Die Mails an die Abteilungen wurden versendet. Kopien gehen an {request.user.email}.")
def long_process(wizard):
'''this method is called via urls.py to determine if a form is part of the IN-Process'''
if ONLY_ONBOARDING:
wizard.set_choice('IN')
return True
else:
data = wizard.get_cleaned_data_for_step('0') or {}
# print(data)
if data.get('choice') != 'CHANGE':
wizard.set_choice('IN')
# print('PROZESS IN')
return True
else:
wizard.set_choice('CHANGE')
# print('PROZESS NOT IN')
return False
def change_process(wizard):
''' this method is called via urls.py to determine if the form is part of the change process'''
# print('CHANGE PROZESS')
return not long_process(wizard)
class EvaFormView(LoginRequiredMixin, CookieWizardView):
template_name = 'austritt/employee_form.html'
form_list = [PersonalForm, WorkingForm, ITForm, OfficeForm, ChangeForm, DummyForm]
instance = None
choice = 'IN'
# maybe we dont need this, if *_process() would be class methods,
# but unsure if this would work fine with the entries in urls.py
def set_choice(self, c):
self.choice = c
def generate_email(self, data):
(first, *_) = data['firstname'].split(maxsplit=1)
(last, *_) = data['lastname'].split(maxsplit=1)
name = first + '.' + last
#if not data['intern']:
# mail = name + '_ext@wikimedia.de'
#else:
mail = name + '@wikimedia.de'
data['email'] = mail
def get_all_cleaned_data(self):
'''this method deletes data which is only used temporary and is not in the modell,
it also changes the mail adress of the employee in some circumstances'''
data = super().get_all_cleaned_data()
self.generate_email(data)
# print("delete CHOICE FROM DATA")
if 'choice' in data:
del data['choice']
return data
def get_context_data(self, form, **kwargs):
'''this method is called to give context data to the template'''
#print('GETCONTEXT')
context = super().get_context_data(form=form, **kwargs)
testmode = settings.DEBUG or settings.MAILTEST
context.update({'choice': self.choice,
'choice_string': TYPE_CHOICES[self.choice],
'TESTMODE': testmode})
# deliver context for forms if we are in the last step
if (self.steps.step1 == 5 or (self.choice != 'IN' and self.steps.step1 == 3)):
context.update({'data': self.beautify_data(self.get_all_cleaned_data()),
'datatable': True,})
return context
def get_form_instance(self,step):
''' this method assures, that we use the same model instance for all steps'''
if self.instance == None:
self.instance = Employee()
return self.instance
def done(self, form_list, **kwargs):
'''this method is called from CookieWizardView after all forms are filled'''
print ('INSTANCE_DICT')
print(self.instance_dict)
# save data to database
for form in form_list:
form.save()
# send data to departments
for dep in MAILS:
response = self.send_mail_to_department(dep)
if not settings.DEBUG:
self.instance.delete()
if response:
return response
else:
return HttpResponseRedirect('success')
def send_mail_to_department(self, department):
'send a mail to the given department with the nececcary notifications'
print(f'send mail to department {department}...')
contact = self.request.user.email
data = self.get_all_cleaned_data()
# some data should be in every mail
newdata = {k: v for k, v in data.items() if (k in BASIC_DATA)}
# only the relevant data should be in the context
newdata.update({k: v for k, v in data.items() if (k in MAILS[department]['DATA'])})
context = {'data': self.beautify_data(newdata), 'contact': contact}
firstname = data['firstname']
lastname = data['lastname']
firstday = data['firstdate_employment']
try:
mail_template = get_template(f'evapp/department_mail.txt')
if settings.MAILTEST:
send_mail(
f'EVA: Neuzugang {firstname} {lastname} {firstday} (MAILTEST)',
mail_template.render(context),
EVA_MAIL,
[EVA_MAIL],
fail_silently=False)
elif department != "SUBMITTER":
send_mail(
f'EVA: Neuzugang {firstname} {lastname} {firstday}',
mail_template.render(context),
EVA_MAIL,
[MAILS[department]['MAIL']],
fail_silently=False)
else:
send_mail(
f'EVA: Neuzugang {firstname} {lastname} {firstday}',
mail_template.render(context),
EVA_MAIL,
[contact],
fail_silently=False)
except BadHeaderError as error:
print(error)
self.instance.delete()
return HttpResponse(f'{error}<p>Invalid header found. Data not saved!')
except SMTPException as error:
print(error)
self.instance.delete()
return HttpResponse(f'{error}<p>Error in sending mails (propably wrong adress?). Data not saved!')
except Exception as error:
print(error)
# self.instance.delete()
return HttpResponse(f'{error}<p>Error in sending mails. Data not saved! Please contact ' + EVA_MAIL)
return False
def beautify_data(self, data):
''' # use long form for contextdata instead of short form if available
#
# ATTENTION!
# This implementation works only for unique keys over all of these dicts from model.py
#
'''
# update values in data dictionary with keys from *_CHOICES if present there
choices = {**DEPARTMENT_CHOICES, **TRANSPONDER_CHOICES,
**OS_CHOICES, **LANG_CHOICES, **KEYBOARD_CHOICES}
data.update({k:choices[v] for k,v in data.items() \
if isinstance(v,collections.abc.Hashable) \
and v in choices})
# replace values in accounts array from *_CHOICES
if 'accounts' in data:
data['accounts'] = [ACCOUNT_CHOICES[c] for c in data['accounts']]
# replace keys in data dictionary with verbose_name
# a bit ugly workaround here: we need to store 'email' away, because it es not in the modell
mail = ''
if 'email' in data:
mail = data.pop('email')
newdata = {self.instance._meta.get_field(k).verbose_name.title() : v for k,v in data.items()}
if mail:
newdata['Email'] = mail
# translate booleans
newdata.update({k:'Ja' for k,v in newdata.items() if isinstance(v,bool) and v == True})
newdata.update({k:'Nein' for k,v in newdata.items() if isinstance(v,bool) and v == False})
# handle some special data types
newdata.update({k:'' for k,v in newdata.items() if v == None})
newdata.update({k:'' for k,v in newdata.items() if v == []})
return newdata

View File

View File

@ -1,10 +0,0 @@
# import Django´s admin interface module
from django.contrib import admin
# import the Employee model from the current app
from .models import Employee
# Register the Employee model with the Django admin site
# This makes the Employee model manageable through the admin interface
admin.site.register([
Employee,
])

View File

@ -1,7 +0,0 @@
# import django´s base AppConfig class
from django.apps import AppConfig
# define the configuration class for the 'evapp' application
class EintrittConfig(AppConfig):
# give the app a name
name = 'eintritt'

View File

@ -1,65 +0,0 @@
from django.db import models
from django.forms import ModelForm, DateInput, Form, ChoiceField, RadioSelect
from django.core.exceptions import ValidationError
from .models import Employee
# class EmployeeForm(ModelForm):
# class Meta:
# model = Employee
# fields = '__all__'
# widgets = {'firstdate_employment': DateInput(attrs={'type': 'date'}),
# 'firstdate_presence': DateInput(attrs={'type': 'date'}),}
class DummyForm(ModelForm):
class Meta:
model = Employee
fields = []
class EvaForm(ModelForm):
'''this base class provides the required css class for all forms'''
required_css_class = 'required'
TYPE_CHOICES = {'IN': 'Eintritt', 'CHANGE': 'Veränderung', 'OUT': 'Austritt'}
# Form to capture basic personal and department-related information
class PersonalForm(EvaForm):
class Meta:
model = Employee
fields = ['firstname', 'lastname', 'department', 'team', 'add_to_wikimediade',]
# Form to capture working conditions and job description
class WorkingForm(EvaForm):
def clean(self):
data = self.cleaned_data
if data['works_in_gs'] and data['desk'] is None:
raise ValidationError('Wer nicht remote arbeitet braucht einen Schreibtisch!')
return data
class Meta:
model = Employee
fields = ['firstdate_employment', 'firstdate_presence', 'jobdescription_german',
'jobdescription_english', 'works_in_gs', 'desk',]
widgets = {'firstdate_employment': DateInput(attrs={'type': 'date'}),
'firstdate_presence': DateInput(attrs={'type': 'date'}),}
class ITForm(EvaForm):
class Meta:
model = Employee
fields = [
'framework', 'os', 'keyboard', 'mobile', 'landline',
'comment', 'language', 'accounts', 'lists', 'rebu2go' ]
class OfficeForm(EvaForm):
class Meta:
model = Employee
fields = ['transponder', 'special', 'post_office_box',]
class ChangeForm(EvaForm):
class Meta:
model = Employee
fields = ['firstdate_employment', 'jobdescription_german', 'jobdescription_english',
'desk', 'comment', 'accounts', 'lists', 'transponder']
widgets = {'firstdate_employment': DateInput(attrs={'type': 'date'}),}

View File

@ -1,46 +0,0 @@
# Generated by Django 3.1.4 on 2021-09-13 12:41
from django.db import migrations, models
import multiselectfield.db.fields
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Employee',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('firstname', models.CharField(max_length=50, verbose_name='Vorname')),
('lastname', models.CharField(max_length=50, verbose_name='Nachname')),
('department', models.CharField(choices=[('PROG', 'Programme'), ('SOFT', 'Softwareentwicklung'), ('CENT', 'Central'), ('VOR', 'Vorstand')], max_length=5, verbose_name='Bereich')),
('team', models.CharField(blank=True, max_length=20, null=True)),
('add_to_wikimediade', models.BooleanField(default=False, verbose_name='Soll auf wikimedia.de irgendwo stehen?')),
('firstdate_employment', models.DateField(null=True, verbose_name='Erster Arbeitstag')),
('firstdate_presence', models.DateField(null=True, verbose_name='Erster Tag der Anwesenheit in der Geschäftsstelle')),
('jobdescription_german', models.CharField(max_length=100, null=True, verbose_name='Stellenbezeichnung(deutsch)')),
('jobdescription_english', models.CharField(max_length=100, null=True, verbose_name='Job description(english)')),
('remote', models.BooleanField(default=True, verbose_name='Braucht Arbeitsplatz in der Geschäftsstelle?')),
('desk', models.CharField(blank=True, max_length=100, null=True, verbose_name='Wo soll der Arbeitsplatz sein?')),
('vendor', models.CharField(choices=[('STANDARD', 'Dell Latitude'), ('LENOVO', 'Lenovo Thinkpad'), ('MAC', 'Mac (nur für Grafiker_innen)')], default='STANDARD', max_length=8, verbose_name='Hersteller')),
('os', models.CharField(choices=[('UBU', 'Ubuntu (Standard)'), ('WIN', 'Windows (bitte Begründung angeben)'), ('MOS', 'Mac OS (nur wenn Mac gewählt)')], default='UBU', max_length=3, verbose_name='Betriebssystem')),
('screen', models.BooleanField(default=False, verbose_name='Zusätzlicher Monitor? Einer ist standard.')),
('mobile', models.BooleanField(default=False, max_length=6, verbose_name='Diensttelefon (Handy)')),
('landline', models.BooleanField(default=False, verbose_name='Festnetznummer (Sipgate)')),
('keyboard', models.CharField(choices=[('DE', 'Deutsch'), ('US', 'USA'), ('OT', 'Anderes (Bitte unten angeben)')], default='DE', max_length=2, verbose_name='Tastaturlayout')),
('comment', models.TextField(blank=True, max_length=500, null=True, verbose_name='zusätzliche IT-Anforderungen')),
('language', models.CharField(choices=[('GER', 'Deutsch'), ('ENG', 'English')], default='GER', max_length=3, verbose_name='Sprache für Onboarding')),
('accounts', multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('OTRSWMDE', 'OTRS Ticketsystem'), ('CIVIC1', 'Civic CRM (allgemein)'), ('CIVIC2', 'Civic CRM (Mailings, impliziert allgemein)'), ('WEB', 'www.wikimedia.de (edit)'), ('BLOG', 'blog.wikimedia.de (edit)'), ('FORUM', 'forum.wikimedia.de')], max_length=37, null=True, verbose_name='Zusätzliche Accounts')),
('lists', models.CharField(blank=True, max_length=100, null=True, verbose_name='Zusätzliche Mailinglisten')),
('rebu2go', models.BooleanField(default=False, verbose_name='Rebu2Go-Zugang benötigt?')),
('transponder', models.CharField(choices=[('NORM', 'Allgemeiner Transponder'), ('SPECIAL', 'Besondere Schließungen (bitte angeben)'), ('NOTRANS', 'Kein Transponder')], default='NORM', max_length=7)),
('special', models.TextField(blank=True, max_length=500, null=True, verbose_name='Besondere Schließungen hier eintragen')),
('post_office_box', models.BooleanField(default=True, verbose_name='Postfach am Empfang benötigt?')),
],
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 3.1.4 on 2021-09-14 10:55
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('eintritt', '0001_initial'),
]
operations = [
migrations.RenameField(
model_name='employee',
old_name='remote',
new_name='works_in_gs',
),
]

View File

@ -1,28 +0,0 @@
# Generated by Django 3.1.4 on 2022-02-08 09:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('eintritt', '0002_auto_20210914_1055'),
]
operations = [
migrations.AlterField(
model_name='employee',
name='department',
field=models.CharField(choices=[('PROG', 'Programme'), ('SOFT', 'Softwareentwicklung'), ('CENT', 'Central'), ('KOMEV', 'Kommunikation und Events'), ('VOR', 'Vorstand')], max_length=5, verbose_name='Bereich'),
),
migrations.AlterField(
model_name='employee',
name='team',
field=models.CharField(blank=True, max_length=50, null=True),
),
migrations.AlterField(
model_name='employee',
name='works_in_gs',
field=models.BooleanField(default=True, verbose_name='Braucht Arbeitsplatz in der Geschäftsstelle?)'),
),
]

View File

@ -1,24 +0,0 @@
# Generated by Django 4.2.4 on 2023-08-11 10:28
from django.db import migrations, models
import multiselectfield.db.fields
class Migration(migrations.Migration):
dependencies = [
('eintritt', '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'),
),
]

View File

@ -1,19 +0,0 @@
# Generated by Django 4.2.4 on 2023-08-17 11:08
from django.db import migrations
import multiselectfield.db.fields
class Migration(migrations.Migration):
dependencies = [
('eintritt', '0004_alter_employee_accounts_alter_employee_department'),
]
operations = [
migrations.AlterField(
model_name='employee',
name='accounts',
field=multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('OTRSWMDE', 'OTRS Ticketsystem'), ('CIVIC1', 'Civic CRM (allgemein)'), ('CIVIC2', 'Civic CRM (Mailings, impliziert allgemein)'), ('WEB', 'www.wikimedia.de (edit)'), ('BLOG', 'blog.wikimedia.de (edit)'), ('FORUM', 'forum.wikimedia.de')], max_length=40, null=True, verbose_name='Zusätzliche Accounts'),
),
]

View File

@ -1,32 +0,0 @@
# Generated by Django 4.2.16 on 2024-11-26 09:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('eintritt', '0005_alter_employee_accounts'),
]
operations = [
migrations.RemoveField(
model_name='employee',
name='vendor',
),
migrations.AddField(
model_name='employee',
name='framework',
field=models.CharField(blank=True, max_length=300, null=True, verbose_name='Möchten Sie vom Standard des Frameworks abweichen, und wenn ja, warum?'),
),
migrations.AlterField(
model_name='employee',
name='add_to_wikimediade',
field=models.CharField(choices=[('NEIN', 'Nein'), ('JA', 'Ja')], default=True, max_length=5, verbose_name='Soll die Person bei Ansprechpartner*innen auf der WMDE-Webseite mit aufgenommen werden?'),
),
migrations.AlterField(
model_name='employee',
name='os',
field=models.CharField(choices=[('FED', 'Fedora (Standard)'), ('WIN', 'Windows (bitte Begründung angeben)'), ('MOS', 'Mac OS (nur wenn Mac gewählt)'), ('UBU', 'Ubuntu')], default='FED', max_length=3, verbose_name='Betriebssystem'),
),
]

View File

@ -1,32 +0,0 @@
# Generated by Django 4.2.17 on 2024-12-17 14:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('eintritt', '0006_remove_employee_vendor_employee_framework_and_more'),
]
operations = [
migrations.RemoveField(
model_name='employee',
name='screen',
),
migrations.AlterField(
model_name='employee',
name='add_to_wikimediade',
field=models.CharField(choices=[('NEIN', 'Nein'), ('JA', 'Ja')], max_length=5, verbose_name='Soll die Person bei Ansprechpartner*innen auf der WMDE-Webseite mit aufgenommen werden?'),
),
migrations.AlterField(
model_name='employee',
name='framework',
field=models.CharField(blank=True, max_length=300, null=True, verbose_name='Möchten Sie vom Standard des Frameworks (Laptop) abweichen, und wenn ja, warum?'),
),
migrations.AlterField(
model_name='employee',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@ -1,86 +0,0 @@
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?'))

View File

@ -1,61 +0,0 @@
# temporary setting while change and exit is not yet fully implemented
ONLY_ONBOARDING = True
# sender mail adress also used for MAILTEST mode
EVA_MAIL = 'it-support@wikimedia.de'
# these Fields should be included in every mail
BASIC_DATA = ['firstname', 'lastname', 'firstdate_employment', 'firstdate_presence',
'jobdescription_german', 'jobdescription_english',]
# for every department: 'MAIL' => mail adress, 'DATA': additional fields to include
# also one copy with all fields to the person filling the form.
MAILS = {
'IT': {
'MAIL': 'wmde-it@wikimedia.de',
'DATA': [
'laptop', 'os', 'comment', 'email', 'landline', 'lists', 'mobile',
'department', 'accounts', 'language', 'screen', 'works_in_gs', 'desk',
'keyboard',
],
},
'OFFICE': {
'MAIL': 'office@wikimedia.de',
'DATA': [
'transponder', 'special', 'post_office_box', 'mobile',
'works_in_gs', 'desk',
],
},
'KOMM': {
'MAIL': 'presse@wikimedia.de',
'DATA': [
'department', 'team', 'add_to_wikimediade'
],
},
'CENTRAL': {
'MAIL': 'anna.noelte@wikimedia.de',
'DATA': [
'department', 'team', 'language', 'mobile', 'rebu2go'
],
},
'HR': {
'MAIL': 'personal@wikimedia.de',
'DATA': [
'department', 'team', 'language',
]
},
'FINANCE': {
'MAIL': 'claudia.langrock@wikimedia.de',
'DATA': [
'rebu2go'
]
},
'SUBMITTER': {
'MAIL': 'submitter@wikimedia.de',
'DATA': [
'department', 'team', 'add_to_wikimediade', 'remote', 'desk', 'vendor',
'os', 'screen', 'mobile', 'landline', 'keyboard', 'comment', 'language',
'accounts', 'lists', 'rebu2go', 'transponder', 'special', 'post_office_box'
]
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

View File

@ -1,37 +0,0 @@
{% load i18n %}
{% load static %}
<!-- Load core JavaScript files needed for admin functionality -->
<script type="text/javascript" src="{% static 'admin/js/core.js' %}"></script>
<script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script>
<script type="text/javascript" src="{% static 'admin/js/jquery.init.js' %}"></script>
<!-- Render any JavaScript or CSS required by form widgets -->
{{ form.media }}
<!-- Load admin base and widgets stylesheets -->
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/base.css' %}" />
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/widgets.css' %}" />
{% load account socialaccount %}
{% block content %}
<center>
<!-- Display logo image -->
<img src="{% static 'evapp/logo.png' %}" />
<!-- Main heading for the page -->
<h1>
E (V A) - Eintritt, (Veränderung, Austritt)<p>
</h1>
<!-- Instruction text for users to log in via cloud (social login) -->
Bitte via Wolke einloggen:
<!-- Include the social login providers list -->
{% include "socialaccount/snippets/provider_list.html" with process="login" %}
</center>
{% endblock %}

View File

@ -1,5 +0,0 @@
{% load i18n %}
{% autoescape off %}
{% for key, value in data.items %}{% if key == 'laptop' %} {{ key }}: {{ value | safe}}{% else %}
{% trans key %}: {{ value }}{% endif %}{% endfor %}
{% endautoescape %}

View File

@ -1,31 +0,0 @@
{% load i18n %}
(english below)
Hallo!
Es gibt einen Neuzugang bei Wikimedia! Hier ( https://wiki.wikimedia.de/wiki/Onboarding ) kannst Du nachsehen,
welche Schritte jetzt für Deine Abteilung nötig werden. Im Folgenden alle Daten,
die Du dafür brauchst:
{% include 'evapp/dataloop.txt' %}
Wenn Du Fragen hast, melde Dich bei {{contact}}.
Grüße, Deine E.V.A.
-------------------------
{% language 'en' %}
Hi!
There is a new employee at Wikimedia! Here ( https://wiki.wikimedia.de/wiki/Onboarding ) you can see, which
steps are now necessary for your department.
All Data you need for this:
{% include 'evapp/dataloop.txt' %}
If you have any questions please write to {{contact}}.
Regards, Your E.V.A.
{% endlanguage %}

View File

@ -1,35 +0,0 @@
{% block content %}
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
{% if next %}
{% if user.is_authenticated %}
<p>Your account doesn't have access to this page. To proceed,
please login with an account that has access.</p>
{% else %}
<p>Please login to see this page.</p>
{% endif %}
{% endif %}
<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<table>
<tr>
<td>{{ form.username.label_tag }}</td>
<td>{{ form.username }}</td>
</tr>
<tr>
<td>{{ form.password.label_tag }}</td>
<td>{{ form.password }}</td>
</tr>
</table>
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
{# Assumes you setup the password_reset view in your URLconf #}
<p><a href="{% url 'password_reset' %}">Lost password?</a></p>
{% endblock %}

View File

@ -1,127 +0,0 @@
from django.test import TestCase
from django.test import Client
from django.contrib.auth.models import User
from django.conf import settings
from django.http import HttpResponse
from django.core import mail
from django.utils import translation
from .forms import ITForm, WorkingForm, OfficeForm, DummyForm
class LoginTestCase(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user('vladimir', 'vladimir@reiherzehe.com', 'reiherzehe')
self.client.login(username='vladimir', password='reiherzehe')
self.response = self.client.get('/')
def testLogin(self):
self.assertContains(self.response, 'Du bist eingeloggt als vladimir@reiherzehe.com', status_code=200)
response_en = self.client.get('/', HTTP_ACCEPT_LANGUAGE='en-us')
self.assertContains(response_en, 'You are logged in as vladimir@reiherzehe.com', status_code=200)
self.assertContains(response_en, 'Firstname', status_code=200)
response_en = self.client.get('/', HTTP_ACCEPT_LANGUAGE='en')
self.assertContains(response_en, 'You are logged in as vladimir@reiherzehe.com', status_code=200)
self.assertContains(response_en, 'Firstname', status_code=200)
def testDebugWarning(self):
with self.settings(DEBUG=True):
self.response = self.client.get('/') # we need to do it again with DEBUG = True
self.assertContains(self.response, "WARNUNG! Test-MODUS aktiviert. Es werden keine Mails verschickt!", status_code=200)
with self.settings(DEBUG=False) and self.settings(MAILTEST=False):
self.response = self.client.get('/') # we need to do it again with DEBUG = False
self.assertNotContains(self.response, "WARNUNG! Test-MODUS aktiviert. Es werden keine Mails verschickt!", status_code=200)
def _postform(self, data, expected_form):
'''helper function to manage the Wizzard'''
response = self.client.post('/', data, follow=True)
# print(type(response))
self.assertEqual(200, self.response.status_code)
if not type(response) == HttpResponse:
if 'form' in response.context:
self.assertFalse(response.context['form'].errors)
else:
raise "NO FORM FOUND"
self.assertEqual(
type(response.context['wizard']['form']),
expected_form
)
return response
def test_department(self):
self.assertContains(self.response, 'Programme', status_code=200)
self.assertContains(self.response, 'Kommunikation und Events', status_code=200)
def test_wizzard_in(self):
''' this test goes through the whole onboarding process of the EvaFormView from start to end '''
self.assertEqual(200, self.response.status_code)
response = self._postform({
'eva_form_view-current_step': '0',
'0-firstname': 'Ara',
'0-lastname': 'Seva',
'0-department': 'CENT',
'0-team': 'Community Communications',
'0-choice': 'IN',
}, WorkingForm)
response = self._postform({
'eva_form_view-current_step': '1',
'1-firstdate_employment': '2021-01-01',
'1-firstdate_presence': '2021-01-01',
'1-jobdescription_german': 'hau drauf',
'1-jobdescription_english': 'und schluss',
'1-works_in_gs': False
}, ITForm)
response = self._postform({
'eva_form_view-current_step': '2',
'2-vendor': 'STANDARD',
'2-os': 'UBU',
'2-keyboard': 'DE',
'2-language': 'GER'
}, OfficeForm)
response = self._postform({
'eva_form_view-current_step': '3',
'3-transponder': 'NORM'
}, DummyForm)
response = self._postform({
'eva_form_view-current_step': '5',
}, DummyForm)
def test_mail(self):
self.test_wizzard_in()
# print(mail.outbox[0].body)
self.assertGreater(len(mail.outbox), 2)
self.assertIn("Vorname", mail.outbox[0].body)
self.assertIn("Firstname", mail.outbox[0].body)
for i in (0,1,3):
self.assertIn("Handy", mail.outbox[i].body)
self.assertIn("Ara Seva", mail.outbox[0].subject)
class NoLoginTestCase(TestCase):
def setUp(self):
self.client = Client()
def test_details(self):
response = self.client.get('/')
self.assertEqual(response.status_code, 302)
response2 = self.client.get(response.url)
self.assertContains( response2, 'Bitte via Wolke einloggen:', status_code=200)
class ITFORMTestCase(TestCase):
def test_mac(self):
form = ITForm(data={"vendor": 'MAC', 'os': 'UBU'})
#print (form.errors)
self.assertEqual(form.non_field_errors(), ['Ein MAC sollte Mac OS installiert haben'])
def test_ubu(self):
form = ITForm(data={"vendor": 'STANDARD', 'os': 'UBU'})
#print (form.errors)
self.assertNotEqual(form.non_field_errors(), ['Ein MAC sollte Mac OS installiert haben'])

View File

@ -1,13 +0,0 @@
from django.urls import path
from .views import EvaFormView, success, long_process, change_process
urlpatterns = [
path('', EvaFormView.as_view(condition_dict = {'1': long_process,
'2': long_process,
'3': long_process,
'4': change_process,}),
name='evaform'),
path('success', success, name='success')
]

View File

@ -1,217 +0,0 @@
from smtplib import SMTPException
import collections
from django.views.generic.edit import CreateView
from django.urls import reverse
from django.http import HttpResponse, HttpResponseRedirect
from django.core.mail import send_mail, BadHeaderError
from django.template.loader import get_template
from formtools.wizard.views import CookieWizardView
from django.shortcuts import render
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
from django.utils.translation import gettext_lazy as _
from .models import Employee, DEPARTMENT_CHOICES, OS_CHOICES, \
LANG_CHOICES, ACCOUNT_CHOICES, TRANSPONDER_CHOICES, KEYBOARD_CHOICES, JANEIN_CHOICES
from .forms import PersonalForm, WorkingForm, ITForm, OfficeForm, DummyForm,\
ChangeForm, TYPE_CHOICES
from .settings import MAILS, EVA_MAIL, BASIC_DATA, ONLY_ONBOARDING
def success(request):
return HttpResponse(f"Vielen Dank! Du hast E.V.A. erfolgreich ausgefüllt. Die Mails an die Abteilungen wurden versendet. Kopien gehen an {request.user.email}.")
def long_process(wizard):
'''this method is called via urls.py to determine if a form is part of the IN-Process'''
if ONLY_ONBOARDING:
wizard.set_choice('IN')
return True
else:
data = wizard.get_cleaned_data_for_step('0') or {}
# print(data)
if data.get('choice') != 'CHANGE':
wizard.set_choice('IN')
# print('PROZESS IN')
return True
else:
wizard.set_choice('CHANGE')
# print('PROZESS NOT IN')
return False
def change_process(wizard):
''' this method is called via urls.py to determine if the form is part of the change process'''
# print('CHANGE PROZESS')
return not long_process(wizard)
class EvaFormView(LoginRequiredMixin, CookieWizardView):
template_name = 'eintritt/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

View File

@ -11,7 +11,6 @@ https://docs.djangoproject.com/en/3.1/ref/settings/
""" """
from pathlib import Path from pathlib import Path
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent BASE_DIR = Path(__file__).resolve().parent.parent
@ -39,10 +38,7 @@ ALLOWED_HOSTS = ['*']
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
'home', 'evapp.apps.EvappConfig',
'austritt',
'veraenderung',
'eintritt.apps.EintrittConfig',
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
@ -62,14 +58,12 @@ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'allauth.account.middleware.AccountMiddleware' ]
]
ROOT_URLCONF = 'eva.urls' ROOT_URLCONF = 'eva.urls'
@ -125,7 +119,7 @@ AUTH_PASSWORD_VALIDATORS = [
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/ # https://docs.djangoproject.com/en/3.1/topics/i18n/
LANGUAGE_CODE = 'de' LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC' TIME_ZONE = 'UTC'
@ -135,7 +129,6 @@ USE_L10N = True
USE_TZ = True USE_TZ = True
LOCALE_PATHS = ( os.path.join(BASE_DIR, 'locale'), )
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/ # https://docs.djangoproject.com/en/3.1/howto/static-files/
@ -163,4 +156,3 @@ ACCOUNT_EMAIL_VERIFICATION = 'none'
# ACCOUNT_EMAIL_REQUIRED = True # ACCOUNT_EMAIL_REQUIRED = True
LOGIN_REDIRECT_URL = 'home' LOGIN_REDIRECT_URL = 'home'
ACCOUNT_LOGOUT_ON_GET = True ACCOUNT_LOGOUT_ON_GET = True
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

View File

@ -81,7 +81,6 @@ MIDDLEWARE = [
'whitenoise.middleware.WhiteNoiseMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
@ -142,7 +141,7 @@ AUTH_PASSWORD_VALIDATORS = [
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/ # https://docs.djangoproject.com/en/3.1/topics/i18n/
LANGUAGE_CODE = 'de' LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC' TIME_ZONE = 'UTC'
@ -152,7 +151,6 @@ USE_L10N = True
USE_TZ = True USE_TZ = True
LOCALE_PATHS = ( os.path.join(BASE_DIR, 'locale'), )
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/ # https://docs.djangoproject.com/en/3.1/howto/static-files/

View File

@ -17,10 +17,7 @@ from django.contrib import admin
from django.urls import path, include from django.urls import path, include
urlpatterns = [ urlpatterns = [
path('eintritt/', include('eintritt.urls')), path('', include("evapp.urls")),
path('', include("home.urls")),
path('austritt/', include("austritt.urls")),
path('veraenderung/', include("veraenderung.urls")),
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('accounts/', include('allauth.urls')), path('accounts/', include('allauth.urls')),

View File

@ -2,4 +2,4 @@ from django.apps import AppConfig
class EvappConfig(AppConfig): class EvappConfig(AppConfig):
name = 'austritt' name = 'evapp'

View File

@ -20,16 +20,22 @@ class EvaForm(ModelForm):
'''this base class provides the required css class for all forms''' '''this base class provides the required css class for all forms'''
required_css_class = 'required' required_css_class = 'required'
TYPE_CHOICES = {'IN': 'Eintritt', 'CHANGE': 'veraenderung', 'OUT': 'Austritt'} # TYPE_CHOICES = {'IN': 'Eintritt', 'CHANGE': 'Veränderung', 'OUT': 'Austritt'}
TYPE_CHOICES = {'IN': 'Eintritt', 'OUT': 'Austritt'}
class PersonalForm(EvaForm): class PersonalForm(EvaForm):
# TODO: comment this back in to use implementation of change or exit process choice = ChoiceField(choices=TYPE_CHOICES.items(), widget=RadioSelect,
# choice = ChoiceField(choices=TYPE_CHOICES.items(), widget=RadioSelect, label='Welcher Prozess soll angestoßen werden?')
# label='Welcher Prozess soll angestoßen werden?')
class Meta: class Meta:
model = Employee model = Employee
fields = ['firstname', 'lastname', 'department', 'team', 'add_to_wikimediade',] fields = ['firstname', 'lastname', 'department', 'team', ]
class OffboardingForm(EvaForm):
class Meta:
model = Employee
fields = ['lastdate_employment']
widgets = {'lastdate_employment' : DateInput(attrs={'type': 'date'})}
class WorkingForm(EvaForm): class WorkingForm(EvaForm):
@ -48,11 +54,17 @@ class WorkingForm(EvaForm):
class ITForm(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: class Meta:
model = Employee model = Employee
fields = [ fields = [
'framework', 'os', 'keyboard', 'mobile', 'landline', 'vendor', 'os', 'keyboard', 'screen', 'mobile', 'comment',
'comment', 'language', 'accounts', 'lists', 'rebu2go' ] 'language', 'accounts', 'lists', 'rebu2go' ]
class OfficeForm(EvaForm): class OfficeForm(EvaForm):
class Meta: class Meta:

View File

@ -20,7 +20,6 @@ class Migration(migrations.Migration):
('lastname', models.CharField(max_length=50, verbose_name='Nachname')), ('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')), ('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)), ('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_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')), ('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_german', models.CharField(max_length=100, null=True, verbose_name='Stellenbezeichnung(deutsch)')),
@ -31,10 +30,9 @@ class Migration(migrations.Migration):
('os', models.CharField(choices=[('UBU', 'Ubuntu (Standard)'), ('WIN', 'Windows (bitte Begründung angeben)'), ('MOS', 'Mac OS (nur wenn Mac gewählt)')], default='UBU', max_length=3, verbose_name='Betriebssystem')), ('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.')), ('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)')), ('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')), ('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')), ('comment', models.TextField(blank=True, max_length=500, null=True, verbose_name='zusätzliche IT-Anforderungen')),
('language', models.CharField(choices=[('GER', 'Deutsch'), ('ENG', 'English')], default='GER', max_length=3, verbose_name='Sprache für Onboarding')), ('language', models.CharField(choices=[('GER', 'Deutsch'), ('ENG', 'English')], default='GER', max_length=3, verbose_name='Sprache')),
('accounts', multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('OTRSWMDE', 'OTRS Ticketsystem'), ('CIVIC1', 'Civic CRM (allgemein)'), ('CIVIC2', 'Civic CRM (Mailings, impliziert allgemein)'), ('WEB', 'www.wikimedia.de (edit)'), ('BLOG', 'blog.wikimedia.de (edit)'), ('FORUM', 'forum.wikimedia.de')], max_length=37, null=True, verbose_name='Zusätzliche Accounts')), ('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')), ('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?')), ('rebu2go', models.BooleanField(default=False, verbose_name='Rebu2Go-Zugang benötigt?')),

View File

@ -6,7 +6,7 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('austritt', '0001_initial'), ('evapp', '0001_initial'),
] ]
operations = [ operations = [

View File

@ -0,0 +1,18 @@
# Generated by Django 3.1.4 on 2021-11-10 08:32
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'),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.1.4 on 2021-11-10 12:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('evapp', '0003_auto_20211110_0832'),
]
operations = [
migrations.AddField(
model_name='employee',
name='lastdate_employment',
field=models.DateField(null=True, verbose_name='Letzter Arbeitstag'),
),
migrations.AddField(
model_name='employee',
name='lastdate_presence',
field=models.DateField(null=True, verbose_name='Letzter Tag der Anwesenheit in der Geschäftsstelle'),
),
]

81
evapp/models.py Normal file
View File

@ -0,0 +1,81 @@
from django.db import models
from multiselectfield import MultiSelectField
# ATTENTION!!!
# No key should be used twice in any of these dicts because of the
# suboptimal implementation in views.EvaFormView.beautify_data()
#
DEPARTMENT_CHOICES = {'PROG': 'Programme',
'SOFT': 'Softwareentwicklung',
'CENT': 'Central',
'KOMEV': 'Kommunikation und Events',
'VOR': 'Vorstand',}
VENDOR_CHOICES = {'STANDARD': 'Dell Latitude',
'LENOVO': 'Lenovo Thinkpad',
'MAC': 'Mac (nur für Grafiker_innen)'}
OS_CHOICES = {'UBU': 'Ubuntu (Standard)',
'WIN': 'Windows (bitte Begründung angeben)',
'MOS': 'Mac OS (nur wenn Mac gewählt)'}
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',}
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=20, null=True, blank=True) # TODO? better with choices?
# 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")
lastdate_employment= models.DateField(null=True, verbose_name="Letzter Arbeitstag")
lastdate_presence = models.DateField(null=True, verbose_name="Letzter 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')
os = models.CharField(max_length=3, choices=OS_CHOICES.items(), default='UBU', verbose_name='Betriebssystem')
screen = models.BooleanField(default=False, verbose_name='Zusätzlicher Monitor? Einer ist standard.')
mobile = models.BooleanField(max_length=6, default=False, verbose_name='Diensttelefon (Handy)')
# 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")
accounts = MultiSelectField(choices=ACCOUNT_CHOICES.items(), 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?')

View File

@ -1,15 +1,13 @@
# temporary setting while change and exit is not yet fully implemented # temporary setting while change and exit is not yet fully implemented
ONLY_ONBOARDING = True ONLY_ONBOARDING = False
# sender mail adress also used for MAILTEST mode # sender mail adress also used for MAILTEST mode
EVA_MAIL = 'it-support@wikimedia.de' EVA_MAIL = 'it-support@wikimedia.de'
# these Fields should be included in every mail # these Fields should be included in every mail
BASIC_DATA = ['firstname', 'lastname', 'firstdate_employment', 'firstdate_presence', BASIC_DATA = ['firstname', 'lastname', 'firstdate_employment', 'firstdate_presence',]
'jobdescription_german', 'jobdescription_english',]
# for every department: 'MAIL' => mail adress, 'DATA': additional fields to include # for every department: 'MAIL' => mail adress, 'DATA': additional fields to include
# also one copy with all fields to the person filling the form.
MAILS = { MAILS = {
'IT': { 'IT': {
'MAIL': 'wmde-it@wikimedia.de', 'MAIL': 'wmde-it@wikimedia.de',
@ -22,20 +20,21 @@ MAILS = {
'OFFICE': { 'OFFICE': {
'MAIL': 'office@wikimedia.de', 'MAIL': 'office@wikimedia.de',
'DATA': [ 'DATA': [
'transponder', 'special', 'post_office_box', 'mobile', 'transponder', 'special', 'post_office_box', 'sim', 'sim2',
'works_in_gs', 'desk', 'works_in_gs', 'desk',
], ],
}, },
'KOMM': { 'KOMM': {
'MAIL': 'presse@wikimedia.de', 'MAIL': 'presse@wikimedia.de',
'DATA': [ 'DATA': [
'department', 'team', 'add_to_wikimediade' 'department', 'team',
'jobdescription_german', 'jobdescription_english',
], ],
}, },
'CENTRAL': { 'CENTRAL': {
'MAIL': 'anna.noelte@wikimedia.de', 'MAIL': 'eileen.miedtank@wikimedia.de',
'DATA': [ 'DATA': [
'department', 'team', 'language', 'mobile', 'rebu2go' 'department', 'team', 'language', 'sim', 'sim2', 'rebu2go'
], ],
}, },
'HR': { 'HR': {
@ -44,18 +43,11 @@ MAILS = {
'department', 'team', 'language', 'department', 'team', 'language',
] ]
}, },
'FINANCE': { 'DIRECTORAT': {
'MAIL': 'claudia.langrock@wikimedia.de', 'MAIL': 'ricarda.busse@wikimedia.de',
'DATA': [ 'DATA': [
'rebu2go' 'team', 'department', 'language',
]
},
'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'
] ]
} }
} }

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -1,5 +1,4 @@
{% load i18n %}
{% autoescape off %} {% autoescape off %}
{% for key, value in data.items %}{% if key == 'laptop' %} {{ key }}: {{ value | safe}}{% else %} {% for key, value in data.items %}{% if key == 'laptop' %} {{ key }}: {{ value | safe}}{% else %}
{% trans key %}: {{ value }}{% endif %}{% endfor %} {{ key }}: {{ value }}{% endif %}{% endfor %}
{% endautoescape %} {% endautoescape %}

View File

@ -0,0 +1,11 @@
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.

View File

@ -14,9 +14,6 @@
{% load socialaccount %} {% load socialaccount %}
{% if user.is_authenticated %} {% if user.is_authenticated %}
{% block content %} {% block content %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<center> <center>
<style> <style>
ul > li { ul > li {
@ -33,23 +30,22 @@
<img src="{% static 'evapp/logo.png' %}" /> <img src="{% static 'evapp/logo.png' %}" />
{% if TESTMODE %} {% if TESTMODE %}
<h1 style="background-color:red;color:white">{% translate "WARNUNG! Test-MODUS aktiviert. Es werden keine Mails verschickt!" %}</h1> <h1 style="background-color:red;color:white">WARNUNG! Test-MODUS aktiviert. Es werden keine Mails verschickt!</h1>
{% endif %} {% endif %}
<h1> <h1>
E (V A) - Eintritt, (Veränderung, Austritt)<p> E (V A) - Eintritt, (Veränderung, Austritt)<p>
</h1>{% translate "Du bist eingeloggt als" %} {{ user.email }} </h1>Du bist eingeloggt als {{ user.email }}
<h2> <h2>
<p> {% translate "Schritt" %} {{ wizard.steps.step1 }} {% translate "von" %} {{ wizard.steps.count }}</p> <p>Schritt {{ wizard.steps.step1 }} von {{ wizard.steps.count }}</p>
<p>{% if wizard.steps.step1 == 1 %} <p>{% if wizard.steps.step1 == 1 %}
{% translate "Angaben zur Person" %} {% endif %} Angaben zur Person {% endif %}
{% if choice == 'IN' %} {% if choice == 'IN' %}
{% if wizard.steps.step1 == 2 %} {% if wizard.steps.step1 == 2 %}
{% translate "Angaben zum neuen Arbeitsverhältnis" %} Angaben zum neuen Arbeitsverhältnis
{% elif wizard.steps.step1 == 3 %} {% elif wizard.steps.step1 == 3 %}
{% translate "IT-relevante Angaben" %} IT-relevante Angaben
{% elif wizard.steps.step1 == 4 %} {% elif wizard.steps.step1 == 4 %}
{% translate "Office-relevante Angaben" %} Office-relevante Angaben
{% endif %} {% endif %}
{% else %} {% else %}
{% if wizard.steps.step1 == 2 %} {% if wizard.steps.step1 == 2 %}
@ -57,19 +53,17 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if datatable == True %} {% if datatable == True %}
{% translate "Bestätigungsschritt" %} Bestätigungsschritt
{% endif %} {% endif %}
</p> </p>
</h2> </h2>
{% if datatable == True %} {% if datatable == True %}
<table id="toggle-heading"> <table>
{% for key, value in data.items %} {% for key, value in data.items %}
<tr><th>{{ key }}</th><th>{{ value }}</th></tr> <tr><th>{{ key }}</th><th>{{ value }}</th></tr>
{% endfor %} {% endfor %}
</table> </table>
{% endif %} {% endif %}
<form action="" method="post"> <form action="" method="post">
{% csrf_token %} {% csrf_token %}
<table> <table>
@ -90,21 +84,21 @@
{% endif %} {% endif %}
</table> </table>
<p> <p>
<span style="color: red">*</span> {% translate "Pflichtfeld" %} <span style="color: red">*</span> Pflichtfeld
<p> <p>
{% if wizard.steps.prev %} {% if wizard.steps.prev %}
<button formnovalidate="formnovalidate" name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">{% translate "Zurück" %}</button> <button formnovalidate="formnovalidate" name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">Zurück</button>
{% endif %} {% endif %}
{% if datatable == True %} {% if datatable == True %}
<button type="submit" value="{% trans "Weiter" %}">{% translate "Abschicken" %}</button> <button type="submit" value="{% trans "Weiter" %}">Abschicken</button>
{% else %} {% else %}
<button type="submit" value="{% trans "Weiter" %}">{% translate "Weiter" %}</button> <button type="submit" value="{% trans "Weiter" %}">Weiter</button>
{% endif %} {% endif %}
</form> </form>
<p> <p>
<a href="{% url 'account_logout' %}">{% translate "logout" %}</a> <a href="{% url 'account_logout' %}">logout</a>
</center> </center>
{% endblock %} {% endblock %}
{% else %} {% else %}
<a href="{% provider_login_url 'nextcloud' %}">{% translate "Bitte einloggen!" %}</a> <a href="{% provider_login_url 'nextcloud' %}">Bitte einloggen!</a>
{% endif %} {% endif %}

View File

@ -4,9 +4,8 @@ from django.contrib.auth.models import User
from django.conf import settings from django.conf import settings
from django.http import HttpResponse from django.http import HttpResponse
from django.core import mail from django.core import mail
from django.utils import translation
from .forms import ITForm, WorkingForm, OfficeForm, DummyForm from .forms import ITForm, WorkingForm, OfficeForm, DummyForm, OffboardingForm
class LoginTestCase(TestCase): class LoginTestCase(TestCase):
def setUp(self): def setUp(self):
@ -17,12 +16,6 @@ class LoginTestCase(TestCase):
def testLogin(self): def testLogin(self):
self.assertContains(self.response, 'Du bist eingeloggt als vladimir@reiherzehe.com', status_code=200) 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): def testDebugWarning(self):
with self.settings(DEBUG=True): with self.settings(DEBUG=True):
@ -62,8 +55,7 @@ class LoginTestCase(TestCase):
'0-firstname': 'Ara', '0-firstname': 'Ara',
'0-lastname': 'Seva', '0-lastname': 'Seva',
'0-department': 'CENT', '0-department': 'CENT',
'0-team': 'Community Communications', '0-choice': 'IN'
'0-choice': 'IN',
}, WorkingForm) }, WorkingForm)
response = self._postform({ response = self._postform({
@ -92,17 +84,37 @@ class LoginTestCase(TestCase):
'eva_form_view-current_step': '5', 'eva_form_view-current_step': '5',
}, DummyForm) }, DummyForm)
def test_wizzard_out(self):
''' this test goes through the whole offboarding 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-choice': 'OUT'
}, OffboardingForm)
response = self._postform({
'eva_form_view-current_step': '4',
'4-lastdate_employment': '2021-01-01',
'4-date_returning': '2021-01-01',
'4-lastdate_bvg': '2021-01-01',
'4-lastdate_bahncard': '2021-01-01',
}, DummyForm)
response = self._postform({
'eva_form_view-current_step': '5',
}, DummyForm)
def test_mail(self): def test_mail(self):
self.test_wizzard_in() self.test_wizzard_in()
# print(mail.outbox[0].body) # print(mail.outbox)
self.assertGreater(len(mail.outbox), 2) 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) self.assertIn("Ara Seva", mail.outbox[0].subject)
class NoLoginTestCase(TestCase): class NoLoginTestCase(TestCase):

View File

@ -1,13 +1,13 @@
from django.urls import path from django.urls import path
from .views import EvaFormView, success, long_process, change_process from .views import EvaFormView, success, long_process, change_process, offboarding_process
urlpatterns = [ urlpatterns = [
path('', EvaFormView.as_view(condition_dict = {'1': long_process, path('', EvaFormView.as_view(condition_dict = {'1': long_process,
'2': long_process, '2': long_process,
'3': long_process, '3': long_process,
'4': change_process,}), '4': offboarding_process,}),
name='evaform'), name='evaform'),
path('success', success, name='success') path('success', success, name='success')
] ]

View File

@ -10,12 +10,11 @@ from formtools.wizard.views import CookieWizardView
from django.shortcuts import render from django.shortcuts import render
from django.conf import settings from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.utils.translation import gettext_lazy as _
from .models import Employee, DEPARTMENT_CHOICES, OS_CHOICES, \ from .models import Employee, DEPARTMENT_CHOICES, OS_CHOICES, VENDOR_CHOICES, \
LANG_CHOICES, ACCOUNT_CHOICES, TRANSPONDER_CHOICES, KEYBOARD_CHOICES, JANEIN_CHOICES LANG_CHOICES, ACCOUNT_CHOICES, TRANSPONDER_CHOICES, KEYBOARD_CHOICES
from .forms import PersonalForm, WorkingForm, ITForm, OfficeForm, DummyForm,\ from .forms import PersonalForm, WorkingForm, ITForm, OfficeForm, DummyForm,\
ChangeForm, TYPE_CHOICES ChangeForm, TYPE_CHOICES, OffboardingForm
from .settings import MAILS, EVA_MAIL, BASIC_DATA, ONLY_ONBOARDING from .settings import MAILS, EVA_MAIL, BASIC_DATA, ONLY_ONBOARDING
def success(request): def success(request):
@ -30,24 +29,38 @@ def long_process(wizard):
else: else:
data = wizard.get_cleaned_data_for_step('0') or {} data = wizard.get_cleaned_data_for_step('0') or {}
# print(data) # print(data)
if data.get('choice') != 'CHANGE': if data.get('choice') == 'IN':
wizard.set_choice('IN') wizard.set_choice('IN')
# print('PROZESS IN') print('PROZESS IN')
return True return True
else: else:
wizard.set_choice('CHANGE') return False
# print('PROZESS NOT IN') #wizard.set_choice('CHANGE')
#print('PROZESS CHANGE')
#return False
def offboarding_process(wizard):
''' this method is called via urls.py to determine if the form is part of the change process'''
print('OFFBOARDING PROZESS')
if not ONLY_ONBOARDING:
data = wizard.get_cleaned_data_for_step('0') or {}
print(data)
if data.get('choice') == 'OUT':
#print("PPPPPPPPPPPPPPP")
wizard.set_choice('OUT')
return True
# wizard.set_choice('IN')
return False return False
def change_process(wizard): def change_process(wizard):
''' this method is called via urls.py to determine if the form is part of the change process''' ''' this method is called via urls.py to determine if the form is part of the change process'''
# print('CHANGE PROZESS') print('CHANGE PROZESS')
return not long_process(wizard) return not long_process(wizard)
class EvaFormView(LoginRequiredMixin, CookieWizardView): class EvaFormView(LoginRequiredMixin, CookieWizardView):
template_name = 'veraenderung/employee_form.html' template_name = 'evapp/employee_form.html'
form_list = [PersonalForm, WorkingForm, ITForm, OfficeForm, ChangeForm, DummyForm] form_list = [PersonalForm, WorkingForm, ITForm, OfficeForm, OffboardingForm, DummyForm]
instance = None instance = None
choice = 'IN' choice = 'IN'
@ -141,29 +154,21 @@ class EvaFormView(LoginRequiredMixin, CookieWizardView):
context = {'data': self.beautify_data(newdata), 'contact': contact} context = {'data': self.beautify_data(newdata), 'contact': contact}
firstname = data['firstname'] firstname = data['firstname']
lastname = data['lastname'] lastname = data['lastname']
firstday = data['firstdate_employment']
try: try:
mail_template = get_template(f'evapp/department_mail.txt') mail_template = get_template(f'evapp/department_mail.txt')
if settings.MAILTEST: if settings.MAILTEST:
send_mail( send_mail(
f'EVA: Neuzugang {firstname} {lastname} {firstday} (MAILTEST)', f'EVA: Neuzugang {firstname} {lastname} (MAILTEST)',
mail_template.render(context), mail_template.render(context),
EVA_MAIL, EVA_MAIL,
[EVA_MAIL], [EVA_MAIL, contact],
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) fail_silently=False)
else: else:
send_mail( send_mail(
f'EVA: Neuzugang {firstname} {lastname} {firstday}', f'EVA: Neuzugang {firstname} {lastname}',
mail_template.render(context), mail_template.render(context),
EVA_MAIL, EVA_MAIL,
[contact], [MAILS[department]['MAIL'], contact],
fail_silently=False) fail_silently=False)
except BadHeaderError as error: except BadHeaderError as error:
print(error) print(error)
@ -189,7 +194,7 @@ class EvaFormView(LoginRequiredMixin, CookieWizardView):
# update values in data dictionary with keys from *_CHOICES if present there # update values in data dictionary with keys from *_CHOICES if present there
choices = {**DEPARTMENT_CHOICES, **TRANSPONDER_CHOICES, choices = {**DEPARTMENT_CHOICES, **TRANSPONDER_CHOICES,
**OS_CHOICES, **LANG_CHOICES, **KEYBOARD_CHOICES} **OS_CHOICES, **LANG_CHOICES, **VENDOR_CHOICES, **KEYBOARD_CHOICES}
data.update({k:choices[v] for k,v in data.items() \ data.update({k:choices[v] for k,v in data.items() \
if isinstance(v,collections.abc.Hashable) \ if isinstance(v,collections.abc.Hashable) \
and v in choices}) and v in choices})

View File

View File

@ -1,3 +0,0 @@
from django.contrib import admin
# Register your models here.

View File

@ -1,9 +0,0 @@
# import django´s base Appconfig class
from django.apps import AppConfig
# configuration class for the 'home' application
class HomeConfig(AppConfig):
# set the default type of primary key field for models in this app
default_auto_field = 'django.db.models.BigAutoField'
# give the application a name
name = 'home'

View File

@ -1,3 +0,0 @@
from django.db import models
# Create your models here.

View File

@ -1,97 +0,0 @@
<!DOCTYPE html>
{% load i18n %}
{% load static %}
<html lang="en">
<head>
<meta charset="UTF-8"> <!-- Set character encoding -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Button Navigation</title>
<style>
/* Flex container to align items in a row */
.container-wrapper {
display: flex;
justify-content: center; /* Centers the images horizontally */
align-items: flex-start; /* Aligns items to the top */
gap: 20px; /* Adds spacing between images */
margin-top: 50px; /* Adjust margin */
}
/* Container needed to position the button. Adjust the width as needed */
.container {
position: relative;
width: 30%;
}
/* Make the image responsive */
.container img {
width: 50%;
height: auto;
}
/* Style the button and place it in the middle of the container/image */
.container .btn {
position: absolute;
top: 120%;
left: 50%;
transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
background-color: #555;
color: white;
font-size: 16px;
padding: 12px 24px;
border: none;
cursor: pointer;
border-radius: 5px;
}
.container .btn:hover {
background-color: black;
}
body {
font-family: Arial, sans-serif;
text-align: center;
margin-top: 175px;
}
</style>
</head>
<body>
<h1>Wählen Sie Ihren nächsten Schritt</h1>
<div class="container-wrapper">
<!-- the first container with image and button for onboarding -->
<div class="container">
<img src="{% static 'evapp/eintritt.png' %}"/>
<button class="btn" onclick="window.location.href='/eintritt';">Eintritt</button>
</div>
<!-- the second container with image and button for onboarding -->
<div class="container">
<img src="{% static 'evapp/veraenderung.png' %}"/>
<button class="btn" onclick="window.location.href='/veraenderung';">Veränderung</button>
</div>
<!-- the third container with image and button for onboarding -->
<div class="container">
<img src="{% static 'evapp/austritt.png' %}"/>
<button class="btn" onclick="window.location.href='/austritt';">Austritt</button>
</div>
</div>
<!--
<div>
<img src="{% static 'evapp/onboarding1.png' %}" width="100" height="100" align="right" />
<img src="{% static 'evapp/offboarding.png' %}" width="100" height="100" align="right" />
<img src="{% static 'evapp/veränderung.png' %}" width="100" height="100" align="right" />
</div>
<button onclick="window.location.href='/onboarding';">Onboarding</button>
<button onclick="window.location.href='/offboarding';">Offboarding</button>
<button onclick="window.location.href='/veränderung';">Veränderung</button>-->
</body>
</html>

View File

@ -1,3 +0,0 @@
from django.test import TestCase
# Create your tests here.

View File

@ -1,9 +0,0 @@
# home/urls.py
from django.urls import path
from . import views # import views from the current app
# Define the URL patterns for the 'home' app
urlpatterns = [
path('', views.index, name='home-index'), # Map the URL '' to the index view with the name 'home-index'
]

View File

@ -1,15 +0,0 @@
from django.shortcuts import render
# Create your views here.
# Import the render function from Django to render templates
from django.shortcuts import render
def index(request):
# Context dictionary to pass dynamic data to the template
context = {
'title': 'Welcome to Pycouse!',
'author': 'Brian',
}
# Render the 'index.html' template located in the 'home' directory
# Pass the context dictionary to the template for dynamic content
return render(request, 'home/index.html', context)

View File

@ -1,208 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-02-07 14:44+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: evapp/models.py:10
msgid "Programme"
msgstr "Programs"
#: evapp/models.py:11
msgid "Softwareentwicklung"
msgstr "Software development"
#: evapp/models.py:13
msgid "Kommunikation und Events"
msgstr "Communication and events"
#: evapp/models.py:14
msgid "Vorstand"
msgstr "Board"
#: evapp/models.py:18
msgid "Mac (nur für Grafiker_innen)"
msgstr "Mac (for graphic designer only)"
#: evapp/models.py:21
msgid "Windows (bitte Begründung angeben)"
msgstr "Windows (please give justification)"
#: evapp/models.py:22
msgid "Mac OS (nur wenn Mac gewählt)"
msgstr "Mac OS (only if you choose Mac)"
#: evapp/models.py:30
msgid "Anderes (Bitte unten angeben)"
msgstr "Others (please note below)"
#: evapp/models.py:33
msgid "Civic CRM (allgemein)"
msgstr "Civic CRM (regular)"
#: evapp/models.py:34
msgid "Civic CRM (Mailings, impliziert allgemein)"
msgstr "Civic CRM (mailings, regular implied)"
#: evapp/models.py:40
msgid "Allgemeiner Transponder"
msgstr "Regular Transponder"
#: evapp/models.py:41
msgid "Besondere Schließungen (bitte angeben)"
msgstr "Special Doors (please note below)"
#: evapp/models.py:42
msgid "Kein Transponder"
msgstr "No transponder"
#: evapp/models.py:50 evapp/templates/evapp/department_mail.txt:26
msgid "Vorname"
msgstr "Firstname"
#: evapp/models.py:51
msgid "Nachname"
msgstr "Lastname"
#: evapp/models.py:53
msgid "Bereich"
msgstr "Department"
#: evapp/models.py:57
msgid "Erster Arbeitstag"
msgstr "First workday"
#: evapp/models.py:58
msgid "Erster Tag der Anwesenheit in der Geschäftsstelle"
msgstr "First day in the office"
#: evapp/models.py:61
msgid "Braucht Arbeitsplatz in der Geschäftsstelle?)"
msgstr "Needs a working place in the office"
#: evapp/models.py:62
msgid "Wo soll der Arbeitsplatz sein?"
msgstr "Where should the desk be?"
#: evapp/models.py:65
msgid "Hersteller"
msgstr "Manufacterer"
#: evapp/models.py:66
msgid "Betriebssystem"
msgstr "Operating system"
#: evapp/models.py:67
msgid "Zusätzlicher Monitor? Einer ist standard."
msgstr "Additional monitor. One is standard."
#: evapp/models.py:68
msgid "Diensttelefon (Handy)"
msgstr "work phone (mobile)"
#: evapp/models.py:70
msgid "Tastaturlayout"
msgstr "Keyboard layout"
#: evapp/models.py:71
msgid "zusätzliche IT-Anforderungen"
msgstr "Additional IT requirements"
#: evapp/models.py:72
msgid "Sprache"
msgstr "Language"
#: evapp/models.py:73
msgid "Zusätzliche Accounts"
msgstr "Additional accounts"
#: evapp/models.py:74
msgid "Zusätzliche Mailinglisten"
msgstr "additional mailing lists"
#: evapp/models.py:75
msgid "Rebu2Go-Zugang benötigt?"
msgstr "Needs Rebu2Go account"
#: evapp/models.py:79
msgid "Besondere Schließungen hier eintragen"
msgstr "Special access for doors"
#: evapp/models.py:80
msgid "Postfach am Empfang benötigt?"
msgstr "Needs mailbox at reception"
#: evapp/templates/evapp/employee_form.html:36
msgid "WARNUNG! Test-MODUS aktiviert. Es werden keine Mails verschickt!"
msgstr "ATTENTION! Test mode activated. No mails will be send."
#: evapp/templates/evapp/employee_form.html:40
msgid "Du bist eingeloggt als"
msgstr "You are logged in as"
#: evapp/templates/evapp/employee_form.html:42
msgid "Schritt"
msgstr "Step"
#: evapp/templates/evapp/employee_form.html:42
msgid "von"
msgstr "from"
#: evapp/templates/evapp/employee_form.html:44
msgid "Angaben zur Person"
msgstr "Personal Data"
#: evapp/templates/evapp/employee_form.html:47
msgid "Angaben zum neuen Arbeitsverhältnis"
msgstr "Details of the employment"
#: evapp/templates/evapp/employee_form.html:49
msgid "IT-relevante Angaben"
msgstr "IT-relevant details"
#: evapp/templates/evapp/employee_form.html:51
msgid "Office-relevante Angaben"
msgstr "Office relevant details"
#: evapp/templates/evapp/employee_form.html:59
msgid "Bestätigungsschritt"
msgstr "Confirmation step"
#: evapp/templates/evapp/employee_form.html:91
msgid "Pflichtfeld"
msgstr "Required field"
#: evapp/templates/evapp/employee_form.html:94
msgid "Zurück"
msgstr "Back"
#: evapp/templates/evapp/employee_form.html:97
#: evapp/templates/evapp/employee_form.html:99
msgid "Weiter"
msgstr "Next"
#: evapp/templates/evapp/employee_form.html:97
msgid "Abschicken"
msgstr "Send"
#: evapp/templates/evapp/employee_form.html:103
msgid "logout"
msgstr "logout"
#: evapp/templates/evapp/employee_form.html:107
msgid "Bitte einloggen!"
msgstr "Please log in!"

View File

@ -1 +0,0 @@
settings_development.py

View File

@ -1,6 +0,0 @@
from django.contrib import admin
from .models import Employee
admin.site.register([
Employee,
])

View File

@ -1,5 +0,0 @@
from django.apps import AppConfig
class EvappConfig(AppConfig):
name = 'veraenderung'

View File

@ -1,46 +0,0 @@
# Generated by Django 3.1.4 on 2021-09-13 12:41
from django.db import migrations, models
import multiselectfield.db.fields
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Employee',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('firstname', models.CharField(max_length=50, verbose_name='Vorname')),
('lastname', models.CharField(max_length=50, verbose_name='Nachname')),
('department', models.CharField(choices=[('PROG', 'Programme'), ('SOFT', 'Softwareentwicklung'), ('CENT', 'Central'), ('VOR', 'Vorstand')], max_length=5, verbose_name='Bereich')),
('team', models.CharField(blank=True, max_length=20, null=True)),
('add_to_wikimediade', models.BooleanField(default=False, verbose_name='Soll auf wikimedia.de irgendwo stehen?')),
('firstdate_employment', models.DateField(null=True, verbose_name='Erster Arbeitstag')),
('firstdate_presence', models.DateField(null=True, verbose_name='Erster Tag der Anwesenheit in der Geschäftsstelle')),
('jobdescription_german', models.CharField(max_length=100, null=True, verbose_name='Stellenbezeichnung(deutsch)')),
('jobdescription_english', models.CharField(max_length=100, null=True, verbose_name='Job description(english)')),
('remote', models.BooleanField(default=True, verbose_name='Braucht Arbeitsplatz in der Geschäftsstelle?')),
('desk', models.CharField(blank=True, max_length=100, null=True, verbose_name='Wo soll der Arbeitsplatz sein?')),
('vendor', models.CharField(choices=[('STANDARD', 'Dell Latitude'), ('LENOVO', 'Lenovo Thinkpad'), ('MAC', 'Mac (nur für Grafiker_innen)')], default='STANDARD', max_length=8, verbose_name='Hersteller')),
('os', models.CharField(choices=[('UBU', 'Ubuntu (Standard)'), ('WIN', 'Windows (bitte Begründung angeben)'), ('MOS', 'Mac OS (nur wenn Mac gewählt)')], default='UBU', max_length=3, verbose_name='Betriebssystem')),
('screen', models.BooleanField(default=False, verbose_name='Zusätzlicher Monitor? Einer ist standard.')),
('mobile', models.BooleanField(default=False, max_length=6, verbose_name='Diensttelefon (Handy)')),
('landline', models.BooleanField(default=False, verbose_name='Festnetznummer (Sipgate)')),
('keyboard', models.CharField(choices=[('DE', 'Deutsch'), ('US', 'USA'), ('OT', 'Anderes (Bitte unten angeben)')], default='DE', max_length=2, verbose_name='Tastaturlayout')),
('comment', models.TextField(blank=True, max_length=500, null=True, verbose_name='zusätzliche IT-Anforderungen')),
('language', models.CharField(choices=[('GER', 'Deutsch'), ('ENG', 'English')], default='GER', max_length=3, verbose_name='Sprache für Onboarding')),
('accounts', multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('OTRSWMDE', 'OTRS Ticketsystem'), ('CIVIC1', 'Civic CRM (allgemein)'), ('CIVIC2', 'Civic CRM (Mailings, impliziert allgemein)'), ('WEB', 'www.wikimedia.de (edit)'), ('BLOG', 'blog.wikimedia.de (edit)'), ('FORUM', 'forum.wikimedia.de')], max_length=37, null=True, verbose_name='Zusätzliche Accounts')),
('lists', models.CharField(blank=True, max_length=100, null=True, verbose_name='Zusätzliche Mailinglisten')),
('rebu2go', models.BooleanField(default=False, verbose_name='Rebu2Go-Zugang benötigt?')),
('transponder', models.CharField(choices=[('NORM', 'Allgemeiner Transponder'), ('SPECIAL', 'Besondere Schließungen (bitte angeben)'), ('NOTRANS', 'Kein Transponder')], default='NORM', max_length=7)),
('special', models.TextField(blank=True, max_length=500, null=True, verbose_name='Besondere Schließungen hier eintragen')),
('post_office_box', models.BooleanField(default=True, verbose_name='Postfach am Empfang benötigt?')),
],
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 3.1.4 on 2021-09-14 10:55
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('veraenderung', '0001_initial'),
]
operations = [
migrations.RenameField(
model_name='employee',
old_name='remote',
new_name='works_in_gs',
),
]

View File

@ -1,28 +0,0 @@
# Generated by Django 3.1.4 on 2022-02-08 09:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('veraenderung', '0002_auto_20210914_1055'),
]
operations = [
migrations.AlterField(
model_name='employee',
name='department',
field=models.CharField(choices=[('PROG', 'Programme'), ('SOFT', 'Softwareentwicklung'), ('CENT', 'Central'), ('KOMEV', 'Kommunikation und Events'), ('VOR', 'Vorstand')], max_length=5, verbose_name='Bereich'),
),
migrations.AlterField(
model_name='employee',
name='team',
field=models.CharField(blank=True, max_length=50, null=True),
),
migrations.AlterField(
model_name='employee',
name='works_in_gs',
field=models.BooleanField(default=True, verbose_name='Braucht Arbeitsplatz in der Geschäftsstelle?)'),
),
]

View File

@ -1,24 +0,0 @@
# Generated by Django 4.2.4 on 2023-08-11 10:28
from django.db import migrations, models
import multiselectfield.db.fields
class Migration(migrations.Migration):
dependencies = [
('veraenderung', '0003_auto_20220208_0955'),
]
operations = [
migrations.AlterField(
model_name='employee',
name='accounts',
field=multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('OTRSWMDE', 'OTRS Ticketsystem'), ('CIVIC1', 'Civic CRM (allgemein)'), ('CIVIC2', 'Civic CRM (Mailings, impliziert allgemein)'), ('WEB', 'www.wikimedia.de (edit)'), ('BLOG', 'blog.wikimedia.de (edit)'), ('FORUM', 'forum.wikimedia.de')], max_length=10, null=True, verbose_name='Zusätzliche Accounts'),
),
migrations.AlterField(
model_name='employee',
name='department',
field=models.CharField(choices=[('COENG', 'Communitys & Engagement'), ('SOFT', 'Softwareentwicklung'), ('CENT', 'Central'), ('KOMAD', 'Kommunikation & Advocacy'), ('VOR', 'Vorstand')], max_length=5, verbose_name='Bereich'),
),
]

View File

@ -1,19 +0,0 @@
# Generated by Django 4.2.4 on 2023-08-17 11:08
from django.db import migrations
import multiselectfield.db.fields
class Migration(migrations.Migration):
dependencies = [
('veraenderung', '0004_alter_employee_accounts_alter_employee_department'),
]
operations = [
migrations.AlterField(
model_name='employee',
name='accounts',
field=multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('OTRSWMDE', 'OTRS Ticketsystem'), ('CIVIC1', 'Civic CRM (allgemein)'), ('CIVIC2', 'Civic CRM (Mailings, impliziert allgemein)'), ('WEB', 'www.wikimedia.de (edit)'), ('BLOG', 'blog.wikimedia.de (edit)'), ('FORUM', 'forum.wikimedia.de')], max_length=40, null=True, verbose_name='Zusätzliche Accounts'),
),
]

View File

@ -1,32 +0,0 @@
# Generated by Django 4.2.16 on 2024-11-26 09:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('veraenderung', '0005_alter_employee_accounts'),
]
operations = [
migrations.RemoveField(
model_name='employee',
name='vendor',
),
migrations.AddField(
model_name='employee',
name='framework',
field=models.CharField(blank=True, max_length=300, null=True, verbose_name='Möchten Sie vom Standard des Frameworks abweichen, und wenn ja, warum?'),
),
migrations.AlterField(
model_name='employee',
name='add_to_wikimediade',
field=models.CharField(choices=[('NEIN', 'Nein'), ('JA', 'Ja')], default=True, max_length=5, verbose_name='Soll die Person bei Ansprechpartner*innen auf der WMDE-Webseite mit aufgenommen werden?'),
),
migrations.AlterField(
model_name='employee',
name='os',
field=models.CharField(choices=[('FED', 'Fedora (Standard)'), ('WIN', 'Windows (bitte Begründung angeben)'), ('MOS', 'Mac OS (nur wenn Mac gewählt)'), ('UBU', 'Ubuntu')], default='FED', max_length=3, verbose_name='Betriebssystem'),
),
]

View File

@ -1,32 +0,0 @@
# Generated by Django 4.2.17 on 2024-12-17 14:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('veraenderung', '0006_remove_employee_vendor_employee_framework_and_more'),
]
operations = [
migrations.RemoveField(
model_name='employee',
name='screen',
),
migrations.AlterField(
model_name='employee',
name='add_to_wikimediade',
field=models.CharField(choices=[('NEIN', 'Nein'), ('JA', 'Ja')], max_length=5, verbose_name='Soll die Person bei Ansprechpartner*innen auf der WMDE-Webseite mit aufgenommen werden?'),
),
migrations.AlterField(
model_name='employee',
name='framework',
field=models.CharField(blank=True, max_length=300, null=True, verbose_name='Möchten Sie vom Standard des Frameworks (Laptop) abweichen, und wenn ja, warum?'),
),
migrations.AlterField(
model_name='employee',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@ -1,87 +0,0 @@
from django.db import models
from multiselectfield import MultiSelectField
from django.utils.translation import gettext_lazy as _
from eintritt.models import Employee
# ATTENTION!!!
# No key should be used twice in any of these dicts because of the
# suboptimal implementation in views.EvaFormView.beautify_data()
#
DEPARTMENT_CHOICES = {'COENG': _('Communitys & Engagement'),
'SOFT': _('Softwareentwicklung'),
'CENT': 'Central',
'KOMAD': _('Kommunikation & Advocacy'),
'VOR': _('Vorstand'),}
#VENDOR_CHOICES = {'STANDARD': 'Dell Latitude',
# 'LENOVO': 'Lenovo Thinkpad',
# 'MAC': _('Mac (nur für Grafiker_innen)')}
OS_CHOICES = {'FED': 'Fedora (Standard)',
'WIN': _('Windows (bitte Begründung angeben)'),
'MOS': _('Mac OS (nur wenn Mac gewählt)'),
'UBU': _('Ubuntu')}
LANG_CHOICES = {'GER': 'Deutsch',
'ENG': 'English',}
KEYBOARD_CHOICES = {'DE': 'Deutsch',
'US': 'USA',
'OT': _('Anderes (Bitte unten angeben)')}
ACCOUNT_CHOICES = {'OTRSWMDE': 'OTRS Ticketsystem',
'CIVIC1': _('Civic CRM (allgemein)'),
'CIVIC2': _("Civic CRM (Mailings, impliziert allgemein)"),
'WEB': 'www.wikimedia.de (edit)',
'BLOG': 'blog.wikimedia.de (edit)',
'FORUM': 'forum.wikimedia.de',
}
TRANSPONDER_CHOICES = {'NORM': _('Allgemeiner Transponder'),
'SPECIAL': _('Besondere Schließungen (bitte angeben)'),
'NOTRANS': _('Kein Transponder'),}
JANEIN_CHOICES = {'NEIN': ('Nein'),
'JA': _('Ja'),}
class Employee(models.Model):
# email adress of user. should not be necessary if we use openauth one day
# usermail = models.EmailField(max_length=50, verbose_name="Deine Mailadresse (Ansprechpartner_in)", default='bestechefin@wikimedia.de')
# personal data
firstname = models.CharField(max_length=50, verbose_name=_("Vorname"))
lastname = models.CharField(max_length=50, verbose_name=_("Nachname"))
# intern = models.BooleanField(verbose_name='Interne_r Mitarbeiter_in?', default=True)
department = models.CharField(max_length=5, choices=DEPARTMENT_CHOICES.items(), verbose_name=_('Bereich'))
team = models.CharField(max_length=50, null=True, blank=True) # TODO? better with choices?
add_to_wikimediade = models.CharField(max_length=5, choices=JANEIN_CHOICES.items(), verbose_name=_("Soll die Person bei Ansprechpartner*innen auf der WMDE-Webseite mit aufgenommen werden?"))
# general work related stuff
firstdate_employment = models.DateField(null=True, verbose_name=_("Erster Arbeitstag"))
firstdate_presence = models.DateField(null=True, verbose_name=_("Erster Tag der Anwesenheit in der Geschäftsstelle"))
jobdescription_german = models.CharField(null=True, max_length=100, verbose_name="Stellenbezeichnung(deutsch)")
jobdescription_english = models.CharField(null=True, max_length=100, verbose_name="Job description(english)")
works_in_gs = models.BooleanField(verbose_name=_('Braucht Arbeitsplatz in der Geschäftsstelle?)'), default=True)
desk = models.CharField(max_length=100, null=True, blank=True, verbose_name=_("Wo soll der Arbeitsplatz sein?"))
# IT related stuff
#vendor = models.CharField(max_length=8, choices=VENDOR_CHOICES.items(), default='STANDARD', verbose_name=_('Hersteller'))
framework = models.CharField(max_length=300, null=True, blank=True, verbose_name=_("Möchten Sie vom Standard des Frameworks (Laptop) abweichen, und wenn ja, warum?"))
os = models.CharField(max_length=3, choices=OS_CHOICES.items(), default='FED', verbose_name=_('Betriebssystem'))
#screen = models.BooleanField(default=False, verbose_name=_('Zusätzlicher Monitor? Einer ist standard.'))
mobile = models.BooleanField(max_length=6, default=False, verbose_name=_('Diensttelefon (Handy)'))
landline = models.BooleanField(default = False, verbose_name=_('Festnetznummer (Sipgate)'))
# sim = models.BooleanField(default=False, verbose_name="Mobilfunkvertrag")
keyboard = models.CharField(max_length=2, choices=KEYBOARD_CHOICES.items(), default='DE', verbose_name=_("Tastaturlayout"))
comment = models.TextField(max_length=500, null=True, blank=True, verbose_name=_("zusätzliche IT-Anforderungen"))
language = models.CharField(max_length=3, choices=LANG_CHOICES.items(), default="GER", verbose_name=_("Sprache für Onboarding"))
accounts = MultiSelectField(choices=ACCOUNT_CHOICES.items(), max_length=40, null=True, blank=True, verbose_name=_("Zusätzliche Accounts"))
lists = models.CharField(max_length=100, null=True, blank=True, verbose_name=_("Zusätzliche Mailinglisten"))
rebu2go = models.BooleanField(verbose_name=_("Rebu2Go-Zugang benötigt?"), default=False)
# office related stuff
transponder = models.CharField(max_length=7, choices=TRANSPONDER_CHOICES.items(), default='NORM')
special = models.TextField(max_length=500, null=True, blank=True, verbose_name=_("Besondere Schließungen hier eintragen"))
post_office_box = models.BooleanField(default=True, verbose_name=_('Postfach am Empfang benötigt?'))

View File

@ -1,61 +0,0 @@
# temporary setting while change and exit is not yet fully implemented
ONLY_ONBOARDING = True
# sender mail adress also used for MAILTEST mode
EVA_MAIL = 'it-support@wikimedia.de'
# these Fields should be included in every mail
BASIC_DATA = ['firstname', 'lastname', 'firstdate_employment', 'firstdate_presence',
'jobdescription_german', 'jobdescription_english',]
# for every department: 'MAIL' => mail adress, 'DATA': additional fields to include
# also one copy with all fields to the person filling the form.
MAILS = {
'IT': {
'MAIL': 'wmde-it@wikimedia.de',
'DATA': [
'laptop', 'os', 'comment', 'email', 'landline', 'lists', 'mobile',
'department', 'accounts', 'language', 'screen', 'works_in_gs', 'desk',
'keyboard',
],
},
'OFFICE': {
'MAIL': 'office@wikimedia.de',
'DATA': [
'transponder', 'special', 'post_office_box', 'mobile',
'works_in_gs', 'desk',
],
},
'KOMM': {
'MAIL': 'presse@wikimedia.de',
'DATA': [
'department', 'team', 'add_to_wikimediade'
],
},
'CENTRAL': {
'MAIL': 'anna.noelte@wikimedia.de',
'DATA': [
'department', 'team', 'language', 'mobile', 'rebu2go'
],
},
'HR': {
'MAIL': 'personal@wikimedia.de',
'DATA': [
'department', 'team', 'language',
]
},
'FINANCE': {
'MAIL': 'claudia.langrock@wikimedia.de',
'DATA': [
'rebu2go'
]
},
'SUBMITTER': {
'MAIL': 'submitter@wikimedia.de',
'DATA': [
'department', 'team', 'add_to_wikimediade', 'remote', 'desk', 'vendor',
'os', 'screen', 'mobile', 'landline', 'keyboard', 'comment', 'language',
'accounts', 'lists', 'rebu2go', 'transponder', 'special', 'post_office_box'
]
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -1,27 +0,0 @@
{% load i18n %}
{% load static %}
<script type="text/javascript" src="{% static 'admin/js/core.js' %}"></script>
<script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script>
<script type="text/javascript" src="{% static 'admin/js/jquery.init.js' %}"></script>
{{ form.media }}
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/base.css' %}" />
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/widgets.css' %}" />
{% load account socialaccount %}
{% block content %}
<center>
<img src="{% static 'evapp/logo.png' %}" />
<h1>
E (V A) - Eintritt, (Veränderung, Austritt)<p>
</h1>
Bitte via Wolke einloggen:
{% include "socialaccount/snippets/provider_list.html" with process="login" %}
</center>
{% endblock %}

View File

@ -1,35 +0,0 @@
{% block content %}
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
{% if next %}
{% if user.is_authenticated %}
<p>Your account doesn't have access to this page. To proceed,
please login with an account that has access.</p>
{% else %}
<p>Please login to see this page.</p>
{% endif %}
{% endif %}
<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<table>
<tr>
<td>{{ form.username.label_tag }}</td>
<td>{{ form.username }}</td>
</tr>
<tr>
<td>{{ form.password.label_tag }}</td>
<td>{{ form.password }}</td>
</tr>
</table>
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
{# Assumes you setup the password_reset view in your URLconf #}
<p><a href="{% url 'password_reset' %}">Lost password?</a></p>
{% endblock %}

View File

@ -1,5 +0,0 @@
{% load i18n %}
{% autoescape off %}
{% for key, value in data.items %}{% if key == 'laptop' %} {{ key }}: {{ value | safe}}{% else %}
{% trans key %}: {{ value }}{% endif %}{% endfor %}
{% endautoescape %}

View File

@ -1,31 +0,0 @@
{% load i18n %}
(english below)
Hallo!
Es gibt einen Neuzugang bei Wikimedia! Hier ( https://wiki.wikimedia.de/wiki/Onboarding ) kannst Du nachsehen,
welche Schritte jetzt für Deine Abteilung nötig werden. Im Folgenden alle Daten,
die Du dafür brauchst:
{% include 'evapp/dataloop.txt' %}
Wenn Du Fragen hast, melde Dich bei {{contact}}.
Grüße, Deine E.V.A.
-------------------------
{% language 'en' %}
Hi!
There is a new employee at Wikimedia! Here ( https://wiki.wikimedia.de/wiki/Onboarding ) you can see, which
steps are now necessary for your department.
All Data you need for this:
{% include 'evapp/dataloop.txt' %}
If you have any questions please write to {{contact}}.
Regards, Your E.V.A.
{% endlanguage %}

View File

@ -1,110 +0,0 @@
{% load i18n %}
{% load static %}
<script type="text/javascript" src="{% static 'admin/js/core.js' %}"></script>
<script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script>
<script type="text/javascript" src="{% static 'admin/js/jquery.init.js' %}"></script>
{{ form.media }}
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/base.css' %}" />
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/widgets.css' %}" />
{% load socialaccount %}
{% if user.is_authenticated %}
{% block content %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<center>
<style>
ul > li {
list-style-type: none;
}
ul {
padding-left: 10;
}
label.required::after {
content: ' *';
color: red;
}
</style>
<img src="{% static 'evapp/logo.png' %}" />
{% if TESTMODE %}
<h1 style="background-color:red;color:white">{% translate "WARNUNG! Test-MODUS aktiviert. Es werden keine Mails verschickt!" %}</h1>
{% endif %}
<h1>
E (V A) - Veränderung, (Veränderung, Austritt)<p>
</h1>{% translate "Du bist eingeloggt als" %} {{ user.email }}
<h2>
<p> {% translate "Schritt" %} {{ wizard.steps.step1 }} {% translate "von" %} {{ wizard.steps.count }}</p>
<p>{% if wizard.steps.step1 == 1 %}
{% translate "Angaben zur Person" %} {% endif %}
{% if choice == 'IN' %}
{% if wizard.steps.step1 == 2 %}
{% translate "Angaben zum neuen Arbeitsverhältnis" %}
{% elif wizard.steps.step1 == 3 %}
{% translate "IT-relevante Angaben" %}
{% elif wizard.steps.step1 == 4 %}
{% translate "Office-relevante Angaben" %}
{% endif %}
{% else %}
{% if wizard.steps.step1 == 2 %}
Veränderungsrelevante Angaben
{% endif %}
{% endif %}
{% if datatable == True %}
{% translate "Bestätigungsschritt" %}
{% endif %}
</p>
</h2>
{% if datatable == True %}
<table id="toggle-heading">
{% for key, value in data.items %}
<tr><th>{{ key }}</th><th>{{ value }}</th></tr>
{% endfor %}
</table>
{% endif %}
<form action="" method="post">
{% csrf_token %}
<table>
{% comment %}
comment this back in if you want to use CHANGE and EXIT process
{% if wizard.steps.step1 > 1 %}
Du hast den Prozess "{{choice_string}}" ausgewählt.
{% endif %}
{% endcomment %}
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
{{ wizard.form }}
{% endif %}
</table>
<p>
<span style="color: red">*</span> {% translate "Pflichtfeld" %}
<p>
{% if wizard.steps.prev %}
<button formnovalidate="formnovalidate" name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">{% translate "Zurück" %}</button>
{% endif %}
{% if datatable == True %}
<button type="submit" value="{% trans "Weiter" %}">{% translate "Abschicken" %}</button>
{% else %}
<button type="submit" value="{% trans "Weiter" %}">{% translate "Weiter" %}</button>
{% endif %}
</form>
<p>
<a href="{% url 'account_logout' %}">{% translate "logout" %}</a>
</center>
{% endblock %}
{% else %}
<a href="{% provider_login_url 'nextcloud' %}">{% translate "Bitte einloggen!" %}</a>
{% endif %}