chol
/
eva
forked from tohe/eva
1
0
Fork 0

Compare commits

..

1 Commits

Author SHA1 Message Date
Masin Al-Dujaili 1cc2a50df8 documentation on how to use systemctl (or service)
Puppet deploys a service unit file so EVA can be started at boot time and logs go the journal instead of some logfile at some opaque location
2024-11-11 10:58:00 +00:00
7 changed files with 86 additions and 89 deletions

140
README.md
View File

@ -1,61 +1,79 @@
# eva # eva
A simple tool for on- and offboarding people in a mid sized organisation. A simple tool for on- and offboarding people in a mid sized organisation.
"EVA" is an german acronym for "Eintritt, Veränderung, Austritt", "EVA" is an german acronym for "Eintritt, Veränderung, Austritt",
meaning "Onboarding, Change, Offboarding" meaning "Onboarding, Change, Offboarding"
# development # development
- install gettext for instance via "apt install gettext" for translations - install gettext for instance via `apt install gettext` for translations
- set up a virtual environment with virtualenvwrapper or some other environment managing tool
- set up a virtual environment with virtualenvwrapper or some other - use this environment and do
environment managing tool ```
pip install django django-multiselectfield django-formtools django-allauth
- use this environment and do ```
- clone this repository
pip install django django-multiselectfield django-formtools django-allauth - `ln -sr eva/settings_development.py eva/settings.py`
- initialise your database with `python manage.py migrate`
- clone this repository - start your development server with `python manage.py runserver`
- ln -sr eva/settings_development.py eva/settings.py # oauth
- initialise your database with - You need to add oauth information in the django backend via .../admin in "Social Accounts"
python manage.py migrate # production
- start your development server with - you can use gunicorn as server for example instead of the django development server.
- we use whitenoise for serving static files
python manage.py runserver - we still use the development SQLITE database from django
# oauth do the following in the project main directory:
```
- You need to add oauth information in the django backend via .../admin in "Social Accounts" ln -sr eva/settings_production.py eva/settings.py
```
# production edit /secrets.json to contain something similar to
```
- you can use gunicorn as server for example instead of the django development server. {
"SECRET_KEY": "THIS IS ANOTHER SECRET!"
- we use whitenoise for serving static files }
```
- we still use the development SQLITE database from django run the following commands:
```
do the following in the project main directory: python3 manage.py migrate
python3 manage.py collectstatic
ln -sr eva/settings_production.py eva/settings.py django-admin compilemessages
```
edit /secrets.json to contain something similar to
As root create a file `/etc/systemd/system` (it's already deployed by puppet when the corresponding manifest is applied):
{
"SECRET_KEY": "THIS IS ANOTHER SECRET!" ```
} # /etc/systemd/system/eva.service
#
run the following commands:
[Unit]
python3 manage.py migrate Description=gunicorn EVA daemon
python3 manage.py collectstatic After=network.target
django-admin compilemessages
[Service]
server starts with Type=simple
User=eva
export PYTHONUNBUFFERED=TRUE; nohup gunicorn --forwarded-allow-ips="*" -b '0:8000' eva.wsgi &> logfile & 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.

View File

@ -65,8 +65,7 @@ MIDDLEWARE = [
'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'

View File

@ -29,7 +29,7 @@ class PersonalForm(EvaForm):
class Meta: class Meta:
model = Employee model = Employee
fields = ['firstname', 'lastname', 'department', 'team', 'add_to_wikimediade',] fields = ['firstname', 'lastname', 'department', 'team', ]
class WorkingForm(EvaForm): class WorkingForm(EvaForm):
@ -57,8 +57,8 @@ class ITForm(EvaForm):
class Meta: class Meta:
model = Employee model = Employee
fields = [ fields = [
'vendor', 'os', 'keyboard', 'screen', '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,7 +30,6 @@ 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')), ('language', models.CharField(choices=[('GER', 'Deutsch'), ('ENG', 'English')], default='GER', max_length=3, verbose_name='Sprache')),

View File

@ -52,7 +52,6 @@ class Employee(models.Model):
# intern = models.BooleanField(verbose_name='Interne_r Mitarbeiter_in?', default=True) # intern = models.BooleanField(verbose_name='Interne_r Mitarbeiter_in?', default=True)
department = models.CharField(max_length=5, choices=DEPARTMENT_CHOICES.items(), verbose_name=_('Bereich')) 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? team = models.CharField(max_length=50, null=True, blank=True) # TODO? better with choices?
add_to_wikimediade = models.BooleanField(default = False, verbose_name=_('Soll auf wikimedia.de irgendwo stehen?'))
# general work related stuff # general work related stuff
firstdate_employment = models.DateField(null=True, verbose_name=_("Erster Arbeitstag")) firstdate_employment = models.DateField(null=True, verbose_name=_("Erster Arbeitstag"))
@ -67,7 +66,6 @@ class Employee(models.Model):
os = models.CharField(max_length=3, choices=OS_CHOICES.items(), default='UBU', verbose_name=_('Betriebssystem')) 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.')) 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)')) 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") # sim = models.BooleanField(default=False, verbose_name="Mobilfunkvertrag")
keyboard = models.CharField(max_length=2, choices=KEYBOARD_CHOICES.items(), default='DE', verbose_name=_("Tastaturlayout")) 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")) comment = models.TextField(max_length=500, null=True, blank=True, verbose_name=_("zusätzliche IT-Anforderungen"))

View File

@ -9,7 +9,6 @@ BASIC_DATA = ['firstname', 'lastname', 'firstdate_employment', 'firstdate_presen
'jobdescription_german', 'jobdescription_english',] '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',
@ -29,7 +28,7 @@ MAILS = {
'KOMM': { 'KOMM': {
'MAIL': 'presse@wikimedia.de', 'MAIL': 'presse@wikimedia.de',
'DATA': [ 'DATA': [
'department', 'team', 'add_to_wikimediade' 'department', 'team',
], ],
}, },
'CENTRAL': { 'CENTRAL': {
@ -55,13 +54,5 @@ MAILS = {
'DATA': [ 'DATA': [
'rebu2go' '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'
]
} }
} }

View File

@ -148,21 +148,14 @@ class EvaFormView(LoginRequiredMixin, CookieWizardView):
f'EVA: Neuzugang {firstname} {lastname} (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}',
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}', 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)