427 lines
12 KiB
Python
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}")
|