plateforme-tfjm2/tfjm/settings.py

427 lines
12 KiB
Python

# Copyright (C) 2020 by Animath
# SPDX-License-Identifier: GPL-3.0-or-later
"""
Django settings for tfjm project.
Generated by 'django-admin startproject' using Django 3.0.5.
For more information on this file, see
https://docs.djangoproject.com/en/5.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.0/ref/settings/
"""
from datetime import datetime
import os
import sys
from django.utils.translation import gettext_lazy as _
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
ADMINS = [("Emmy D'Anello", "emmy.danello@animath.fr")]
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.getenv('DJANGO_SECRET_KEY', 'CHANGE_ME_IN_ENV_SETTINGS')
# dev in development mode, prod in production mode
TFJM_STAGE = os.getenv('TFJM_STAGE', 'dev')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = TFJM_STAGE != "prod"
SITE_ID = 1
ALLOWED_HOSTS = ['*']
# Application definition
INSTALLED_APPS = [
'daphne',
'django.contrib.admin',
'django.contrib.admindocs',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.forms',
'channels',
'crispy_forms',
'crispy_bootstrap5',
'django_filters',
'django_tables2',
'haystack',
'logs',
'phonenumber_field',
'pipeline',
'polymorphic',
'rest_framework',
'rest_framework.authtoken',
'api',
'chat',
'draw',
'registration',
'participation',
]
if "test" not in sys.argv: # pragma: no cover
INSTALLED_APPS += [
'django_extensions',
'mailer',
]
if TFJM_STAGE == "prod": # pragma: no cover
INSTALLED_APPS += [
'channels_redis',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.contrib.sites.middleware.CurrentSiteMiddleware',
'django.middleware.gzip.GZipMiddleware',
'pipeline.middleware.MinifyHTMLMiddleware',
'tfjm.middlewares.SessionMiddleware',
'tfjm.middlewares.FetchMiddleware',
]
ROOT_URLCONF = 'tfjm.urls'
LOGIN_REDIRECT_URL = "index"
LOGOUT_REDIRECT_URL = "login"
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'tfjm/templates')],
'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',
'tfjm.context_processors.tfjm_context',
],
},
},
]
FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'
ASGI_APPLICATION = 'tfjm.asgi.application'
WSGI_APPLICATION = 'tfjm.wsgi.application'
# Password validation
# https://docs.djangoproject.com/en/5.0/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',
},
]
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.BCryptPasswordHasher',
]
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAdminUser'
],
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
],
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 50,
}
# Internationalization
# https://docs.djangoproject.com/en/5.0/topics/i18n/
LANGUAGE_CODE = 'en'
LANGUAGES = [
('en', _('English')),
('fr', _('French')),
]
TIME_ZONE = 'Europe/Paris'
USE_I18N = True
USE_L10N = True
USE_TZ = True
LOCALE_PATHS = [os.path.join(BASE_DIR, "locale")]
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.0/howto/static-files/
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "tfjm/static"),
]
STATIC_ROOT = os.path.join(BASE_DIR, "static")
STORAGES = {
'staticfiles': {
'BACKEND': 'pipeline.storage.PipelineStorage',
},
}
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
'pipeline.finders.PipelineFinder',
)
PIPELINE = {
'DISABLE_WRAPPER': True,
'JAVASCRIPT': {
'main': {
'source_filenames': (
'tfjm/js/main.js',
'tfjm/js/theme.js',
),
'output_filename': 'tfjm/js/main.min.js',
},
'theme': {
'source_filenames': (
'tfjm/js/theme.js',
),
'output_filename': 'tfjm/js/theme.min.js',
},
'chat': {
'source_filenames': (
'tfjm/js/chat.js',
),
'output_filename': 'tfjm/js/chat.min.js',
},
'draw': {
'source_filenames': (
'tfjm/js/draw.js',
),
'output_filename': 'tfjm/js/draw.min.js',
},
},
}
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap5'
CRISPY_TEMPLATE_PACK = 'bootstrap5'
DJANGO_TABLES2_TEMPLATE = 'django_tables2/bootstrap5.html'
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.elasticsearch7_backend.Elasticsearch7SearchEngine'
if os.getenv("HAYSTACK_INDEX_NAME", None) else 'haystack.backends.simple_backend.SimpleEngine',
'URL': 'http://elasticsearch:9200/',
'INDEX_NAME': os.getenv('HAYSTACK_INDEX_NAME', 'inscription-tfjm'),
}
}
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor' if os.getenv("HAYSTACK_INDEX_NAME", None) \
else 'haystack.signals.BaseSignalProcessor'
_db_type = os.getenv('DJANGO_DB_TYPE', 'sqlite').lower()
if _db_type == 'mysql' or _db_type.startswith('postgres') or _db_type == 'psql': # pragma: no cover
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql' if _db_type == 'mysql' else 'django.db.backends.postgresql',
'NAME': os.environ.get('DJANGO_DB_NAME', 'tfjm'),
'USER': os.environ.get('DJANGO_DB_USER', 'tfjm'),
'PASSWORD': os.environ.get('DJANGO_DB_PASSWORD', 'CHANGE_ME_IN_ENV_SETTINGS'),
'HOST': os.environ.get('DJANGO_DB_HOST', 'localhost'),
'PORT': os.environ.get('DJANGO_DB_PORT', ''), # Use default port
}
}
# Connections expire after 10 seconds
CONN_MAX_AGE = 10
CONN_HEALTH_CHECKS = True
else:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, os.getenv('DJANGO_DB_HOST', 'db.sqlite3')),
}
}
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels.layers.InMemoryChannelLayer"
}
}
# Custom phone number format
PHONENUMBER_DB_FORMAT = 'NATIONAL'
PHONENUMBER_DEFAULT_REGION = 'FR'
# Hello Asso API creds
HELLOASSO_CLIENT_ID = os.getenv('HELLOASSO_CLIENT_ID', 'CHANGE_ME_IN_ENV_SETTINGS')
HELLOASSO_CLIENT_SECRET = os.getenv('HELLOASSO_CLIENT_SECRET', 'CHANGE_ME_IN_ENV_SETTINGS')
HELLOASSO_TEST_ENDPOINT = False # Enable custom test endpoint, for unit tests
GOOGLE_SERVICE_CLIENT = {
"type": "service_account",
"project_id": os.getenv("GOOGLE_PROJECT_ID", "plateforme-tfjm"),
"private_key_id": os.getenv("GOOGLE_PRIVATE_KEY_ID", "CHANGE_ME_IN_ENV_SETTINGS"),
"private_key": os.getenv("GOOGLE_PRIVATE_KEY", "CHANGE_ME_IN_ENV_SETTINGS").replace("\\n", "\n"),
"client_email": os.getenv("GOOGLE_CLIENT_EMAIL", "CHANGE_ME_IN_ENV_SETTINGS"),
"client_id": os.getenv("GOOGLE_CLIENT_ID", "CHANGE_ME_IN_ENV_SETTINGS"),
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": os.getenv("GOOGLE_CLIENT_X509_CERT_URL", "CHANGE_ME_IN_ENV_SETTINGS"),
"universe_domain": "googleapis.com"
}
# The ID of the Google Drive folder where to store the notation sheets
NOTES_DRIVE_FOLDER_ID = os.getenv("NOTES_DRIVE_FOLDER_ID", "CHANGE_ME_IN_ENV_SETTINGS")
# Custom parameters
FORBIDDEN_TRIGRAMS = [
"BIT",
"CNO",
"CRO",
"CUL",
"FTG",
"FCK",
"FUC",
"FUK",
"FYS",
"HIV",
"IST",
"MST",
"KKK",
"KYS",
"SEX",
]
TFJM_APP = os.getenv("TFJM_APP", "TFJM") # Change to ETEAM for the ETEAM tournament
if TFJM_STAGE == "prod": # pragma: no cover
from .settings_prod import * # noqa: F401,F403
else:
from .settings_dev import * # noqa: F401,F403
try:
from .settings_local import * # noqa: F401,F403
except ImportError:
pass
if TFJM_APP == "TFJM":
PREFERRED_LANGUAGE_CODE = 'fr'
APP_NAME = "TFJM²"
TEAM_CODE_LENGTH = 3
RECOMMENDED_SOLUTIONS_COUNT = 5
NB_ROUNDS = 2
HAS_OBSERVER = False
HAS_FINAL = True
ML_MANAGEMENT = True
PAYMENT_MANAGEMENT = True
SINGLE_TOURNAMENT = False
HEALTH_SHEET_REQUIRED = True
VACCINE_SHEET_REQUIRED = True
MOTIVATION_LETTER_REQUIRED = True
SUGGEST_ANIMATH = True
FIRST_EDITION = 2011
HOME_PAGE_LINK = "https://tfjm.org/"
LOGO_FILE = "tfjm.svg"
RULES_LINK = "https://tfjm.org/reglement"
REGISTRATION_DATES = dict(
open=datetime.fromisoformat("2025-01-15T12:00:00+0100"),
close=datetime.fromisoformat("2025-03-02T22:00:00+0100"),
)
PROBLEMS = [
"Triominos",
"Rassemblements mathématiques",
"Tournoi de ping-pong",
"Dépollution de la Seine",
"Électron libre",
"Pièces truquées",
"Drôles de cookies",
"Création d'un jeu",
]
elif TFJM_APP == "ETEAM":
PREFERRED_LANGUAGE_CODE = 'en'
APP_NAME = "ETEAM"
TEAM_CODE_LENGTH = 4
RECOMMENDED_SOLUTIONS_COUNT = 6
NB_ROUNDS = 3
HAS_OBSERVER = True
HAS_FINAL = False
ML_MANAGEMENT = False
PAYMENT_MANAGEMENT = False
SINGLE_TOURNAMENT = True
HEALTH_SHEET_REQUIRED = False
VACCINE_SHEET_REQUIRED = False
MOTIVATION_LETTER_REQUIRED = False
SUGGEST_ANIMATH = False
FIRST_EDITION = 2024
HOME_PAGE_LINK = "https://eteam.tfjm.org/"
LOGO_FILE = "eteam.png"
RULES_LINK = "https://eteam.tfjm.org/rules/"
REGISTRATION_DATES = dict(
open=datetime.fromisoformat("2024-06-01T12:00:00+0200"),
close=datetime.fromisoformat("2024-07-04T20:00:00+0200"),
)
PROBLEMS = [
"Exploring Flatland",
"A Mazing Hive",
"Coin tossing",
"The rainbow bridge",
"Arithmetic and shopping",
"A fence for the goats",
"Generalized Tic-Tac-Toe",
"Polyhedral construction",
"Landing a probe",
"Catching the rabbit",
]
else:
raise ValueError(f"Unknown app: {TFJM_APP}")