Compare commits
8 Commits
master
...
offboardin
Author | SHA1 | Date |
---|---|---|
Benni Bärmann | 9c62624580 | |
Benni Bärmann | 2b6bc19296 | |
Benni Bärmann | 9901c94e16 | |
Benni Bärmann | e61c28aae3 | |
Benni Bärmann | da8c744227 | |
Benni Bärmann | 86f30c8479 | |
Benni Bärmann | 60debb6e86 | |
Benni Bärmann | 4917d24fd5 |
137
README.md
137
README.md
|
@ -1,79 +1,58 @@
|
|||
# eva
|
||||
|
||||
A simple tool for on- and offboarding people in a mid sized organisation.
|
||||
|
||||
"EVA" is an german acronym for "Eintritt, Veränderung, Austritt",
|
||||
meaning "Onboarding, Change, Offboarding"
|
||||
|
||||
# development
|
||||
|
||||
- install gettext for instance via `apt install gettext` for translations
|
||||
- set up a virtual environment with virtualenvwrapper or some other environment managing tool
|
||||
- use this environment and do
|
||||
```
|
||||
pip install django django-multiselectfield django-formtools django-allauth
|
||||
```
|
||||
- clone this repository
|
||||
- `ln -sr eva/settings_development.py eva/settings.py`
|
||||
- initialise your database with `python manage.py migrate`
|
||||
- start your development server with `python manage.py runserver`
|
||||
|
||||
# oauth
|
||||
|
||||
- You need to add oauth information in the django backend via .../admin in "Social Accounts"
|
||||
|
||||
# production
|
||||
|
||||
- you can use gunicorn as server for example instead of the django development server.
|
||||
- we use whitenoise for serving static files
|
||||
- we still use the development SQLITE database from django
|
||||
|
||||
do the following in the project main directory:
|
||||
```
|
||||
ln -sr eva/settings_production.py eva/settings.py
|
||||
```
|
||||
edit /secrets.json to contain something similar to
|
||||
```
|
||||
{
|
||||
"SECRET_KEY": "THIS IS ANOTHER SECRET!"
|
||||
}
|
||||
```
|
||||
run the following commands:
|
||||
```
|
||||
python3 manage.py migrate
|
||||
python3 manage.py collectstatic
|
||||
django-admin compilemessages
|
||||
```
|
||||
|
||||
As root create a file `/etc/systemd/system` (it's already deployed by puppet when the corresponding manifest is applied):
|
||||
|
||||
```
|
||||
# /etc/systemd/system/eva.service
|
||||
#
|
||||
|
||||
[Unit]
|
||||
Description=gunicorn EVA daemon
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=eva
|
||||
Group=eva
|
||||
RuntimeDirectory=eva
|
||||
WorkingDirectory=/home/eva/eva
|
||||
Environment=PYTHONUNBUFFERED=TRUE
|
||||
ExecStart=/usr/bin/gunicorn --forwarded-allow-ips='*' -b '0:8000' eva.wsgi
|
||||
ExecReload=/bin/kill -s HUP $MAINPID
|
||||
KillMode=mixed
|
||||
TimeoutStopSec=5
|
||||
PrivateTmp=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Adapt the paths in the file accordingly. Then, still as root, run:
|
||||
```
|
||||
systemctl enable --now eva.service
|
||||
```
|
||||
This enables the service to start at boot time and starts it immediately now. The daemon logs to the journal. You can see the last 10 lines by running `systemctl status eva.service` or the whole log by running `journalctl -u eva.service`. The usual switches to manipulate that output are available.
|
||||
# eva
|
||||
|
||||
A simple tool for on- and offboarding people in a mid sized organisation.
|
||||
|
||||
"EVA" is an german acronym for "Eintritt, Veränderung, Austritt",
|
||||
meaning "Onboarding, Change, Offboarding"
|
||||
|
||||
# development
|
||||
|
||||
- set up a virtual environment with virtualenvwrapper or some other
|
||||
environment managing tool
|
||||
|
||||
- use this environment and do
|
||||
|
||||
pip install django django-multiselectfield django-formtools django-allauth
|
||||
|
||||
- clone this repository
|
||||
|
||||
- ln -sr eva/settings_development.py eva/settings.py
|
||||
|
||||
- initialise your database with
|
||||
|
||||
python manage.py migrate
|
||||
|
||||
- start your development server with
|
||||
|
||||
python manage.py runserver
|
||||
|
||||
# oauth
|
||||
|
||||
- You need to add oauth information in the django backend via .../admin in "Social Accounts"
|
||||
|
||||
# production
|
||||
|
||||
- you can use gunicorn as server for example instead of the django development server.
|
||||
|
||||
- we use whitenoise for serving static files
|
||||
|
||||
- we still use the development SQLITE database from django
|
||||
|
||||
do the following in the project main directory:
|
||||
|
||||
ln -sr eva/settings_production.py eva/settings.py
|
||||
|
||||
edit /secrets.json to contain something similar to
|
||||
|
||||
{
|
||||
"SECRET_KEY": "THIS IS ANOTHER SECRET!"
|
||||
}
|
||||
|
||||
run the following commands:
|
||||
|
||||
python3 manage.py migrate
|
||||
python3 manage.py collectstatic
|
||||
|
||||
server starts with
|
||||
|
||||
export PYTHONUNBUFFERED=TRUE; nohup gunicorn --forwarded-allow-ips="*" -b '0:8000' eva.wsgi &> logfile &
|
||||
|
|
5
TODO
5
TODO
|
@ -1,8 +1,3 @@
|
|||
* switch to django 4
|
||||
|
||||
|
||||
minor stuff:
|
||||
|
||||
* remove dot before "Nextcloud" at login page.
|
||||
|
||||
* add Testmode warning at login screen
|
||||
|
|
|
@ -11,7 +11,6 @@ https://docs.djangoproject.com/en/3.1/ref/settings/
|
|||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
@ -59,7 +58,6 @@ MIDDLEWARE = [
|
|||
'django.middleware.security.SecurityMiddleware',
|
||||
'whitenoise.middleware.WhiteNoiseMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
|
@ -121,7 +119,7 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/3.1/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'de'
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
|
@ -131,7 +129,6 @@ USE_L10N = True
|
|||
|
||||
USE_TZ = True
|
||||
|
||||
LOCALE_PATHS = ( os.path.join(BASE_DIR, 'locale'), )
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/3.1/howto/static-files/
|
||||
|
|
|
@ -81,7 +81,6 @@ MIDDLEWARE = [
|
|||
'whitenoise.middleware.WhiteNoiseMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
|
@ -142,7 +141,7 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/3.1/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'de'
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
|
@ -152,7 +151,6 @@ USE_L10N = True
|
|||
|
||||
USE_TZ = True
|
||||
|
||||
LOCALE_PATHS = ( os.path.join(BASE_DIR, 'locale'), )
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/3.1/howto/static-files/
|
||||
|
|
|
@ -20,17 +20,23 @@ 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'}
|
||||
# TYPE_CHOICES = {'IN': 'Eintritt', 'CHANGE': 'Veränderung', 'OUT': 'Austritt'}
|
||||
TYPE_CHOICES = {'IN': 'Eintritt', '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?')
|
||||
choice = ChoiceField(choices=TYPE_CHOICES.items(), widget=RadioSelect,
|
||||
label='Welcher Prozess soll angestoßen werden?')
|
||||
|
||||
class Meta:
|
||||
model = Employee
|
||||
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):
|
||||
|
||||
def clean(self):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 3.1.4 on 2022-02-08 09:55
|
||||
# Generated by Django 3.1.4 on 2021-11-10 08:32
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
@ -15,14 +15,4 @@ class Migration(migrations.Migration):
|
|||
name='department',
|
||||
field=models.CharField(choices=[('PROG', 'Programme'), ('SOFT', 'Softwareentwicklung'), ('CENT', 'Central'), ('KOMEV', 'Kommunikation und Events'), ('VOR', 'Vorstand')], max_length=5, verbose_name='Bereich'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='employee',
|
||||
name='team',
|
||||
field=models.CharField(blank=True, max_length=50, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='employee',
|
||||
name='works_in_gs',
|
||||
field=models.BooleanField(default=True, verbose_name='Braucht Arbeitsplatz in der Geschäftsstelle?)'),
|
||||
),
|
||||
]
|
|
@ -1,24 +0,0 @@
|
|||
# Generated by Django 4.2.4 on 2023-08-11 10:28
|
||||
|
||||
from django.db import migrations, models
|
||||
import multiselectfield.db.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evapp', '0003_auto_20220208_0955'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='employee',
|
||||
name='accounts',
|
||||
field=multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('OTRSWMDE', 'OTRS Ticketsystem'), ('CIVIC1', 'Civic CRM (allgemein)'), ('CIVIC2', 'Civic CRM (Mailings, impliziert allgemein)'), ('WEB', 'www.wikimedia.de (edit)'), ('BLOG', 'blog.wikimedia.de (edit)'), ('FORUM', 'forum.wikimedia.de')], max_length=10, null=True, verbose_name='Zusätzliche Accounts'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='employee',
|
||||
name='department',
|
||||
field=models.CharField(choices=[('COENG', 'Communitys & Engagement'), ('SOFT', 'Softwareentwicklung'), ('CENT', 'Central'), ('KOMAD', 'Kommunikation & Advocacy'), ('VOR', 'Vorstand')], max_length=5, verbose_name='Bereich'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,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'),
|
||||
),
|
||||
]
|
|
@ -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 = [
|
||||
('evapp', '0004_alter_employee_accounts_alter_employee_department'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='employee',
|
||||
name='accounts',
|
||||
field=multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('OTRSWMDE', 'OTRS Ticketsystem'), ('CIVIC1', 'Civic CRM (allgemein)'), ('CIVIC2', 'Civic CRM (Mailings, impliziert allgemein)'), ('WEB', 'www.wikimedia.de (edit)'), ('BLOG', 'blog.wikimedia.de (edit)'), ('FORUM', 'forum.wikimedia.de')], max_length=40, null=True, verbose_name='Zusätzliche Accounts'),
|
||||
),
|
||||
]
|
|
@ -1,25 +1,24 @@
|
|||
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'),
|
||||
DEPARTMENT_CHOICES = {'PROG': 'Programme',
|
||||
'SOFT': 'Softwareentwicklung',
|
||||
'CENT': 'Central',
|
||||
'KOMAD': _('Kommunikation & Advocacy'),
|
||||
'VOR': _('Vorstand'),}
|
||||
'KOMEV': 'Kommunikation und Events',
|
||||
'VOR': 'Vorstand',}
|
||||
|
||||
VENDOR_CHOICES = {'STANDARD': 'Dell Latitude',
|
||||
'LENOVO': 'Lenovo Thinkpad',
|
||||
'MAC': _('Mac (nur für Grafiker_innen)')}
|
||||
'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)')}
|
||||
'WIN': 'Windows (bitte Begründung angeben)',
|
||||
'MOS': 'Mac OS (nur wenn Mac gewählt)'}
|
||||
|
||||
|
||||
LANG_CHOICES = {'GER': 'Deutsch',
|
||||
|
@ -27,19 +26,19 @@ LANG_CHOICES = {'GER': 'Deutsch',
|
|||
|
||||
KEYBOARD_CHOICES = {'DE': 'Deutsch',
|
||||
'US': 'USA',
|
||||
'OT': _('Anderes (Bitte unten angeben)')}
|
||||
'OT': 'Anderes (Bitte unten angeben)'}
|
||||
|
||||
ACCOUNT_CHOICES = {'OTRSWMDE': 'OTRS Ticketsystem',
|
||||
'CIVIC1': _('Civic CRM (allgemein)'),
|
||||
'CIVIC2': _("Civic CRM (Mailings, impliziert allgemein)"),
|
||||
'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'),}
|
||||
TRANSPONDER_CHOICES = {'NORM': 'Allgemeiner Transponder',
|
||||
'SPECIAL': 'Besondere Schließungen (bitte angeben)',
|
||||
'NOTRANS': 'Kein Transponder',}
|
||||
|
||||
class Employee(models.Model):
|
||||
|
||||
|
@ -47,34 +46,36 @@ class Employee(models.Model):
|
|||
# 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"))
|
||||
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?
|
||||
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"))
|
||||
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?"))
|
||||
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)'))
|
||||
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(), 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)
|
||||
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?'))
|
||||
special = models.TextField(max_length=500, null=True, blank=True, verbose_name="Besondere Schließungen hier eintragen")
|
||||
post_office_box = models.BooleanField(default=True, verbose_name='Postfach am Empfang benötigt?')
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
# 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
|
||||
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',]
|
||||
BASIC_DATA = ['firstname', 'lastname', 'firstdate_employment', 'firstdate_presence',]
|
||||
|
||||
# for every department: 'MAIL' => mail adress, 'DATA': additional fields to include
|
||||
MAILS = {
|
||||
|
@ -21,7 +20,7 @@ MAILS = {
|
|||
'OFFICE': {
|
||||
'MAIL': 'office@wikimedia.de',
|
||||
'DATA': [
|
||||
'transponder', 'special', 'post_office_box', 'mobile',
|
||||
'transponder', 'special', 'post_office_box', 'sim', 'sim2',
|
||||
'works_in_gs', 'desk',
|
||||
],
|
||||
},
|
||||
|
@ -29,12 +28,13 @@ MAILS = {
|
|||
'MAIL': 'presse@wikimedia.de',
|
||||
'DATA': [
|
||||
'department', 'team',
|
||||
'jobdescription_german', 'jobdescription_english',
|
||||
],
|
||||
},
|
||||
'CENTRAL': {
|
||||
'MAIL': 'anna.noelte@wikimedia.de',
|
||||
'MAIL': 'eileen.miedtank@wikimedia.de',
|
||||
'DATA': [
|
||||
'department', 'team', 'language', 'mobile', 'rebu2go'
|
||||
'department', 'team', 'language', 'sim', 'sim2', 'rebu2go'
|
||||
],
|
||||
},
|
||||
'HR': {
|
||||
|
@ -48,11 +48,6 @@ MAILS = {
|
|||
'DATA': [
|
||||
'team', 'department', 'language',
|
||||
]
|
||||
},
|
||||
'FINANCE': {
|
||||
'MAIL': 'claudia.langrock@wikimedia.de',
|
||||
'DATA': [
|
||||
'rebu2go'
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{% load i18n %}
|
||||
{% autoescape off %}
|
||||
{% for key, value in data.items %}{% if key == 'laptop' %} {{ key }}: {{ value | safe}}{% else %}
|
||||
{% trans key %}: {{ value }}{% endif %}{% endfor %}
|
||||
{{ key }}: {{ value }}{% endif %}{% endfor %}
|
||||
{% endautoescape %}
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
{% load i18n %}
|
||||
|
||||
(english below)
|
||||
|
||||
Hallo!
|
||||
|
||||
Es gibt einen Neuzugang bei Wikimedia! Hier ( https://wiki.wikimedia.de/wiki/Onboarding ) kannst Du nachsehen,
|
||||
|
@ -13,19 +9,3 @@ die Du dafür brauchst:
|
|||
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 %}
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
{% load socialaccount %}
|
||||
{% if user.is_authenticated %}
|
||||
{% block content %}
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
<center>
|
||||
<style>
|
||||
ul > li {
|
||||
|
@ -33,22 +30,22 @@
|
|||
|
||||
<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>
|
||||
<h1 style="background-color:red;color:white">WARNUNG! Test-MODUS aktiviert. Es werden keine Mails verschickt!</h1>
|
||||
{% endif %}
|
||||
<h1>
|
||||
E (V A) - Eintritt, (Veränderung, Austritt)<p>
|
||||
</h1>{% translate "Du bist eingeloggt als" %} {{ user.email }}
|
||||
</h1>Du bist eingeloggt als {{ user.email }}
|
||||
<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 %}
|
||||
{% translate "Angaben zur Person" %} {% endif %}
|
||||
Angaben zur Person {% endif %}
|
||||
{% if choice == 'IN' %}
|
||||
{% if wizard.steps.step1 == 2 %}
|
||||
{% translate "Angaben zum neuen Arbeitsverhältnis" %}
|
||||
Angaben zum neuen Arbeitsverhältnis
|
||||
{% elif wizard.steps.step1 == 3 %}
|
||||
{% translate "IT-relevante Angaben" %}
|
||||
IT-relevante Angaben
|
||||
{% elif wizard.steps.step1 == 4 %}
|
||||
{% translate "Office-relevante Angaben" %}
|
||||
Office-relevante Angaben
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if wizard.steps.step1 == 2 %}
|
||||
|
@ -56,7 +53,7 @@
|
|||
{% endif %}
|
||||
{% endif %}
|
||||
{% if datatable == True %}
|
||||
{% translate "Bestätigungsschritt" %}
|
||||
Bestätigungsschritt
|
||||
{% endif %}
|
||||
</p>
|
||||
</h2>
|
||||
|
@ -87,21 +84,21 @@
|
|||
{% endif %}
|
||||
</table>
|
||||
<p>
|
||||
<span style="color: red">*</span> {% translate "Pflichtfeld" %}
|
||||
<span style="color: red">*</span> Pflichtfeld
|
||||
<p>
|
||||
{% 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 %}
|
||||
{% if datatable == True %}
|
||||
<button type="submit" value="{% trans "Weiter" %}">{% translate "Abschicken" %}</button>
|
||||
<button type="submit" value="{% trans "Weiter" %}">Abschicken</button>
|
||||
{% else %}
|
||||
<button type="submit" value="{% trans "Weiter" %}">{% translate "Weiter" %}</button>
|
||||
<button type="submit" value="{% trans "Weiter" %}">Weiter</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
<p>
|
||||
<a href="{% url 'account_logout' %}">{% translate "logout" %}</a>
|
||||
<a href="{% url 'account_logout' %}">logout</a>
|
||||
</center>
|
||||
{% endblock %}
|
||||
{% else %}
|
||||
<a href="{% provider_login_url 'nextcloud' %}">{% translate "Bitte einloggen!" %}</a>
|
||||
<a href="{% provider_login_url 'nextcloud' %}">Bitte einloggen!</a>
|
||||
{% endif %}
|
||||
|
|
|
@ -4,9 +4,8 @@ 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
|
||||
from .forms import ITForm, WorkingForm, OfficeForm, DummyForm, OffboardingForm
|
||||
|
||||
class LoginTestCase(TestCase):
|
||||
def setUp(self):
|
||||
|
@ -17,12 +16,6 @@ class LoginTestCase(TestCase):
|
|||
|
||||
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):
|
||||
|
@ -62,8 +55,7 @@ class LoginTestCase(TestCase):
|
|||
'0-firstname': 'Ara',
|
||||
'0-lastname': 'Seva',
|
||||
'0-department': 'CENT',
|
||||
'0-team': 'Community Communications',
|
||||
'0-choice': 'IN',
|
||||
'0-choice': 'IN'
|
||||
}, WorkingForm)
|
||||
|
||||
response = self._postform({
|
||||
|
@ -92,17 +84,37 @@ class LoginTestCase(TestCase):
|
|||
'eva_form_view-current_step': '5',
|
||||
}, 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):
|
||||
self.test_wizzard_in()
|
||||
# print(mail.outbox[0].body)
|
||||
# print(mail.outbox)
|
||||
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):
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
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 = [
|
||||
path('', EvaFormView.as_view(condition_dict = {'1': long_process,
|
||||
'2': long_process,
|
||||
'3': long_process,
|
||||
|
||||
'4': change_process,}),
|
||||
|
||||
'4': offboarding_process,}),
|
||||
name='evaform'),
|
||||
path('success', success, name='success')
|
||||
]
|
||||
|
|
|
@ -10,12 +10,11 @@ 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, VENDOR_CHOICES, \
|
||||
LANG_CHOICES, ACCOUNT_CHOICES, TRANSPONDER_CHOICES, KEYBOARD_CHOICES
|
||||
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
|
||||
|
||||
def success(request):
|
||||
|
@ -30,24 +29,38 @@ def long_process(wizard):
|
|||
else:
|
||||
data = wizard.get_cleaned_data_for_step('0') or {}
|
||||
# print(data)
|
||||
if data.get('choice') != 'CHANGE':
|
||||
if data.get('choice') == 'IN':
|
||||
wizard.set_choice('IN')
|
||||
# print('PROZESS IN')
|
||||
print('PROZESS IN')
|
||||
return True
|
||||
else:
|
||||
wizard.set_choice('CHANGE')
|
||||
# print('PROZESS NOT IN')
|
||||
return False
|
||||
#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
|
||||
|
||||
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')
|
||||
print('CHANGE PROZESS')
|
||||
return not long_process(wizard)
|
||||
|
||||
|
||||
class EvaFormView(LoginRequiredMixin, CookieWizardView):
|
||||
template_name = 'evapp/employee_form.html'
|
||||
form_list = [PersonalForm, WorkingForm, ITForm, OfficeForm, ChangeForm, DummyForm]
|
||||
form_list = [PersonalForm, WorkingForm, ITForm, OfficeForm, OffboardingForm, DummyForm]
|
||||
instance = None
|
||||
choice = 'IN'
|
||||
|
||||
|
|
|
@ -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!"
|
Loading…
Reference in New Issue