forked from beba/foerderbarometer
Compare commits
43 Commits
4aeb330fa4
...
d1ae90b6a1
| Author | SHA1 | Date |
|---|---|---|
|
|
d1ae90b6a1 | |
|
|
d933f5a32b | |
|
|
6698d6a6f3 | |
|
|
0b9fb801bd | |
|
|
0efaa6910d | |
|
|
32f8a8f50f | |
|
|
f4698c3894 | |
|
|
0839439671 | |
|
|
295f41ff75 | |
|
|
c295f7182b | |
|
|
945550b8f6 | |
|
|
e44e0eb31c | |
|
|
52315d4378 | |
|
|
17f763ba66 | |
|
|
6306567ebd | |
|
|
165ad050ad | |
|
|
5696072604 | |
|
|
4c458c16d7 | |
|
|
60a5538033 | |
|
|
d40c53bc80 | |
|
|
e28d68516a | |
|
|
88e2c959d0 | |
|
|
61dcce3505 | |
|
|
858e8519bd | |
|
|
fe50d9b465 | |
|
|
edd0eb2205 | |
|
|
1495621ef0 | |
|
|
d3f18b0b93 | |
|
|
f0c8ca71bb | |
|
|
b5a0fbde98 | |
|
|
eaddabbcdd | |
|
|
0afabe9325 | |
|
|
d5e6a7c343 | |
|
|
eb44094639 | |
|
|
e5cea49673 | |
|
|
233151968b | |
|
|
fe8985504e | |
|
|
80d1d17ff6 | |
|
|
02ee0097a8 | |
|
|
70c88440aa | |
|
|
fa8d0ab898 | |
|
|
b9fe0c9a1f | |
|
|
e0538bbc9a |
|
|
@ -0,0 +1,23 @@
|
||||||
|
[run]
|
||||||
|
branch = on
|
||||||
|
source = input
|
||||||
|
omit =
|
||||||
|
# ignore the tests itself
|
||||||
|
*/test*.py
|
||||||
|
*/tests/*.py
|
||||||
|
|
||||||
|
# ignore wsgi & asgi
|
||||||
|
*/?sgi.py
|
||||||
|
|
||||||
|
[report]
|
||||||
|
include =
|
||||||
|
input/*
|
||||||
|
exclude_lines =
|
||||||
|
pragma: no cover
|
||||||
|
raise NotImplementedError
|
||||||
|
omit =
|
||||||
|
# they are excluded in run, so
|
||||||
|
# doesn't need to be reported
|
||||||
|
*/test*.py
|
||||||
|
*/tests/*.py
|
||||||
|
*/?sgi.py
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
ENVIRONMENT = develop
|
||||||
|
DEBUG = yes
|
||||||
|
SECRET_KEY = not-a-secret-key
|
||||||
|
DATABASE_ENGINE = sqlite3
|
||||||
|
EMAIL_BACKEND = console
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
ENVIRONMENT = production
|
||||||
|
DEBUG = no
|
||||||
|
SECRET_KEY = <enter a secret key>
|
||||||
|
HOST = https://foerderung.wikimedia.de
|
||||||
|
DATABASE_ENGINE = mysql
|
||||||
|
DATABASE_PASSWORD = <enter a database password>
|
||||||
|
EMAIL_BACKEND = smtp
|
||||||
|
EMAIL_HOST_USER = <enter an email host user>
|
||||||
|
EMAIL_HOST_PASSWORD = <enter an email host password>
|
||||||
|
OAUTH_ENABLED = yes
|
||||||
|
OAUTH_CLIENT_NAME = <enter a client name>
|
||||||
|
OAUTH_CLIENT_ID = <enter a client id>
|
||||||
|
OAUTH_CLIENT_SECRET = <enter a client secret>
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
# secret passwords and so
|
# secret passwords and so
|
||||||
/secrets.json
|
/secrets.json
|
||||||
/staticfiles
|
/staticfiles
|
||||||
# /foerderbarometer/settings.py
|
|
||||||
# /foerderbarometer/*settings*
|
|
||||||
/input/settings.py
|
|
||||||
/nohup.out
|
/nohup.out
|
||||||
/logfile
|
/logfile
|
||||||
*~
|
*~
|
||||||
|
|
@ -93,6 +90,10 @@ target/
|
||||||
profile_default/
|
profile_default/
|
||||||
ipython_config.py
|
ipython_config.py
|
||||||
|
|
||||||
|
# IDEA
|
||||||
|
/*.iml
|
||||||
|
/.idea
|
||||||
|
|
||||||
# pyenv
|
# pyenv
|
||||||
.python-version
|
.python-version
|
||||||
|
|
||||||
|
|
@ -139,3 +140,6 @@ dmypy.json
|
||||||
|
|
||||||
# Pyre type checker
|
# Pyre type checker
|
||||||
.pyre/
|
.pyre/
|
||||||
|
|
||||||
|
# MacOS
|
||||||
|
.DS_Store
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
stages:
|
||||||
|
- test
|
||||||
|
|
||||||
|
test:
|
||||||
|
stage: test
|
||||||
|
image: python:3.11-bookworm
|
||||||
|
variables:
|
||||||
|
DJANGO_SETTINGS_MODULE: foerderbarometer.settings
|
||||||
|
ENVIRONMENT: test
|
||||||
|
SECRET_KEY: this-is-not-a-secret-key
|
||||||
|
DATABASE_ENGINE: mysql
|
||||||
|
DATABASE_HOST: mariadb
|
||||||
|
DATABASE_USER: root
|
||||||
|
DATABASE_PASSWORD: fdb
|
||||||
|
OAUTH_ENABLED: no
|
||||||
|
services:
|
||||||
|
- name: mariadb:10.6
|
||||||
|
variables:
|
||||||
|
MARIADB_ROOT_PASSWORD: fdb
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
coverage: /(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/
|
||||||
|
before_script:
|
||||||
|
- pip install -r requirements.txt coverage
|
||||||
|
script:
|
||||||
|
- coverage run manage.py test --noinput
|
||||||
|
after_script:
|
||||||
|
- coverage report
|
||||||
51
README.md
51
README.md
|
|
@ -2,9 +2,13 @@
|
||||||
|
|
||||||
purpose: gather data from intern(WMDE) and extern(volunteers) forms to create a database ('förderdatenbank') and send emails with links for a questionary.
|
purpose: gather data from intern(WMDE) and extern(volunteers) forms to create a database ('förderdatenbank') and send emails with links for a questionary.
|
||||||
|
|
||||||
## installation and development setup
|
## manual development setup
|
||||||
|
|
||||||
ln -sr foerderbarometer/settings_development.py foerderbarometer/settings.py
|
cp .env.develop.example .env
|
||||||
|
|
||||||
|
By default a SQLite database is used for development.
|
||||||
|
To use a MariaDB change `DATABASE_ENGINE` in .env to `mysql` and amend `DATABASE_*` variables according to your setup.
|
||||||
|
For further information see the production setup below.
|
||||||
|
|
||||||
build the database with
|
build the database with
|
||||||
|
|
||||||
|
|
@ -24,6 +28,25 @@ access via
|
||||||
http://localhost:8000/intern/ (login required)
|
http://localhost:8000/intern/ (login required)
|
||||||
http://localhost:8000/admin/ (login reqiured)
|
http://localhost:8000/admin/ (login reqiured)
|
||||||
|
|
||||||
|
## docker compose development setup
|
||||||
|
|
||||||
|
The project comes with a `docker-compose.yml` file to run the project in a containerized environment.
|
||||||
|
|
||||||
|
cp .env.develop.example .env
|
||||||
|
docker compose up
|
||||||
|
|
||||||
|
The setup will use a containerized MariaDB database.
|
||||||
|
|
||||||
|
Create your superuser account with
|
||||||
|
|
||||||
|
docker compose exec django python3 manage.py createsuperuser
|
||||||
|
|
||||||
|
You can access the application via
|
||||||
|
|
||||||
|
http://localhost:8000/
|
||||||
|
http://localhost:8000/intern/ (login required)
|
||||||
|
http://localhost:8000/admin/ (login required)
|
||||||
|
|
||||||
## additional admin functionality
|
## additional admin functionality
|
||||||
|
|
||||||
The admin page is the standard admin page delivered by django but with two additional functionalities:
|
The admin page is the standard admin page delivered by django but with two additional functionalities:
|
||||||
|
|
@ -65,16 +88,26 @@ run some tests with
|
||||||
|
|
||||||
## production setup
|
## production setup
|
||||||
|
|
||||||
ln -sr foerderbarometer/settings_production.py foerderbarometer/settings.py
|
cp .env.production.example .env
|
||||||
|
|
||||||
edit /secrets.json to contain something similar to
|
edit .env and fill in the missing secrets
|
||||||
|
|
||||||
{
|
SECRET_KEY
|
||||||
"DATABASE_PASSWORD": "THIS IS TOP SECRET!",
|
DATABASE_PASSWORD
|
||||||
"SECRET_KEY": "THIS IS ANOTHER SECRET!"
|
EMAIL_HOST_USER
|
||||||
}
|
EMAIL_HOST_PASSWORD
|
||||||
|
OAUTH_CLIENT_NAME
|
||||||
|
OAUTH_CLIENT_ID
|
||||||
|
OAUTH_CLIENT_SECRET
|
||||||
|
|
||||||
edit foerderbarometer/settings_production.py according to your database setup (tested with MariaDB 10.0.36)
|
amend database variables to .env according to your database setup (tested with MariaDB 10.0.36), e.g.
|
||||||
|
|
||||||
|
DATABASE_NAME
|
||||||
|
DATABASE_USER
|
||||||
|
DATABASE_HOST
|
||||||
|
DATABASE_PORT
|
||||||
|
|
||||||
|
for a full set of all possible env vars have a look at foerderbarometer/settings.py
|
||||||
|
|
||||||
run the following commands:
|
run the following commands:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
services:
|
||||||
|
django:
|
||||||
|
image: python:3-alpine
|
||||||
|
working_dir: /app
|
||||||
|
volumes:
|
||||||
|
- .:/app
|
||||||
|
environment:
|
||||||
|
PYTHONUNBUFFERED: 1
|
||||||
|
DATABASE_ENGINE: mysql
|
||||||
|
DATABASE_HOST: mariadb
|
||||||
|
DATABASE_USER: fdb
|
||||||
|
DATABASE_PASSWORD: fdb
|
||||||
|
ports:
|
||||||
|
- 8000:8000
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
apk update
|
||||||
|
apk add gcc
|
||||||
|
apk add mariadb-dev
|
||||||
|
apk add musl-dev
|
||||||
|
pip install -Ur requirements.txt
|
||||||
|
python manage.py migrate
|
||||||
|
python manage.py runserver 0.0.0.0:8000
|
||||||
|
|
||||||
|
mariadb:
|
||||||
|
image: mariadb
|
||||||
|
environment:
|
||||||
|
MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: yes
|
||||||
|
MARIADB_DATABASE: fdb
|
||||||
|
MARIADB_USER: fdb
|
||||||
|
MARIADB_PASSWORD: fdb
|
||||||
|
|
@ -0,0 +1,168 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
from input.utils.settings import env, password_validators
|
||||||
|
|
||||||
|
BASE_DIR = Path(__file__).parents[1]
|
||||||
|
|
||||||
|
load_dotenv(BASE_DIR / '.env')
|
||||||
|
|
||||||
|
DEBUG = env('DEBUG', False)
|
||||||
|
|
||||||
|
SECRET_KEY = env('SECRET_KEY')
|
||||||
|
|
||||||
|
ALLOWED_HOSTS = ['*']
|
||||||
|
|
||||||
|
HOST = env('HOST', 'http://localhost:8000')
|
||||||
|
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
'input.apps.InputConfig',
|
||||||
|
'django.contrib.admin',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.staticfiles',
|
||||||
|
'formtools',
|
||||||
|
]
|
||||||
|
|
||||||
|
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',
|
||||||
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
]
|
||||||
|
|
||||||
|
ROOT_URLCONF = 'foerderbarometer.urls'
|
||||||
|
|
||||||
|
DJANGO_TEMPLATES = {
|
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
'DIRS': [],
|
||||||
|
'APP_DIRS': True,
|
||||||
|
'OPTIONS': {
|
||||||
|
'context_processors': [
|
||||||
|
'django.template.context_processors.debug',
|
||||||
|
'django.template.context_processors.request',
|
||||||
|
'django.contrib.auth.context_processors.auth',
|
||||||
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
TEMPLATES = [DJANGO_TEMPLATES]
|
||||||
|
|
||||||
|
WSGI_APPLICATION = 'foerderbarometer.wsgi.application'
|
||||||
|
|
||||||
|
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
||||||
|
|
||||||
|
DATABASE_ENGINE = env('DATABASE_ENGINE', 'mysql')
|
||||||
|
|
||||||
|
DATABASE_SQLITE = {
|
||||||
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
|
'NAME': BASE_DIR / 'db.sqlite3',
|
||||||
|
}
|
||||||
|
|
||||||
|
DATABASE_MYSQL = {
|
||||||
|
'ENGINE': 'django.db.backends.mysql',
|
||||||
|
'NAME': env('DATABASE_NAME', 'fdb'),
|
||||||
|
'USER': env('DATABASE_USER', 'fdb'),
|
||||||
|
'PASSWORD': env('DATABASE_PASSWORD'),
|
||||||
|
'HOST': env('DATABASE_HOST', 'localhost'),
|
||||||
|
'PORT': env('DATABASE_PORT', 3306),
|
||||||
|
'OPTIONS': {
|
||||||
|
'charset' : 'utf8',
|
||||||
|
'use_unicode' : True,
|
||||||
|
'init_command': 'SET '
|
||||||
|
'storage_engine=INNODB,'
|
||||||
|
'character_set_connection=utf8,'
|
||||||
|
'collation_connection=utf8_bin'
|
||||||
|
},
|
||||||
|
'TEST_CHARSET': 'utf8',
|
||||||
|
'TEST_COLLATION': 'utf8_general_ci',
|
||||||
|
}
|
||||||
|
|
||||||
|
if DATABASE_ENGINE == 'mysql':
|
||||||
|
DATABASE_DEFAULT = DATABASE_MYSQL
|
||||||
|
else:
|
||||||
|
DATABASE_DEFAULT = DATABASE_SQLITE
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
'default': DATABASE_DEFAULT,
|
||||||
|
}
|
||||||
|
|
||||||
|
EMAIL_BACKEND = env('EMAIL_BACKEND', 'console')
|
||||||
|
|
||||||
|
if EMAIL_BACKEND == 'smtp':
|
||||||
|
EMAIL_HOST = env('EMAIL_HOST', 'email.wikimedia.de')
|
||||||
|
EMAIL_PORT = env('EMAIL_PORT', 587)
|
||||||
|
EMAIL_USE_TLS = env('EMAIL_USE_TLS', True)
|
||||||
|
EMAIL_HOST_USER = env('EMAIL_HOST_USER')
|
||||||
|
EMAIL_HOST_PASSWORD = env('EMAIL_HOST_PASSWORD')
|
||||||
|
|
||||||
|
EMAIL_BACKEND = f'django.core.mail.backends.{EMAIL_BACKEND}.EmailBackend'
|
||||||
|
EMAIL_URL_PREFIX = env('EMAIL_URL_PREFIX', HOST)
|
||||||
|
|
||||||
|
AUTH_PASSWORD_VALIDATORS = password_validators(
|
||||||
|
'UserAttributeSimilarityValidator',
|
||||||
|
'MinimumLengthValidator',
|
||||||
|
'CommonPasswordValidator',
|
||||||
|
'NumericPasswordValidator',
|
||||||
|
)
|
||||||
|
|
||||||
|
USE_I18N = True
|
||||||
|
USE_L10N = True
|
||||||
|
LANGUAGE_CODE = env('LANGUAGE_CODE', 'en-us')
|
||||||
|
|
||||||
|
USE_TZ = True
|
||||||
|
TIME_ZONE = env('TIME_ZONE', 'UTC')
|
||||||
|
|
||||||
|
STATIC_ROOT = BASE_DIR / 'staticfiles'
|
||||||
|
STATIC_URL = '/static/'
|
||||||
|
|
||||||
|
FORM_RENDERER = 'input.forms.TableFormRenderer'
|
||||||
|
|
||||||
|
if OAUTH_ENABLED := env('OAUTH_ENABLED', not DEBUG):
|
||||||
|
MIDDLEWARE += ['input.middleware.oauth.OAuthMiddleware']
|
||||||
|
|
||||||
|
OAUTH_CLIENT_NAME = env('OAUTH_CLIENT_NAME')
|
||||||
|
|
||||||
|
OAUTH_CLIENT = {
|
||||||
|
'client_id': env('OAUTH_CLIENT_ID'),
|
||||||
|
'client_secret': env('OAUTH_CLIENT_SECRET'),
|
||||||
|
'access_token_url': 'https://meta.wikimedia.org/w/rest.php/oauth2/access_token',
|
||||||
|
'authorize_url': 'https://meta.wikimedia.org/w/rest.php/oauth2/authorize',
|
||||||
|
'api_base_url': 'https://meta.wikimedia.org/w/rest.php/oauth2/resource',
|
||||||
|
'redirect_uri': env('OAUTH_REDIRECT_URI', f'{HOST}/oauth/callback'),
|
||||||
|
'client_kwargs': {
|
||||||
|
'scope': 'basic',
|
||||||
|
'token_placement': 'header'
|
||||||
|
},
|
||||||
|
'userinfo_endpoint': 'resource/profile',
|
||||||
|
}
|
||||||
|
|
||||||
|
OAUTH_URL_WHITELISTS = ['/admin']
|
||||||
|
|
||||||
|
OAUTH_COOKIE_SESSION_ID = 'sso_session_id'
|
||||||
|
|
||||||
|
IF_EMAIL = env('IF_EMAIL', 'community@wikimedia.de')
|
||||||
|
|
||||||
|
SURVEY_EMAIL = env('SURVEY_EMAIL', 'sandro.halank@wikimedia.de')
|
||||||
|
SURVEY_PREFIX = env('SURVEY_PREFIX', 'https://wikimedia.sslsurvey.de/Foerderbarometer/?')
|
||||||
|
|
||||||
|
DATAPROTECTION = 'https://www.wikimedia.de/datenschutz/#datenerfassung'
|
||||||
|
FOERDERRICHTLINIEN = 'https://de.wikipedia.org/wiki/Wikipedia:Wikimedia_Deutschland/Richtlinie_zur_Förderung_der_Communitys'
|
||||||
|
|
||||||
|
NUTZUNGSBEDINGUNGEN = 'static/input/nutzungsbedingungen.html'
|
||||||
|
NUTZUNGSBEDINGUNGEN_EMAIL_SERVICE = 'static/input/nutzungsbedingungen-mail.pdf'
|
||||||
|
NUTZUNGSBEDINGUNGEN_MAILINGLISTEN = 'static/input/nutzungsbedingungen-mailinglisten.pdf'
|
||||||
|
NUTZUNGSBEDINGUNGEN_LITERATURSTIPENDIUM = 'static/input/nutzungsbedingungen-literaturstipendium.pdf'
|
||||||
|
NUTZUNGSBEDINGUNGEN_OTRS = 'static/input/2025_Nutzungsvereinbarung_OTRS.docx.pdf'
|
||||||
|
NUTZUNGSBEDINGUNGEN_VISITENKARTEN = 'static/input/nutzungsbedingungen-visitenkarten.pdf'
|
||||||
|
|
@ -1,203 +0,0 @@
|
||||||
"""
|
|
||||||
Django settings for foerderbarometer project.
|
|
||||||
|
|
||||||
Generated by 'django-admin startproject' using Django 3.1.1.
|
|
||||||
|
|
||||||
For more information on this file, see
|
|
||||||
https://docs.djangoproject.com/en/3.1/topics/settings/
|
|
||||||
|
|
||||||
For the full list of settings and their values, see
|
|
||||||
https://docs.djangoproject.com/en/3.1/ref/settings/
|
|
||||||
"""
|
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
|
||||||
|
|
||||||
# prefix for urls in mails
|
|
||||||
URLPREFIX = 'https://fdb-devel.wikimedia.de'
|
|
||||||
|
|
||||||
|
|
||||||
# mails in development go to stdout
|
|
||||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
|
||||||
|
|
||||||
CSRF_TRUSTED_ORIGINS = ['https://fdb-devel.wikimedia.de']
|
|
||||||
|
|
||||||
# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
|
||||||
|
|
||||||
EMAIL_HOST = 'xemail.wikimedia.de'
|
|
||||||
EMAIL_PORT = '587'
|
|
||||||
EMAIL_USE_TLS = True
|
|
||||||
#EMAIL_HOST_USER = get_secret('EMAIL_HOST_USER')
|
|
||||||
#EMAIL_HOST_PASSWORD = get_secret('EMAIL_HOST_PASSWORD')
|
|
||||||
|
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
||||||
|
|
||||||
# get secrets
|
|
||||||
with open(os.path.join(BASE_DIR, 'secrets.json')) as secrets_file:
|
|
||||||
secrets = json.load(secrets_file)
|
|
||||||
|
|
||||||
def get_secret(setting, secrets=secrets):
|
|
||||||
"""Get secret setting or fail with ImproperlyConfigured"""
|
|
||||||
try:
|
|
||||||
return secrets[setting]
|
|
||||||
except KeyError:
|
|
||||||
raise ImproperlyConfigured("Set the {} setting".format(setting))
|
|
||||||
|
|
||||||
|
|
||||||
# Quick-start development settings - unsuitable for production
|
|
||||||
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
|
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
|
||||||
SECRET_KEY = get_secret('SECRET_KEY')
|
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
|
||||||
DEBUG = True
|
|
||||||
|
|
||||||
STATIC_ROOT = BASE_DIR / 'staticfiles'
|
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['*']
|
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
|
||||||
'input.apps.InputConfig',
|
|
||||||
'django.contrib.admin',
|
|
||||||
'django.contrib.auth',
|
|
||||||
'django.contrib.contenttypes',
|
|
||||||
'django.contrib.sessions',
|
|
||||||
'django.contrib.messages',
|
|
||||||
'django.contrib.staticfiles',
|
|
||||||
'formtools',
|
|
||||||
]
|
|
||||||
|
|
||||||
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',
|
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
|
||||||
'input.middleware.oauth.OAuthMiddleware'
|
|
||||||
]
|
|
||||||
|
|
||||||
ROOT_URLCONF = 'foerderbarometer.urls'
|
|
||||||
|
|
||||||
TEMPLATES = [
|
|
||||||
{
|
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
|
||||||
'DIRS': [],
|
|
||||||
'APP_DIRS': True,
|
|
||||||
'OPTIONS': {
|
|
||||||
'context_processors': [
|
|
||||||
'django.template.context_processors.debug',
|
|
||||||
'django.template.context_processors.request',
|
|
||||||
'django.contrib.auth.context_processors.auth',
|
|
||||||
'django.contrib.messages.context_processors.messages',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
WSGI_APPLICATION = 'foerderbarometer.wsgi.application'
|
|
||||||
|
|
||||||
|
|
||||||
# Database
|
|
||||||
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
|
|
||||||
|
|
||||||
DATABASES = {
|
|
||||||
'default': {
|
|
||||||
'ENGINE': 'django.db.backends.mysql',
|
|
||||||
'NAME': 'fdbdevel',
|
|
||||||
'USER': 'fdbdevel',
|
|
||||||
'PASSWORD': get_secret('DATABASE_PASSWORD'),
|
|
||||||
'HOST': '10.0.6.224', # Or an IP Address that your database is hosted on
|
|
||||||
# 'PORT': '3306',
|
|
||||||
#optional:
|
|
||||||
'OPTIONS': {
|
|
||||||
'charset' : 'utf8',
|
|
||||||
'use_unicode' : True,
|
|
||||||
'init_command': 'SET '
|
|
||||||
'storage_engine=INNODB,'
|
|
||||||
'character_set_connection=utf8,'
|
|
||||||
'collation_connection=utf8_bin'
|
|
||||||
#'sql_mode=STRICT_TRANS_TABLES,' # see note below
|
|
||||||
#'SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED',
|
|
||||||
},
|
|
||||||
'TEST_CHARSET': 'utf8',
|
|
||||||
'TEST_COLLATION': 'utf8_general_ci',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Password validation
|
|
||||||
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
|
|
||||||
|
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# Internationalization
|
|
||||||
# https://docs.djangoproject.com/en/3.1/topics/i18n/
|
|
||||||
|
|
||||||
LANGUAGE_CODE = 'en-us'
|
|
||||||
|
|
||||||
TIME_ZONE = 'UTC'
|
|
||||||
|
|
||||||
USE_I18N = True
|
|
||||||
|
|
||||||
USE_L10N = True
|
|
||||||
|
|
||||||
USE_TZ = True
|
|
||||||
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
|
||||||
# https://docs.djangoproject.com/en/3.1/howto/static-files/
|
|
||||||
|
|
||||||
STATIC_URL = '/static/'
|
|
||||||
|
|
||||||
# needed since django 3.2
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
|
||||||
|
|
||||||
|
|
||||||
# OAuth Settings
|
|
||||||
OAUTH_URL_WHITELISTS = ['/admin']
|
|
||||||
|
|
||||||
OAUTH_CLIENT_NAME = '<name-of-the-configured-wikimedia-app>'
|
|
||||||
OAUTH_CLIENT_NAME = get_secret('OAUTH_CLIENT_NAME')
|
|
||||||
|
|
||||||
|
|
||||||
OAUTH_CLIENT = {
|
|
||||||
'client_id': get_secret('OAUTH_CLIENT_ID'),
|
|
||||||
'client_secret': get_secret('OAUTH_CLIENT_SECRET'),
|
|
||||||
'access_token_url': 'https://meta.wikimedia.org/w/rest.php/oauth2/access_token',
|
|
||||||
'authorize_url': 'https://meta.wikimedia.org/w/rest.php/oauth2/authorize',
|
|
||||||
'api_base_url': 'https://meta.wikimedia.org/w/rest.php/oauth2/resource',
|
|
||||||
'redirect_uri': 'https://fdb-devel.wikimedia.de/oauth/callback',
|
|
||||||
'client_kwargs': {
|
|
||||||
'scope': 'basic',
|
|
||||||
'token_placement': 'header'
|
|
||||||
},
|
|
||||||
'userinfo_endpoint': 'resource/profile',
|
|
||||||
}
|
|
||||||
|
|
||||||
OAUTH_COOKIE_SESSION_ID = 'sso_session_id'
|
|
||||||
|
|
@ -1,151 +0,0 @@
|
||||||
"""
|
|
||||||
Django settings for foerderbarometer project.
|
|
||||||
|
|
||||||
Generated by 'django-admin startproject' using Django 3.1.1.
|
|
||||||
|
|
||||||
For more information on this file, see
|
|
||||||
https://docs.djangoproject.com/en/3.1/topics/settings/
|
|
||||||
|
|
||||||
For the full list of settings and their values, see
|
|
||||||
https://docs.djangoproject.com/en/3.1/ref/settings/
|
|
||||||
"""
|
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
|
||||||
|
|
||||||
# prefix for urls in mails
|
|
||||||
URLPREFIX = 'http://localhost:8000'
|
|
||||||
|
|
||||||
# mails in development go to stdout
|
|
||||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
||||||
|
|
||||||
# get secrets
|
|
||||||
with open(os.path.join(BASE_DIR, 'secrets.json')) as secrets_file:
|
|
||||||
secrets = json.load(secrets_file)
|
|
||||||
|
|
||||||
def get_secret(setting, secrets=secrets):
|
|
||||||
"""Get secret setting or fail with ImproperlyConfigured"""
|
|
||||||
try:
|
|
||||||
return secrets[setting]
|
|
||||||
except KeyError:
|
|
||||||
raise ImproperlyConfigured("Set the {} setting".format(setting))
|
|
||||||
|
|
||||||
|
|
||||||
# Quick-start development settings - unsuitable for production
|
|
||||||
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
|
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
|
||||||
SECRET_KEY = '*&7p9#_n$@^%0z49s+7jpy@+j1rw_hqh05knyd6y2*!0)r&b6h'
|
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
|
||||||
DEBUG = True
|
|
||||||
|
|
||||||
STATIC_ROOT = BASE_DIR / 'staticfiles'
|
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['*']
|
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
|
||||||
'input.apps.InputConfig',
|
|
||||||
'django.contrib.admin',
|
|
||||||
'django.contrib.auth',
|
|
||||||
'django.contrib.contenttypes',
|
|
||||||
'django.contrib.sessions',
|
|
||||||
'django.contrib.messages',
|
|
||||||
'django.contrib.staticfiles',
|
|
||||||
'formtools',
|
|
||||||
]
|
|
||||||
|
|
||||||
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',
|
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
|
||||||
]
|
|
||||||
|
|
||||||
ROOT_URLCONF = 'foerderbarometer.urls'
|
|
||||||
|
|
||||||
TEMPLATES = [
|
|
||||||
{
|
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
|
||||||
'DIRS': [],
|
|
||||||
'APP_DIRS': True,
|
|
||||||
'OPTIONS': {
|
|
||||||
'context_processors': [
|
|
||||||
'django.template.context_processors.debug',
|
|
||||||
'django.template.context_processors.request',
|
|
||||||
'django.contrib.auth.context_processors.auth',
|
|
||||||
'django.contrib.messages.context_processors.messages',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
WSGI_APPLICATION = 'foerderbarometer.wsgi.application'
|
|
||||||
|
|
||||||
|
|
||||||
# Database
|
|
||||||
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
|
|
||||||
|
|
||||||
DATABASES = {
|
|
||||||
'default': {
|
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
|
||||||
'NAME': BASE_DIR / 'db.sqlite3',
|
|
||||||
'PASSWORD': get_secret('DATABASE_PASSWORD')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Password validation
|
|
||||||
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
|
|
||||||
|
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# Internationalization
|
|
||||||
# https://docs.djangoproject.com/en/3.1/topics/i18n/
|
|
||||||
|
|
||||||
LANGUAGE_CODE = 'en-us'
|
|
||||||
|
|
||||||
TIME_ZONE = 'UTC'
|
|
||||||
|
|
||||||
USE_I18N = True
|
|
||||||
|
|
||||||
USE_L10N = True
|
|
||||||
|
|
||||||
USE_TZ = True
|
|
||||||
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
|
||||||
# https://docs.djangoproject.com/en/3.1/howto/static-files/
|
|
||||||
|
|
||||||
STATIC_URL = '/static/'
|
|
||||||
|
|
||||||
# needed since django 3.2
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
|
||||||
|
|
@ -1,158 +0,0 @@
|
||||||
"""
|
|
||||||
Django settings for foerderbarometer project.
|
|
||||||
|
|
||||||
Generated by 'django-admin startproject' using Django 3.1.1.
|
|
||||||
|
|
||||||
For more information on this file, see
|
|
||||||
https://docs.djangoproject.com/en/3.1/topics/settings/
|
|
||||||
|
|
||||||
For the full list of settings and their values, see
|
|
||||||
https://docs.djangoproject.com/en/3.1/ref/settings/
|
|
||||||
"""
|
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
|
||||||
|
|
||||||
# prefix for urls in mails
|
|
||||||
URLPREFIX = 'http://localhost:8000'
|
|
||||||
|
|
||||||
# mails in development go to stdout
|
|
||||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
|
||||||
|
|
||||||
EMAIL_HOST = 'email.wikimedia.de'
|
|
||||||
EMAIL_PORT = '587'
|
|
||||||
EMAIL_USE_TLS = True
|
|
||||||
EMAIL_HOST_USER = '636ea784dd6ec43'
|
|
||||||
EMAIL_HOST_PASSWORD = 'wsgqp4ZaVRZZEpRJ'
|
|
||||||
|
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
||||||
|
|
||||||
# get secrets
|
|
||||||
with open(os.path.join(BASE_DIR, 'secrets.json')) as secrets_file:
|
|
||||||
secrets = json.load(secrets_file)
|
|
||||||
|
|
||||||
def get_secret(setting, secrets=secrets):
|
|
||||||
"""Get secret setting or fail with ImproperlyConfigured"""
|
|
||||||
try:
|
|
||||||
return secrets[setting]
|
|
||||||
except KeyError:
|
|
||||||
raise ImproperlyConfigured("Set the {} setting".format(setting))
|
|
||||||
|
|
||||||
|
|
||||||
# Quick-start development settings - unsuitable for production
|
|
||||||
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
|
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
|
||||||
SECRET_KEY = '*&7p9#_n$@^%0z49s+7jpy@+j1rw_hqh05knyd6y2*!0)r&b6h'
|
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
|
||||||
DEBUG = True
|
|
||||||
|
|
||||||
STATIC_ROOT = BASE_DIR / 'staticfiles'
|
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['*']
|
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
|
||||||
'input.apps.InputConfig',
|
|
||||||
'django.contrib.admin',
|
|
||||||
'django.contrib.auth',
|
|
||||||
'django.contrib.contenttypes',
|
|
||||||
'django.contrib.sessions',
|
|
||||||
'django.contrib.messages',
|
|
||||||
'django.contrib.staticfiles',
|
|
||||||
'formtools',
|
|
||||||
]
|
|
||||||
|
|
||||||
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',
|
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
|
||||||
]
|
|
||||||
|
|
||||||
ROOT_URLCONF = 'foerderbarometer.urls'
|
|
||||||
|
|
||||||
TEMPLATES = [
|
|
||||||
{
|
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
|
||||||
'DIRS': [],
|
|
||||||
'APP_DIRS': True,
|
|
||||||
'OPTIONS': {
|
|
||||||
'context_processors': [
|
|
||||||
'django.template.context_processors.debug',
|
|
||||||
'django.template.context_processors.request',
|
|
||||||
'django.contrib.auth.context_processors.auth',
|
|
||||||
'django.contrib.messages.context_processors.messages',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
WSGI_APPLICATION = 'foerderbarometer.wsgi.application'
|
|
||||||
|
|
||||||
|
|
||||||
# Database
|
|
||||||
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
|
|
||||||
|
|
||||||
DATABASES = {
|
|
||||||
'default': {
|
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
|
||||||
'NAME': BASE_DIR / 'db.sqlite3',
|
|
||||||
'PASSWORD': get_secret('DATABASE_PASSWORD')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Password validation
|
|
||||||
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
|
|
||||||
|
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# Internationalization
|
|
||||||
# https://docs.djangoproject.com/en/3.1/topics/i18n/
|
|
||||||
|
|
||||||
LANGUAGE_CODE = 'en-us'
|
|
||||||
|
|
||||||
TIME_ZONE = 'UTC'
|
|
||||||
|
|
||||||
USE_I18N = True
|
|
||||||
|
|
||||||
USE_L10N = True
|
|
||||||
|
|
||||||
USE_TZ = True
|
|
||||||
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
|
||||||
# https://docs.djangoproject.com/en/3.1/howto/static-files/
|
|
||||||
|
|
||||||
STATIC_URL = '/static/'
|
|
||||||
|
|
||||||
# needed since django 3.2
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
|
||||||
|
|
@ -1,175 +0,0 @@
|
||||||
"""
|
|
||||||
Django settings for foerderbarometer project.
|
|
||||||
|
|
||||||
Generated by 'django-admin startproject' using Django 3.1.1.
|
|
||||||
|
|
||||||
For more information on this file, see
|
|
||||||
https://docs.djangoproject.com/en/3.1/topics/settings/
|
|
||||||
|
|
||||||
For the full list of settings and their values, see
|
|
||||||
https://docs.djangoproject.com/en/3.1/ref/settings/
|
|
||||||
"""
|
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
|
||||||
|
|
||||||
# prefix for urls in mails
|
|
||||||
URLPREFIX = 'http://localhost:8000'
|
|
||||||
|
|
||||||
# mails in development go to stdout
|
|
||||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
||||||
|
|
||||||
# get secrets
|
|
||||||
with open(os.path.join(BASE_DIR, 'secrets.json')) as secrets_file:
|
|
||||||
secrets = json.load(secrets_file)
|
|
||||||
|
|
||||||
def get_secret(setting, secrets=secrets):
|
|
||||||
"""Get secret setting or fail with ImproperlyConfigured"""
|
|
||||||
try:
|
|
||||||
return secrets[setting]
|
|
||||||
except KeyError:
|
|
||||||
raise ImproperlyConfigured("Set the {} setting".format(setting))
|
|
||||||
|
|
||||||
|
|
||||||
# Quick-start development settings - unsuitable for production
|
|
||||||
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
|
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
|
||||||
SECRET_KEY = '*&7p9#_n$@^%0z49s+7jpy@+j1rw_hqh05knyd6y2*!0)r&b6h'
|
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
|
||||||
DEBUG = True
|
|
||||||
|
|
||||||
STATIC_ROOT = BASE_DIR / 'staticfiles'
|
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['*']
|
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
|
||||||
'input.apps.InputConfig',
|
|
||||||
'django.contrib.admin',
|
|
||||||
'django.contrib.auth',
|
|
||||||
'django.contrib.contenttypes',
|
|
||||||
'django.contrib.sessions',
|
|
||||||
'django.contrib.messages',
|
|
||||||
'django.contrib.staticfiles',
|
|
||||||
'formtools',
|
|
||||||
]
|
|
||||||
|
|
||||||
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',
|
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
|
||||||
]
|
|
||||||
|
|
||||||
ROOT_URLCONF = 'foerderbarometer.urls'
|
|
||||||
|
|
||||||
TEMPLATES = [
|
|
||||||
{
|
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
|
||||||
'DIRS': [],
|
|
||||||
'APP_DIRS': True,
|
|
||||||
'OPTIONS': {
|
|
||||||
'context_processors': [
|
|
||||||
'django.template.context_processors.debug',
|
|
||||||
'django.template.context_processors.request',
|
|
||||||
'django.contrib.auth.context_processors.auth',
|
|
||||||
'django.contrib.messages.context_processors.messages',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
WSGI_APPLICATION = 'foerderbarometer.wsgi.application'
|
|
||||||
|
|
||||||
|
|
||||||
# Database
|
|
||||||
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
|
|
||||||
#
|
|
||||||
# DATABASES = {
|
|
||||||
# 'default': {
|
|
||||||
# 'ENGINE': 'django.db.backends.sqlite3',
|
|
||||||
# 'NAME': BASE_DIR / 'db.sqlite3',
|
|
||||||
# 'PASSWORD': get_secret('DATABASE_PASSWORD')
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
|
|
||||||
DATABASES = {
|
|
||||||
'default': {
|
|
||||||
'ENGINE': 'django.db.backends.mysql',
|
|
||||||
'NAME': 'fdb',
|
|
||||||
'USER': 'fdb',
|
|
||||||
'PASSWORD': get_secret('DATABASE_PASSWORD'),
|
|
||||||
'HOST': 'localhost', # Or an IP Address that your database is hosted on
|
|
||||||
# 'PORT': '3306',
|
|
||||||
#optional:
|
|
||||||
'OPTIONS': {
|
|
||||||
'charset' : 'utf8',
|
|
||||||
'use_unicode' : True,
|
|
||||||
'init_command': 'SET '
|
|
||||||
'storage_engine=INNODB,'
|
|
||||||
'character_set_connection=utf8,'
|
|
||||||
'collation_connection=utf8_bin'
|
|
||||||
#'sql_mode=STRICT_TRANS_TABLES,' # see note below
|
|
||||||
#'SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED',
|
|
||||||
},
|
|
||||||
'TEST_CHARSET': 'utf8',
|
|
||||||
'TEST_COLLATION': 'utf8_general_ci',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Password validation
|
|
||||||
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
|
|
||||||
|
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# Internationalization
|
|
||||||
# https://docs.djangoproject.com/en/3.1/topics/i18n/
|
|
||||||
|
|
||||||
LANGUAGE_CODE = 'en-us'
|
|
||||||
|
|
||||||
TIME_ZONE = 'UTC'
|
|
||||||
|
|
||||||
USE_I18N = True
|
|
||||||
|
|
||||||
USE_L10N = True
|
|
||||||
|
|
||||||
USE_TZ = True
|
|
||||||
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
|
||||||
# https://docs.djangoproject.com/en/3.1/howto/static-files/
|
|
||||||
|
|
||||||
STATIC_URL = '/static/'
|
|
||||||
|
|
||||||
# needed since django 3.2
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
|
||||||
|
|
@ -1,171 +0,0 @@
|
||||||
"""
|
|
||||||
Django settings for foerderbarometer project.
|
|
||||||
|
|
||||||
Generated by 'django-admin startproject' using Django 3.1.1.
|
|
||||||
|
|
||||||
For more information on this file, see
|
|
||||||
https://docs.djangoproject.com/en/3.1/topics/settings/
|
|
||||||
|
|
||||||
For the full list of settings and their values, see
|
|
||||||
https://docs.djangoproject.com/en/3.1/ref/settings/
|
|
||||||
"""
|
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
|
||||||
|
|
||||||
# mails in development go to stdout
|
|
||||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
|
||||||
|
|
||||||
EMAIL_HOST = 'email.wikimedia.de'
|
|
||||||
EMAIL_PORT = '587'
|
|
||||||
EMAIL_USE_TLS = True
|
|
||||||
EMAIL_HOST_USER = '636ea784dd6ec43'
|
|
||||||
EMAIL_HOST_PASSWORD = 'wsgqp4ZaVRZZEpRJ'
|
|
||||||
|
|
||||||
# prefix for urls in mails
|
|
||||||
URLPREFIX = 'http://foerderung.wikimedia.de'
|
|
||||||
|
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
||||||
|
|
||||||
# get secrets
|
|
||||||
with open(os.path.join(BASE_DIR, 'secrets.json')) as secrets_file:
|
|
||||||
secrets = json.load(secrets_file)
|
|
||||||
|
|
||||||
def get_secret(setting, secrets=secrets):
|
|
||||||
"""Get secret setting or fail with ImproperlyConfigured"""
|
|
||||||
try:
|
|
||||||
return secrets[setting]
|
|
||||||
except KeyError:
|
|
||||||
raise ImproperlyConfigured("Set the {} setting".format(setting))
|
|
||||||
|
|
||||||
|
|
||||||
# Quick-start development settings - unsuitable for production
|
|
||||||
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
|
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
|
||||||
SECRET_KEY = get_secret('SECRET_KEY')
|
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
|
||||||
DEBUG = False
|
|
||||||
|
|
||||||
STATIC_ROOT = BASE_DIR / 'staticfiles'
|
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['*']
|
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
|
||||||
'input.apps.InputConfig',
|
|
||||||
'django.contrib.admin',
|
|
||||||
'django.contrib.auth',
|
|
||||||
'django.contrib.contenttypes',
|
|
||||||
'django.contrib.sessions',
|
|
||||||
'django.contrib.messages',
|
|
||||||
'django.contrib.staticfiles',
|
|
||||||
'formtools',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
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',
|
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
|
||||||
]
|
|
||||||
|
|
||||||
ROOT_URLCONF = 'foerderbarometer.urls'
|
|
||||||
|
|
||||||
TEMPLATES = [
|
|
||||||
{
|
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
|
||||||
'DIRS': [],
|
|
||||||
'APP_DIRS': True,
|
|
||||||
'OPTIONS': {
|
|
||||||
'context_processors': [
|
|
||||||
'django.template.context_processors.debug',
|
|
||||||
'django.template.context_processors.request',
|
|
||||||
'django.contrib.auth.context_processors.auth',
|
|
||||||
'django.contrib.messages.context_processors.messages',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
WSGI_APPLICATION = 'foerderbarometer.wsgi.application'
|
|
||||||
|
|
||||||
|
|
||||||
# Database
|
|
||||||
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
|
|
||||||
|
|
||||||
DATABASES = {
|
|
||||||
'default': {
|
|
||||||
'ENGINE': 'django.db.backends.mysql',
|
|
||||||
'NAME': 'fdbdevel',
|
|
||||||
'USER': 'fdbdevel',
|
|
||||||
'PASSWORD': get_secret('DATABASE_PASSWORD'),
|
|
||||||
'HOST': '10.0.6.7', # Or an IP Address that your database is hosted on
|
|
||||||
# 'PORT': '3306',
|
|
||||||
#optional:
|
|
||||||
'OPTIONS': {
|
|
||||||
'charset' : 'utf8',
|
|
||||||
'use_unicode' : True,
|
|
||||||
'init_command': 'SET '
|
|
||||||
'storage_engine=INNODB,'
|
|
||||||
'character_set_connection=utf8,'
|
|
||||||
'collation_connection=utf8_bin'
|
|
||||||
#'sql_mode=STRICT_TRANS_TABLES,' # see note below
|
|
||||||
#'SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED',
|
|
||||||
},
|
|
||||||
'TEST_CHARSET': 'utf8',
|
|
||||||
'TEST_COLLATION': 'utf8_general_ci',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Password validation
|
|
||||||
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
|
|
||||||
|
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# Internationalization
|
|
||||||
# https://docs.djangoproject.com/en/3.1/topics/i18n/
|
|
||||||
|
|
||||||
LANGUAGE_CODE = 'en-us'
|
|
||||||
|
|
||||||
TIME_ZONE = 'UTC'
|
|
||||||
|
|
||||||
USE_I18N = True
|
|
||||||
|
|
||||||
USE_L10N = True
|
|
||||||
|
|
||||||
USE_TZ = True
|
|
||||||
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
|
||||||
# https://docs.djangoproject.com/en/3.1/howto/static-files/
|
|
||||||
|
|
||||||
STATIC_URL = '/static/'
|
|
||||||
|
|
@ -3,8 +3,20 @@ import csv
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
|
||||||
from .models import Account, Project, HonoraryCertificate, Library, IFG, Travel,\
|
from .models import (
|
||||||
Email, BusinessCard, List, Literature
|
Account,
|
||||||
|
Project,
|
||||||
|
HonoraryCertificate,
|
||||||
|
Library,
|
||||||
|
ELiterature,
|
||||||
|
Software,
|
||||||
|
IFG,
|
||||||
|
Travel,
|
||||||
|
Email,
|
||||||
|
BusinessCard,
|
||||||
|
List,
|
||||||
|
Literature,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def export_as_csv(self, request, queryset):
|
def export_as_csv(self, request, queryset):
|
||||||
|
|
@ -45,7 +57,7 @@ class ProjectAdmin(admin.ModelAdmin):
|
||||||
class BusinessCardAdmin(admin.ModelAdmin):
|
class BusinessCardAdmin(admin.ModelAdmin):
|
||||||
save_as = True
|
save_as = True
|
||||||
search_fields = ('realname', 'service_id', 'granted', 'granted_date', 'project')
|
search_fields = ('realname', 'service_id', 'granted', 'granted_date', 'project')
|
||||||
list_display = ('realname', 'service_id', 'granted', 'granted_date', 'project')
|
list_display = ('realname', 'service_id', 'granted', 'granted_date', 'project', 'terms_accepted')
|
||||||
list_display_links = ('realname', 'service_id')
|
list_display_links = ('realname', 'service_id')
|
||||||
# action = ['export_as_csv']
|
# action = ['export_as_csv']
|
||||||
date_hierarchy = 'granted_date'
|
date_hierarchy = 'granted_date'
|
||||||
|
|
@ -57,7 +69,7 @@ class BusinessCardAdmin(admin.ModelAdmin):
|
||||||
class LiteratureAdmin(admin.ModelAdmin):
|
class LiteratureAdmin(admin.ModelAdmin):
|
||||||
save_as = True
|
save_as = True
|
||||||
search_fields = ('realname', 'service_id', 'granted', 'granted_date')
|
search_fields = ('realname', 'service_id', 'granted', 'granted_date')
|
||||||
list_display = ('realname', 'service_id', 'granted', 'granted_date')
|
list_display = ('realname', 'service_id', 'granted', 'granted_date', 'terms_accepted')
|
||||||
list_display_links = ('realname', 'service_id')
|
list_display_links = ('realname', 'service_id')
|
||||||
date_hierarchy = 'granted_date'
|
date_hierarchy = 'granted_date'
|
||||||
readonly_fields = ['service_id']
|
readonly_fields = ['service_id']
|
||||||
|
|
@ -78,7 +90,8 @@ class HonoraryCertificateAdmin(admin.ModelAdmin):
|
||||||
class Media:
|
class Media:
|
||||||
js = ('dropdown/js/otrs_link.js',)
|
js = ('dropdown/js/otrs_link.js',)
|
||||||
|
|
||||||
@admin.register(Library)
|
|
||||||
|
@admin.register(Library, ELiterature, Software)
|
||||||
class LibraryAdmin(admin.ModelAdmin):
|
class LibraryAdmin(admin.ModelAdmin):
|
||||||
save_as = True
|
save_as = True
|
||||||
search_fields = ('realname', 'service_id', 'granted', 'granted_date')
|
search_fields = ('realname', 'service_id', 'granted', 'granted_date')
|
||||||
|
|
@ -86,6 +99,21 @@ class LibraryAdmin(admin.ModelAdmin):
|
||||||
list_display_links = ('realname', 'service_id')
|
list_display_links = ('realname', 'service_id')
|
||||||
date_hierarchy = 'granted_date'
|
date_hierarchy = 'granted_date'
|
||||||
readonly_fields = ['service_id']
|
readonly_fields = ['service_id']
|
||||||
|
exclude = ['type']
|
||||||
|
|
||||||
|
def get_queryset(self, request):
|
||||||
|
return super().get_queryset(request).filter(type=self.model.TYPE)
|
||||||
|
|
||||||
|
def formfield_for_dbfield(self, db_field, request, **kwargs):
|
||||||
|
if db_field.name == 'library':
|
||||||
|
kwargs['label'] = self.model.LIBRARY_LABEL
|
||||||
|
kwargs['help_text'] = self.model.LIBRARY_HELP_TEXT
|
||||||
|
|
||||||
|
elif db_field.name == 'duration':
|
||||||
|
kwargs['help_text'] = self.model.DURATION_HELP_TEXT
|
||||||
|
|
||||||
|
return super().formfield_for_dbfield(db_field, request, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(IFG)
|
@admin.register(IFG)
|
||||||
class IFGAdmin(admin.ModelAdmin):
|
class IFGAdmin(admin.ModelAdmin):
|
||||||
|
|
@ -103,9 +131,8 @@ class TravelAdmin(admin.ModelAdmin):
|
||||||
list_display = ('realname', 'service_id', 'granted', 'granted_date', 'project_end', 'project', 'project_end_quartal')
|
list_display = ('realname', 'service_id', 'granted', 'granted_date', 'project_end', 'project', 'project_end_quartal')
|
||||||
list_display_links = ('realname', 'project')
|
list_display_links = ('realname', 'project')
|
||||||
date_hierarchy = 'project_end'
|
date_hierarchy = 'project_end'
|
||||||
readonly_fields = ('project_end_quartal', 'project_end')
|
|
||||||
autocomplete_fields = ['project']
|
autocomplete_fields = ['project']
|
||||||
readonly_fields = ['service_id']
|
readonly_fields = ['service_id', 'project_end', 'project_end_quartal']
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
js = ('dropdown/js/otrs_link.js',)
|
js = ('dropdown/js/otrs_link.js',)
|
||||||
|
|
@ -115,7 +142,7 @@ class TravelAdmin(admin.ModelAdmin):
|
||||||
class EmailAdmin(admin.ModelAdmin):
|
class EmailAdmin(admin.ModelAdmin):
|
||||||
save_as = True
|
save_as = True
|
||||||
search_fields = ('realname', 'service_id', 'granted', 'granted_date')
|
search_fields = ('realname', 'service_id', 'granted', 'granted_date')
|
||||||
list_display = ('realname', 'service_id', 'granted', 'granted_date')
|
list_display = ('realname', 'service_id', 'granted', 'granted_date', 'terms_accepted')
|
||||||
list_display_links = ('realname', 'service_id')
|
list_display_links = ('realname', 'service_id')
|
||||||
date_hierarchy = 'granted_date'
|
date_hierarchy = 'granted_date'
|
||||||
radio_fields = {'adult': admin.VERTICAL}
|
radio_fields = {'adult': admin.VERTICAL}
|
||||||
|
|
@ -128,7 +155,7 @@ class EmailAdmin(admin.ModelAdmin):
|
||||||
class ListAdmin(admin.ModelAdmin):
|
class ListAdmin(admin.ModelAdmin):
|
||||||
save_as = True
|
save_as = True
|
||||||
search_fields = ('realname', 'service_id', 'granted', 'granted_date')
|
search_fields = ('realname', 'service_id', 'granted', 'granted_date')
|
||||||
list_display = ('realname', 'service_id', 'granted', 'granted_date')
|
list_display = ('realname', 'service_id', 'granted', 'granted_date', 'terms_accepted')
|
||||||
list_display_links = ('realname', 'service_id')
|
list_display_links = ('realname', 'service_id')
|
||||||
date_hierarchy = 'granted_date'
|
date_hierarchy = 'granted_date'
|
||||||
readonly_fields = ['service_id']
|
readonly_fields = ['service_id']
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
"""
|
|
||||||
ASGI config for oauth_demo project.
|
|
||||||
|
|
||||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
|
||||||
|
|
||||||
For more information on this file, see
|
|
||||||
https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from django.core.asgi import get_asgi_application
|
|
||||||
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'oauth_demo.settings')
|
|
||||||
|
|
||||||
application = get_asgi_application()
|
|
||||||
|
|
@ -1,15 +1,30 @@
|
||||||
from django.db import models
|
from django.conf import settings
|
||||||
from django.forms import ModelForm, DateField, ChoiceField, RadioSelect, BooleanField
|
from django.forms import ModelForm, ChoiceField, RadioSelect, BooleanField
|
||||||
from django.contrib.admin.widgets import AdminDateWidget
|
from django.contrib.admin.widgets import AdminDateWidget
|
||||||
|
from django.forms.renderers import DjangoTemplates
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from .models import Project, Volunteer, ConcreteVolunteer, Extern, ConcreteExtern, IFG, Library, TYPE_CHOICES,\
|
from .models import (
|
||||||
HonoraryCertificate, Travel, Email, Literature, List,\
|
TYPE_CHOICES,
|
||||||
BusinessCard
|
Project,
|
||||||
from .settings import DATAPROTECTION, FOERDERRICHTLINIEN, NUTZUNGSBEDINGUNGEN
|
ConcreteVolunteer,
|
||||||
|
ConcreteExtern,
|
||||||
|
IFG,
|
||||||
|
Library,
|
||||||
|
ELiterature,
|
||||||
|
Software,
|
||||||
|
HonoraryCertificate,
|
||||||
|
Travel,
|
||||||
|
Email,
|
||||||
|
Literature,
|
||||||
|
List,
|
||||||
|
BusinessCard,
|
||||||
|
)
|
||||||
|
|
||||||
from . import settings
|
|
||||||
|
|
||||||
|
class TableFormRenderer(DjangoTemplates):
|
||||||
|
form_template_name = 'django/forms/table.html'
|
||||||
|
|
||||||
|
|
||||||
class FdbForm(ModelForm):
|
class FdbForm(ModelForm):
|
||||||
|
|
@ -39,7 +54,7 @@ class ExternForm(FdbForm):
|
||||||
|
|
||||||
check = BooleanField(required=True,
|
check = BooleanField(required=True,
|
||||||
label=format_html("Ich stimme den <a href='{}' target='_blank' rel='noopener'>Datenschutzbestimmungen</a> und der<br> <a href='{}' target='_blank' rel='noopener'>Richtlinie zur Förderung der Communitys</a> zu",
|
label=format_html("Ich stimme den <a href='{}' target='_blank' rel='noopener'>Datenschutzbestimmungen</a> und der<br> <a href='{}' target='_blank' rel='noopener'>Richtlinie zur Förderung der Communitys</a> zu",
|
||||||
DATAPROTECTION, FOERDERRICHTLINIEN))
|
settings.DATAPROTECTION, settings.FOERDERRICHTLINIEN))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ConcreteExtern
|
model = ConcreteExtern
|
||||||
|
|
@ -59,8 +74,8 @@ class InternForm(FdbForm):
|
||||||
exclude = ('granted', 'granted_date', 'survey_mail_send', 'survey_mail_date', 'mail_state')
|
exclude = ('granted', 'granted_date', 'survey_mail_send', 'survey_mail_date', 'mail_state')
|
||||||
|
|
||||||
|
|
||||||
HOTEL_CHOICES = {'TRUE': format_html('Hotelzimmer benötigt'),
|
HOTEL_CHOICES = {'TRUE': mark_safe('Hotelzimmer benötigt'),
|
||||||
'FALSE': format_html('Kein Hotelzimmer benötigt')
|
'FALSE': mark_safe('Kein Hotelzimmer benötigt')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -91,6 +106,7 @@ class TravelForm(FdbForm):
|
||||||
'all': ('css/dateFieldNoNowShortcutInTravels.css',)
|
'all': ('css/dateFieldNoNowShortcutInTravels.css',)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class LibraryForm(FdbForm):
|
class LibraryForm(FdbForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -98,6 +114,26 @@ class LibraryForm(FdbForm):
|
||||||
fields = ['cost', 'library', 'duration', 'notes', 'survey_mail_send']
|
fields = ['cost', 'library', 'duration', 'notes', 'survey_mail_send']
|
||||||
exclude = ['intern_notes', 'survey_mail_send', 'mail_state']
|
exclude = ['intern_notes', 'survey_mail_send', 'mail_state']
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.fields['library'].label = self._meta.model.LIBRARY_LABEL
|
||||||
|
self.fields['library'].help_text = self._meta.model.LIBRARY_HELP_TEXT
|
||||||
|
self.fields['duration'].help_text = self._meta.model.DURATION_HELP_TEXT
|
||||||
|
|
||||||
|
|
||||||
|
class ELiteratureForm(LibraryForm):
|
||||||
|
|
||||||
|
class Meta(LibraryForm.Meta):
|
||||||
|
model = ELiterature
|
||||||
|
|
||||||
|
|
||||||
|
class SoftwareForm(LibraryForm):
|
||||||
|
|
||||||
|
class Meta(LibraryForm.Meta):
|
||||||
|
model = Software
|
||||||
|
|
||||||
|
|
||||||
class HonoraryCertificateForm(FdbForm):
|
class HonoraryCertificateForm(FdbForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -116,26 +152,20 @@ class IFGForm(FdbForm):
|
||||||
|
|
||||||
|
|
||||||
class CheckForm(FdbForm):
|
class CheckForm(FdbForm):
|
||||||
termstoaccept = NUTZUNGSBEDINGUNGEN
|
termstoaccept = settings.NUTZUNGSBEDINGUNGEN
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.fields['check'] = BooleanField(
|
|
||||||
required=True,
|
# Check if the model field 'terms_accepted' is present
|
||||||
label=format_html(
|
if 'terms_accepted' in self.fields:
|
||||||
|
# Make the field required (HTML5 validation)
|
||||||
|
self.fields['terms_accepted'].required = True
|
||||||
|
# Set custom label with link to terms
|
||||||
|
self.fields['terms_accepted'].label = format_html(
|
||||||
"Ich stimme den <a href='{}'>Nutzungsbedingungen</a> zu",
|
"Ich stimme den <a href='{}'>Nutzungsbedingungen</a> zu",
|
||||||
self.termstoaccept
|
self.termstoaccept
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""Baseclass for all classes which need a check for Nutzungsbedingungen"""
|
|
||||||
# def __init__(self, *args, **kwargs):
|
|
||||||
# check = BooleanField(required=True,
|
|
||||||
# label=format_html("Ich stimme den <a href='{}'>Nutzungsbedingungen</a> zu",
|
|
||||||
# termstoaccept))
|
|
||||||
# NUTZUNGSBEDINGUNGEN))
|
|
||||||
|
|
||||||
|
|
||||||
class LiteratureForm(CheckForm):
|
class LiteratureForm(CheckForm):
|
||||||
|
|
@ -146,13 +176,13 @@ class LiteratureForm(CheckForm):
|
||||||
self.fields['selfbuy_give_data'].required = True
|
self.fields['selfbuy_give_data'].required = True
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Literature
|
model = Literature
|
||||||
fields = ['cost', 'info', 'source', 'notes', 'selfbuy', 'selfbuy_data', 'selfbuy_give_data']
|
fields = ['cost', 'info', 'source', 'notes', 'selfbuy', 'selfbuy_data', 'selfbuy_give_data', 'terms_accepted']
|
||||||
exclude = ['intern_notes', 'survey_mail_send', 'mail_state']
|
exclude = ['intern_notes', 'survey_mail_send', 'mail_state']
|
||||||
class Media:
|
class Media:
|
||||||
js = ('dropdown/js/literature.js',)
|
js = ('dropdown/js/literature.js',)
|
||||||
|
|
||||||
ADULT_CHOICES = {'TRUE': format_html('Ich bin volljährig.'),
|
ADULT_CHOICES = {'TRUE': mark_safe('Ich bin volljährig.'),
|
||||||
'FALSE': format_html('Ich bin noch nicht volljährig.')
|
'FALSE': mark_safe('Ich bin noch nicht volljährig.')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -173,7 +203,7 @@ class EmailForm(CheckForm):
|
||||||
# TODO: add some javascript to show/hide other-field
|
# TODO: add some javascript to show/hide other-field
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Email
|
model = Email
|
||||||
fields = ['domain', 'address', 'other', 'adult']
|
fields = ['domain', 'address', 'other', 'adult', 'terms_accepted']
|
||||||
exclude = ['intern_notes', 'survey_mail_send', 'mail_state']
|
exclude = ['intern_notes', 'survey_mail_send', 'mail_state']
|
||||||
class Media:
|
class Media:
|
||||||
js = ('dropdown/js/mail.js',)
|
js = ('dropdown/js/mail.js',)
|
||||||
|
|
@ -191,7 +221,7 @@ class BusinessCardForm(CheckForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = BusinessCard
|
model = BusinessCard
|
||||||
exclude = ['intern_notes', 'survey_mail_send', 'mail_state']
|
exclude = ['intern_notes', 'survey_mail_send', 'mail_state']
|
||||||
fields = ['project', 'data', 'variant', 'url_of_pic', 'send_data_to_print', 'sent_to']
|
fields = ['project', 'data', 'variant', 'url_of_pic', 'send_data_to_print', 'sent_to', 'terms_accepted']
|
||||||
class Media:
|
class Media:
|
||||||
js = ('dropdown/js/businessCard.js',)
|
js = ('dropdown/js/businessCard.js',)
|
||||||
|
|
||||||
|
|
@ -200,6 +230,5 @@ class ListForm(CheckForm):
|
||||||
termstoaccept = settings.NUTZUNGSBEDINGUNGEN_MAILINGLISTEN
|
termstoaccept = settings.NUTZUNGSBEDINGUNGEN_MAILINGLISTEN
|
||||||
class Meta:
|
class Meta:
|
||||||
model = List
|
model = List
|
||||||
fields = ['domain', 'address']
|
fields = ['domain', 'address', 'terms_accepted']
|
||||||
exclude = ['intern_notes', 'survey_mail_send','mail_state']
|
exclude = ['intern_notes', 'survey_mail_send','mail_state']
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,16 @@
|
||||||
from datetime import date, timedelta
|
from datetime import date, timedelta
|
||||||
import sys
|
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand
|
||||||
from django.template.loader import get_template
|
from django.template.loader import get_template
|
||||||
from django.core.mail import send_mail, BadHeaderError, EmailMessage
|
from django.core.mail import BadHeaderError
|
||||||
from django.core.mail import EmailMultiAlternatives
|
from django.core.mail import EmailMultiAlternatives
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from input.models import Project, Library, HonoraryCertificate, Travel, Email,\
|
from input.models import Project, Library, HonoraryCertificate, Travel, Email,\
|
||||||
BusinessCard, List, IFG, Literature
|
BusinessCard, List, IFG, Literature
|
||||||
from input.settings import IF_EMAIL, SURVEYPREFIX, SURVEY_EMAIL
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
''' mails will be send here:
|
''' mails will be sent here:
|
||||||
|
|
||||||
- two weeks after confirmation of support for volunteer (/extern) send link
|
- two weeks after confirmation of support for volunteer (/extern) send link
|
||||||
with surveylink
|
with surveylink
|
||||||
|
|
@ -34,14 +32,14 @@ class Command(BaseCommand):
|
||||||
'type': type,
|
'type': type,
|
||||||
'name': name,
|
'name': name,
|
||||||
'pid': pid,
|
'pid': pid,
|
||||||
'SURVEYPREFIX': SURVEYPREFIX, }
|
'SURVEY_PREFIX': settings.SURVEY_PREFIX, }
|
||||||
txt_mail_template = get_template('input/survey_mail.txt')
|
txt_mail_template = get_template('input/survey_mail.txt')
|
||||||
html_mail_template = get_template('input/survey_mail.html')
|
html_mail_template = get_template('input/survey_mail.html')
|
||||||
try:
|
try:
|
||||||
subject, from_email, to = 'Dein Feedback zur Förderung durch Wikimedia Deutschland', IF_EMAIL, email
|
subject, from_email, to = 'Dein Feedback zur Förderung durch Wikimedia Deutschland', settings.IF_EMAIL, email
|
||||||
text_content = txt_mail_template.render(context)
|
text_content = txt_mail_template.render(context)
|
||||||
html_content = html_mail_template.render(context)
|
html_content = html_mail_template.render(context)
|
||||||
msg = EmailMultiAlternatives(subject, text_content, from_email, [to], bcc=[SURVEY_EMAIL])
|
msg = EmailMultiAlternatives(subject, text_content, from_email, [to], bcc=[settings.SURVEY_EMAIL])
|
||||||
msg.attach_alternative(html_content, "text/html")
|
msg.attach_alternative(html_content, "text/html")
|
||||||
msg.send()
|
msg.send()
|
||||||
#print('survey mail would have been send')
|
#print('survey mail would have been send')
|
||||||
|
|
@ -53,7 +51,7 @@ class Command(BaseCommand):
|
||||||
# bcc=[SURVEY_EMAIL])
|
# bcc=[SURVEY_EMAIL])
|
||||||
#survey_mail.send(fail_silently=False)
|
#survey_mail.send(fail_silently=False)
|
||||||
except BadHeaderError:
|
except BadHeaderError:
|
||||||
return HttpResponse('Invalid header found.')
|
return HttpResponse('Invalid header found.') # FIXME HttpResponse???
|
||||||
|
|
||||||
print(f'send surveylinkemail to {email}...')
|
print(f'send surveylinkemail to {email}...')
|
||||||
|
|
||||||
|
|
@ -77,10 +75,10 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
for project in old:
|
for project in old:
|
||||||
context = {'project': project}
|
context = {'project': project}
|
||||||
context['URLPREFIX'] = settings.URLPREFIX
|
context['URL_PREFIX'] = settings.EMAIL_URL_PREFIX
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subject, from_email, to = 'Projektende erreicht', IF_EMAIL, IF_EMAIL
|
subject, from_email, to = 'Projektende erreicht', settings.IF_EMAIL, settings.IF_EMAIL
|
||||||
text_content = txt_mail_template.render(context)
|
text_content = txt_mail_template.render(context)
|
||||||
html_content = html_mail_template.render(context)
|
html_content = html_mail_template.render(context)
|
||||||
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
|
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
|
||||||
|
|
@ -120,11 +118,11 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
for project in approved_end:
|
for project in approved_end:
|
||||||
context = {'project': project}
|
context = {'project': project}
|
||||||
context['URLPREFIX'] = settings.URLPREFIX
|
context['URL_PREFIX'] = settings.EMAIL_URL_PREFIX
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subject, from_email, to = 'Projektende erreicht', IF_EMAIL, project.email
|
subject, from_email, to = 'Projektende erreicht', settings.IF_EMAIL, project.email
|
||||||
text_content = txt_mail_template.render(context)
|
text_content = txt_mail_template.render(context)
|
||||||
html_content = html_mail_template.render(context)
|
html_content = html_mail_template.render(context)
|
||||||
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
|
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
|
||||||
|
|
@ -132,7 +130,7 @@ class Command(BaseCommand):
|
||||||
msg.send()
|
msg.send()
|
||||||
#print('if and of project approved mail would have been sent')
|
#print('if and of project approved mail would have been sent')
|
||||||
|
|
||||||
inform_subject, inform_from_email, inform_to = 'Projektorganisator*in wurde informiert', IF_EMAIL, IF_EMAIL
|
inform_subject, inform_from_email, inform_to = 'Projektorganisator*in wurde informiert', settings.IF_EMAIL, settings.IF_EMAIL
|
||||||
inform_text_content = txt_informMail_template.render(context)
|
inform_text_content = txt_informMail_template.render(context)
|
||||||
inform_html_content = html_informMail_template.render(context)
|
inform_html_content = html_informMail_template.render(context)
|
||||||
inform_msg = EmailMultiAlternatives(inform_subject, inform_text_content, inform_from_email, [inform_to])
|
inform_msg = EmailMultiAlternatives(inform_subject, inform_text_content, inform_from_email, [inform_to])
|
||||||
|
|
@ -178,9 +176,9 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
for project in approved_notHappened:
|
for project in approved_notHappened:
|
||||||
context = {'project': project}
|
context = {'project': project}
|
||||||
context['URLPREFIX'] = settings.URLPREFIX
|
context['URL_PREFIX'] = settings.EMAIL_URL_PREFIX
|
||||||
try:
|
try:
|
||||||
subject, from_email, to = 'Projektende erreicht', IF_EMAIL, project.email
|
subject, from_email, to = 'Projektende erreicht', settings.IF_EMAIL, project.email
|
||||||
text_content = txt_mail_template.render(context)
|
text_content = txt_mail_template.render(context)
|
||||||
html_content = html_mail_template.render(context)
|
html_content = html_mail_template.render(context)
|
||||||
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
|
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
|
||||||
|
|
@ -195,7 +193,7 @@ class Command(BaseCommand):
|
||||||
# [project.email],
|
# [project.email],
|
||||||
# fail_silently=False)
|
# fail_silently=False)
|
||||||
|
|
||||||
inform_subject, inform_from_email, inform_to = 'Projektorganisator*in wurde informiert', IF_EMAIL, IF_EMAIL
|
inform_subject, inform_from_email, inform_to = 'Projektorganisator*in wurde informiert', settings.IF_EMAIL, settings.IF_EMAIL
|
||||||
inform_text_content = txt_informMail_template.render(context)
|
inform_text_content = txt_informMail_template.render(context)
|
||||||
inform_html_content = html_informMail_template.render(context)
|
inform_html_content = html_informMail_template.render(context)
|
||||||
inform_msg = EmailMultiAlternatives(inform_subject, inform_text_content, inform_from_email, [inform_to])
|
inform_msg = EmailMultiAlternatives(inform_subject, inform_text_content, inform_from_email, [inform_to])
|
||||||
|
|
@ -297,16 +295,15 @@ class Command(BaseCommand):
|
||||||
'''send survey link 2 weeks after mailadresss, mailinglist or businesscards are granted'''
|
'''send survey link 2 weeks after mailadresss, mailinglist or businesscards are granted'''
|
||||||
lastdate = date.today() - timedelta(days=14)
|
lastdate = date.today() - timedelta(days=14)
|
||||||
|
|
||||||
typefield = ('MAIL','VIS','LIST')
|
models = Email, BusinessCard, List
|
||||||
count = 0
|
types = 'MAIL', 'VIS', 'LIST'
|
||||||
for c in ('Email', 'BusinessCard', 'List'):
|
|
||||||
# get class via string
|
for model, typ in zip(models, types):
|
||||||
supported = getattr(sys.modules[__name__], c).objects.filter(granted=True)\
|
supported = model.objects.filter(granted=True)\
|
||||||
.filter(granted_date__lt = lastdate)\
|
.filter(granted_date__lt = lastdate)\
|
||||||
.exclude(survey_mail_send=True)\
|
.exclude(survey_mail_send=True)\
|
||||||
.exclude(mail_state = 'END')
|
.exclude(mail_state = 'END')
|
||||||
self.surveymails_to_object(supported, type=typefield[count])
|
self.surveymails_to_object(supported, type=typ)
|
||||||
count += 1
|
|
||||||
|
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from django.core.management import call_command
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.utils.timezone import localdate
|
||||||
|
|
||||||
|
from input.models import Project, Account, Email, Library, HonoraryCertificate
|
||||||
|
|
||||||
|
|
||||||
|
class ManagementCommandTestCase(TestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
cls.account = Account.objects.create(code='test')
|
||||||
|
|
||||||
|
def test_sendmails(self):
|
||||||
|
today = localdate(None)
|
||||||
|
start = today - datetime.timedelta(days=2)
|
||||||
|
end = today - datetime.timedelta(days=1)
|
||||||
|
granted = today - datetime.timedelta(days=15)
|
||||||
|
|
||||||
|
def create_project(name, **kwargs):
|
||||||
|
kwargs.setdefault('account', self.account)
|
||||||
|
kwargs.setdefault('start', start)
|
||||||
|
kwargs.setdefault('end', end)
|
||||||
|
|
||||||
|
return Project.objects.create(name=name, **kwargs)
|
||||||
|
|
||||||
|
create_project('Test end_of_projects_reached')
|
||||||
|
create_project('Test end_of_projects_approved', status='END', mail_state='INF')
|
||||||
|
create_project('Test notHappened_of_projects_approved', status='NOT', mail_state='INF')
|
||||||
|
|
||||||
|
Email.objects.create(
|
||||||
|
domain='SOURCE',
|
||||||
|
address='cosmocode',
|
||||||
|
adult='TRUE',
|
||||||
|
granted=True,
|
||||||
|
granted_date=granted,
|
||||||
|
)
|
||||||
|
|
||||||
|
Library.objects.create(
|
||||||
|
type='BIB',
|
||||||
|
library='Test',
|
||||||
|
duration='1 Jahr',
|
||||||
|
cost=100,
|
||||||
|
granted=True,
|
||||||
|
granted_date=granted,
|
||||||
|
)
|
||||||
|
|
||||||
|
HonoraryCertificate.objects.create(
|
||||||
|
request_url='https://example.com',
|
||||||
|
granted=True,
|
||||||
|
granted_date=granted,
|
||||||
|
project=create_project('Test surveymails_to_hon'),
|
||||||
|
)
|
||||||
|
|
||||||
|
call_command('sendmails')
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
from authlib.integrations.base_client import OAuthError
|
from authlib.integrations.base_client import OAuthError
|
||||||
from authlib.integrations.django_client import OAuth
|
from authlib.integrations.django_client import OAuth
|
||||||
from authlib.oauth2.rfc6749 import OAuth2Token
|
from authlib.oauth2.rfc6749 import OAuth2Token
|
||||||
|
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.utils.deprecation import MiddlewareMixin
|
from django.utils.deprecation import MiddlewareMixin
|
||||||
from foerderbarometer import settings
|
from django.conf import settings
|
||||||
from input import views
|
|
||||||
from input import models
|
from input.models import Extern
|
||||||
|
from input.views import ExternView
|
||||||
|
|
||||||
|
|
||||||
class OAuthMiddleware(MiddlewareMixin):
|
class OAuthMiddleware(MiddlewareMixin):
|
||||||
|
|
||||||
|
|
@ -14,7 +17,7 @@ class OAuthMiddleware(MiddlewareMixin):
|
||||||
self.oauth = OAuth()
|
self.oauth = OAuth()
|
||||||
|
|
||||||
def process_request(self, request):
|
def process_request(self, request):
|
||||||
# added this if clause to get the landing page before oauth
|
# added this if-clause to get the landing page before oauth
|
||||||
if request.path == '/':
|
if request.path == '/':
|
||||||
return self.get_response(request)
|
return self.get_response(request)
|
||||||
if settings.OAUTH_URL_WHITELISTS is not None:
|
if settings.OAUTH_URL_WHITELISTS is not None:
|
||||||
|
|
@ -37,12 +40,12 @@ class OAuthMiddleware(MiddlewareMixin):
|
||||||
self.clear_session(request)
|
self.clear_session(request)
|
||||||
request.session['token'] = sso_client.authorize_access_token(request)
|
request.session['token'] = sso_client.authorize_access_token(request)
|
||||||
# print('blub', request.session['token'])
|
# print('blub', request.session['token'])
|
||||||
models.Extern.username = self.get_current_user(sso_client, request)['username']
|
Extern.username = self.get_current_user(sso_client, request)['username']
|
||||||
if self.get_current_user(sso_client, request) is not None:
|
if self.get_current_user(sso_client, request) is not None:
|
||||||
redirect_uri = request.session.pop('redirect_uri', None)
|
redirect_uri = request.session.pop('redirect_uri', None)
|
||||||
if redirect_uri is not None:
|
if redirect_uri is not None:
|
||||||
return redirect(redirect_uri)
|
return redirect(redirect_uri)
|
||||||
return redirect(views.ExternView)
|
return redirect(ExternView)
|
||||||
|
|
||||||
if request.session.get('token', None) is not None:
|
if request.session.get('token', None) is not None:
|
||||||
current_user = self.get_current_user(sso_client, request)
|
current_user = self.get_current_user(sso_client, request)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
# Generated by Django 5.2.5 on 2025-08-20 09:13
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('input', '0096_auto_20230106_1338'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='businesscard',
|
||||||
|
name='realname',
|
||||||
|
field=models.CharField(default='', help_text='Bitte gib deinen Vornamen und deinen Nachnamen ein.', max_length=200, null=True, verbose_name='Realname'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='businesscard',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(help_text='Wikimedia Benutzer_innenname', max_length=200, null=True, verbose_name='Benutzer_innenname'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='concreteextern',
|
||||||
|
name='realname',
|
||||||
|
field=models.CharField(default='', help_text='Bitte gib deinen Vornamen und deinen Nachnamen ein.', max_length=200, null=True, verbose_name='Realname'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='concreteextern',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(help_text='Wikimedia Benutzer_innenname', max_length=200, null=True, verbose_name='Benutzer_innenname'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='concretevolunteer',
|
||||||
|
name='realname',
|
||||||
|
field=models.CharField(default='', help_text='Bitte gib deinen Vornamen und deinen Nachnamen ein.', max_length=200, null=True, verbose_name='Realname'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='email',
|
||||||
|
name='realname',
|
||||||
|
field=models.CharField(default='', help_text='Bitte gib deinen Vornamen und deinen Nachnamen ein.', max_length=200, null=True, verbose_name='Realname'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='email',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(help_text='Wikimedia Benutzer_innenname', max_length=200, null=True, verbose_name='Benutzer_innenname'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='honorarycertificate',
|
||||||
|
name='realname',
|
||||||
|
field=models.CharField(default='', help_text='Bitte gib deinen Vornamen und deinen Nachnamen ein.', max_length=200, null=True, verbose_name='Realname'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='ifg',
|
||||||
|
name='realname',
|
||||||
|
field=models.CharField(default='', help_text='Bitte gib deinen Vornamen und deinen Nachnamen ein.', max_length=200, null=True, verbose_name='Realname'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='ifg',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(help_text='Wikimedia Benutzer_innenname', max_length=200, null=True, verbose_name='Benutzer_innenname'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='library',
|
||||||
|
name='realname',
|
||||||
|
field=models.CharField(default='', help_text='Bitte gib deinen Vornamen und deinen Nachnamen ein.', max_length=200, null=True, verbose_name='Realname'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='library',
|
||||||
|
name='type',
|
||||||
|
field=models.CharField(choices=[('BIB', '<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Zugang_zu_Fachliteratur#Bibliotheksstipendium" target="_blank" rel="noopener">Bibliotheksstipendium</a>'), ('ELIT', '<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Zugang_zu_Fachliteratur#eLiteraturstipendium" target="_blank" rel="noopener">eLiteraturstipendium</a>'), ('MAIL', '<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/E-Mail-Adressen_und_Visitenkarten#E-Mail-Adressen" target="_blank" rel="noopener">E-Mail-Adresse</a>'), ('IFG', '<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Gebührenerstattungen_für_Behördenanfragen" target="_blank" rel="noopener">Kostenübernahme IFG-Anfrage</a>'), ('LIT', '<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Zugang_zu_Fachliteratur#Literaturstipendium" target="_blank" rel="noopener">Literaturstipendium</a>'), ('LIST', '<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/E-Mail-Adressen_und_Visitenkarten#Mailinglisten" target="_blank" rel="noopener">Mailingliste</a>'), ('TRAV', '<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Reisekostenerstattungen" target="_blank" rel="noopener">Reisekosten</a>'), ('SOFT', '<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Software-Stipendien" target="_blank" rel="noopener">Softwarestipendium</a>'), ('VIS', '<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/E-Mail-Adressen_und_Visitenkarten#Visitenkarten" target="_blank" rel="noopener">Visitenkarten</a>')], default='BIB', max_length=4),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='library',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(help_text='Wikimedia Benutzer_innenname', max_length=200, null=True, verbose_name='Benutzer_innenname'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='list',
|
||||||
|
name='realname',
|
||||||
|
field=models.CharField(default='', help_text='Bitte gib deinen Vornamen und deinen Nachnamen ein.', max_length=200, null=True, verbose_name='Realname'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='list',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(help_text='Wikimedia Benutzer_innenname', max_length=200, null=True, verbose_name='Benutzer_innenname'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='literature',
|
||||||
|
name='realname',
|
||||||
|
field=models.CharField(default='', help_text='Bitte gib deinen Vornamen und deinen Nachnamen ein.', max_length=200, null=True, verbose_name='Realname'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='literature',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(help_text='Wikimedia Benutzer_innenname', max_length=200, null=True, verbose_name='Benutzer_innenname'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='project',
|
||||||
|
name='realname',
|
||||||
|
field=models.CharField(default='', help_text='Bitte gib deinen Vornamen und deinen Nachnamen ein.', max_length=200, null=True, verbose_name='Realname'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='travel',
|
||||||
|
name='realname',
|
||||||
|
field=models.CharField(default='', help_text='Bitte gib deinen Vornamen und deinen Nachnamen ein.', max_length=200, null=True, verbose_name='Realname'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='travel',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(help_text='Wikimedia Benutzer_innenname', max_length=200, null=True, verbose_name='Benutzer_innenname'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Generated by Django 5.2.5 on 2025-08-20 10:01
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('input', '0097_alter_realname_and_username'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ELiterature',
|
||||||
|
fields=[
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'proxy': True,
|
||||||
|
'indexes': [],
|
||||||
|
'constraints': [],
|
||||||
|
},
|
||||||
|
bases=('input.library',),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Software',
|
||||||
|
fields=[
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'proxy': True,
|
||||||
|
'indexes': [],
|
||||||
|
'constraints': [],
|
||||||
|
},
|
||||||
|
bases=('input.library',),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='library',
|
||||||
|
name='type',
|
||||||
|
field=models.CharField(choices=[('BIB', '<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Zugang_zu_Fachliteratur#Bibliotheksstipendium" target="_blank" rel="noopener">Bibliotheksstipendium</a>'), ('ELIT', '<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Zugang_zu_Fachliteratur#eLiteraturstipendium" target="_blank" rel="noopener">eLiteraturstipendium</a>'), ('SOFT', '<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Software-Stipendien" target="_blank" rel="noopener">Softwarestipendium</a>')], default='BIB', max_length=4),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Generated by Django 5.2.5 on 2025-08-26 11:28
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('input', '0098_add_eliterature_and_software_proxies'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='businesscard',
|
||||||
|
name='terms_accepted',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='Nutzungsbedingungen zugestimmt'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='email',
|
||||||
|
name='terms_accepted',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='Nutzungsbedingungen zugestimmt'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='list',
|
||||||
|
name='terms_accepted',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='Nutzungsbedingungen zugestimmt'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='literature',
|
||||||
|
name='terms_accepted',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='Nutzungsbedingungen zugestimmt'),
|
||||||
|
),
|
||||||
|
]
|
||||||
155
input/models.py
155
input/models.py
|
|
@ -2,20 +2,28 @@ from datetime import date
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
from .settings import ACCOUNTS
|
|
||||||
|
|
||||||
|
|
||||||
EMAIL_STATES = {'NONE': 'noch keine Mail versendet',
|
EMAIL_STATES = {'NONE': 'noch keine Mail versendet',
|
||||||
'INF': 'die Benachrichtigung zur Projektabschlussmail wurde versendet',
|
'INF': 'die Benachrichtigung zur Projektabschlussmail wurde versendet',
|
||||||
'CLOSE': 'die Projektabschlussmail wurde versendet',
|
'CLOSE': 'die Projektabschlussmail wurde versendet',
|
||||||
'END': 'alle automatischen Mails, auch surveyMail, wurden versendet'}
|
'END': 'alle automatischen Mails, auch surveyMail, wurden versendet'}
|
||||||
|
|
||||||
|
|
||||||
|
class TermsConsentMixin(models.Model):
|
||||||
|
"""Abstract mixin to add a terms_accepted field for documenting user consent."""
|
||||||
|
|
||||||
|
terms_accepted = models.BooleanField(default=False, verbose_name="Nutzungsbedingungen zugestimmt")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
class Volunteer(models.Model):
|
class Volunteer(models.Model):
|
||||||
realname = models.CharField(max_length=200, null=True, verbose_name="Realname",
|
realname = models.CharField(max_length=200, null=True, verbose_name="Realname",
|
||||||
help_text="Bitte gib deinen Vornamen und deinen Nachnamen ein.", default='')
|
help_text="Bitte gib deinen Vornamen und deinen Nachnamen ein.", default='')
|
||||||
email = models.EmailField(max_length=200, null=True, verbose_name='E-Mail-Adresse',
|
email = models.EmailField(max_length=200, null=True, verbose_name='E-Mail-Adresse',
|
||||||
help_text=format_html('Bitte gib deine E-Mail-Adresse ein, damit dich<br>Wikimedia Deutschland bei Rückfragen oder für<br>die Zusage kontaktieren kann.'))
|
help_text=mark_safe('Bitte gib deine E-Mail-Adresse ein, damit dich<br>Wikimedia Deutschland bei Rückfragen oder für<br>die Zusage kontaktieren kann.'))
|
||||||
|
|
||||||
# the following Fields are not supposed to be edited by users
|
# the following Fields are not supposed to be edited by users
|
||||||
|
|
||||||
|
|
@ -42,7 +50,7 @@ class Extern(Volunteer):
|
||||||
''' abstract basis class for all data entered by extern volunteers '''
|
''' abstract basis class for all data entered by extern volunteers '''
|
||||||
|
|
||||||
username = models.CharField(max_length=200, null=True, verbose_name='Benutzer_innenname',
|
username = models.CharField(max_length=200, null=True, verbose_name='Benutzer_innenname',
|
||||||
help_text=format_html("Wikimedia Benutzer_innenname"))
|
help_text=mark_safe("Wikimedia Benutzer_innenname"))
|
||||||
|
|
||||||
# the following Fields are not supposed to be edited by users
|
# the following Fields are not supposed to be edited by users
|
||||||
service_id = models.CharField(max_length=15, null=True, blank=True)
|
service_id = models.CharField(max_length=15, null=True, blank=True)
|
||||||
|
|
@ -154,16 +162,8 @@ class Project(Volunteer):
|
||||||
# self.pid = str(self.account.code) + str(self.pk).zfill(3)
|
# self.pid = str(self.account.code) + str(self.pk).zfill(3)
|
||||||
print (("Hallo Leute! Ich save jetzt mal MIT PID DANN!!!",self.pid))
|
print (("Hallo Leute! Ich save jetzt mal MIT PID DANN!!!",self.pid))
|
||||||
|
|
||||||
|
if self.end:
|
||||||
# generation of field quartals
|
self.end_quartal = f'Q{self.end.month // 4 + 1}'
|
||||||
if self.end.month in [1, 2, 3]:
|
|
||||||
self.end_quartal = 'Q1'
|
|
||||||
if self.end.month in [4, 5, 6]:
|
|
||||||
self.end_quartal = 'Q2'
|
|
||||||
if self.end.month in [7, 8, 9]:
|
|
||||||
self.end_quartal = 'Q3'
|
|
||||||
if self.end.month in [10, 11, 12]:
|
|
||||||
self.end_quartal = 'Q4'
|
|
||||||
|
|
||||||
super().save()
|
super().save()
|
||||||
|
|
||||||
|
|
@ -198,8 +198,8 @@ TRANSPORT_CHOICES = {'BAHN': 'Bahn',
|
||||||
PAYEDBY_CHOICES = {'WMDE': 'WMDE',
|
PAYEDBY_CHOICES = {'WMDE': 'WMDE',
|
||||||
'REQU': 'Antragstellender Mensch'}
|
'REQU': 'Antragstellender Mensch'}
|
||||||
|
|
||||||
HOTEL_CHOICES = {'TRUE': format_html('Hotelzimmer benötigt'),
|
HOTEL_CHOICES = {'TRUE': mark_safe('Hotelzimmer benötigt'),
|
||||||
'FALSE': format_html('Kein Hotelzimmer benötigt')
|
'FALSE': mark_safe('Kein Hotelzimmer benötigt')
|
||||||
}
|
}
|
||||||
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
@ -254,47 +254,95 @@ class Grant(Extern):
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
TYPE_CHOICES = {'BIB': format_html('<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Zugang_zu_Fachliteratur#Bibliotheksstipendium" target="_blank" rel="noopener">Bibliotheksstipendium</a>'),
|
def type_link(path, label):
|
||||||
'ELIT': format_html('<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Zugang_zu_Fachliteratur#eLiteraturstipendium" target="_blank" rel="noopener">eLiteraturstipendium</a>'),
|
return format_html(
|
||||||
'MAIL': format_html('<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/E-Mail-Adressen_und_Visitenkarten#E-Mail-Adressen" target="_blank" rel="noopener">E-Mail-Adresse</a>'),
|
format_string='<a href="{href}" target="_blank" rel="noopener">{label}</a>',
|
||||||
'IFG': format_html('<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Gebührenerstattungen_für_Behördenanfragen" target="_blank" rel="noopener">Kostenübernahme IFG-Anfrage</a>'),
|
href=f'https://de.wikipedia.org/wiki/Wikipedia:Förderung/{path}',
|
||||||
'LIT': format_html('<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Zugang_zu_Fachliteratur#Literaturstipendium" target="_blank" rel="noopener">Literaturstipendium</a>'),
|
label=label,
|
||||||
'LIST': format_html('<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/E-Mail-Adressen_und_Visitenkarten#Mailinglisten" target="_blank" rel="noopener">Mailingliste</a>'),
|
)
|
||||||
'TRAV': format_html('<a href="https://de.wikipedia.org/wiki/Wikipedia:F%C3%B6rderung/Reisekostenerstattungen" target="_blank" rel="noopener">Reisekosten</a>'),
|
|
||||||
'SOFT': format_html('<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Software-Stipendien" target="_blank" rel="noopener">Softwarestipendium</a>'),
|
|
||||||
'VIS': format_html('<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/E-Mail-Adressen_und_Visitenkarten#Visitenkarten" target="_blank" rel="noopener">Visitenkarten</a>'),
|
TYPE_BIB = 'BIB'
|
||||||
|
TYPE_ELIT = 'ELIT'
|
||||||
|
TYPE_MAIL = 'MAIL'
|
||||||
|
TYPE_IFG = 'IFG'
|
||||||
|
TYPE_LIT = 'LIT'
|
||||||
|
TYPE_LIST = 'LIST'
|
||||||
|
TYPE_TRAV = 'TRAV'
|
||||||
|
TYPE_SOFT = 'SOFT'
|
||||||
|
TYPE_VIS = 'VIS'
|
||||||
|
|
||||||
|
TYPE_CHOICES = {
|
||||||
|
TYPE_BIB: type_link('Zugang_zu_Fachliteratur#Bibliotheksstipendium', 'Bibliotheksstipendium'),
|
||||||
|
TYPE_ELIT: type_link('Zugang_zu_Fachliteratur#eLiteraturstipendium', 'eLiteraturstipendium'),
|
||||||
|
TYPE_MAIL: type_link('E-Mail-Adressen_und_Visitenkarten#E-Mail-Adressen', 'E-Mail-Adresse'),
|
||||||
|
TYPE_IFG: type_link('Gebührenerstattungen_für_Behördenanfragen', 'Kostenübernahme IFG-Anfrage'),
|
||||||
|
TYPE_LIT: type_link('Zugang_zu_Fachliteratur#Literaturstipendium', 'Literaturstipendium'),
|
||||||
|
TYPE_LIST: type_link('E-Mail-Adressen_und_Visitenkarten#Mailinglisten', 'Mailingliste'),
|
||||||
|
TYPE_TRAV: type_link('Reisekostenerstattungen', 'Reisekosten'),
|
||||||
|
TYPE_SOFT: type_link('Software-Stipendien', 'Softwarestipendium'),
|
||||||
|
TYPE_VIS: type_link('E-Mail-Adressen_und_Visitenkarten#Visitenkarten', 'Visitenkarten'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LIBRARY_TYPES = TYPE_BIB, TYPE_ELIT, TYPE_SOFT
|
||||||
|
LIBRARY_TYPE_CHOICES = [(choice, TYPE_CHOICES[choice]) for choice in LIBRARY_TYPES]
|
||||||
|
|
||||||
# same model is used for Library, ELitStip and Software!
|
# same model is used for Library, ELitStip and Software!
|
||||||
class Library(Grant):
|
class Library(Grant):
|
||||||
|
TYPE = TYPE_BIB
|
||||||
|
LIBRARY_LABEL = 'Bibliothek'
|
||||||
|
LIBRARY_HELP_TEXT = 'Für welche Bibliothek gilt das Stipendium?'
|
||||||
|
DURATION_HELP_TEXT = mark_safe('In welchem Zeitraum möchtest du recherchieren oder<br>wie lange ist der Bibliotheksausweis gültig?')
|
||||||
|
|
||||||
type = models.CharField(
|
type = models.CharField(max_length=4, choices=LIBRARY_TYPE_CHOICES, default=TYPE_BIB)
|
||||||
max_length=4,
|
|
||||||
choices=TYPE_CHOICES.items(), #attention: actually only BIB, ELIT, SOFT should be used here
|
|
||||||
default='BIB',
|
|
||||||
)
|
|
||||||
library = models.CharField(max_length=200)
|
library = models.CharField(max_length=200)
|
||||||
duration = models.CharField(max_length=100, verbose_name="Dauer")
|
duration = models.CharField(max_length=100, verbose_name="Dauer")
|
||||||
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name="interne Anmerkungen")
|
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name="interne Anmerkungen")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.library
|
return self.library
|
||||||
|
|
||||||
|
def save(self, **kwargs):
|
||||||
|
self.type = self.TYPE
|
||||||
|
|
||||||
SELFBUY_CHOICES = {'TRUE': format_html('Ich möchte das Werk selbst kaufen und per Kostenerstattung bei Wikimedia Deutschland abrechnen.'),
|
return super().save(**kwargs)
|
||||||
'FALSE': format_html('Ich möchte, dass Wikimedia Deutschland das Werk für mich kauft'),
|
|
||||||
|
|
||||||
|
class ELiterature(Library):
|
||||||
|
TYPE = TYPE_ELIT
|
||||||
|
LIBRARY_LABEL = 'Datenbank/Online-Ressource'
|
||||||
|
LIBRARY_HELP_TEXT = 'Für welche Datenbank/Online-Ressource gilt das Stipendium?'
|
||||||
|
DURATION_HELP_TEXT = 'Wie lange gilt der Zugang?'
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
proxy = True
|
||||||
|
|
||||||
|
|
||||||
|
class Software(Library):
|
||||||
|
TYPE = TYPE_SOFT
|
||||||
|
LIBRARY_LABEL = 'Software'
|
||||||
|
LIBRARY_HELP_TEXT = 'Für welche Software gilt das Stipendium?'
|
||||||
|
DURATION_HELP_TEXT = 'Wie lange gilt die Lizenz?'
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
proxy = True
|
||||||
|
|
||||||
|
|
||||||
|
SELFBUY_CHOICES = {'TRUE': mark_safe('Ich möchte das Werk selbst kaufen und per Kostenerstattung bei Wikimedia Deutschland abrechnen.'),
|
||||||
|
'FALSE': mark_safe('Ich möchte, dass Wikimedia Deutschland das Werk für mich kauft'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Literature(Grant):
|
class Literature(TermsConsentMixin, Grant):
|
||||||
info = models.CharField(max_length=500, verbose_name='Informationen zum Werk',
|
info = models.CharField(max_length=500, verbose_name='Informationen zum Werk',
|
||||||
help_text=format_html("Bitte gib alle Informationen zum benötigten Werk an,<br>\
|
help_text=mark_safe("Bitte gib alle Informationen zum benötigten Werk an,<br>\
|
||||||
die eine eindeutige Identifizierung ermöglichen (Autor, Titel, Verlag, ISBN, ...)"))
|
die eine eindeutige Identifizierung ermöglichen (Autor, Titel, Verlag, ISBN, ...)"))
|
||||||
source = models.CharField(max_length=200, verbose_name='Bezugsquelle',
|
source = models.CharField(max_length=200, verbose_name='Bezugsquelle',
|
||||||
help_text="Bitte gib an, wo du das Werk kaufen möchtest.")
|
help_text="Bitte gib an, wo du das Werk kaufen möchtest.")
|
||||||
selfbuy = models.CharField( max_length=10, verbose_name='Selbstkauf?', choices=SELFBUY_CHOICES.items(), default='TRUE')
|
selfbuy = models.CharField( max_length=10, verbose_name='Selbstkauf?', choices=SELFBUY_CHOICES.items(), default='TRUE')
|
||||||
selfbuy_give_data = models.BooleanField(verbose_name=format_html('Datenweitergabe erlauben'), help_text=format_html('Ich stimme der Weitergabe meiner Daten (Name, Postadresse) an den von mir angegebenen Anbieter/Dienstleister zu.'))
|
selfbuy_give_data = models.BooleanField(verbose_name=mark_safe('Datenweitergabe erlauben'), help_text=mark_safe('Ich stimme der Weitergabe meiner Daten (Name, Postadresse) an den von mir angegebenen Anbieter/Dienstleister zu.'))
|
||||||
selfbuy_data = models.TextField(max_length=1000, verbose_name='Persönliche Daten sowie Adresse', default='',\
|
selfbuy_data = models.TextField(max_length=1000, verbose_name='Persönliche Daten sowie Adresse', default='',\
|
||||||
help_text=format_html("Bitte gib hier alle persönlichen Daten an, die wir benötigen, um das Werk<br>\
|
help_text=mark_safe("Bitte gib hier alle persönlichen Daten an, die wir benötigen, um das Werk<br>\
|
||||||
für dich zu kaufen und es dir anschließend zu schicken (z.B. Vorname Nachname, Anschrift, <br>\
|
für dich zu kaufen und es dir anschließend zu schicken (z.B. Vorname Nachname, Anschrift, <br>\
|
||||||
Telefonnummer, E-Mail-Adresse usw.). Trenne die einzelnen Angaben durch Zeilenumbrüche."))
|
Telefonnummer, E-Mail-Adresse usw.). Trenne die einzelnen Angaben durch Zeilenumbrüche."))
|
||||||
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name="interne Anmerkungen")
|
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name="interne Anmerkungen")
|
||||||
|
|
@ -325,24 +373,24 @@ MAIL_CHOICES = {'REALNAME': 'Vorname.Nachname',
|
||||||
'USERNAME': 'Username',
|
'USERNAME': 'Username',
|
||||||
'OTHER': 'Sonstiges:'}
|
'OTHER': 'Sonstiges:'}
|
||||||
|
|
||||||
ADULT_CHOICES = {'TRUE': format_html('Ich bin volljährig.'),
|
ADULT_CHOICES = {'TRUE': mark_safe('Ich bin volljährig.'),
|
||||||
'FALSE': format_html('Ich bin noch nicht volljährig.')
|
'FALSE': mark_safe('Ich bin noch nicht volljährig.')
|
||||||
}
|
}
|
||||||
|
|
||||||
class Email(Domain):
|
class Email(TermsConsentMixin, Domain):
|
||||||
address = models.CharField(max_length=50,
|
address = models.CharField(max_length=50,
|
||||||
choices=MAIL_CHOICES.items(),
|
choices=MAIL_CHOICES.items(),
|
||||||
default='USERNAME', verbose_name='Adressbestandteil',
|
default='USERNAME', verbose_name='Adressbestandteil',
|
||||||
help_text=format_html("Bitte gib hier den gewünschten Adressbestandteil an,<br>der sich vor der Domain befinden soll."))
|
help_text=mark_safe("Bitte gib hier den gewünschten Adressbestandteil an,<br>der sich vor der Domain befinden soll."))
|
||||||
|
|
||||||
other = models.CharField(max_length=50,blank=True,null=True, verbose_name="Sonstiges")
|
other = models.CharField(max_length=50,blank=True,null=True, verbose_name="Sonstiges")
|
||||||
adult = models.CharField( max_length=10, verbose_name='Volljährigkeit', choices=ADULT_CHOICES.items(), default='FALSE')
|
adult = models.CharField( max_length=10, verbose_name='Volljährigkeit', choices=ADULT_CHOICES.items(), default='FALSE')
|
||||||
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name="interne Anmerkungen")
|
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name="interne Anmerkungen")
|
||||||
|
|
||||||
class List(Domain):
|
class List(TermsConsentMixin, Domain):
|
||||||
address = models.CharField(max_length=50, default='NO_ADDRESS',
|
address = models.CharField(max_length=50, default='NO_ADDRESS',
|
||||||
verbose_name="Adressbestandteil für Projektmailingliste",
|
verbose_name="Adressbestandteil für Projektmailingliste",
|
||||||
help_text=format_html("Bitte gib hier den gewünschten Adressbestandteil an,<br>der sich vor der Domain befinden soll."))
|
help_text=mark_safe("Bitte gib hier den gewünschten Adressbestandteil an,<br>der sich vor der Domain befinden soll."))
|
||||||
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name="interne Anmerkungen")
|
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name="interne Anmerkungen")
|
||||||
|
|
||||||
PROJECT_CHOICE = {'PEDIA': 'Wikipedia',
|
PROJECT_CHOICE = {'PEDIA': 'Wikipedia',
|
||||||
|
|
@ -358,13 +406,13 @@ PROJECT_CHOICE = {'PEDIA': 'Wikipedia',
|
||||||
BC_VARIANT = {'PIC': 'mit Bild',
|
BC_VARIANT = {'PIC': 'mit Bild',
|
||||||
'NOPIC': 'ohne Bild'}
|
'NOPIC': 'ohne Bild'}
|
||||||
|
|
||||||
class BusinessCard(Extern):
|
class BusinessCard(TermsConsentMixin, Extern):
|
||||||
project = models.CharField(max_length=20, choices=PROJECT_CHOICE.items(),
|
project = models.CharField(max_length=20, choices=PROJECT_CHOICE.items(),
|
||||||
default='PEDIA', verbose_name='Wikimedia-Projekt',
|
default='PEDIA', verbose_name='Wikimedia-Projekt',
|
||||||
help_text='Für welches Wikimedia-Projekt möchtest Du Visitenkarten?')
|
help_text='Für welches Wikimedia-Projekt möchtest Du Visitenkarten?')
|
||||||
|
|
||||||
data = models.TextField(max_length=1000, verbose_name='Persönliche Daten für die Visitenkarten', default='',
|
data = models.TextField(max_length=1000, verbose_name='Persönliche Daten für die Visitenkarten', default='',
|
||||||
help_text=format_html("Bitte gib hier alle persönlichen Daten an, und zwar genau so,<br>\
|
help_text=mark_safe("Bitte gib hier alle persönlichen Daten an, und zwar genau so,<br>\
|
||||||
wie sie (auch in der entsprechenden Reihenfolge) auf den Visitenkarten stehen sollen<br>\
|
wie sie (auch in der entsprechenden Reihenfolge) auf den Visitenkarten stehen sollen<br>\
|
||||||
(z.B. Vorname Nachname, Benutzer:/Benutzerin:, Benutzer-/-innenname, Anschrift,<br>\
|
(z.B. Vorname Nachname, Benutzer:/Benutzerin:, Benutzer-/-innenname, Anschrift,<br>\
|
||||||
Telefonnummer, E-Mail-Adresse usw.). Trenne die einzelnen Angaben durch Zeilenumbrüche.<br>\
|
Telefonnummer, E-Mail-Adresse usw.). Trenne die einzelnen Angaben durch Zeilenumbrüche.<br>\
|
||||||
|
|
@ -373,12 +421,25 @@ class BusinessCard(Extern):
|
||||||
Adresse, die du ebenfalls beantragen kannst, sofern du nicht bereits eine besitzt."))
|
Adresse, die du ebenfalls beantragen kannst, sofern du nicht bereits eine besitzt."))
|
||||||
variant = models.CharField(max_length=5, choices=BC_VARIANT.items(),
|
variant = models.CharField(max_length=5, choices=BC_VARIANT.items(),
|
||||||
default='NOPIC', verbose_name='Variante',
|
default='NOPIC', verbose_name='Variante',
|
||||||
help_text=format_html('so sehen die Varianten aus: <a href="https://upload.wikimedia.org/wikipedia/commons/c/cd/Muster_Visitenkarten_WMDE_2018.jpg">\
|
help_text=mark_safe('so sehen die Varianten aus: <a href="https://upload.wikimedia.org/wikipedia/commons/c/cd/Muster_Visitenkarten_WMDE_2018.jpg">\
|
||||||
mit Bild</a> <a href="https://upload.wikimedia.org/wikipedia/commons/d/d3/Muster_Visitenkarte_WMDE.png">ohne Bild</a>' ))
|
mit Bild</a> <a href="https://upload.wikimedia.org/wikipedia/commons/d/d3/Muster_Visitenkarte_WMDE.png">ohne Bild</a>' ))
|
||||||
|
|
||||||
url_of_pic = models.CharField(max_length=200, verbose_name='Url des Bildes', default='', help_text="Bitte gib die Wikimedia-Commons-URL des Bildes an.")
|
url_of_pic = models.CharField(max_length=200, verbose_name='Url des Bildes', default='', help_text="Bitte gib die Wikimedia-Commons-URL des Bildes an.")
|
||||||
|
|
||||||
sent_to = models.TextField(max_length=1000, verbose_name='Versandadresse',
|
sent_to = models.TextField(max_length=1000, verbose_name='Versandadresse',
|
||||||
default='', help_text="Bitte gib den Namen und die vollständige Adresse ein, an welche die Visitenkarten geschickt werden sollen.")
|
default='', help_text="Bitte gib den Namen und die vollständige Adresse ein, an welche die Visitenkarten geschickt werden sollen.")
|
||||||
send_data_to_print = models.BooleanField(default=False, verbose_name=format_html('Datenweitergabe erlauben'), help_text=format_html('Hiermit erlaube ich die Weitergabe meiner Daten (Name, Postadresse) an den von Wikimedia<br> Deutschland ausgewählten Dienstleister (z. B. <a href="wir-machen-druck.de">wir-machen-druck.de</a>) zum Zwecke des direkten <br> Versands der Druckerzeugnisse an mich.'))
|
send_data_to_print = models.BooleanField(default=False, verbose_name=mark_safe('Datenweitergabe erlauben'), help_text=mark_safe('Hiermit erlaube ich die Weitergabe meiner Daten (Name, Postadresse) an den von Wikimedia<br> Deutschland ausgewählten Dienstleister (z. B. <a href="wir-machen-druck.de">wir-machen-druck.de</a>) zum Zwecke des direkten <br> Versands der Druckerzeugnisse an mich.'))
|
||||||
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name="interne Anmerkungen")
|
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name="interne Anmerkungen")
|
||||||
|
|
||||||
|
|
||||||
|
MODELS = {
|
||||||
|
TYPE_BIB: Library,
|
||||||
|
TYPE_ELIT: ELiterature,
|
||||||
|
TYPE_MAIL: Email,
|
||||||
|
TYPE_IFG: IFG,
|
||||||
|
TYPE_LIT: Literature,
|
||||||
|
TYPE_LIST: List,
|
||||||
|
TYPE_TRAV: Travel,
|
||||||
|
TYPE_SOFT: Software,
|
||||||
|
TYPE_VIS: BusinessCard,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
# mail for IF-OTRS
|
|
||||||
IF_EMAIL = 'community@wikimedia.de'
|
|
||||||
#IF_EMAIL = 'test-luca-ext@wikimedia.de'
|
|
||||||
#SURVEY_EMAIL = 'christof.pins@wikimedia.de'
|
|
||||||
#SURVEY_EMAIL = 'luca.wulf@cannabinieri.de'
|
|
||||||
SURVEY_EMAIL = 'sandro.halank@wikimedia.de'
|
|
||||||
# prefix for urls
|
|
||||||
SURVEYPREFIX = 'https://wikimedia.sslsurvey.de/Foerderbarometer/?'
|
|
||||||
|
|
||||||
# some links
|
|
||||||
DATAPROTECTION = "https://www.wikimedia.de/datenschutz/#datenerfassung"
|
|
||||||
#FOERDERRICHTLINIEN = "https://de.wikipedia.org/wiki/Wikipedia:Wikimedia_Deutschland/F%C3%B6rderrichtlinien"
|
|
||||||
FOERDERRICHTLINIEN = "https://de.wikipedia.org/wiki/Wikipedia:Wikimedia_Deutschland/Richtlinie_zur_Förderung_der_Communitys"
|
|
||||||
|
|
||||||
NUTZUNGSBEDINGUNGEN = 'static/input/nutzungsbedingungen.html'
|
|
||||||
NUTZUNGSBEDINGUNGEN_EMAIL_SERVICE = 'static/input/nutzungsbedingungen-mail.pdf'
|
|
||||||
NUTZUNGSBEDINGUNGEN_MAILINGLISTEN = 'static/input/nutzungsbedingungen-mailinglisten.pdf'
|
|
||||||
NUTZUNGSBEDINGUNGEN_LITERATURSTIPENDIUM = 'static/input/nutzungsbedingungen-literaturstipendium.pdf'
|
|
||||||
NUTZUNGSBEDINGUNGEN_OTRS = 'static/input/2025_Nutzungsvereinbarung_OTRS.docx.pdf'
|
|
||||||
NUTZUNGSBEDINGUNGEN_VISITENKARTEN = 'static/input/nutzungsbedingungen-visitenkarten.pdf'
|
|
||||||
|
|
||||||
LANGUAGE_CODE = 'de'
|
|
||||||
TIME_ZONE = 'UTC'
|
|
||||||
USE_I18N = True
|
|
||||||
USE_L10N = True
|
|
||||||
USE_TZ = True
|
|
||||||
|
|
||||||
|
|
||||||
ACCOUNTS ={ # "21103": '21103 Willkommen',
|
|
||||||
"DEF": 'DEFAULT VALUE, you hould not see this!',
|
|
||||||
"21111": '21111 Förderung (reaktiv)',
|
|
||||||
"21112": '21112 WikiCon',
|
|
||||||
# "21113": '21113 Wikimania/Unterstützung Ehrenamtliche',
|
|
||||||
"21115": '21115 Lokale Räume, Berlin',
|
|
||||||
"21116": '21116 Lokale Räume, Hamburg',
|
|
||||||
"21117": '21117 Lokale Räume, Hannover',
|
|
||||||
"21118": '21118 Lokale Räume, Köln',
|
|
||||||
"21119": '21119 Lokale Räume, München',
|
|
||||||
"21120": '21120 Lokale Räume, Fürth',
|
|
||||||
"21125": '21125 Lokale Räume, allgemein',
|
|
||||||
"21130": '21130 GLAM-Förderung',
|
|
||||||
"21131": '21131 Initiative Förderung',
|
|
||||||
# "21134": '21134 Größe',
|
|
||||||
# "21137": '21137 Beitragen',
|
|
||||||
# "21138": '21138 Vermittlung',
|
|
||||||
"21140": '21140 Wikipedia-Kampagne',
|
|
||||||
"21141": '21141 Wikipedia-Onboarding',
|
|
||||||
"21150": '21150 Fürsorge und Online-Kommunikationskultur',}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# teken from working oauth prototype as additional settings
|
|
||||||
|
|
||||||
WSGI_APPLICATION = 'oauth_demo.wsgi.application'
|
|
||||||
|
|
||||||
# OAuth Settings
|
|
||||||
OAUTH_URL_WHITELISTS = []
|
|
||||||
|
|
||||||
OAUTH_CLIENT_NAME = '<name-of-the-configured-wikimedia-app>'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
OAUTH_CLIENT = {
|
|
||||||
'client_id': '<client-application-key-of-wikimedia-app>',
|
|
||||||
'client_secret': '<client-application-secret-of-wikimedia-app>',
|
|
||||||
'access_token_url': 'https://meta.wikimedia.org/w/rest.php/oauth2/access_token',
|
|
||||||
'authorize_url': 'https://meta.wikimedia.org/w/rest.php/oauth2/authorize',
|
|
||||||
'api_base_url': 'https://meta.wikimedia.org/w/rest.php/oauth2/resource',
|
|
||||||
'redirect_uri': 'http://localhost:8000/oauth/callback',
|
|
||||||
'client_kwargs': {
|
|
||||||
'scope': 'basic',
|
|
||||||
'token_placement': 'header'
|
|
||||||
},
|
|
||||||
'userinfo_endpoint': 'resource/profile',
|
|
||||||
}
|
|
||||||
|
|
||||||
OAUTH_COOKIE_SESSION_ID = 'sso_session_id'
|
|
||||||
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
# mail for IF-OTRS
|
|
||||||
IF_EMAIL = 'community@wikimedia.de'
|
|
||||||
#IF_EMAIL = 'test-luca-ext@wikimedia.de'
|
|
||||||
#SURVEY_EMAIL = 'christof.pins@wikimedia.de'
|
|
||||||
#SURVEY_EMAIL = 'luca.wulf@cannabinieri.de'
|
|
||||||
SURVEY_EMAIL = 'sandro.halank@wikimedia.de'
|
|
||||||
# prefix for urls
|
|
||||||
SURVEYPREFIX = 'https://wikimedia.sslsurvey.de/Foerderbarometer/?'
|
|
||||||
|
|
||||||
# some links
|
|
||||||
DATAPROTECTION = "https://www.wikimedia.de/datenschutz/#datenerfassung"
|
|
||||||
FOERDERRICHTLINIEN = "https://de.wikipedia.org/wiki/Wikipedia:Wikimedia_Deutschland/F%C3%B6rderrichtlinien"
|
|
||||||
NUTZUNGSBEDINGUNGEN = 'static/input/nutzungsbedingungen.html'
|
|
||||||
|
|
||||||
LANGUAGE_CODE = 'de'
|
|
||||||
TIME_ZONE = 'UTC'
|
|
||||||
USE_I18N = True
|
|
||||||
USE_L10N = True
|
|
||||||
USE_TZ = True
|
|
||||||
|
|
||||||
|
|
||||||
ACCOUNTS ={ # "21103": '21103 Willkommen',
|
|
||||||
"DEF": 'DEFAULT VALUE, you hould not see this!',
|
|
||||||
"21111": '21111 Förderung (reaktiv)',
|
|
||||||
"21112": '21112 WikiCon',
|
|
||||||
# "21113": '21113 Wikimania/Unterstützung Ehrenamtliche',
|
|
||||||
"21115": '21115 Lokale Räume, Berlin',
|
|
||||||
"21116": '21116 Lokale Räume, Hamburg',
|
|
||||||
"21117": '21117 Lokale Räume, Hannover',
|
|
||||||
"21118": '21118 Lokale Räume, Köln',
|
|
||||||
"21119": '21119 Lokale Räume, München',
|
|
||||||
"21120": '21120 Lokale Räume, Fürth',
|
|
||||||
"21125": '21125 Lokale Räume, allgemein',
|
|
||||||
"21130": '21130 GLAM-Förderung',
|
|
||||||
"21131": '21131 Initiative Förderung',
|
|
||||||
# "21134": '21134 Größe',
|
|
||||||
# "21137": '21137 Beitragen',
|
|
||||||
# "21138": '21138 Vermittlung',
|
|
||||||
"21140": '21140 Wikipedia-Kampagne',
|
|
||||||
"21141": '21141 Wikipedia-Onboarding',
|
|
||||||
"21150": '21150 Fürsorge und Online-Kommunikationskultur',}
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
ul > li {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label.required::after {
|
||||||
|
content: ' *';
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer-15 {
|
||||||
|
height: 15%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer-5 {
|
||||||
|
height: 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-centered {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
.page-centered .button-login {
|
||||||
|
width: 40vw;
|
||||||
|
height: 6vh;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 4vh;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: #79AEC8;
|
||||||
|
color: #000000;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 10px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-centered .button-login:hover {
|
||||||
|
background-color: #659DB8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-centered .button-login:focus-visible {
|
||||||
|
outline: 2px solid #000;
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,7 @@ Ende erreicht.<br><br>
|
||||||
|
|
||||||
Hier könnt ihr es in der Datenbank editieren:
|
Hier könnt ihr es in der Datenbank editieren:
|
||||||
<br><br>
|
<br><br>
|
||||||
<a href="{{URLPREFIX}}/admin/input/project/{{project.pk}}/change">{{URLPREFIX}}/admin/input/project/{{project.pk}}/change</a>
|
<a href="{{URL_PREFIX}}/admin/input/project/{{project.pk}}/change">{{URL_PREFIX}}/admin/input/project/{{project.pk}}/change</a>
|
||||||
<br><br>
|
<br><br>
|
||||||
mit freundlichen Grüßen, Eure Lieblingsdatenbank
|
mit freundlichen Grüßen, Eure Lieblingsdatenbank
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,6 @@ Ende erreicht.
|
||||||
|
|
||||||
Hier könnt ihr es in der Datenbank editieren:
|
Hier könnt ihr es in der Datenbank editieren:
|
||||||
|
|
||||||
{{URLPREFIX}}/admin/input/project/{{project.pk}}/change
|
{{URL_PREFIX}}/admin/input/project/{{project.pk}}/change
|
||||||
|
|
||||||
mit freundlichen Grüßen, Eure Lieblingsdatenbank
|
mit freundlichen Grüßen, Eure Lieblingsdatenbank
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ Ende erreicht.
|
||||||
<br><br>
|
<br><br>
|
||||||
Hier könnt ihr es in der Datenbank editieren:
|
Hier könnt ihr es in der Datenbank editieren:
|
||||||
<br><br>
|
<br><br>
|
||||||
<a href="{{URLPREFIX}}/admin/input/project/{{project.pk}}/change">{{URLPREFIX}}/admin/input/project/{{project.pk}}/change</a>
|
<a href="{{URL_PREFIX}}/admin/input/project/{{project.pk}}/change">{{URL_PREFIX}}/admin/input/project/{{project.pk}}/change</a>
|
||||||
<br><br>
|
<br><br>
|
||||||
|
|
||||||
Projektorganisator*in wurde über den Projektabschluss informiert.
|
Projektorganisator*in wurde über den Projektabschluss informiert.
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ Ende erreicht.
|
||||||
|
|
||||||
Hier könnt ihr es in der Datenbank editieren:
|
Hier könnt ihr es in der Datenbank editieren:
|
||||||
|
|
||||||
{{URLPREFIX}}/admin/input/project/{{project.pk}}/change
|
{{URL_PREFIX}}/admin/input/project/{{project.pk}}/change
|
||||||
|
|
||||||
|
|
||||||
Projektorganisator*in wurde über den Projektabschluss informiert.
|
Projektorganisator*in wurde über den Projektabschluss informiert.
|
||||||
|
|
|
||||||
|
|
@ -28,29 +28,29 @@ Sendungsadrese: {{data.send_to}} <br> {% endif %}
|
||||||
|
|
||||||
Zum Eintrag in der Förderdatenbank:
|
Zum Eintrag in der Förderdatenbank:
|
||||||
{% if data.choice == 'BIB' %}
|
{% if data.choice == 'BIB' %}
|
||||||
<a href="{{data.urlprefix}}/admin/input/library/{{data.pk}}/change">{{data.urlprefix}}/admin/input/library/{{data.pk}}/change</a>
|
<a href="{{data.url_prefix}}/admin/input/library/{{data.pk}}/change">{{data.url_prefix}}/admin/input/library/{{data.pk}}/change</a>
|
||||||
{% elif data.choice == 'ELIT'%}
|
{% elif data.choice == 'ELIT'%}
|
||||||
<a href="{{data.urlprefix}}/admin/input/library/{{data.pk}}/change">{{data.urlprefix}}/admin/input/library/{{data.pk}}/change</a>
|
<a href="{{data.url_prefix}}/admin/input/library/{{data.pk}}/change">{{data.url_prefix}}/admin/input/library/{{data.pk}}/change</a>
|
||||||
{% elif data.choice == 'LIT'%}
|
{% elif data.choice == 'LIT'%}
|
||||||
<a href="{{data.urlprefix}}/admin/input/literature/{{data.pk}}/change">{{data.urlprefix}}/admin/input/literature/{{data.pk}}/change</a>
|
<a href="{{data.url_prefix}}/admin/input/literature/{{data.pk}}/change">{{data.url_prefix}}/admin/input/literature/{{data.pk}}/change</a>
|
||||||
{% elif data.choice == 'MAIL'%}
|
{% elif data.choice == 'MAIL'%}
|
||||||
<a href="{{data.urlprefix}}/admin/input/email/{{data.pk}}/change">{{data.urlprefix}}/admin/input/email/{{data.pk}}/change</a>
|
<a href="{{data.url_prefix}}/admin/input/email/{{data.pk}}/change">{{data.url_prefix}}/admin/input/email/{{data.pk}}/change</a>
|
||||||
{% elif data.choice == 'IFG'%}
|
{% elif data.choice == 'IFG'%}
|
||||||
<a href="{{data.urlprefix}}/admin/input/ifg/{{data.pk}}/change">{{data.urlprefix}}/admin/input/ifg/{{data.pk}}/change</a>
|
<a href="{{data.url_prefix}}/admin/input/ifg/{{data.pk}}/change">{{data.url_prefix}}/admin/input/ifg/{{data.pk}}/change</a>
|
||||||
{% elif data.choice == 'LIST'%}
|
{% elif data.choice == 'LIST'%}
|
||||||
<a href="{{data.urlprefix}}/admin/input/list/{{data.pk}}/change">{{data.urlprefix}}/admin/input/list/{{data.pk}}/change</a>
|
<a href="{{data.url_prefix}}/admin/input/list/{{data.pk}}/change">{{data.url_prefix}}/admin/input/list/{{data.pk}}/change</a>
|
||||||
{% elif data.choice == 'TRAV'%}
|
{% elif data.choice == 'TRAV'%}
|
||||||
<a href="{{data.urlprefix}}/admin/input/travel/{{data.pk}}/change">{{data.urlprefix}}/admin/input/travel/{{data.pk}}/change</a>
|
<a href="{{data.url_prefix}}/admin/input/travel/{{data.pk}}/change">{{data.url_prefix}}/admin/input/travel/{{data.pk}}/change</a>
|
||||||
{% elif data.choice == 'SOFT'%}
|
{% elif data.choice == 'SOFT'%}
|
||||||
<a href="{{data.urlprefix}}/admin/input/library/{{data.pk}}/change">{{data.urlprefix}}/admin/input/library/{{data.pk}}/change</a>
|
<a href="{{data.url_prefix}}/admin/input/library/{{data.pk}}/change">{{data.url_prefix}}/admin/input/library/{{data.pk}}/change</a>
|
||||||
{% elif data.choice == 'VIS'%}
|
{% elif data.choice == 'VIS'%}
|
||||||
<a href="{{data.urlprefix}}/admin/input/businesscard/{{data.pk}}/change">{{data.urlprefix}}/admin/input/businesscard/{{data.pk}}/change</a>
|
<a href="{{data.url_prefix}}/admin/input/businesscard/{{data.pk}}/change">{{data.url_prefix}}/admin/input/businesscard/{{data.pk}}/change</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<br><br>
|
<br><br>
|
||||||
|
|
||||||
Zum Genehmigen hier klicken: <a href="{{data.urlprefix}}{% url 'authorize' data.choice data.pk %}">{{data.urlprefix}}{% url 'authorize' data.choice data.pk %}</a>
|
Zum Genehmigen hier klicken: <a href="{{data.url_prefix}}{% url 'authorize' data.choice data.pk %}">{{data.url_prefix}}{% url 'authorize' data.choice data.pk %}</a>
|
||||||
<br><br>
|
<br><br>
|
||||||
Zu Ablehnen hier klicken: <a href="{{data.urlprefix}}{% url 'deny' data.choice data.pk %}">{{data.urlprefix}}{% url 'deny' data.choice data.pk %}</a>
|
Zu Ablehnen hier klicken: <a href="{{data.url_prefix}}{% url 'deny' data.choice data.pk %}">{{data.url_prefix}}{% url 'deny' data.choice data.pk %}</a>
|
||||||
<br><br>
|
<br><br>
|
||||||
Stets zu Diensten, Deine Förderdatenbank
|
Stets zu Diensten, Deine Förderdatenbank
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,28 +25,28 @@ Sendungsadrese: {{data.send_to}} {% endif %}
|
||||||
|
|
||||||
Zum Eintrag in der Förderdatenbank:
|
Zum Eintrag in der Förderdatenbank:
|
||||||
{% if data.choice == 'BIB' %}
|
{% if data.choice == 'BIB' %}
|
||||||
<a href="{{data.urlprefix}}/admin/input/library/{{data.pk}}/change">{{data.urlprefix}}/admin/input/library/{{data.pk}}/change</a>
|
<a href="{{data.url_prefix}}/admin/input/library/{{data.pk}}/change">{{data.url_prefix}}/admin/input/library/{{data.pk}}/change</a>
|
||||||
{% elif data.choice == 'ELIT'%}
|
{% elif data.choice == 'ELIT'%}
|
||||||
<a href="{{data.urlprefix}}/admin/input/library/{{data.pk}}/change">{{data.urlprefix}}/admin/input/library/{{data.pk}}/change</a>
|
<a href="{{data.url_prefix}}/admin/input/library/{{data.pk}}/change">{{data.url_prefix}}/admin/input/library/{{data.pk}}/change</a>
|
||||||
{% elif data.choice == 'LIT'%}
|
{% elif data.choice == 'LIT'%}
|
||||||
<a href="{{data.urlprefix}}/admin/input/literature/{{data.pk}}/change">{{data.urlprefix}}/admin/input/literature/{{data.pk}}/change</a>
|
<a href="{{data.url_prefix}}/admin/input/literature/{{data.pk}}/change">{{data.url_prefix}}/admin/input/literature/{{data.pk}}/change</a>
|
||||||
{% elif data.choice == 'MAIL'%}
|
{% elif data.choice == 'MAIL'%}
|
||||||
<a href="{{data.urlprefix}}/admin/input/email/{{data.pk}}/change">{{data.urlprefix}}/admin/input/email/{{data.pk}}/change</a>
|
<a href="{{data.url_prefix}}/admin/input/email/{{data.pk}}/change">{{data.url_prefix}}/admin/input/email/{{data.pk}}/change</a>
|
||||||
{% elif data.choice == 'IFG'%}
|
{% elif data.choice == 'IFG'%}
|
||||||
<a href="{{data.urlprefix}}/admin/input/ifg/{{data.pk}}/change">{{data.urlprefix}}/admin/input/ifg/{{data.pk}}/change</a>
|
<a href="{{data.url_prefix}}/admin/input/ifg/{{data.pk}}/change">{{data.url_prefix}}/admin/input/ifg/{{data.pk}}/change</a>
|
||||||
{% elif data.choice == 'LIST'%}
|
{% elif data.choice == 'LIST'%}
|
||||||
<a href="{{data.urlprefix}}/admin/input/list/{{data.pk}}/change">{{data.urlprefix}}/admin/input/list/{{data.pk}}/change</a>
|
<a href="{{data.url_prefix}}/admin/input/list/{{data.pk}}/change">{{data.url_prefix}}/admin/input/list/{{data.pk}}/change</a>
|
||||||
{% elif data.choice == 'TRAV'%}
|
{% elif data.choice == 'TRAV'%}
|
||||||
<a href="{{data.urlprefix}}/admin/input/travel/{{data.pk}}/change">{{data.urlprefix}}/admin/input/travel/{{data.pk}}/change</a>
|
<a href="{{data.url_prefix}}/admin/input/travel/{{data.pk}}/change">{{data.url_prefix}}/admin/input/travel/{{data.pk}}/change</a>
|
||||||
{% elif data.choice == 'SOFT'%}
|
{% elif data.choice == 'SOFT'%}
|
||||||
<a href="{{data.urlprefix}}/admin/input/library/{{data.pk}}/change">{{data.urlprefix}}/admin/input/library/{{data.pk}}/change</a>
|
<a href="{{data.url_prefix}}/admin/input/library/{{data.pk}}/change">{{data.url_prefix}}/admin/input/library/{{data.pk}}/change</a>
|
||||||
{% elif data.choice == 'VIS'%}
|
{% elif data.choice == 'VIS'%}
|
||||||
<a href="{{data.urlprefix}}/admin/input/businesscard/{{data.pk}}/change">{{data.urlprefix}}/admin/input/businesscard/{{data.pk}}/change</a>
|
<a href="{{data.url_prefix}}/admin/input/businesscard/{{data.pk}}/change">{{data.url_prefix}}/admin/input/businesscard/{{data.pk}}/change</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
Zum Genehmigen hier klicken: {{data.urlprefix}}{% url 'authorize' data.choice data.pk %}
|
Zum Genehmigen hier klicken: {{data.url_prefix}}{% url 'authorize' data.choice data.pk %}
|
||||||
|
|
||||||
Zu Ablehnen hier klicken: {{data.urlprefix}}{% url 'deny' data.choice data.pk %}
|
Zu Ablehnen hier klicken: {{data.url_prefix}}{% url 'deny' data.choice data.pk %}
|
||||||
|
|
||||||
Stets zu Diensten, Deine Förderdatenbank
|
Stets zu Diensten, Deine Förderdatenbank
|
||||||
|
|
|
||||||
|
|
@ -1,69 +1,38 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/base.css' %}"/>
|
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/base.css' %}"/>
|
||||||
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/widgets.css' %}"/>
|
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/widgets.css' %}"/>
|
||||||
|
<link rel="stylesheet" type="text/css" href="{% static 'css/base.css' %}"/>
|
||||||
|
<link rel="stylesheet" type="text/css" href="{% static 'css/button.css' %}">
|
||||||
|
|
||||||
|
<div class="page-centered">
|
||||||
{% load i18n %}
|
<div class="spacer-5"></div>
|
||||||
|
<p role="heading" aria-level="1">
|
||||||
{% csrf_token %}
|
|
||||||
|
|
||||||
|
|
||||||
<center>
|
|
||||||
<style>
|
|
||||||
ul > li {
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
ul {
|
|
||||||
padding-left: 10;
|
|
||||||
}
|
|
||||||
label.required::after {
|
|
||||||
content: ' *';
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.div15 {
|
|
||||||
height: 15%;
|
|
||||||
}
|
|
||||||
.div5 {
|
|
||||||
height: 5%;
|
|
||||||
}
|
|
||||||
.button1 {
|
|
||||||
width: 40vw;
|
|
||||||
height: 6vh;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 4vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="div5"></div>
|
|
||||||
<p>
|
|
||||||
Herzlich willkommen im Förderanfrageportal von Wikimedia Deutschland!
|
Herzlich willkommen im Förderanfrageportal von Wikimedia Deutschland!
|
||||||
</p>
|
</p>
|
||||||
<div class="div5"></div>
|
<div class="spacer-5"></div>
|
||||||
<!-- <a href="http://fdb-devel.wikimedia.de/extern"style="float:right;padding-right:10%;">OAUTH</a>
|
|
||||||
<a href="http://fdb-devel.wikimedia.de/extern" style="float:left;padding-left:10%;">OAUTH</a> -->
|
|
||||||
<p>
|
<p>
|
||||||
<img src="https://upload.wikimedia.org/wikipedia/commons/c/c4/Figuren_klein.jpg"><p>
|
<img src="https://upload.wikimedia.org/wikipedia/commons/c/c4/Figuren_klein.jpg">
|
||||||
Um eine Unterstützungsleistung im Rahmen der Förderangebote anfragen zu können, verifiziere dich bitte mit deinem Wikimedia-Konto.
|
</p>
|
||||||
|
<p>
|
||||||
|
Um eine Unterstützungsleistung im Rahmen der Förderangebote anfragen zu können, verifiziere dich bitte mit
|
||||||
|
deinem Wikimedia-Konto.
|
||||||
<br>Weitere Informationen und Hintergründe findest du unter
|
<br>Weitere Informationen und Hintergründe findest du unter
|
||||||
<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Förderangebote">
|
<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Förderangebote">
|
||||||
Förderportal</a> in der deutschsprachigen Wikipedia.
|
Förderportal</a> in der deutschsprachigen Wikipedia.
|
||||||
<p>
|
</p>
|
||||||
<div class="div5"></div>
|
<div class="spacer-5"></div>
|
||||||
<div class="button button1"><a href="/extern"><div class="button1_text">Anmelden</div></a></div>
|
<a href="/extern" class="button button-login">Anmelden</a>
|
||||||
<div class="div5"></div>
|
<div class="spacer-5"></div>
|
||||||
<div class="div5"></div>
|
<div class="spacer-5"></div>
|
||||||
<br>Für alle Fragen wende dich gern an das <a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Wikimedia_Deutschland">Team Communitys und Engagement</a>.
|
<br>Für alle Fragen wende dich gern an das <a
|
||||||
<br>Für interessierte Hacker gibts auch den <a href="https://srcsrv.wikimedia.de/beba/foerderbarometer">Sourcecode</a> zum Formular und was damit passiert.
|
href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Wikimedia_Deutschland">Team Communitys und
|
||||||
|
Engagement</a>.
|
||||||
|
<br>Für interessierte Hacker gibts auch den <a
|
||||||
|
href="https://srcsrv.wikimedia.de/beba/foerderbarometer">Sourcecode</a> zum Formular und was damit passiert.
|
||||||
<p>
|
<p>
|
||||||
<a href="https://www.wikimedia.de/impressum/">Impressum</a>
|
<a href="https://www.wikimedia.de/impressum/">Impressum</a>
|
||||||
<p>
|
</p>
|
||||||
</center>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Projektförderung ab 1.000,— EUR</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Projektförderung mit einer Gesamtsumme ab 1.000,— EUR</h1>
|
||||||
|
<p>Für Projektförderungen ab 1.000,— EUR ist ein öffentlicher Projektplan erforderlich.</p>
|
||||||
|
<p><em>Dummy-Content – wird bei Freigabe ersetzt.</em></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -34,7 +34,7 @@ Förderprogramme im Sinne der Communitys weiter zu verbessern. Wir freuen uns,
|
||||||
wenn du dir kurz die Zeit dafür nehmen würdest. Die Umfrage mit weiteren
|
wenn du dir kurz die Zeit dafür nehmen würdest. Die Umfrage mit weiteren
|
||||||
Informationen findest du unter dem folgenden Link:<br>
|
Informationen findest du unter dem folgenden Link:<br>
|
||||||
|
|
||||||
<a href="{{SURVEYPREFIX}}{% if type == 'PRO' %}O{% else %}I{% endif %}=1&{{pid}}=1">{{SURVEYPREFIX}}{% if type == 'PRO' %}O{% else %}I{% endif %}=1&{{pid}}=1</a><br><br>
|
<a href="{{SURVEY_PREFIX}}{% if type == 'PRO' %}O{% else %}I{% endif %}=1&{{pid}}=1">{{SURVEY_PREFIX}}{% if type == 'PRO' %}O{% else %}I{% endif %}=1&{{pid}}=1</a><br><br>
|
||||||
|
|
||||||
Da dies eine automatisch erzeugte Nachricht ist, wende dich bei Rückfragen zur Umfrage bitte an <a href="mailto: community@wikimedia.de">community@wikimedia.de</a>
|
Da dies eine automatisch erzeugte Nachricht ist, wende dich bei Rückfragen zur Umfrage bitte an <a href="mailto: community@wikimedia.de">community@wikimedia.de</a>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,6 @@ Förderprogramme im Sinne der Communitys weiter zu verbessern. Wir freuen uns,
|
||||||
wenn du dir kurz die Zeit dafür nehmen würdest. Die Umfrage mit weiteren
|
wenn du dir kurz die Zeit dafür nehmen würdest. Die Umfrage mit weiteren
|
||||||
Informationen findest du unter dem folgenden Link:
|
Informationen findest du unter dem folgenden Link:
|
||||||
|
|
||||||
{{SURVEYPREFIX}}{% if type == 'PRO' %}O{% else %}I{% endif %}=1&{{pid}}=1
|
{{SURVEY_PREFIX}}{% if type == 'PRO' %}O{% else %}I{% endif %}=1&{{pid}}=1
|
||||||
|
|
||||||
Da dies eine automatisch erzeugte Nachricht ist, wende dich bei Rückfragen zur Umfrage bitte an community@wikimedia.de
|
Da dies eine automatisch erzeugte Nachricht ist, wende dich bei Rückfragen zur Umfrage bitte an community@wikimedia.de
|
||||||
|
|
|
||||||
155
input/tests.py
155
input/tests.py
|
|
@ -1,155 +0,0 @@
|
||||||
from django.test import TestCase, Client
|
|
||||||
from django.conf import settings
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.http import HttpResponse
|
|
||||||
from datetime import date
|
|
||||||
|
|
||||||
from .models import HonoraryCertificate, Project, Account, Literature
|
|
||||||
from .forms import LibraryForm
|
|
||||||
|
|
||||||
class TestWithoutLogin(TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
#this setting supress an unwanted warning about missing root dir
|
|
||||||
settings.WHITENOISE_AUTOREFRESH = True
|
|
||||||
self.client = Client()
|
|
||||||
|
|
||||||
def test_set_granted(self):
|
|
||||||
'''test if the model function set_granted() works as intended'''
|
|
||||||
obj = HonoraryCertificate.objects.create(realname='hurzel',email='hurzel@web.de')
|
|
||||||
self.assertEqual(obj.granted,None)
|
|
||||||
HonoraryCertificate.set_granted(obj.pk, True)
|
|
||||||
obj2 = HonoraryCertificate.objects.get(pk=obj.pk)
|
|
||||||
self.assertEqual(obj2.granted,True)
|
|
||||||
|
|
||||||
def test_source_link(self):
|
|
||||||
'''test if link to source code is included in main page'''
|
|
||||||
response = self.client.get('', follow=True)
|
|
||||||
#print (response.content)
|
|
||||||
self.assertContains(response,'<a href="https://srcsrv.wikimedia.de/beba/foerderbarometer">Sourcecode</a>')
|
|
||||||
|
|
||||||
def test_access_denied(self):
|
|
||||||
'''test if /intern redirects to login page if not logged in'''
|
|
||||||
response = self.client.get('/intern', follow=True)
|
|
||||||
self.assertContains(response,'password')
|
|
||||||
|
|
||||||
def _postform(self, data, expected_form):
|
|
||||||
'''helper function to manage the Wizzard'''
|
|
||||||
response = self.client.post('/', data, follow=False)
|
|
||||||
self.assertEqual(200, self.response.status_code)
|
|
||||||
if not type(response) == HttpResponse:
|
|
||||||
if 'form' in response.context:
|
|
||||||
print('CONTENT')
|
|
||||||
print(response.content)
|
|
||||||
print('ITEMS')
|
|
||||||
print(response.items())
|
|
||||||
print('DATA')
|
|
||||||
print(data)
|
|
||||||
self.assertFalse(response.context['form'].errors)
|
|
||||||
else:
|
|
||||||
if expected_form:
|
|
||||||
print(response.context)
|
|
||||||
raise BaseException("NO FORM FOUND")
|
|
||||||
else:
|
|
||||||
self.assertContains(response,"Deine Anfrage wurde gesendet.")
|
|
||||||
self.assertEqual(
|
|
||||||
type(response.context['wizard']['form']),
|
|
||||||
expected_form
|
|
||||||
)
|
|
||||||
return response
|
|
||||||
|
|
||||||
def _notest_bib(self): # renamed because not working
|
|
||||||
'''full run through the forms to check Bibliotheksstipendium'''
|
|
||||||
self.response = self.client.get('/')
|
|
||||||
self.assertEqual(200, self.response.status_code)
|
|
||||||
|
|
||||||
print("\n\nEINS EINS\n\n")
|
|
||||||
|
|
||||||
response = self._postform({
|
|
||||||
'extern_view-current_step': '0',
|
|
||||||
'0-realname': 'vladimir reiherzehe',
|
|
||||||
'0-email': 'vlre@wikimedia.de',
|
|
||||||
'0-username': 'stoffel',
|
|
||||||
'0-choice': 'BIB',
|
|
||||||
'0-check': True
|
|
||||||
}, LibraryForm)
|
|
||||||
|
|
||||||
print("\n\nZWEI ZWEI\n\n")
|
|
||||||
|
|
||||||
response = self._postform({
|
|
||||||
'extern_view-current_step': '1',
|
|
||||||
'1-cost': 'teuroooo!',
|
|
||||||
'1-duration': 'looooong',
|
|
||||||
'1-library': 'of congress',
|
|
||||||
}, None)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TestWithLogin(TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
User.objects.create_superuser('testuser', 'nomail@nomail.com', 'testpasswd')
|
|
||||||
self.client = Client()
|
|
||||||
self.user = User.objects.create_user('vladimir', 'vladimir@reiherzehe.com', 'reiherzehe')
|
|
||||||
|
|
||||||
def test_access(self):
|
|
||||||
'''test if /intern gives an answer'''
|
|
||||||
self.assertEqual(self.client.login(username='testuser', password='testpasswd'), True)
|
|
||||||
response = self.client.get('/intern')
|
|
||||||
self.assertContains(response,'Übersicht aller Förderangebote')
|
|
||||||
|
|
||||||
def test_project_of_year(self):
|
|
||||||
''' test if the finance id is resettet ad start of year'''
|
|
||||||
acc = Account.objects.create()
|
|
||||||
acc.code='1234'
|
|
||||||
acc.description='blabla'
|
|
||||||
acc.save()
|
|
||||||
startdate = date(2022,1,1)
|
|
||||||
obj = Project.objects.create(account= acc, name='testproject', start=startdate)
|
|
||||||
self.assertEqual(obj.project_of_year,1)
|
|
||||||
|
|
||||||
obj2 = Project.objects.create(account= acc, name='testproject2', start=startdate)
|
|
||||||
self.assertEqual(obj2.project_of_year,2)
|
|
||||||
|
|
||||||
olddate = date(2021,12,31)
|
|
||||||
obj4 = Project.objects.create(account= acc, name='testproject2', start=olddate)
|
|
||||||
|
|
||||||
obj3 = Project.objects.create(account= acc, name='testproject2', start=startdate)
|
|
||||||
self.assertEqual(obj3.project_of_year,3)
|
|
||||||
|
|
||||||
def test_finance_id(self):
|
|
||||||
''' test if the finance counting is correct'''
|
|
||||||
acc = Account.objects.create(code='1234', description='blabla')
|
|
||||||
startdate = date(2022,1,1)
|
|
||||||
obj = Project.objects.create(account= acc, name='testproject', start=startdate)
|
|
||||||
self.assertEqual(obj.finance_id,"1234001")
|
|
||||||
|
|
||||||
obj2 = Project.objects.create(account= acc, name='testproject2', start=startdate)
|
|
||||||
self.assertEqual(obj2.finance_id,"1234002")
|
|
||||||
|
|
||||||
olddate = date(2021,12,31)
|
|
||||||
obj4 = Project.objects.create(account= acc, name='testproject2', start=olddate)
|
|
||||||
|
|
||||||
obj3 = Project.objects.create(account= acc, name='testproject2', start=startdate)
|
|
||||||
self.assertEqual(obj3.finance_id,"1234003")
|
|
||||||
|
|
||||||
# def test_pid(self):
|
|
||||||
# ''' test if the pid counting is correct '''
|
|
||||||
# acc = Account.objects.create(code='1234', description='blabla')
|
|
||||||
# startdate = date(2022,1,1)
|
|
||||||
# obj = Project.objects.create(account= acc, name='testproject', start=startdate)
|
|
||||||
# self.assertEqual(obj.pid,"1234001")
|
|
||||||
# self.assertEqual(obj.account.code,"1234")
|
|
||||||
#
|
|
||||||
# obj2 = Project.objects.create(account= acc, name='testproject2', start=startdate)
|
|
||||||
# self.assertEqual(obj2.pid,"1234002")
|
|
||||||
#
|
|
||||||
# olddate = date(2021,12,31)
|
|
||||||
# obj4 = Project.objects.create(account= acc, name='testproject2', start=olddate)
|
|
||||||
#
|
|
||||||
# obj3 = Project.objects.create(account= acc, name='testproject2', start=startdate)
|
|
||||||
# self.assertEqual(obj3.pid,"1234004")
|
|
||||||
|
|
||||||
def test_literature(self):
|
|
||||||
obj = Literature.objects.create(cost='100', notes='jolo')
|
|
||||||
self.assertEqual(obj.service_id,'Literature1')
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
from .models import ModelTestCase
|
||||||
|
from .views import AuthenticatedViewTestCase, AnonymousViewTestCase
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
from datetime import date
|
||||||
|
from unittest import skip
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from input.models import HonoraryCertificate, Project, Account, Literature
|
||||||
|
|
||||||
|
|
||||||
|
class ModelTestCase(TestCase):
|
||||||
|
|
||||||
|
def test_set_granted(self):
|
||||||
|
'''test if the model function set_granted() works as intended'''
|
||||||
|
obj = HonoraryCertificate.objects.create(realname='hurzel',email='hurzel@web.de')
|
||||||
|
self.assertEqual(obj.granted,None)
|
||||||
|
HonoraryCertificate.set_granted(obj.pk, True)
|
||||||
|
obj2 = HonoraryCertificate.objects.get(pk=obj.pk)
|
||||||
|
self.assertEqual(obj2.granted,True)
|
||||||
|
|
||||||
|
def test_project_of_year(self):
|
||||||
|
''' test if the finance id is resettet ad start of year'''
|
||||||
|
acc = Account.objects.create()
|
||||||
|
acc.code='1234'
|
||||||
|
acc.description='blabla'
|
||||||
|
acc.save()
|
||||||
|
startdate = date(2022,1,1)
|
||||||
|
obj = Project.objects.create(account= acc, name='testproject', start=startdate)
|
||||||
|
self.assertEqual(obj.project_of_year,1)
|
||||||
|
|
||||||
|
obj2 = Project.objects.create(account= acc, name='testproject2', start=startdate)
|
||||||
|
self.assertEqual(obj2.project_of_year,2)
|
||||||
|
|
||||||
|
olddate = date(2021,12,31)
|
||||||
|
obj4 = Project.objects.create(account= acc, name='testproject2', start=olddate)
|
||||||
|
|
||||||
|
obj3 = Project.objects.create(account= acc, name='testproject2', start=startdate)
|
||||||
|
self.assertEqual(obj3.project_of_year,3)
|
||||||
|
|
||||||
|
@skip('Finance ID generation has been changed and this test has not been adapted accordingly.')
|
||||||
|
def test_finance_id(self):
|
||||||
|
''' test if the finance counting is correct'''
|
||||||
|
acc = Account.objects.create(code='1234', description='blabla')
|
||||||
|
startdate = date(2022,1,1)
|
||||||
|
obj = Project.objects.create(account= acc, name='testproject', start=startdate)
|
||||||
|
self.assertEqual(obj.finance_id,"1234001")
|
||||||
|
|
||||||
|
obj2 = Project.objects.create(account= acc, name='testproject2', start=startdate)
|
||||||
|
self.assertEqual(obj2.finance_id,"1234002")
|
||||||
|
|
||||||
|
olddate = date(2021,12,31)
|
||||||
|
obj4 = Project.objects.create(account= acc, name='testproject2', start=olddate)
|
||||||
|
|
||||||
|
obj3 = Project.objects.create(account= acc, name='testproject2', start=startdate)
|
||||||
|
self.assertEqual(obj3.finance_id,"1234003")
|
||||||
|
|
||||||
|
# def test_pid(self):
|
||||||
|
# ''' test if the pid counting is correct '''
|
||||||
|
# acc = Account.objects.create(code='1234', description='blabla')
|
||||||
|
# startdate = date(2022,1,1)
|
||||||
|
# obj = Project.objects.create(account= acc, name='testproject', start=startdate)
|
||||||
|
# self.assertEqual(obj.pid,"1234001")
|
||||||
|
# self.assertEqual(obj.account.code,"1234")
|
||||||
|
#
|
||||||
|
# obj2 = Project.objects.create(account= acc, name='testproject2', start=startdate)
|
||||||
|
# self.assertEqual(obj2.pid,"1234002")
|
||||||
|
#
|
||||||
|
# olddate = date(2021,12,31)
|
||||||
|
# obj4 = Project.objects.create(account= acc, name='testproject2', start=olddate)
|
||||||
|
#
|
||||||
|
# obj3 = Project.objects.create(account= acc, name='testproject2', start=startdate)
|
||||||
|
# self.assertEqual(obj3.pid,"1234004")
|
||||||
|
|
||||||
|
def test_literature(self):
|
||||||
|
obj = Literature.objects.create(cost='100', notes='jolo', selfbuy_give_data=False)
|
||||||
|
self.assertEqual(obj.service_id, f'Literature{obj.id}')
|
||||||
|
|
@ -0,0 +1,133 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from input.models import Library
|
||||||
|
from input.utils.testing import create_superuser, login, request
|
||||||
|
|
||||||
|
|
||||||
|
class AnonymousViewTestCase(TestCase):
|
||||||
|
|
||||||
|
def test_index(self):
|
||||||
|
response = request(self, 'index')
|
||||||
|
|
||||||
|
self.assertContains(response,'<a href="https://srcsrv.wikimedia.de/beba/foerderbarometer">Sourcecode</a>')
|
||||||
|
|
||||||
|
def test_extern(self):
|
||||||
|
request(self, 'extern')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_step_data(step, data):
|
||||||
|
return {'extern_view-current_step': step, **data}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_first_step_data(cls, choice):
|
||||||
|
return cls.get_step_data(0, {
|
||||||
|
'0-realname': 'Test',
|
||||||
|
'0-email': 'test@example.com',
|
||||||
|
'0-choice': choice,
|
||||||
|
'0-check': True,
|
||||||
|
})
|
||||||
|
|
||||||
|
def helper_extern(self, choice, text, data):
|
||||||
|
first_step_data = self.get_first_step_data(choice)
|
||||||
|
|
||||||
|
response = request(self, 'extern', data=first_step_data)
|
||||||
|
|
||||||
|
self.assertContains(response, text)
|
||||||
|
|
||||||
|
second_step_data = self.get_step_data(1, data)
|
||||||
|
|
||||||
|
response = request(self, 'extern', data=second_step_data)
|
||||||
|
|
||||||
|
self.assertContains(response, 'Deine Anfrage wurde gesendet.')
|
||||||
|
|
||||||
|
def test_extern_first_steps(self):
|
||||||
|
types = [
|
||||||
|
('BIB', 'Bibliotheksausweis'),
|
||||||
|
('ELIT', 'Online-Ressource'),
|
||||||
|
('MAIL', 'Mailadresse beantragen'),
|
||||||
|
('IFG', 'gewonnenen Informationen'),
|
||||||
|
('LIT', 'Literatur verwenden'),
|
||||||
|
('LIST', 'Mailingliste beantragen'),
|
||||||
|
('TRAV', 'Transportmittel'),
|
||||||
|
('SOFT', 'Lizenz'),
|
||||||
|
('VIS', 'DIN 5008'),
|
||||||
|
]
|
||||||
|
|
||||||
|
for choice, text in types:
|
||||||
|
with self.subTest(type=choice):
|
||||||
|
self.client.session.clear()
|
||||||
|
|
||||||
|
data = self.get_first_step_data(choice)
|
||||||
|
response = request(self, 'extern', data=data)
|
||||||
|
|
||||||
|
self.assertContains(response, text)
|
||||||
|
|
||||||
|
def test_extern_travel(self):
|
||||||
|
self.helper_extern('TRAV', 'Transportmittel', {
|
||||||
|
'project_name': 'Test',
|
||||||
|
'transport': 'BAHN',
|
||||||
|
'travelcost': 10,
|
||||||
|
'checkin': '2025-01-01',
|
||||||
|
'checkout': '2025-01-02',
|
||||||
|
'hotel': 'TRUE',
|
||||||
|
'notes': '',
|
||||||
|
})
|
||||||
|
|
||||||
|
def test_extern_lit(self):
|
||||||
|
self.helper_extern('LIT', 'Literatur verwenden', {
|
||||||
|
'cost': 20,
|
||||||
|
'info': 'Test',
|
||||||
|
'source': 'Test',
|
||||||
|
'notes': '',
|
||||||
|
'selfbuy': 'TRUE',
|
||||||
|
'selfbuy_data': 'NONE',
|
||||||
|
'selfbuy_give_data': True,
|
||||||
|
'check': True,
|
||||||
|
})
|
||||||
|
|
||||||
|
def test_extern_bib(self):
|
||||||
|
self.helper_extern('BIB', 'Bibliotheksausweis', {
|
||||||
|
'cost': 20,
|
||||||
|
'library': 'Test',
|
||||||
|
'duration': 'Test',
|
||||||
|
'notes': '',
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class AuthenticatedViewTestCase(TestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
cls.user = create_superuser('staff')
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
login(self)
|
||||||
|
|
||||||
|
def test_export(self):
|
||||||
|
request(self, 'export')
|
||||||
|
|
||||||
|
def helper_auth_deny(self, view, expected):
|
||||||
|
obj = Library.objects.create(library='Test')
|
||||||
|
|
||||||
|
request(self, view, args=[obj.type, obj.id])
|
||||||
|
|
||||||
|
obj.refresh_from_db(fields=['granted'])
|
||||||
|
|
||||||
|
self.assertEqual(obj.granted, expected)
|
||||||
|
|
||||||
|
def helper_auth_deny_error(self, view):
|
||||||
|
response = request(self, view, args=['TEST', 1])
|
||||||
|
|
||||||
|
self.assertContains(response, 'ERROR')
|
||||||
|
|
||||||
|
def test_authorize(self):
|
||||||
|
self.helper_auth_deny('authorize', True)
|
||||||
|
|
||||||
|
def test_authorize_error(self):
|
||||||
|
self.helper_auth_deny_error('authorize')
|
||||||
|
|
||||||
|
def test_deny(self):
|
||||||
|
self.helper_auth_deny('deny', False)
|
||||||
|
|
||||||
|
def test_deny_error(self):
|
||||||
|
self.helper_auth_deny_error('deny')
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from .views import ExternView, index, done, authorize, deny, InternView, export
|
from django.views.generic import TemplateView
|
||||||
from django.contrib import admin
|
from .views import ExternView, index, done, authorize, deny, export
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', index, name='index'),
|
path('', index, name='index'),
|
||||||
path('extern', ExternView.as_view(), name='extern'),
|
path('extern', ExternView.as_view(), name='extern'),
|
||||||
# path('intern', InternView.as_view(), name='intern'),
|
|
||||||
path('admin/', admin.site.urls),
|
|
||||||
path('saved', done, name='done'),
|
path('saved', done, name='done'),
|
||||||
path('export', export, name='export'),
|
path('export', export, name='export'),
|
||||||
path('authorize/<str:choice>/<int:pk>', authorize, name='authorize'),
|
path('authorize/<str:choice>/<int:pk>', authorize, name='authorize'),
|
||||||
path('deny/<str:choice>/<int:pk>', deny, name='deny'),
|
path('deny/<str:choice>/<int:pk>', deny, name='deny'),
|
||||||
|
path('extern/info/projektfoerderung-ab-1000/',
|
||||||
|
TemplateView.as_view(template_name='input/info_project_funding_gt_1000.html'),
|
||||||
|
name="info-foerderprojekt-ab-1000"),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
YES_NO = {
|
||||||
|
'y': True,
|
||||||
|
'Y': True,
|
||||||
|
'yes': True,
|
||||||
|
'YES': True,
|
||||||
|
'n': False,
|
||||||
|
'N': False,
|
||||||
|
'no': False,
|
||||||
|
'NO': False,
|
||||||
|
}
|
||||||
|
|
||||||
|
ZERO_ONE = {
|
||||||
|
1: True,
|
||||||
|
'1': True,
|
||||||
|
0: False,
|
||||||
|
'0': False,
|
||||||
|
}
|
||||||
|
|
||||||
|
TRUE_FALSE = {
|
||||||
|
True: True,
|
||||||
|
'TRUE': True,
|
||||||
|
'True': True,
|
||||||
|
'true': True,
|
||||||
|
't': True,
|
||||||
|
False: False,
|
||||||
|
'FALSE': False,
|
||||||
|
'False': False,
|
||||||
|
'false': False,
|
||||||
|
'f': False,
|
||||||
|
None: False,
|
||||||
|
}
|
||||||
|
|
||||||
|
ON_OFF = {
|
||||||
|
'on': True,
|
||||||
|
'ON': True,
|
||||||
|
'off': False,
|
||||||
|
'OFF': False,
|
||||||
|
}
|
||||||
|
|
||||||
|
TRUTHY = {
|
||||||
|
**YES_NO,
|
||||||
|
**ZERO_ONE,
|
||||||
|
**TRUE_FALSE,
|
||||||
|
**ON_OFF,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def ask(question, default=False, truthy=None):
|
||||||
|
response = input(question).strip()
|
||||||
|
|
||||||
|
return (truthy or YES_NO).get(response, default)
|
||||||
|
|
||||||
|
|
||||||
|
confirm = ask
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from .confirmation import TRUTHY
|
||||||
|
|
||||||
|
|
||||||
|
def env(key, default=None, parser=None):
|
||||||
|
value = os.environ.get(key)
|
||||||
|
|
||||||
|
if value is None:
|
||||||
|
return default
|
||||||
|
|
||||||
|
if parser is None:
|
||||||
|
if default is None:
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
parser = type(default)
|
||||||
|
|
||||||
|
if parser is bool:
|
||||||
|
return truthy(value, default)
|
||||||
|
|
||||||
|
return parser(value)
|
||||||
|
|
||||||
|
|
||||||
|
def truthy(value, default=False):
|
||||||
|
return TRUTHY.get(value, default)
|
||||||
|
|
||||||
|
|
||||||
|
def password_validators(*validators):
|
||||||
|
return list(_parse_password_validators(validators))
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_password_validators(validators):
|
||||||
|
for validator in validators:
|
||||||
|
if isinstance(validator, (tuple, list)):
|
||||||
|
validator, options = validator
|
||||||
|
else:
|
||||||
|
validator, options = validator, {}
|
||||||
|
|
||||||
|
if '.' not in validator:
|
||||||
|
validator = 'django.contrib.auth.password_validation.%s' % validator
|
||||||
|
|
||||||
|
yield dict(NAME=validator, OPTIONS=options)
|
||||||
|
|
@ -0,0 +1,152 @@
|
||||||
|
from typing import Any, Iterable, Mapping, Union, Tuple, Protocol
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
|
from django.conf import settings
|
||||||
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
from django.http.response import HttpResponseRedirectBase, StreamingHttpResponse
|
||||||
|
from django.shortcuts import resolve_url
|
||||||
|
from django.template import Context
|
||||||
|
from django.template.response import TemplateResponse
|
||||||
|
from django.test import Client, SimpleTestCase
|
||||||
|
from django.urls import reverse, ResolverMatch
|
||||||
|
from django.utils.http import urlencode
|
||||||
|
|
||||||
|
FormData = dict
|
||||||
|
JSONDict = dict
|
||||||
|
JSONList = list
|
||||||
|
RequestData = Union[FormData, JSONDict, JSONList]
|
||||||
|
QueryParams = Union[Mapping[str, Any], Iterable[Tuple[str, Any]]]
|
||||||
|
|
||||||
|
|
||||||
|
class TestClientResponse(Protocol):
|
||||||
|
client: Client
|
||||||
|
request: HttpRequest
|
||||||
|
templates: list
|
||||||
|
context: Context
|
||||||
|
resolver_match: ResolverMatch
|
||||||
|
|
||||||
|
def json(self) -> Union[JSONList, JSONDict]: ...
|
||||||
|
|
||||||
|
|
||||||
|
Response = Union[
|
||||||
|
HttpResponse,
|
||||||
|
HttpResponseRedirectBase,
|
||||||
|
StreamingHttpResponse,
|
||||||
|
TemplateResponse,
|
||||||
|
TestClientResponse,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectWithGetAbsoluteURLMethod(Protocol):
|
||||||
|
|
||||||
|
def get_absolute_url(self) -> str: ...
|
||||||
|
|
||||||
|
|
||||||
|
URL = Union[str, ObjectWithGetAbsoluteURLMethod]
|
||||||
|
URLArgs = Union[tuple, list]
|
||||||
|
URLKwargs = dict
|
||||||
|
|
||||||
|
|
||||||
|
def get_url(url: URL, args: URLArgs = None, kwargs: URLKwargs = None) -> str:
|
||||||
|
"""
|
||||||
|
Helper to reverse the given url name.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if args or kwargs:
|
||||||
|
return reverse(url, args=args, kwargs=kwargs)
|
||||||
|
|
||||||
|
return resolve_url(url)
|
||||||
|
|
||||||
|
|
||||||
|
def get_handler(test_case: SimpleTestCase, method: str = None, data=None):
|
||||||
|
if data:
|
||||||
|
method = str.lower(method or 'POST')
|
||||||
|
else:
|
||||||
|
method = str.lower(method or 'GET')
|
||||||
|
|
||||||
|
return getattr(test_case.client, method)
|
||||||
|
|
||||||
|
|
||||||
|
def request(
|
||||||
|
test_case: SimpleTestCase,
|
||||||
|
url: URL,
|
||||||
|
status_code: int = None,
|
||||||
|
expected_url: URL = None,
|
||||||
|
args: URLArgs = None,
|
||||||
|
kwargs: URLKwargs = None,
|
||||||
|
headers: dict = None,
|
||||||
|
msg: str = None,
|
||||||
|
query_params: QueryParams = None,
|
||||||
|
method: str = None,
|
||||||
|
data: RequestData = None,
|
||||||
|
**options,
|
||||||
|
) -> Response:
|
||||||
|
"""
|
||||||
|
A helper to make a request with the test case's http client.
|
||||||
|
|
||||||
|
The given args and kwargs are used to reverse the url
|
||||||
|
but not the expected url. When expected url needs
|
||||||
|
args/kwargs pass an absolute url instead.
|
||||||
|
|
||||||
|
All additional kwargs are passed as post parameters.
|
||||||
|
When posting without parameters just pass post=True.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = data or options or None
|
||||||
|
handler = get_handler(test_case, method, data)
|
||||||
|
url = get_url(url, args, kwargs)
|
||||||
|
|
||||||
|
if query_params:
|
||||||
|
url = f'{url}?%s' % urlencode(query_params, doseq=True)
|
||||||
|
|
||||||
|
headers = headers or {}
|
||||||
|
status_code = status_code or 200
|
||||||
|
response = handler(url, data=data, **headers)
|
||||||
|
msg = msg or getattr(response, 'content', None)
|
||||||
|
|
||||||
|
if expected_url:
|
||||||
|
test_case.assertRedirects(
|
||||||
|
response=response,
|
||||||
|
expected_url=get_url(expected_url),
|
||||||
|
target_status_code=status_code,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
test_case.assertEqual(response.status_code, status_code, msg=msg)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def login(test_case: SimpleTestCase, user=None, password: str = None) -> bool:
|
||||||
|
"""
|
||||||
|
Logs in the user trying to use the raw password or the given password.
|
||||||
|
Force logs in the user when no password is found.
|
||||||
|
"""
|
||||||
|
|
||||||
|
user = user or getattr(test_case, 'user')
|
||||||
|
password = password or getattr(user, 'raw_password', password)
|
||||||
|
|
||||||
|
if password is None:
|
||||||
|
return test_case.client.force_login(user=user) or True
|
||||||
|
|
||||||
|
return test_case.client.login(username=user.username, password=password)
|
||||||
|
|
||||||
|
|
||||||
|
def create_user(username: str, *, model=None, **kwargs):
|
||||||
|
model = model or apps.get_model(settings.AUTH_USER_MODEL)
|
||||||
|
password = kwargs.setdefault('password', 'P4sSW0rD')
|
||||||
|
|
||||||
|
kwargs.setdefault('email', f'{username}@test.case')
|
||||||
|
kwargs.setdefault(model.USERNAME_FIELD, username)
|
||||||
|
|
||||||
|
user = model.objects.create_user(**kwargs)
|
||||||
|
|
||||||
|
user.raw_password = password
|
||||||
|
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
def create_superuser(username: str, **kwargs):
|
||||||
|
kwargs['is_superuser'] = True
|
||||||
|
kwargs['is_staff'] = True
|
||||||
|
|
||||||
|
return create_user(username, **kwargs)
|
||||||
178
input/views.py
178
input/views.py
|
|
@ -1,67 +1,65 @@
|
||||||
from datetime import date
|
|
||||||
from smtplib import SMTPException
|
from smtplib import SMTPException
|
||||||
|
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.forms import modelformset_factory
|
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
from formtools.wizard.views import CookieWizardView
|
from formtools.wizard.views import CookieWizardView
|
||||||
from django.core.mail import send_mail, BadHeaderError, EmailMultiAlternatives
|
from django.core.mail import BadHeaderError, EmailMultiAlternatives
|
||||||
from django.conf import settings
|
|
||||||
from django.template.loader import get_template
|
from django.template.loader import get_template
|
||||||
from django.template import Context
|
from django.conf import settings
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
||||||
from django.utils.html import format_html
|
|
||||||
from django.utils.translation import gettext as _
|
|
||||||
|
|
||||||
from .forms import ProjectForm, ExternForm, LibraryForm, IFGForm, LiteratureForm,\
|
from .forms import (
|
||||||
HonoraryCertificateForm, InternForm, TravelForm, EmailForm,\
|
ExternForm,
|
||||||
ListForm, BusinessCardForm, INTERN_CHOICES
|
LibraryForm,
|
||||||
from .models import Project, TYPE_CHOICES, Library, Literature, Travel, IFG, BusinessCard, Email, List
|
ELiteratureForm,
|
||||||
from .settings import IF_EMAIL
|
SoftwareForm,
|
||||||
|
IFGForm,
|
||||||
|
LiteratureForm,
|
||||||
|
TravelForm,
|
||||||
|
EmailForm,
|
||||||
|
ListForm,
|
||||||
|
BusinessCardForm,
|
||||||
|
)
|
||||||
|
from .models import TYPE_CHOICES, MODELS, TYPE_BIB, TYPE_ELIT, TYPE_SOFT
|
||||||
|
|
||||||
|
LIBRARY_FORMS = {
|
||||||
|
TYPE_BIB: LibraryForm,
|
||||||
|
TYPE_ELIT: ELiteratureForm,
|
||||||
|
TYPE_SOFT: SoftwareForm,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def auth_deny(choice, pk, auth):
|
def auth_deny(choice, pk, auth):
|
||||||
if choice in ('BIB', 'ELIT', 'SOFT'):
|
if choice not in MODELS:
|
||||||
Library.set_granted(pk,auth)
|
|
||||||
elif choice == 'LIT':
|
|
||||||
Literature.set_granted(pk,auth)
|
|
||||||
elif choice == 'IFG':
|
|
||||||
IFG.set_granted(pk,auth)
|
|
||||||
elif choice == 'TRAV':
|
|
||||||
Travel.set_granted(pk,auth)
|
|
||||||
elif choice == 'VIS':
|
|
||||||
BusinessCard.set_granted(pk,auth)
|
|
||||||
elif choice == 'MAIL':
|
|
||||||
Email.set_granted(pk,auth)
|
|
||||||
elif choice == 'LIST':
|
|
||||||
List.set_granted(pk,auth)
|
|
||||||
else:
|
|
||||||
return HttpResponse(f'ERROR! UNKNOWN CHOICE TYPE! {choice}')
|
return HttpResponse(f'ERROR! UNKNOWN CHOICE TYPE! {choice}')
|
||||||
return False
|
|
||||||
|
MODELS[choice].set_granted(pk, auth)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def export(request):
|
def export(request):
|
||||||
'''export the project database to a csv'''
|
'''export the project database to a csv'''
|
||||||
return HttpResponse('WE WANT CSV!')
|
return HttpResponse('WE WANT CSV!')
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def authorize(request, choice, pk):
|
def authorize(request, choice, pk):
|
||||||
'''If IF grant a support they click a link in a mail which leads here.
|
'''If IF grant a support they click a link in a mail which leads here.
|
||||||
We write the granted field in the database here and set a timestamp.'''
|
We write the granted field in the database here and set a timestamp.'''
|
||||||
|
|
||||||
ret = auth_deny(choice, pk, True)
|
if ret := auth_deny(choice, pk, True):
|
||||||
if ret:
|
|
||||||
return ret
|
return ret
|
||||||
else:
|
else:
|
||||||
return HttpResponse(f"AUTHORIZED! choice: {choice}, pk: {pk}")
|
return HttpResponse(f"AUTHORIZED! choice: {choice}, pk: {pk}")
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def deny(request, choice, pk):
|
def deny(request, choice, pk):
|
||||||
'''If IF denies a support they click a link in a mail which leads here
|
'''If IF denies a support they click a link in a mail which leads here
|
||||||
We write the granted field in the database here.'''
|
We write the granted field in the database here.'''
|
||||||
|
|
||||||
ret = auth_deny(choice, pk, False)
|
if ret := auth_deny(choice, pk, False):
|
||||||
if ret:
|
|
||||||
return ret
|
return ret
|
||||||
else:
|
else:
|
||||||
return HttpResponse(f"DENIED! choice: {choice}, pk: {pk}")
|
return HttpResponse(f"DENIED! choice: {choice}, pk: {pk}")
|
||||||
|
|
@ -70,95 +68,10 @@ def deny(request, choice, pk):
|
||||||
def done(request):
|
def done(request):
|
||||||
return HttpResponse("Deine Anfrage wurde gesendet. Du erhältst in Kürze eine E-Mail-Benachrichtigung mit deinen Angaben. Für alle Fragen kontaktiere bitte das Team Communitys und Engagement unter community@wikimedia.de.")
|
return HttpResponse("Deine Anfrage wurde gesendet. Du erhältst in Kürze eine E-Mail-Benachrichtigung mit deinen Angaben. Für alle Fragen kontaktiere bitte das Team Communitys und Engagement unter community@wikimedia.de.")
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
return render(request, 'input/index.html')
|
return render(request, 'input/index.html')
|
||||||
|
|
||||||
class InternView(LoginRequiredMixin, CookieWizardView):
|
|
||||||
'''This View is for WMDE-employees only'''
|
|
||||||
|
|
||||||
template_name = 'input/extern.html'
|
|
||||||
form_list = [InternForm, ProjectForm]
|
|
||||||
|
|
||||||
def get_form(self, step=None, data=None, files=None):
|
|
||||||
'''this function determines which part of the multipart form is
|
|
||||||
displayed next'''
|
|
||||||
|
|
||||||
if step is None:
|
|
||||||
step = self.steps.current
|
|
||||||
print ("get_form() step " + step)
|
|
||||||
|
|
||||||
if step == '1':
|
|
||||||
prev_data = self.get_cleaned_data_for_step('0')
|
|
||||||
choice = prev_data.get('choice')
|
|
||||||
print(f'choice detection: {INTERN_CHOICES[choice]}')
|
|
||||||
if choice == 'HON':
|
|
||||||
form = HonoraryCertificateForm(data)
|
|
||||||
elif choice == 'PRO':
|
|
||||||
form = ProjectForm(data)
|
|
||||||
elif choice == 'TRAV':
|
|
||||||
form = TravelForm(data)
|
|
||||||
else:
|
|
||||||
raise RuntimeError(f'ERROR! UNKNOWN FORMTYPE {choice} in InternView')
|
|
||||||
self.choice = choice
|
|
||||||
else:
|
|
||||||
form = super().get_form(step, data, files)
|
|
||||||
form.fields['realname'].help_text = format_html("Vor- und Zuname (Realname), Wer hat das Projekt beantragt?<br>\
|
|
||||||
Wer ist Hauptansprechperson? Bei WMDE-MAs immer „(WMDE)“,<br>\
|
|
||||||
bei externen Partnern „(PART)“ hinzufügen.")
|
|
||||||
return form
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
if hasattr(self, 'choice'):
|
|
||||||
context["choice"] = INTERN_CHOICES[self.choice]
|
|
||||||
return context
|
|
||||||
|
|
||||||
def done(self, form_list, **kwargs):
|
|
||||||
print('InternView.done() reached')
|
|
||||||
# gather data from all forms
|
|
||||||
data = {}
|
|
||||||
for form in form_list:
|
|
||||||
data = {**data, **form.cleaned_data}
|
|
||||||
|
|
||||||
if data['choice'] == 'LIT':
|
|
||||||
if data['selfbuy'] == 'TRUE':
|
|
||||||
data['selfbuy_give_data'] = 'False'
|
|
||||||
|
|
||||||
# write data to database
|
|
||||||
form = form.save(commit=False)
|
|
||||||
# we have to copy the data from the first form here
|
|
||||||
# this is ugly code. how can we copy this without explicit writing?
|
|
||||||
# i found no way to access the ModelForm.Meta.exclude-tupel
|
|
||||||
form.realname = data['realname']
|
|
||||||
# form.username = data['username']
|
|
||||||
form.email = data['email']
|
|
||||||
form.granted = True
|
|
||||||
form.granted_date = date.today()
|
|
||||||
|
|
||||||
if data['choice'] == 'LIT':
|
|
||||||
form.selfbuy_give_data = data['selfbuy_give_data']
|
|
||||||
|
|
||||||
form.save()
|
|
||||||
|
|
||||||
return done(self.request)
|
|
||||||
|
|
||||||
# these where used as labels in the second form TYPE_CHOICES is used for the first form and the
|
|
||||||
# text above the second form. only used for BIB, SOFT, ELIT in the moment
|
|
||||||
LABEL_CHOICES = {'BIB': format_html('Bibliothek'),
|
|
||||||
'ELIT': format_html('Datenbank/Online-Ressource'),
|
|
||||||
'MAIL': format_html('E-Mail-Adresse'),
|
|
||||||
'IFG': format_html('Kostenübernahme IFG-Anfrage'),
|
|
||||||
'LIT': format_html('Literaturstipendium'),
|
|
||||||
'LIST': format_html('Mailingliste'),
|
|
||||||
'TRAV': format_html('Reisekosten'),
|
|
||||||
'SOFT': format_html('Software'),
|
|
||||||
'VIS': format_html('Visitenkarten'),
|
|
||||||
}
|
|
||||||
|
|
||||||
HELP_CHOICES = {'BIB': format_html("In welchem Zeitraum möchtest du recherchieren oder<br>wie lange ist der Bibliotheksausweis gültig?"),
|
|
||||||
'ELIT': "Wie lange gilt der Zugang?",
|
|
||||||
'SOFT': "Wie lange gilt die Lizenz?",
|
|
||||||
}
|
|
||||||
|
|
||||||
class ExternView(CookieWizardView):
|
class ExternView(CookieWizardView):
|
||||||
'''This View is for Volunteers'''
|
'''This View is for Volunteers'''
|
||||||
|
|
@ -180,15 +93,12 @@ class ExternView(CookieWizardView):
|
||||||
print(f'choice detection in ExternView: {TYPE_CHOICES[choice]}')
|
print(f'choice detection in ExternView: {TYPE_CHOICES[choice]}')
|
||||||
if choice == 'IFG':
|
if choice == 'IFG':
|
||||||
form = IFGForm(data)
|
form = IFGForm(data)
|
||||||
form.fields['notes'].help_text = format_html("Bitte gib an, wie die gewonnenen Informationen den<br>Wikimedia-Projekten zugute kommen sollen.")
|
form.fields['notes'].help_text = mark_safe("Bitte gib an, wie die gewonnenen Informationen den<br>Wikimedia-Projekten zugute kommen sollen.")
|
||||||
elif choice in ('BIB', 'SOFT', 'ELIT'):
|
elif choice in LIBRARY_FORMS:
|
||||||
form = LibraryForm(data)
|
form = LIBRARY_FORMS[choice](data)
|
||||||
form.fields['library'].label = LABEL_CHOICES[choice]
|
|
||||||
form.fields['library'].help_text = f"Für welche {LABEL_CHOICES[choice]} gilt das Stipendium?"
|
|
||||||
form.fields['duration'].help_text = HELP_CHOICES[choice]
|
|
||||||
elif choice == 'MAIL':
|
elif choice == 'MAIL':
|
||||||
form = EmailForm(data)
|
form = EmailForm(data)
|
||||||
form.fields['domain'].help_text = format_html("Mit welcher Domain, bzw. für welches Wikimedia-Projekt,<br>möchtest du eine Mailadresse beantragen?")
|
form.fields['domain'].help_text = mark_safe("Mit welcher Domain, bzw. für welches Wikimedia-Projekt,<br>möchtest du eine Mailadresse beantragen?")
|
||||||
elif choice == 'LIT':
|
elif choice == 'LIT':
|
||||||
form = LiteratureForm(data)
|
form = LiteratureForm(data)
|
||||||
form.fields['notes'].help_text = "Bitte gib an, wofür du die Literatur verwenden möchtest."
|
form.fields['notes'].help_text = "Bitte gib an, wofür du die Literatur verwenden möchtest."
|
||||||
|
|
@ -196,10 +106,10 @@ class ExternView(CookieWizardView):
|
||||||
form = BusinessCardForm(data)
|
form = BusinessCardForm(data)
|
||||||
elif choice == 'LIST':
|
elif choice == 'LIST':
|
||||||
form = ListForm(data)
|
form = ListForm(data)
|
||||||
form.fields['domain'].help_text = format_html("Mit welcher Domain, bzw. für welches Wikimedia-Projekt,<br>möchtest du eine Mailingliste beantragen?")
|
form.fields['domain'].help_text = mark_safe("Mit welcher Domain, bzw. für welches Wikimedia-Projekt,<br>möchtest du eine Mailingliste beantragen?")
|
||||||
elif choice == 'TRAV':
|
elif choice == 'TRAV':
|
||||||
form = TravelForm(data)
|
form = TravelForm(data)
|
||||||
else:
|
else: # pragma: no cover
|
||||||
raise RuntimeError(f'ERROR! UNKNOWN FORMTYPE {choice} in ExternView')
|
raise RuntimeError(f'ERROR! UNKNOWN FORMTYPE {choice} in ExternView')
|
||||||
self.choice = choice
|
self.choice = choice
|
||||||
else:
|
else:
|
||||||
|
|
@ -219,8 +129,6 @@ class ExternView(CookieWizardView):
|
||||||
for form in form_list:
|
for form in form_list:
|
||||||
data = {**data, **form.cleaned_data}
|
data = {**data, **form.cleaned_data}
|
||||||
|
|
||||||
data['username'] = self.request.session['user']['username']
|
|
||||||
|
|
||||||
if data['choice'] == 'LIT':
|
if data['choice'] == 'LIT':
|
||||||
if data['selfbuy'] == 'TRUE':
|
if data['selfbuy'] == 'TRUE':
|
||||||
data['selfbuy_give_data'] = 'False'
|
data['selfbuy_give_data'] = 'False'
|
||||||
|
|
@ -233,8 +141,10 @@ class ExternView(CookieWizardView):
|
||||||
if data['choice'] == 'LIT':
|
if data['choice'] == 'LIT':
|
||||||
modell.selfbuy_give_data = data['selfbuy_give_data']
|
modell.selfbuy_give_data = data['selfbuy_give_data']
|
||||||
|
|
||||||
|
if user := self.request.session.get('user'):
|
||||||
|
modell.username = user['username']
|
||||||
|
|
||||||
modell.realname = data['realname']
|
modell.realname = data['realname']
|
||||||
modell.username = data['username']
|
|
||||||
modell.email = data['email']
|
modell.email = data['email']
|
||||||
# write type of form in some cases
|
# write type of form in some cases
|
||||||
if data['choice'] in ('BIB', 'ELIT', 'SOFT'):
|
if data['choice'] in ('BIB', 'ELIT', 'SOFT'):
|
||||||
|
|
@ -244,7 +154,7 @@ class ExternView(CookieWizardView):
|
||||||
|
|
||||||
# add some data to context for mail templates
|
# add some data to context for mail templates
|
||||||
data['pk'] = modell.pk
|
data['pk'] = modell.pk
|
||||||
data['urlprefix'] = settings.URLPREFIX
|
data['url_prefix'] = settings.EMAIL_URL_PREFIX
|
||||||
data['grant'] = ('LIT', 'SOFT', 'ELIT', 'BIB', 'IFG')
|
data['grant'] = ('LIT', 'SOFT', 'ELIT', 'BIB', 'IFG')
|
||||||
data['DOMAIN'] = ('MAIL', 'LIST')
|
data['DOMAIN'] = ('MAIL', 'LIST')
|
||||||
data['typestring'] = TYPE_CHOICES[data['choice']]
|
data['typestring'] = TYPE_CHOICES[data['choice']]
|
||||||
|
|
@ -257,7 +167,7 @@ class ExternView(CookieWizardView):
|
||||||
txt_mail_template1 = get_template('input/ifg_volunteer_mail.txt')
|
txt_mail_template1 = get_template('input/ifg_volunteer_mail.txt')
|
||||||
html_mail_template1 = get_template('input/ifg_volunteer_mail.html')
|
html_mail_template1 = get_template('input/ifg_volunteer_mail.html')
|
||||||
|
|
||||||
subject1, from_email1, to1 = 'Formular ausgefüllt', IF_EMAIL, data['email']
|
subject1, from_email1, to1 = 'Formular ausgefüllt', settings.IF_EMAIL, data['email']
|
||||||
text_content1 = txt_mail_template1.render(context)
|
text_content1 = txt_mail_template1.render(context)
|
||||||
html_content1 = html_mail_template1.render(context)
|
html_content1 = html_mail_template1.render(context)
|
||||||
msg1 = EmailMultiAlternatives(subject1, text_content1, from_email1, [to1])
|
msg1 = EmailMultiAlternatives(subject1, text_content1, from_email1, [to1])
|
||||||
|
|
@ -275,7 +185,7 @@ class ExternView(CookieWizardView):
|
||||||
txt_mail_template = get_template('input/if_mail.txt')
|
txt_mail_template = get_template('input/if_mail.txt')
|
||||||
html_mail_template = get_template('input/if_mail.html')
|
html_mail_template = get_template('input/if_mail.html')
|
||||||
|
|
||||||
subject, from_email, to = 'Formular ausgefüllt', IF_EMAIL, IF_EMAIL
|
subject, from_email, to = 'Formular ausgefüllt', settings.IF_EMAIL, settings.IF_EMAIL
|
||||||
text_content = txt_mail_template.render(context)
|
text_content = txt_mail_template.render(context)
|
||||||
html_content = html_mail_template.render(context)
|
html_content = html_mail_template.render(context)
|
||||||
msg2 = EmailMultiAlternatives(subject, text_content, from_email, [to])
|
msg2 = EmailMultiAlternatives(subject, text_content, from_email, [to])
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,7 @@
|
||||||
asgiref==3.2.10
|
Authlib==1.6.1
|
||||||
Authlib==1.2.1
|
Django==5.2.5
|
||||||
certifi==2023.7.22
|
django-formtools==2.5.1
|
||||||
cffi==1.16.0
|
gunicorn==23.0.0
|
||||||
chardet==5.2.0
|
mysqlclient==2.2.7
|
||||||
charset-normalizer==3.3.0
|
python-dotenv==1.1.1
|
||||||
cryptography==41.0.4
|
whitenoise==6.9.0
|
||||||
Django==3.1.2
|
|
||||||
django-formtools==2.4
|
|
||||||
gunicorn==20.0.4
|
|
||||||
idna==3.4
|
|
||||||
mysqlclient==2.1.1
|
|
||||||
pycparser==2.21
|
|
||||||
pytz==2023.3.post1
|
|
||||||
requests==2.31.0
|
|
||||||
six==1.16.0
|
|
||||||
sqlparse==0.4.3
|
|
||||||
typing_extensions==4.8.0
|
|
||||||
urllib3==2.0.6
|
|
||||||
whitenoise==6.2.0
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue