Compare commits
15 commits
main
...
Aufinal/pr
Author | SHA1 | Date | |
---|---|---|---|
|
317077951b | ||
|
5ee66b453a | ||
|
3fee57061d | ||
|
d3ec952de8 | ||
|
f45812a076 | ||
|
700272ac22 | ||
|
764865c9d8 | ||
|
43026cecd2 | ||
|
d7263fc9e0 | ||
|
1d24478c1d | ||
|
df10e754a5 | ||
|
bf05b0a7d4 | ||
|
ef0076781e | ||
|
d01d4d4d51 | ||
|
4289eed6d5 |
68 changed files with 914 additions and 4341 deletions
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"PROTOCOL": "http",
|
||||
"URL": "annuaire.example.com",
|
||||
"PORT": 80
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"SPI": {
|
||||
"PROTOCOL": "ldaps",
|
||||
"URL": "ldap.example.com",
|
||||
"PORT": 636
|
||||
},
|
||||
"CRI": {
|
||||
"PROTOCOL": "ldap",
|
||||
"URL": "ldap.example.com",
|
||||
"PORT": 636
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
insecure-key
|
1
.envrc
1
.envrc
|
@ -1 +0,0 @@
|
|||
use nix
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -9,12 +9,10 @@
|
|||
|
||||
/venv/
|
||||
.vagrant/
|
||||
/static/
|
||||
static/
|
||||
|
||||
*.sqlite3
|
||||
fiches/templates/fiches/base_old.html
|
||||
fiches/static/fiches/css_old/
|
||||
|
||||
.vscode
|
||||
.direnv
|
||||
annuaire/settings/secret.py
|
||||
|
|
27
README.md
27
README.md
|
@ -1,6 +1,8 @@
|
|||
# Annuaire des élèves de l'ENS
|
||||
|
||||
## Installation
|
||||
## Environnement de développement
|
||||
|
||||
### Méthode facile : environnement virtuel python
|
||||
|
||||
Il est fortement conseillé d'utiliser un environnement virtuel pour Python.
|
||||
|
||||
|
@ -33,6 +35,29 @@ Vous êtes prêts à développer ! Lancez l'annuaire avec :
|
|||
|
||||
python manage.py runserver
|
||||
|
||||
|
||||
### Plus compliqué : machine virtuelle Vagrant
|
||||
|
||||
Pour avoir une situation plus proche de la situation en production, il est possible de faire tourner le site depuis une
|
||||
machine virtuelle Windows. Pour cela, il faut d'abord installer `vagrant` et `virtualbox` :
|
||||
|
||||
sudo apt install vagrant virtualbox
|
||||
|
||||
Ensuite, la commande `vagrant up` devrait créer et configurer la machine virtuelle ; un peu de patience, cela peut prendre
|
||||
du temps ! Une fois fini, il y a deux possibilités :
|
||||
|
||||
- `vagrant ssh` permet de se connecter à la machine, et d'effectuer des opérations à l'aide de `manage.py`. On peut effectuer
|
||||
à peu près les mêmes opérations que pour un virtualenv classique, à une différence près : il faut utiliser
|
||||
|
||||
```
|
||||
python manage.py runserver 0.0.0.0:8000
|
||||
```
|
||||
|
||||
pour lancer le serveur, afin d'y avoir accès depuis son navigateur.
|
||||
|
||||
- un serveur normalement très proche de celui de production (avec `gunicorn` + `nginx`) tourne en permanence sur la machine ;
|
||||
il suffit de visiter `127.0.0.1:8080` pour y avoir accès !
|
||||
|
||||
## Développement
|
||||
|
||||
En manque d'inspiration ? N'hésitez pas à aller lire les issues ouvertes actuellement, il y en a pour tous les niveaux !
|
||||
|
|
47
Vagrantfile
vendored
Normal file
47
Vagrantfile
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
# All Vagrant configuration is done below. The "2" in Vagrant.configure
|
||||
# configures the configuration version (we support older styles for
|
||||
# backwards compatibility). Please don't change it unless you know what
|
||||
# you're doing.
|
||||
Vagrant.configure(2) do |config|
|
||||
# The most common configuration options are documented and commented below.
|
||||
# For a complete reference, please see the online documentation at
|
||||
# https://docs.vagrantup.com.
|
||||
|
||||
config.vm.box = "ubuntu/focal64"
|
||||
|
||||
# On associe le port 80 dans la machine virtuelle avec le port 8080 de notre
|
||||
# ordinateur, et le port 8000 avec le port 8000.
|
||||
config.vm.network :forwarded_port, guest: 80, host: 8080
|
||||
config.vm.network :forwarded_port, guest: 8000, host: 8000
|
||||
|
||||
# Create a private network, which allows host-only access to the machine
|
||||
# using a specific IP.
|
||||
# config.vm.network "private_network", ip: "192.168.33.10"
|
||||
|
||||
# Provider-specific configuration so you can fine-tune various
|
||||
# backing providers for Vagrant. These expose provider-specific options.
|
||||
# Example for VirtualBox:
|
||||
#
|
||||
# config.vm.provider "virtualbox" do |vb|
|
||||
# # Display the VirtualBox GUI when booting the machine
|
||||
# vb.gui = true
|
||||
#
|
||||
# # Customize the amount of memory on the VM:
|
||||
# vb.memory = "1024"
|
||||
# end
|
||||
#
|
||||
# View the documentation for the provider you are using for more
|
||||
# information on available options.
|
||||
|
||||
# Enable provisioning with a shell script. Additional provisioners such as
|
||||
# Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
|
||||
# documentation for more information about their specific syntax and use.
|
||||
# config.vm.provision "shell", inline: <<-SHELL
|
||||
# sudo apt-get update
|
||||
# sudo apt-get install -y apache2
|
||||
# SHELL
|
||||
config.vm.provision :shell, path: "provisioning/bootstrap.sh", args: ENV['PWD']
|
||||
end
|
1
annuaire/settings/.gitignore
vendored
Normal file
1
annuaire/settings/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
secret.py
|
0
annuaire/settings/__init__.py
Normal file
0
annuaire/settings/__init__.py
Normal file
130
annuaire/settings/common.py
Normal file
130
annuaire/settings/common.py
Normal file
|
@ -0,0 +1,130 @@
|
|||
"""
|
||||
Settings communs entre setups de dev et de production.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# ---
|
||||
# Secrets
|
||||
# ---
|
||||
|
||||
try:
|
||||
from . import secret
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"The secret.py file is missing.\n"
|
||||
"For a development environment, simply copy secret_example.py"
|
||||
)
|
||||
|
||||
|
||||
def import_secret(name):
|
||||
"""
|
||||
Shorthand for importing a value from the secret module and raising an
|
||||
informative exception if a secret is missing.
|
||||
"""
|
||||
try:
|
||||
return getattr(secret, name)
|
||||
except AttributeError:
|
||||
raise RuntimeError("Secret missing: {}".format(name))
|
||||
|
||||
|
||||
SECRET_KEY = import_secret("SECRET_KEY")
|
||||
ADMINS = import_secret("ADMINS")
|
||||
SERVER_EMAIL = import_secret("SERVER_EMAIL")
|
||||
EMAIL_HOST = import_secret("EMAIL_HOST")
|
||||
|
||||
|
||||
# ---
|
||||
# Défauts Django
|
||||
# ---
|
||||
|
||||
DEBUG = False # False by default feels safer
|
||||
TESTING = len(sys.argv) > 1 and sys.argv[1] == "test"
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
INSTALLED_APPS = [
|
||||
"django.contrib.admin",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"django_cas_ng",
|
||||
"fiches",
|
||||
]
|
||||
|
||||
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",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = "annuaire.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",
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
"django.contrib.auth.backends.ModelBackend",
|
||||
"django_cas_ng.backends.CASBackend",
|
||||
)
|
||||
|
||||
WSGI_APPLICATION = "annuaire.wsgi.application"
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/dev/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = "fr-fr"
|
||||
TIME_ZONE = "UTC"
|
||||
USE_I18N = True
|
||||
USE_L10N = True
|
||||
USE_TZ = True
|
||||
|
||||
# ---
|
||||
# Settings CAS
|
||||
# ---
|
||||
|
||||
CAS_SERVER_URL = "https://cas.eleves.ens.fr/"
|
||||
CAS_VERSION = "2"
|
||||
|
||||
# ---
|
||||
# LDAP et annuaire ENS
|
||||
# ---
|
||||
|
||||
# Est-ce vraiment nécessaire de les garder secrets ?
|
||||
LDAP = {
|
||||
"SPI": {
|
||||
"PROTOCOL": "ldaps",
|
||||
"URL": "ldap.spi.ens.fr",
|
||||
"PORT": 636,
|
||||
},
|
||||
"CRI": {
|
||||
"PROTOCOL": "ldap",
|
||||
"URL": "annuaire.ens.fr",
|
||||
"PORT": 389,
|
||||
},
|
||||
}
|
||||
|
||||
ANNUAIRE = {
|
||||
"PROTOCOL": "http",
|
||||
"URL": "annuaireweb.ens.fr",
|
||||
"PORT": 80,
|
||||
}
|
62
annuaire/settings/local.py
Normal file
62
annuaire/settings/local.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
"""
|
||||
Settings pour le dev local de l'annuaire (hors vagrant).
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from .common import * # NOQA
|
||||
from .common import BASE_DIR, INSTALLED_APPS, MIDDLEWARE, TESTING
|
||||
|
||||
# ---
|
||||
# Tweaks for debug/local development
|
||||
# ---
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
DEBUG = True
|
||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||
|
||||
STATIC_URL = "/static/"
|
||||
MEDIA_URL = "/media/"
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
|
||||
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
|
||||
}
|
||||
}
|
||||
|
||||
# Use the default cache backend for local development
|
||||
CACHES = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}}
|
||||
|
||||
# Pas besoin de sécurité en local
|
||||
AUTH_PASSWORD_VALIDATORS = []
|
||||
PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]
|
||||
|
||||
|
||||
# ---
|
||||
# Debug tool bar
|
||||
# ---
|
||||
|
||||
|
||||
def show_toolbar(request):
|
||||
"""
|
||||
On active la debug-toolbar en mode développement local sauf :
|
||||
- dans l'admin où ça ne sert pas à grand chose;
|
||||
- si la variable d'environnement DJANGO_NO_DDT est à 1 → ça permet de la désactiver
|
||||
sans modifier ce fichier en exécutant `export DJANGO_NO_DDT=1` dans le terminal
|
||||
qui lance `./manage.py runserver`.
|
||||
|
||||
Autre side effect de cette fonction : on ne fait pas la vérification de INTERNAL_IPS
|
||||
que ferait la debug-toolbar par défaut, ce qui la fait fonctionner aussi à
|
||||
l'intérieur de Vagrant (comportement non testé depuis un moment…)
|
||||
"""
|
||||
env_no_ddt = bool(os.environ.get("DJANGO_NO_DDT", None))
|
||||
return DEBUG and not env_no_ddt and not request.path.startswith("/admin/")
|
||||
|
||||
|
||||
if not TESTING:
|
||||
INSTALLED_APPS = INSTALLED_APPS + ["debug_toolbar"]
|
||||
MIDDLEWARE = ["debug_toolbar.middleware.DebugToolbarMiddleware"] + MIDDLEWARE
|
||||
DEBUG_TOOLBAR_CONFIG = {"SHOW_TOOLBAR_CALLBACK": show_toolbar}
|
81
annuaire/settings/prod.py
Normal file
81
annuaire/settings/prod.py
Normal file
|
@ -0,0 +1,81 @@
|
|||
"""
|
||||
Settings pour la mise en production de l'annuaire.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from .common import * # NOQA
|
||||
from .common import (
|
||||
BASE_DIR,
|
||||
import_secret,
|
||||
)
|
||||
|
||||
# ---
|
||||
# Prod-specific secrets
|
||||
# ---
|
||||
|
||||
REDIS_PASSWD = import_secret("REDIS_PASSWD")
|
||||
REDIS_DB = import_secret("REDIS_DB")
|
||||
REDIS_HOST = import_secret("REDIS_HOST")
|
||||
REDIS_PORT = import_secret("REDIS_PORT")
|
||||
|
||||
DBNAME = import_secret("DBNAME")
|
||||
DBUSER = import_secret("DBUSER")
|
||||
DBPASSWD = import_secret("DBPASSWD")
|
||||
|
||||
# ---
|
||||
# À modifier possiblement lors de la mise en production
|
||||
# ---
|
||||
|
||||
ALLOWED_HOSTS = ["annuaire.eleves.ens.fr", "www.annuaire.eleves.ens.fr"]
|
||||
|
||||
STATIC_ROOT = os.path.join(
|
||||
os.path.dirname(os.path.dirname(BASE_DIR)), "public", "annuaire", "static"
|
||||
)
|
||||
|
||||
STATIC_URL = "/static/"
|
||||
MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), "media")
|
||||
MEDIA_URL = "/media/"
|
||||
|
||||
# ---
|
||||
# Cache settings
|
||||
# ---
|
||||
|
||||
CACHES = {
|
||||
"default": {
|
||||
"BACKEND": "redis_cache.RedisCache",
|
||||
"LOCATION": "redis://:{passwd}@{host}:{port}/{db}".format(
|
||||
passwd=REDIS_PASSWD, host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DB
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
# ---
|
||||
# Prod database settings
|
||||
# ---
|
||||
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.postgresql_psycopg2",
|
||||
"NAME": DBNAME,
|
||||
"USER": DBUSER,
|
||||
"PASSWORD": DBPASSWD,
|
||||
"HOST": os.environ.get("DBHOST", "localhost"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", # noqa
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
|
||||
},
|
||||
]
|
15
annuaire/settings/secret_example.py
Normal file
15
annuaire/settings/secret_example.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
SECRET_KEY = "$=kp$3e=xh)*4h8(_g#lprlmve_vs9_xv9hlgse%+uk9nhc==x"
|
||||
ADMINS = None
|
||||
SERVER_EMAIL = "root@localhost"
|
||||
EMAIL_HOST = None
|
||||
|
||||
|
||||
# Ne pas modifier si on utilise vagrant !
|
||||
DBUSER = "annuaire"
|
||||
DBNAME = "annuaire"
|
||||
DBPASSWD = "O1LxCADDA6Px5SiKvifjvdp3DSjfbp"
|
||||
|
||||
REDIS_PASSWD = "dummy"
|
||||
REDIS_PORT = 6379
|
||||
REDIS_DB = 0
|
||||
REDIS_HOST = "127.0.0.1"
|
24
annuaire/settings/vagrant.py
Normal file
24
annuaire/settings/vagrant.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
"""
|
||||
Settings pour le développement de l'annuaire avec vagrant.
|
||||
Essaie de rester le plus fidèle possible aux settings de production,
|
||||
avec des différences les plus minimes possibles.
|
||||
"""
|
||||
|
||||
from .prod import * # noqa
|
||||
from .prod import TESTING, INSTALLED_APPS, MIDDLEWARE
|
||||
from .local import show_toolbar
|
||||
|
||||
DEBUG = True
|
||||
|
||||
MEDIA_ROOT = "/srv/annuaire/media"
|
||||
STATIC_ROOT = "/srv/annuaire/static"
|
||||
|
||||
EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
|
||||
EMAIL_FILE_PATH = "/var/mail/django"
|
||||
|
||||
ALLOWED_HOSTS = ["127.0.0.1", "localhost", "0.0.0.0"]
|
||||
|
||||
if not TESTING:
|
||||
INSTALLED_APPS += ["debug_toolbar"]
|
||||
MIDDLEWARE = ["debug_toolbar.middleware.DebugToolbarMiddleware"] + MIDDLEWARE
|
||||
DEBUG_TOOLBAR_CONFIG = {"SHOW_TOOLBAR_CALLBACK": show_toolbar}
|
|
@ -14,20 +14,23 @@ Including another URLconf
|
|||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from django.contrib import admin
|
||||
from django.urls import include, path
|
||||
from django.urls import path, include
|
||||
from django.conf.urls.static import static
|
||||
from fiches.views import BirthdayView, HomeView
|
||||
import django_cas_ng.views as cas_views
|
||||
|
||||
urlpatterns = [
|
||||
path("", include("fiches.urls")),
|
||||
path("admin/", admin.site.urls),
|
||||
path("authens/", include("authens.urls")),
|
||||
path("i18n/", include("django.conf.urls.i18n")),
|
||||
path("fiche/", include("fiches.urls")),
|
||||
path("", HomeView.as_view(), name="home"),
|
||||
path("birthday", BirthdayView.as_view(), name="birthday"),
|
||||
path("accounts/login/", cas_views.LoginView.as_view(), name="cas_ng_login"),
|
||||
path("logout", cas_views.LogoutView.as_view(), name="cas_ng_logout"),
|
||||
]
|
||||
|
||||
if settings.DEBUG and "debug_toolbar" in settings.INSTALLED_APPS:
|
||||
from debug_toolbar import urls as debug_urls
|
||||
import debug_toolbar
|
||||
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + [
|
||||
path("__debug__/", include(debug_urls))
|
||||
]
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
urlpatterns += [path("__debug__/", include(debug_toolbar.urls))]
|
|
@ -11,6 +11,6 @@ import os
|
|||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'app.settings')
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'annuaire.settings')
|
||||
|
||||
application = get_wsgi_application()
|
183
app/settings.py
183
app/settings.py
|
@ -1,183 +0,0 @@
|
|||
"""
|
||||
Django settings for the annuaire project
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from loadcredential import Credentials
|
||||
|
||||
credentials = Credentials(env_prefix="ANNUAIRE_")
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
# WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = credentials["SECRET_KEY"]
|
||||
|
||||
# WARNING: don't run with debug turned on in production!
|
||||
DEBUG = credentials.get_json("DEBUG", False)
|
||||
|
||||
ALLOWED_HOSTS = credentials.get_json("ALLOWED_HOSTS", [])
|
||||
|
||||
ADMINS = credentials.get_json("ADMINS", [])
|
||||
|
||||
|
||||
###
|
||||
# List the installed applications
|
||||
|
||||
INSTALLED_APPS = [
|
||||
"django.contrib.admin",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"authens",
|
||||
"fiches",
|
||||
]
|
||||
|
||||
|
||||
###
|
||||
# List the installed middlewares
|
||||
|
||||
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",
|
||||
]
|
||||
|
||||
|
||||
###
|
||||
# The main url configuration
|
||||
|
||||
ROOT_URLCONF = "app.urls"
|
||||
|
||||
|
||||
###
|
||||
# Template configuration:
|
||||
# - Django Templating Language is used
|
||||
# - Application directories can be used
|
||||
|
||||
|
||||
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",
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
###
|
||||
# Database configuration
|
||||
# -> https://docs.djangoproject.com/en/4.2/ref/settings/#databases
|
||||
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||
|
||||
DATABASES = credentials.get_json(
|
||||
"DATABASES",
|
||||
{
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
"NAME": BASE_DIR / "db.sqlite3",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
CACHES = credentials.get_json(
|
||||
"CACHES",
|
||||
default={
|
||||
"default": {
|
||||
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
###
|
||||
# WSGI application configuration
|
||||
|
||||
WSGI_APPLICATION = "app.wsgi.application"
|
||||
|
||||
|
||||
###
|
||||
# Staticfiles configuration
|
||||
|
||||
STATIC_ROOT = credentials["STATIC_ROOT"]
|
||||
STATIC_URL = "/static/"
|
||||
|
||||
MEDIA_ROOT = credentials.get("MEDIA_ROOT", BASE_DIR / "media")
|
||||
MEDIA_URL = "/media/"
|
||||
|
||||
|
||||
###
|
||||
# Internationalization configuration
|
||||
# -> https://docs.djangoproject.com/en/4.2/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = "fr-fr"
|
||||
TIME_ZONE = "Europe/Paris"
|
||||
|
||||
USE_I18N = True
|
||||
USE_L10N = True
|
||||
USE_TZ = True
|
||||
|
||||
LANGUAGES = [
|
||||
("fr", _("Français")),
|
||||
("en", _("Anglais")),
|
||||
]
|
||||
|
||||
LOCALE_PATHS = [BASE_DIR / "locale"]
|
||||
|
||||
|
||||
###
|
||||
# Authentication configuration
|
||||
|
||||
AUTHENS_USE_OLDCAS = False
|
||||
AUTHENS_USE_PASSWORD = False
|
||||
|
||||
LOGIN_URL = reverse_lazy("authens:login")
|
||||
LOGOUT_REDIRECT_URL = reverse_lazy("home")
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
"django.contrib.auth.backends.ModelBackend",
|
||||
"fiches.backends.BackendFiches",
|
||||
)
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = map(
|
||||
lambda v: {"NAME": f"django.contrib.auth.password_validation.{v}"},
|
||||
[
|
||||
"UserAttributeSimilarityValidator",
|
||||
"MinimumLengthValidator",
|
||||
"CommonPasswordValidator",
|
||||
"NumericPasswordValidator",
|
||||
],
|
||||
)
|
||||
|
||||
LDAP = credentials.get_json("LDAP")
|
||||
ANNUAIRE = credentials.get_json("ANNUAIRE")
|
||||
|
||||
# FIXME: Add correct email settings
|
||||
|
||||
# Development settings
|
||||
if DEBUG:
|
||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||
AUTH_PASSWORD_VALIDATORS = []
|
||||
PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]
|
||||
|
||||
LOGIN_URL = reverse_lazy("authens:login")
|
||||
AUTHENS_USE_PASSWORD = True
|
45
default.nix
45
default.nix
|
@ -1,45 +0,0 @@
|
|||
{
|
||||
sources ? import ./npins,
|
||||
pkgs ? import sources.nixpkgs { },
|
||||
}:
|
||||
|
||||
let
|
||||
nix-pkgs = import sources.nix-pkgs { inherit pkgs; };
|
||||
|
||||
python3 = pkgs.python3.override {
|
||||
packageOverrides = _: _: {
|
||||
inherit (nix-pkgs) authens loadcredential;
|
||||
};
|
||||
};
|
||||
in
|
||||
|
||||
{
|
||||
devShell = pkgs.mkShell {
|
||||
name = "annuaire.dev";
|
||||
|
||||
packages = [
|
||||
(python3.withPackages (ps: [
|
||||
ps.django
|
||||
ps.pillow
|
||||
ps.loadcredential
|
||||
ps.authens
|
||||
ps.python-dateutil
|
||||
]))
|
||||
];
|
||||
|
||||
env = {
|
||||
DJANGO_SETTINGS_MODULE = "app.settings";
|
||||
|
||||
CREDENTIALS_DIRECTORY = builtins.toString ./.credentials;
|
||||
|
||||
ANNUAIRE_DEBUG = builtins.toJSON true;
|
||||
ANNUAIRE_STATIC_ROOT = builtins.toString ./.static;
|
||||
};
|
||||
|
||||
shellHook = ''
|
||||
if [ ! -d .static ]; then
|
||||
mkdir .static
|
||||
fi
|
||||
'';
|
||||
};
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
from authens.backends import ENSCASBackend, ENSCASError
|
||||
from authens.utils import parse_entrance_year
|
||||
|
||||
from fiches.models import Profile
|
||||
|
||||
|
||||
class BackendFiches(ENSCASBackend):
|
||||
"""Wrapper around AuthENS's CAS authentication backend.
|
||||
Ensures the required field promotion year is non-empty"""
|
||||
|
||||
def create_user(self, username, attributes):
|
||||
"""create a CAS user
|
||||
overwrite to add required promotion"""
|
||||
user = super().create_user(username, attributes)
|
||||
entrance_year = parse_entrance_year(attributes.get("homeDirectory"))
|
||||
if entrance_year is None:
|
||||
raise ENSCASError("Entrance year not available")
|
||||
Profile.objects.create(user=user, promotion=entrance_year)
|
||||
return user
|
|
@ -1,24 +1,17 @@
|
|||
from django import forms
|
||||
from django.forms.models import inlineformset_factory
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from fiches.models import Address, Department, Mail, Phone, Profile, Social
|
||||
from fiches.models import Profile, Department, Phone, Social, Mail, Address
|
||||
|
||||
|
||||
class ProfileForm(forms.ModelForm):
|
||||
birth_date = forms.DateField(
|
||||
input_formats=["%Y-%m-%d"],
|
||||
required=False,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Profile
|
||||
exclude = ["user"]
|
||||
|
||||
|
||||
class SearchForm(forms.Form):
|
||||
name = forms.CharField(label=_("Nom/Surnom"), max_length=1023, required=False)
|
||||
year = forms.IntegerField(label=_("Promotion"), required=False)
|
||||
name = forms.CharField(label="Nom/Surnom", max_length=1023, required=False)
|
||||
year = forms.IntegerField(label="Promotion", required=False)
|
||||
department = forms.ModelMultipleChoiceField(
|
||||
queryset=Department.objects.all(), required=False
|
||||
)
|
||||
|
@ -30,13 +23,13 @@ class SearchForm(forms.Form):
|
|||
and not cleaned_data["year"]
|
||||
and not cleaned_data["department"]
|
||||
):
|
||||
raise forms.ValidationError(_("Tous les champs sont vides"), code="invalid")
|
||||
raise forms.ValidationError(("Tous les champs sont vides"), code="invalid")
|
||||
|
||||
|
||||
PhoneFormSet = inlineformset_factory(Profile, Phone, exclude=[], extra=0)
|
||||
PhoneFormSet = inlineformset_factory(Profile, Phone, exclude=[])
|
||||
|
||||
SocialFormSet = inlineformset_factory(Profile, Social, exclude=[], extra=0)
|
||||
SocialFormSet = inlineformset_factory(Profile, Social, exclude=[])
|
||||
|
||||
MailFormSet = inlineformset_factory(Profile, Mail, exclude=[], extra=0)
|
||||
MailFormSet = inlineformset_factory(Profile, Mail, exclude=[])
|
||||
|
||||
AddressFormSet = inlineformset_factory(Profile, Address, exclude=[], extra=0)
|
||||
AddressFormSet = inlineformset_factory(Profile, Address, exclude=[])
|
||||
|
|
|
@ -25,9 +25,16 @@ class LDAP:
|
|||
def __init__(self):
|
||||
""" Initialize the LDAP object """
|
||||
|
||||
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
|
||||
self.ldap_obj = ldap.initialize(
|
||||
"{PROTOCOL}://{URL}:{PORT}".format(**self.LDAP_SERVER)
|
||||
)
|
||||
self.ldap_obj.set_option(ldap.OPT_REFERRALS, 0)
|
||||
self.ldap_obj.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
|
||||
self.ldap_obj.set_option(ldap.OPT_X_TLS, ldap.OPT_X_TLS_DEMAND)
|
||||
self.ldap_obj.set_option(ldap.OPT_X_TLS_DEMAND, True)
|
||||
self.ldap_obj.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
|
||||
self.ldap_obj.set_option(ldap.OPT_TIMEOUT, 10)
|
||||
|
||||
def search(self, filters="(objectClass=*)"):
|
||||
"""Do a ldap.search_s with the given filters, as specified for
|
||||
|
@ -73,11 +80,11 @@ class ClipperLDAP(LDAP):
|
|||
raise ValueError("Invalid home directory")
|
||||
|
||||
# Ça casse en 2100, mais le système de naming de sas aussi...
|
||||
promo = 2000 + int(promo)
|
||||
promo = "20" + promo
|
||||
|
||||
return promo, self.verbose_depts.get(dept, None)
|
||||
|
||||
def get_clipper_list(self, promo_filter=None, verbosity=1, stdout=None):
|
||||
def get_clipper_list(self, promo_filter=None):
|
||||
"""Extrait la liste des comptes clipper présents dans le LDAP.
|
||||
Renvoie une liste de `namedTuple` contenant leur nom, prénom, adresse mail et
|
||||
département/année d'entrée.
|
||||
|
@ -101,11 +108,7 @@ class ClipperLDAP(LDAP):
|
|||
clipper_list.append(
|
||||
ClipperAccount(uid, name, email, promo, dept)
|
||||
)
|
||||
if verbosity == 3:
|
||||
stdout.write("Compte clipper trouvé : {}".format(uid))
|
||||
except ValueError:
|
||||
if verbosity >= 2:
|
||||
stdout.write("Entrée malformée trouvée : {}".format(entry))
|
||||
pass
|
||||
|
||||
return clipper_list
|
||||
|
@ -118,7 +121,7 @@ class AnnuaireLDAP(LDAP):
|
|||
search_base = "ou=people,dc=ens,dc=fr"
|
||||
attr_list = ["uid", "sn", "givenName", "ou"]
|
||||
|
||||
def try_match(self, profile, verbosity=1, stderr=None):
|
||||
def try_match(self, profile):
|
||||
"""Essaie de trouver une entrée correspondant au profile donné dans
|
||||
l'annuaire de l'ENS. L'heuristique est la suivante : il est très probable
|
||||
que le prénom de la personne commence par le premier mot de `profile.full_name`,
|
||||
|
@ -133,12 +136,11 @@ class AnnuaireLDAP(LDAP):
|
|||
|
||||
if len(search_name) > 0:
|
||||
if len(search_name) > 2:
|
||||
if verbosity >= 2:
|
||||
stderr.write(
|
||||
"Erreur : deux logins CRI trouvés pour {} ({})".format(
|
||||
profile.user.username, profile.promotion
|
||||
)
|
||||
print(
|
||||
"Erreur : deux résultats trouvés pour {}".format(
|
||||
profile.user.username
|
||||
)
|
||||
)
|
||||
return None
|
||||
|
||||
return self.extract_ldap_info(search_name[0], "uid")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from datetime import date
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from fiches.models import Department, Profile
|
||||
|
||||
|
@ -16,7 +16,7 @@ class Command(BaseCommand):
|
|||
group.add_argument(
|
||||
"--all", action="store_true", help="Importe l'intégralité des promotions"
|
||||
)
|
||||
group.add_argument("--promo", type=int, help="Spécifie la promotion à importer")
|
||||
group.add_argument("--promo", help="Spécifie la promotion à importer")
|
||||
|
||||
def get_current_promo(self):
|
||||
today = date.today()
|
||||
|
@ -24,28 +24,21 @@ class Command(BaseCommand):
|
|||
if today.month < 9:
|
||||
year -= 1
|
||||
|
||||
return year
|
||||
return str(year)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
if options["all"]:
|
||||
promo = None
|
||||
elif options["promo"] is not None:
|
||||
promo = options["promo"]
|
||||
if promo < 100:
|
||||
promo = 2000 + promo
|
||||
if len(promo) == 2:
|
||||
promo = "20" + promo
|
||||
else:
|
||||
promo = self.get_current_promo()
|
||||
|
||||
if promo < 2000 or promo > 2100:
|
||||
raise CommandError("Promotion invalide : {}".format(promo))
|
||||
|
||||
verbosity = options["verbosity"]
|
||||
|
||||
# On récupère la liste des élèves à créer
|
||||
ldap = ClipperLDAP()
|
||||
clipper_list = ldap.get_clipper_list(
|
||||
promo_filter=promo, verbosity=verbosity, stdout=self.stdout
|
||||
)
|
||||
clipper_list = ldap.get_clipper_list(promo_filter=promo)
|
||||
|
||||
# On vire les élèves déjà existants
|
||||
existing_users = set(User.objects.values_list("username", flat=True))
|
||||
|
@ -74,18 +67,6 @@ class Command(BaseCommand):
|
|||
)
|
||||
)
|
||||
|
||||
# À décommenter pour utilisation locale (avec SQLite)
|
||||
|
||||
# def _manual_ids(cls, to_create):
|
||||
# pid = getattr(cls.objects.order_by("-id").first(), "id", 1)
|
||||
# for p in to_create:
|
||||
# pid += 1
|
||||
# p.id = pid
|
||||
|
||||
# _manual_ids(User, users_to_create)
|
||||
# _manual_ids(Profile, profiles_to_create)
|
||||
# _manual_ids(Profile.department.through, dept_m2m_to_create)
|
||||
|
||||
User.objects.bulk_create(users_to_create)
|
||||
for profile in profiles_to_create:
|
||||
profile.user_id = profile.user.id
|
||||
|
@ -94,10 +75,8 @@ class Command(BaseCommand):
|
|||
dept_m2m.profile_id = dept_m2m.profile.id
|
||||
Profile.department.through.objects.bulk_create(dept_m2m_to_create)
|
||||
|
||||
if verbosity >= 1:
|
||||
self.stdout.write(
|
||||
(
|
||||
"Création de {} utilisateur·ices et de {}"
|
||||
" profils effectuée avec succès"
|
||||
).format(len(users_to_create), len(profiles_to_create))
|
||||
print(
|
||||
"Création de {} utilisateur·ices et de {} profils effectuée avec succès".format(
|
||||
len(users_to_create), len(profiles_to_create)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -13,14 +13,14 @@ from ._ldap import AnnuaireLDAP
|
|||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Si possible, importe les photos des conscrit·e·s dans l'annuaire."
|
||||
help = "Si possible, import les photos des conscrit·e·s dans l'annuaire."
|
||||
|
||||
def add_arguments(self, parser):
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument(
|
||||
"--all", action="store_true", help="Importe l'intégralité des promotions"
|
||||
)
|
||||
group.add_argument("--promo", type=int, help="Spécifie la promotion à importer")
|
||||
group.add_argument("--promo", help="Spécifie la promotion à importer")
|
||||
|
||||
def get_current_promo(self):
|
||||
today = date.today()
|
||||
|
@ -35,13 +35,11 @@ class Command(BaseCommand):
|
|||
promo = None
|
||||
elif options["promo"] is not None:
|
||||
promo = options["promo"]
|
||||
if promo < 100:
|
||||
promo = 2000 + promo
|
||||
if len(promo) == 2:
|
||||
promo = "20" + promo
|
||||
else:
|
||||
promo = self.get_current_promo()
|
||||
|
||||
verbosity = options["verbosity"]
|
||||
|
||||
no_images = Profile.objects.select_related("user").filter(picture="")
|
||||
|
||||
if promo is not None:
|
||||
|
@ -53,9 +51,7 @@ class Command(BaseCommand):
|
|||
success = 0
|
||||
|
||||
for profile in no_images:
|
||||
cri_login = cri_ldap.try_match(
|
||||
profile, verbosity=verbosity, stderr=self.stderr
|
||||
)
|
||||
cri_login = cri_ldap.try_match(profile)
|
||||
if cri_login is not None:
|
||||
img_url = "{}/photos/{}.jpg".format(base_annuaire, cri_login)
|
||||
try:
|
||||
|
@ -65,32 +61,13 @@ class Command(BaseCommand):
|
|||
content=File(BytesIO(istream.read())),
|
||||
)
|
||||
success += 1
|
||||
if verbosity == 3:
|
||||
self.stdout.write(
|
||||
"Photo trouvée pour {} ({})".format(
|
||||
profile.user.username, profile.promotion
|
||||
)
|
||||
)
|
||||
except HTTPError:
|
||||
# Parfois, même si on trouve un login CRI, il y a une erreur 404.
|
||||
# Dans ce cas, pas de photo : on échoue gracieusement.
|
||||
if verbosity >= 2:
|
||||
self.stdout.write(
|
||||
"Login CRI trouvé mais pas de photo pour {} ({})".format(
|
||||
profile.user.username, profile.promotion
|
||||
)
|
||||
)
|
||||
pass
|
||||
elif verbosity >= 2:
|
||||
self.stdout.write(
|
||||
"Pas de login CRI trouvé pour {} ({})".format(
|
||||
profile.user.username, profile.promotion
|
||||
)
|
||||
)
|
||||
|
||||
if verbosity >= 1:
|
||||
self.stdout.write(
|
||||
"{} profils traités ; {} images importées.".format(
|
||||
no_images.count(), success
|
||||
)
|
||||
print(
|
||||
"{} profils traités ; {} images importées.".format(
|
||||
no_images.count(), success
|
||||
)
|
||||
)
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
# Generated by Django 2.2.17 on 2021-01-28 00:19
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fiches', '0008_auto_20201113_1038'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='address',
|
||||
name='content',
|
||||
field=models.TextField(verbose_name='adresse'),
|
||||
),
|
||||
]
|
|
@ -1,23 +0,0 @@
|
|||
# Generated by Django 2.2.17 on 2021-01-28 10:27
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fiches', '0009_multiline_address'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='profile',
|
||||
name='experiences',
|
||||
field=models.TextField(blank=True, verbose_name='expériences'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='profile',
|
||||
name='past_studies',
|
||||
field=models.TextField(blank=True, verbose_name='études passées'),
|
||||
),
|
||||
]
|
|
@ -1,11 +1,7 @@
|
|||
from datetime import date
|
||||
from typing import Optional
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.validators import MinValueValidator
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.core.validators import MinValueValidator
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
class Profile(models.Model):
|
||||
|
@ -30,8 +26,6 @@ class Profile(models.Model):
|
|||
birth_date = models.DateField(
|
||||
blank=True, null=True, verbose_name=_("date de naissance")
|
||||
)
|
||||
past_studies = models.TextField(blank=True, verbose_name=_("études passées"))
|
||||
experiences = models.TextField(blank=True, verbose_name=_("expériences"))
|
||||
thurne = models.CharField(blank=True, max_length=100, verbose_name=_("thurne"))
|
||||
text_field = models.TextField(blank=True, verbose_name=_("champ libre"))
|
||||
printing = models.BooleanField(
|
||||
|
@ -41,18 +35,10 @@ class Profile(models.Model):
|
|||
default=False, verbose_name=_("conserver la fiche annuaire ?")
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
def __str__(self):
|
||||
return self.full_name
|
||||
|
||||
@property
|
||||
def age(self) -> Optional[int]:
|
||||
if self.birth_date is not None:
|
||||
return relativedelta(date.today(), self.birth_date).year
|
||||
|
||||
def birthday(self) -> str:
|
||||
if self.birth_date is None:
|
||||
return "Unknown"
|
||||
|
||||
def birthday(self):
|
||||
return self.birth_date.strftime("%d%m")
|
||||
|
||||
|
||||
|
@ -61,7 +47,7 @@ class Department(models.Model):
|
|||
max_length=255, verbose_name=_("nom du département"), unique=True
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
|
@ -72,7 +58,7 @@ class Phone(models.Model):
|
|||
name = models.CharField(max_length=255, verbose_name=_("type"))
|
||||
number = models.CharField(max_length=1023, verbose_name=_("numéro"))
|
||||
|
||||
def __str__(self) -> str:
|
||||
def __str__(self):
|
||||
return "{} : {}".format(self.name, self.number)
|
||||
|
||||
|
||||
|
@ -94,7 +80,7 @@ class Mail(models.Model):
|
|||
name = models.CharField(max_length=255, verbose_name=_("type"))
|
||||
mail = models.CharField(max_length=1023, verbose_name=_("adresse mail"))
|
||||
|
||||
def __str__(self) -> str:
|
||||
def __str__(self):
|
||||
return "{} : {}".format(self.name, self.mail)
|
||||
|
||||
|
||||
|
@ -103,7 +89,7 @@ class Address(models.Model):
|
|||
Profile, on_delete=models.CASCADE, verbose_name=_("profil")
|
||||
)
|
||||
name = models.CharField(max_length=255, verbose_name=_("type"))
|
||||
content = models.TextField(verbose_name=_("adresse"))
|
||||
content = models.CharField(max_length=1023, verbose_name=_("adresse"))
|
||||
|
||||
def __str__(self) -> str:
|
||||
def __str__(self):
|
||||
return "{} : {}".format(self.name, self.content)
|
||||
|
|
Binary file not shown.
|
@ -28,38 +28,73 @@
|
|||
font-weight: 400;
|
||||
src: local("Source Code Pro Regular"), local("SourceCodePro-Regular"), url("../fonts/source-code-pro/source-code-pro-v12-latin-regular.woff2") format("woff2"), url("../fonts/source-code-pro/source-code-pro-v12-latin-regular.woff") format("woff");
|
||||
}
|
||||
a, #main-menu a a, #account-area a {
|
||||
#main-menu a a, #account-area a, a {
|
||||
text-decoration: none;
|
||||
color: #FFDC00;
|
||||
}
|
||||
|
||||
a:hover, #main-menu a a:hover, #account-area a:hover,
|
||||
a:active,
|
||||
#main-menu a a:hover, #account-area a:hover, a:hover,
|
||||
#main-menu a a:active,
|
||||
#account-area a:active,
|
||||
a:focus,
|
||||
a:active,
|
||||
#main-menu a a:focus,
|
||||
#account-area a:focus {
|
||||
#account-area a:focus,
|
||||
a:focus {
|
||||
color: #ffbb00;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body, html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
grid-template-areas: "aside main";
|
||||
background-color: #301827;
|
||||
color: #FFFFFF;
|
||||
font-family: "Fira Sans", "Roboto", sans-serif;
|
||||
}
|
||||
|
||||
#aside {
|
||||
grid-area: aside;
|
||||
padding: 0 0 0 10vw;
|
||||
background-color: #1f0e19;
|
||||
box-shadow: 4px 0 0 rgba(31, 14, 25, 0.3);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
#main {
|
||||
grid-area: main;
|
||||
padding: 0 10vw 0 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
background-color: #301827;
|
||||
z-index: 500;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1200px) {
|
||||
#header {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#main {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
#menu {
|
||||
display: grid;
|
||||
float: right;
|
||||
width: 320px;
|
||||
grid-template-columns: auto;
|
||||
grid-template-rows: auto auto auto auto auto 15px;
|
||||
grid-template-rows: auto auto auto auto 120px;
|
||||
grid-template-areas: "title" "language" "search" "menu" "account";
|
||||
}
|
||||
#menu #hamburger {
|
||||
display: none;
|
||||
grid-area: hamburger;
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TS1VaHOwg4pChOlkoKuKoVShChVIrtOpgcukXNGlIUlwcBdeCgx+LVQcXZ10dXAVB8APEzc1J0UVK/F9SaBHjwXE/3t173L0DhGaVqWZPHFA1y8gkE2IuvyoGXyEggD6EEZeYqc+l0yl4jq97+Ph6F+NZ3uf+HGGlYDLAJxLPMt2wiDeIpzctnfM+cYSVJYX4nHjcoAsSP3JddvmNc8lhgWdGjGxmnjhCLJa6WO5iVjZU4iniqKJqlC/kXFY4b3FWq3XWvid/YaigrSxzneYIkljEEtIQIaOOCqqwEKNVI8VEhvYTHv5hx58ml0yuChg5FlCDCsnxg//B727N4uSEmxRKAIEX2/4YBYK7QKth29/Htt06AfzPwJXW8deawMwn6Y2OFj0CBraBi+uOJu8BlzvA0JMuGZIj+WkKxSLwfkbflAcGb4H+Nbe39j5OH4AsdZW6AQ4OgbESZa97vLu3u7d/z7T7+wEsq3KLN3ZkxAAAAAlwSFlzAAAA7AAAAOwBeShxvQAAADNJREFUWMPt1EERAEAIA7Ee/h0hjpPBg0RBp49NrnvTmc0Btf2AARVAipVQikGKpdgArvsZZAhH7c55ywAAAABJRU5ErkJggg==");
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
float: right;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#title {
|
||||
grid-area: title;
|
||||
|
@ -101,7 +136,6 @@ a:focus,
|
|||
background-position: center;
|
||||
border: none;
|
||||
transition: 25ms ease-in-out;
|
||||
cursor: pointer;
|
||||
}
|
||||
#search-area button:hover {
|
||||
background-color: #FFDC00;
|
||||
|
@ -113,16 +147,11 @@ a:focus,
|
|||
text-align: center;
|
||||
}
|
||||
#language_switch .language {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: inline-block;
|
||||
background: none;
|
||||
border: none;
|
||||
color: #DFDFDF;
|
||||
}
|
||||
#language_switch .language:hover {
|
||||
color: #FFDC00;
|
||||
cursor: pointer;
|
||||
}
|
||||
#language_switch .language::after,
|
||||
#language_switch .current-language::after {
|
||||
|
@ -171,7 +200,7 @@ a:focus,
|
|||
#account-area {
|
||||
grid-area: account;
|
||||
display: block;
|
||||
padding-top: 20px;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
#account-area .clipper {
|
||||
|
@ -188,7 +217,6 @@ a:focus,
|
|||
color: #FFFFFF;
|
||||
box-shadow: 2px 2px 0 rgba(31, 14, 25, 0.3);
|
||||
transition: 25ms ease-in-out;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.content button:hover,
|
||||
|
@ -229,7 +257,7 @@ a:focus,
|
|||
|
||||
.content {
|
||||
max-width: 780px;
|
||||
width: 70vw;
|
||||
width: 50vw;
|
||||
margin: 20px;
|
||||
}
|
||||
.content h2 {
|
||||
|
@ -246,10 +274,10 @@ a:focus,
|
|||
}
|
||||
#content-home form {
|
||||
display: grid;
|
||||
width: min(400px, 100%);
|
||||
width: 400px;
|
||||
margin: 0 auto;
|
||||
grid-template-columns: 30% 65%;
|
||||
gap: 5%;
|
||||
grid-template-columns: 150px 250px;
|
||||
gap: 20px;
|
||||
}
|
||||
#content-home form label {
|
||||
grid-column: 1;
|
||||
|
@ -275,7 +303,7 @@ a:focus,
|
|||
|
||||
#content-view-profile {
|
||||
display: grid;
|
||||
grid-template-rows: min(150px, 45%) auto auto;
|
||||
grid-template-rows: 150px auto auto;
|
||||
grid-template-columns: 3fr 1fr;
|
||||
grid-template-areas: "header header" "infos infos" "free-text free-text";
|
||||
gap: 20px;
|
||||
|
@ -334,23 +362,16 @@ a:focus,
|
|||
}
|
||||
#content-view-profile .infos > *.multi-entry ul.value li {
|
||||
padding: 0;
|
||||
display: grid;
|
||||
grid-template-areas: "type value";
|
||||
}
|
||||
#content-view-profile .infos > *.multi-entry ul.value li .type {
|
||||
display: inline-block;
|
||||
margin: 0 1em 0 0;
|
||||
color: #DFDFDF;
|
||||
font-style: italic;
|
||||
grid-area: type;
|
||||
}
|
||||
#content-view-profile .infos > *.multi-entry ul.value li .value {
|
||||
display: inline-block;
|
||||
grid-area: value;
|
||||
text-align: right;
|
||||
}
|
||||
#content-view-profile .infos > *.multi-entry ul.value li:not(:last-child) {
|
||||
margin-bottom: 10px;
|
||||
float: right;
|
||||
}
|
||||
#content-view-profile .free-text {
|
||||
grid-area: free-text;
|
||||
|
@ -365,7 +386,7 @@ a:focus,
|
|||
}
|
||||
|
||||
#content-edit-profile form {
|
||||
width: max(600px, 80%);
|
||||
width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#content-edit-profile form .form-entry {
|
||||
|
@ -400,27 +421,19 @@ a:focus,
|
|||
grid-template-areas: "errors errors errors" "type-input value-input remove-button";
|
||||
column-gap: 10px;
|
||||
margin: 0 0 10px 0;
|
||||
align-items: center;
|
||||
}
|
||||
#content-edit-profile form .form-sub-entry.hidden {
|
||||
display: none;
|
||||
}
|
||||
#content-edit-profile form .form-sub-entry input:nth-child(1) {
|
||||
grid-area: type-input;
|
||||
max-height: 30px;
|
||||
}
|
||||
#content-edit-profile form .form-sub-entry input:nth-child(2) {
|
||||
grid-area: value-input;
|
||||
}
|
||||
#content-edit-profile form .form-sub-entry textarea:nth-child(2) {
|
||||
grid-area: value-input;
|
||||
max-height: 35px;
|
||||
resize: none;
|
||||
}
|
||||
#content-edit-profile form .form-sub-entry .remove-button {
|
||||
grid-area: remove-button;
|
||||
min-height: 30px;
|
||||
max-height: 30px;
|
||||
min-width: 30px;
|
||||
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="18px" height="18px"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>');
|
||||
background-size: 80%;
|
||||
|
@ -471,9 +484,7 @@ a:focus,
|
|||
}
|
||||
#content-edit-profile #free-text-edit-form textarea {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
padding: 5px;
|
||||
resize: none;
|
||||
}
|
||||
#content-edit-profile #photo-edit-form .current-photo {
|
||||
margin: 0 0 20px 0;
|
||||
|
@ -512,103 +523,3 @@ a:focus,
|
|||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body, html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
display: grid;
|
||||
grid-template-areas: "aside main";
|
||||
background-color: #301827;
|
||||
color: #FFFFFF;
|
||||
font-family: "Fira Sans", "Roboto", sans-serif;
|
||||
overflow-y: scroll;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#aside {
|
||||
grid-area: aside;
|
||||
padding: 0 0 0 10vw;
|
||||
background-color: #1f0e19;
|
||||
box-shadow: 4px 0 0 rgba(31, 14, 25, 0.3);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#main {
|
||||
grid-area: main;
|
||||
padding: 0 10vw 0 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
background-color: #301827;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
body {
|
||||
grid-template-rows: max-content auto;
|
||||
grid-template-areas: "aside" "main";
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
#aside,
|
||||
#main {
|
||||
padding: 0;
|
||||
box-shadow: unset;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
#content-home,
|
||||
#content-birthdays,
|
||||
#content-view-profile,
|
||||
#content-edit-profile {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#content-home form input[type=submit] {
|
||||
grid-column: 1/3;
|
||||
}
|
||||
|
||||
#title {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
|
||||
.content h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
#footer,
|
||||
.content {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
#menu {
|
||||
width: auto;
|
||||
float: none;
|
||||
grid-template-columns: auto 50px;
|
||||
grid-template-areas: "title hamburger" "language language" "search search" "menu menu" "account account";
|
||||
}
|
||||
#menu #hamburger {
|
||||
display: grid;
|
||||
}
|
||||
#menu #main-menu,
|
||||
#menu #account-area {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#search-area {
|
||||
justify-content: center;
|
||||
margin: auto;
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=annuaire.css.map */
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"sourceRoot":"","sources":["../scss/_fonts.scss","../scss/_links.scss","../scss/_colors.scss","../scss/_header.scss","../scss/_buttons.scss","../scss/_errors.scss","../scss/_content.scss","../scss/_footer.scss","../scss/_common.scss"],"names":[],"mappings":";AASA;AACA;EACI;EACA;EACA;EACA;;AAKJ;AACA;EACI;EACA;EACA;EACA;;AAKJ;AACA;EACI;EACA;EACA;EACA;;AAUJ;AAUA;AACA;EACI;EACA;EACA;EACA;;ACzDJ;EACI;EACA,OCOQ;;;ADJZ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAGI,OCEc;;;ACJlB;EACI;EACA;EACA;EAEA;EACA;EACA,qBACI;;AAMJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAIN;EACI;EAGA;EACA;EACA;EACA;;AAIA;EACI;;;AAIR;EACI;EACA;EACA;EACA,QAhDgB;EAiDhB;EACA;;AAGA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI,kBDzEI;;;AC6EZ;EACI;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EAEA,OD5Fc;;AC+FlB;EACI,OD7FI;EC8FJ;;AAGJ;AAAA;EAEI;EACA;EACA;EACA,ODzGc;;AC4GlB;EACI;;AAGJ;EACI;;;AAKJ;EAEI;EACA;EACA;EACA;EAEA,OD1HI;EC2HJ;;AAGJ;EAEI,OD/HU;;;ACmIlB;EAEI;EAEA;EACA,ODjIa;ECkIb;;AAEA;EAEI;EACA;EACA;EACA;EAEA,ODnJI;ECoJJ;;AAGJ;EAEI,ODxJU;;;AC4JlB;EACI;EACA;EACA;EACA;;AAIA;EACI,aH7GU;;;AIlElB;AAAA;AAAA;EACI;EACA,kBFUqB;EETrB;EACA;EACA,OFSe;EERf;EACA;EACA;;;AAGJ;AAAA;AAAA;EACI,kBFC2B;;;AGf/B;EACI;EACA;;;AAGJ;EACI;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;;;ACrBR;EACI,OJqBW;;;AIlBf;EACI;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAWJ;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;;AAMJ;EACI;;AAGJ;EACI;;AAGJ;EACI;EACA;;AAGJ;EACI;;AAIA;EACI;;;AAYhB;EACI;EACA;EACA;EACA,qBACI;EAGJ;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;AAEA;EACI;EACA,OJnGU;EIoGV;EACA;EACA;;AAIR;EACI;EACA;EACA;EACA;;AAGJ;EACI;EAEA;EACA,OJpHS;EIqHT;;AAGJ;EACI;;AAGJ;EACI;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,kBJtHe;EIuHf;;AAEA;EACI;EACA;EACA;;AAEA;EACI;EACA;EACA;;AAEA;EACI;EACA;EACA,OJvJF;EIwJE;EACA;;AAGJ;EACI;EACA;EACA;;AAGJ;EACE;;AASlB;EACI;EACA;;AAEA;EACI;;AAGJ;EACI;EACA;;;AAMR;EACI;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA,kBJpLe;EIqLf;;AAEA;EACI;;AAGJ;EACI;EACA,OJ7MC;EI8MD;EACA;EACA;;AAGJ;EACI;;AAGJ;EAGI;EACA;;AAIR;EACI;EACA;EACA;EACA,qBACI;EAEJ;EACA;EACA;;AAEA;EACI;;AAMJ;EACI;EACA;;AAEJ;EAAqB;;AACrB;EACI;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;AAAA;EAEI;;AAGJ;EACI;;AAUA;EAAqB;;AACrB;EAAqB;;AAGrB;AAAA;EAEI;;AAIR;EAEI;;AAIR;EACI;;AAGJ;EACI;;AAIA;EACI;EACA;;AAIR;EACI;EACA;;AAIA;EACI;;AAGJ;EACI;EACA;EACA;;AAMR;EACI;EACA;EACA;EACA;;AAKJ;EACI;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;;AAKJ;EACI;;AAGJ;EACI;;AAGJ;EACI;EACA,OJ3WC;EI4WD;;AASJ;EACI;;;AC7XhB;EACI;EACA;EACA,OLGkB;EKFlB;;AAEA;EACI;EACA;;;ACNR;EACI;;;AAMJ;EACI;;;AAGJ;EACI;EACA,qBACI;EAEJ,kBNnBc;EMoBd,ONdQ;EMeR,aR6CY;EQ5CZ;EACA;;;AAGJ;EACI;EACA;EACA,kBN5Be;EM6Bf;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,kBNxCc;EMyCd;;;AAIJ;EACE;IACE;IACA,qBACI;;;EAIN;IACE;;;EAGF;AAAA;IAEE;IACA;IACA;;;EAGF;AAAA;AAAA;AAAA;IAIE;;;EAKE;IACE;;;EAKN;IACE;;;EAIA;IACE;;;EAIJ;AAAA;IAEE;;;EAGF;IACE;IACA;IACA;IACA,qBACE;;EAMF;IACE;;EAGF;AAAA;IAEE;;;EAIJ;IACE;IACA;IACA","file":"annuaire.css"}
|
|
@ -1,258 +0,0 @@
|
|||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Hidenao Miyamoto
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
.datepicker {
|
||||
display: none;
|
||||
}
|
||||
.datepicker.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.datepicker-dropdown {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 20;
|
||||
padding-top: 4px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-top {
|
||||
padding-top: 0;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.datepicker-picker {
|
||||
display: inline-block;
|
||||
border-radius: 0px;
|
||||
background-color: #301827;
|
||||
}
|
||||
.datepicker-dropdown .datepicker-picker {
|
||||
box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);
|
||||
}
|
||||
.datepicker-picker span {
|
||||
display: block;
|
||||
flex: 1;
|
||||
border: 0;
|
||||
border-radius: 0px;
|
||||
cursor: default;
|
||||
text-align: center;
|
||||
-webkit-touch-callout: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.datepicker-main {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.datepicker-footer {
|
||||
box-shadow: inset 0 1px 1px rgba(10, 10, 10, 0.1);
|
||||
background-color: whitesmoke;
|
||||
}
|
||||
|
||||
.datepicker-grid, .datepicker-view .days-of-week, .datepicker-view, .datepicker-controls {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.datepicker-grid {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.datepicker-view .days .datepicker-cell, .datepicker-view .dow {
|
||||
flex-basis: 14.2857142857%;
|
||||
}
|
||||
|
||||
.datepicker-view.datepicker-grid .datepicker-cell {
|
||||
flex-basis: 25%;
|
||||
}
|
||||
|
||||
.datepicker-cell, .datepicker-view .week {
|
||||
height: 2.25rem;
|
||||
line-height: 2.25rem;
|
||||
}
|
||||
|
||||
.datepicker-title {
|
||||
box-shadow: inset 0 -1px 1px rgba(10, 10, 10, 0.1);
|
||||
background-color: #1f0e19;
|
||||
padding: 0.375rem 0.75rem;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.datepicker-header .datepicker-controls {
|
||||
padding: 2px 2px 0;
|
||||
}
|
||||
.datepicker-controls .button {
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0;
|
||||
border: 1px solid #dbdbdb;
|
||||
border-radius: 0px;
|
||||
box-shadow: none;
|
||||
background-color: white;
|
||||
cursor: pointer;
|
||||
padding: calc(0.375em - 1px) 0.75em;
|
||||
height: 2.25em;
|
||||
vertical-align: top;
|
||||
text-align: center;
|
||||
line-height: 1.5;
|
||||
white-space: nowrap;
|
||||
color: #363636;
|
||||
font-size: 1rem;
|
||||
}
|
||||
.datepicker-controls .button:focus, .datepicker-controls .button:active {
|
||||
outline: none;
|
||||
}
|
||||
.datepicker-controls .button:hover {
|
||||
border-color: #b5b5b5;
|
||||
color: #363636;
|
||||
}
|
||||
.datepicker-controls .button:focus {
|
||||
border-color: #3273dc;
|
||||
color: #363636;
|
||||
}
|
||||
.datepicker-controls .button:focus:not(:active) {
|
||||
box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25);
|
||||
}
|
||||
.datepicker-controls .button:active {
|
||||
border-color: #4a4a4a;
|
||||
color: #363636;
|
||||
}
|
||||
.datepicker-controls .button[disabled] {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.datepicker-header .datepicker-controls .button {
|
||||
border-color: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
.datepicker-header .datepicker-controls .button:hover {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.datepicker-header .datepicker-controls .button:focus:not(:active) {
|
||||
box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
.datepicker-header .datepicker-controls .button:active {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
.datepicker-header .datepicker-controls .button[disabled] {
|
||||
box-shadow: none;
|
||||
}
|
||||
.datepicker-footer .datepicker-controls .button {
|
||||
margin: calc(0.375rem - 1px) 0.375rem;
|
||||
border-radius: 0px;
|
||||
width: 100%;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.datepicker-controls .view-switch {
|
||||
flex: auto;
|
||||
}
|
||||
.datepicker-controls .prev-btn,
|
||||
.datepicker-controls .next-btn {
|
||||
padding-right: 0.375rem;
|
||||
padding-left: 0.375rem;
|
||||
width: 2.25rem;
|
||||
}
|
||||
.datepicker-controls .prev-btn.disabled,
|
||||
.datepicker-controls .next-btn.disabled {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.datepicker-view .dow {
|
||||
height: 1.5rem;
|
||||
line-height: 1.5rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
.datepicker-view .week {
|
||||
width: 2.25rem;
|
||||
color: #b5b5b5;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
@media (max-width: 22.5rem) {
|
||||
.datepicker-view .week {
|
||||
width: 1.96875rem;
|
||||
}
|
||||
}
|
||||
|
||||
.datepicker-grid {
|
||||
width: 15.75rem;
|
||||
}
|
||||
@media (max-width: 22.5rem) {
|
||||
.calendar-weeks + .days .datepicker-grid {
|
||||
width: 13.78125rem;
|
||||
}
|
||||
}
|
||||
|
||||
.datepicker-cell:not(.disabled):hover {
|
||||
background-color: #281420;
|
||||
cursor: pointer;
|
||||
}
|
||||
.datepicker-cell.focused:not(.selected) {
|
||||
background-color: #1f1019;
|
||||
}
|
||||
.datepicker-cell.selected, .datepicker-cell.selected:hover {
|
||||
background-color: #3b1e31;
|
||||
color: #ffdc00;
|
||||
font-weight: 600;
|
||||
}
|
||||
.datepicker-cell.disabled {
|
||||
color: #dbdbdb;
|
||||
}
|
||||
.datepicker-cell.prev:not(.disabled), .datepicker-cell.next:not(.disabled) {
|
||||
color: #7a7a7a;
|
||||
}
|
||||
.datepicker-cell.prev.selected, .datepicker-cell.next.selected {
|
||||
color: #ccb000;
|
||||
}
|
||||
.datepicker-cell.highlighted:not(.selected):not(.today) {
|
||||
border-radius: 0;
|
||||
background-color: #3b1e31;
|
||||
}
|
||||
.datepicker-cell.highlighted:not(.selected):not(.today):not(.disabled):hover {
|
||||
background-color: #331a2a;
|
||||
}
|
||||
.datepicker-cell.highlighted:not(.selected):not(.today).focused {
|
||||
background-color: #1f1019;
|
||||
}
|
||||
.datepicker-cell.today:not(.selected) {
|
||||
background-color: #3b1e31;
|
||||
}
|
||||
.datepicker-cell.today:not(.selected):not(.disabled) {
|
||||
color: #fff;
|
||||
}
|
||||
.datepicker-cell.today.focused:not(.selected) {
|
||||
background-color: #331a2a;
|
||||
}
|
||||
.datepicker-view.datepicker-grid .datepicker-cell {
|
||||
height: 4.5rem;
|
||||
line-height: 4.5rem;
|
||||
}
|
||||
|
||||
.datepicker-input.in-edit {
|
||||
border-color: #2366d1;
|
||||
}
|
||||
.datepicker-input.in-edit:focus, .datepicker-input.in-edit:active {
|
||||
box-shadow: 0 0 0.25em 0.25em rgba(35, 102, 209, 0.2);
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=datepicker.css.map */
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"sourceRoot":"","sources":["../scss/datepicker.scss"],"names":[],"mappings":"AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0FA;EACE;;AAEA;EACE;;;AAIJ;EACE;EACA;EACA;EACA,SAvDc;EAwDd,aA1DmB;;AA4DnB;EACE;EACA,gBA9DiB;;;AAkErB;EACE;EACA,eA3EiB;EA4EjB,kBA9EoB;;AAgFpB;EACE,YAvEiB;;AA0EnB;EACE;EACA;EACA;EACA,eAtFe;EAuFf;EACA;EACA;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA,kBA/GM;;;AAkHR;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE,QA3GkB;EA4GlB,aA5GkB;;;AA+GpB;EACE;EACA,kBAnH0B;EAoH1B;EACA;EACA,aA3HoB;;;AAiIpB;EACE;;AAMA;EAqBE;EACA;EACA;EACA;EACA;EACA;EACA,eAzKa;EA0Kb;EACA,kBAvLE;EAwLF;EACA;EACA;EACA;EACA;EACA,aA/KgB;EAgLhB;EACA,OA7LC;EA8LD,WAjLgB;;AAmLhB;EAEE;;AAGF;EACE,cAlMK;EAmML,OAvMD;;AA0MD;EACE,cA1MD;EA2MC,OA5MD;;AA8MC;EACE;;AAIJ;EACE,cAjNI;EAkNJ,OArND;;AAwND;EACE;;AAGF;EA1KJ;EACA;;AA4KM;EACE;;AAIA;EACE;;AAIJ;EACE;;AAGF;EACE;;AAIJ;EA3LJ;EACA,eA7CuB;EA8CvB;EACA,WA5CmB;;AA0OnB;EACE;;AAGF;AAAA;EAEE;EACA;EACA,OAzOgB;;AA2OhB;AAAA;EACE;;;AAYJ;EAEE;EACA;EACA;EACA,aApQkB;;AAuQpB;EAEE,OAlQgB;EAmQhB,OAtRS;EAuRT,WA7QiB;;AA+QjB;EANF;IAOI,OAhPmB;;;;AAqPzB;EAIE;;AAEA;EACE;IACE;;;;AAQJ;EACE;EACA;;AAGF;EACE,kBAjS6B;;AAqS7B;EAEE,kBApS8B;EAqS9B,OApSmB;EAqSnB,aApSyB;;AAwS7B;EACE,OAjUW;;AAsUX;EACE,OAnTmB;;AAsTrB;EACE;;AAIJ;EACE;EACA,kBAtTmC;;AAwTnC;EACE;;AAGF;EACE,kBArU2B;;AA0U7B;EACE,kBArU2B;;AAuU3B;EACE,OAvUc;;AA2UlB;EACE;;AAQJ;EAEE;EACA;;;AAIJ;EACE,cAlV8B;;AAoV9B;EAEE","file":"datepicker.css"}
|
1
fiches/static/fiches/js/datepicker.min.js
vendored
1
fiches/static/fiches/js/datepicker.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -53,10 +53,31 @@ class SubEntry {
|
|||
this.display();
|
||||
}
|
||||
|
||||
reindex(newIndex) {
|
||||
// Replace -<old index>- by -<new index>- in the values of the
|
||||
// id and name attributes of every child of this sub-entry's element
|
||||
const attributesToUpdate = ["id", "name"];
|
||||
|
||||
for (let childElement of this.subEntryElement.childNodes) {
|
||||
for (let attribute of attributesToUpdate) {
|
||||
if (!childElement.hasAttribute(attribute)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const newValue = childElement
|
||||
.getAttribute(attribute)
|
||||
.replace(`-${this.index}-`, `-${newIndex}-`);
|
||||
childElement.setAttribute(attribute, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
this.index = newIndex;
|
||||
}
|
||||
|
||||
startHandlingSubEntryRemoveButtonClicks() {
|
||||
this.removeButtonElement.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
this.markForDeletionAndHide();;
|
||||
this.parentFormEntry.removeSubEntry(this);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -104,10 +125,6 @@ class MultiEntryFormEntry {
|
|||
}
|
||||
|
||||
get nbSubEntries() {
|
||||
return this.subEntries.length;
|
||||
}
|
||||
|
||||
get nbActiveSubEntries() {
|
||||
return this.subEntries
|
||||
.filter(subEntry =>
|
||||
!subEntry.subEntryElement.classList.contains("hidden")
|
||||
|
@ -115,6 +132,13 @@ class MultiEntryFormEntry {
|
|||
.length;
|
||||
}
|
||||
|
||||
get hasHiddenSubEntries() {
|
||||
return this.subEntries
|
||||
.findIndex(subEntry =>
|
||||
subEntry.subEntryElement.classList.contains("hidden")
|
||||
) >= 0;
|
||||
}
|
||||
|
||||
createInitialSubEntries() {
|
||||
const subEntryElements = this.formEntryElement.querySelectorAll(".form-sub-entry");
|
||||
this.initialNbSubEntries = subEntryElements.length;
|
||||
|
@ -129,31 +153,64 @@ class MultiEntryFormEntry {
|
|||
}
|
||||
|
||||
createNewSubEntry() {
|
||||
if (this.nbActiveSubEntries === this.maxNbSubEntries) {
|
||||
if (this.nbSubEntries === this.maxNbSubEntries) {
|
||||
console.log(`The max. number of sub-entries has been reached (${this.maxNbSubEntries}).`);
|
||||
return;
|
||||
}
|
||||
|
||||
// If there are hidden sub-entries,
|
||||
// it means one of the initial sub-entry elements should be reset and displayed
|
||||
if (this.hasHiddenSubEntries) {
|
||||
// Reset and display the first hidden sub-entry
|
||||
const existingSubEntry = this.subEntries.find(
|
||||
subEntry => subEntry.subEntryElement.classList.contains("hidden")
|
||||
);
|
||||
|
||||
existingSubEntry.resetAndDisplay();
|
||||
}
|
||||
|
||||
// Otherwise, it means a new sub-entry (element) must be created
|
||||
// and appended to the form entry element
|
||||
else {
|
||||
const newSubEntryIndex = this.nbSubEntries;
|
||||
const newSubEntry = SubEntry.fromTemplate(
|
||||
this.subEntryTemplateElement,
|
||||
newSubEntryIndex,
|
||||
this
|
||||
);
|
||||
this.subEntryTemplateElement,
|
||||
newSubEntryIndex,
|
||||
this
|
||||
);
|
||||
|
||||
this.subEntries.push(newSubEntry);
|
||||
this.formEntryElement.insertBefore(
|
||||
newSubEntry.subEntryElement,
|
||||
this.newSubEntryButtonElement
|
||||
);
|
||||
|
||||
this.subEntries.push(newSubEntry);
|
||||
this.formEntryElement.insertBefore(
|
||||
newSubEntry.subEntryElement,
|
||||
this.newSubEntryButtonElement
|
||||
);
|
||||
|
||||
// Increment Django's TOTAL_FORMS hidden form input
|
||||
this.totalFormsElement.value = (parseInt(this.totalFormsElement.value) + 1).toString();
|
||||
// Increment Django's TOTAL_FORMS hidden form input
|
||||
this.totalFormsElement.value = (parseInt(this.totalFormsElement.value) + 1).toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
removeSubEntry(subEntry) {
|
||||
// If the index of the sub-entry to remove is below the initial number of sub-entries,
|
||||
// it means one of the initial sub entry elements should be marked for deletion and hidden
|
||||
const removedSubEntryIndex = subEntry.index;
|
||||
if (removedSubEntryIndex < this.initialNbSubEntries) {
|
||||
subEntry.markForDeletionAndHide();
|
||||
}
|
||||
|
||||
// Otherwise, delete the sub-entry (and remove its element from the DOM)
|
||||
// and reindex other user-created sub-entries if need be
|
||||
else {
|
||||
subEntry.subEntryElement.remove();
|
||||
|
||||
this.subEntries.splice(subEntry.index, 1);
|
||||
for (let index = this.removeSubEntryIndex; index < this.subEntries.length; index++) {
|
||||
this.subEntries[index].reindex(index);
|
||||
}
|
||||
|
||||
// Decrement Django's TOTAL_FORMS hidden form input
|
||||
this.totalFormsElement.value = (parseInt(this.totalFormsElement.value) - 1).toString();
|
||||
}
|
||||
}
|
||||
|
||||
startHandlingNewSubEntryButtonClicks() {
|
||||
|
|
|
@ -7,10 +7,9 @@
|
|||
font-size: 1.2rem;
|
||||
color: colors.$page-button-text;
|
||||
box-shadow: 2px 2px 0 colors.$shadow;
|
||||
transition: 25ms ease-in-out;
|
||||
cursor: pointer;
|
||||
transition: 25ms ease-in-out;
|
||||
}
|
||||
|
||||
%button:hover {
|
||||
background-color: colors.$page-button-background-hover;
|
||||
}
|
||||
}
|
|
@ -15,14 +15,13 @@ body, html {
|
|||
|
||||
body {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
grid-template-areas:
|
||||
"aside main";
|
||||
|
||||
background-color: colors.$page-background;
|
||||
color: colors.$page-text;
|
||||
font-family: fonts.$regular-fonts;
|
||||
overflow-y: scroll;
|
||||
border:none;
|
||||
}
|
||||
|
||||
#aside {
|
||||
|
@ -30,7 +29,7 @@ body {
|
|||
padding: 0 0 0 10vw;
|
||||
background-color: colors.$aside-background;
|
||||
box-shadow: 4px 0 0 colors.$shadow;
|
||||
z-index: 10;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
#main {
|
||||
|
@ -40,84 +39,16 @@ body {
|
|||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
background-color: colors.$main-background;
|
||||
z-index: 5;
|
||||
z-index: 500;
|
||||
}
|
||||
|
||||
// Pour les vues mobile
|
||||
@media screen and (max-width: 900px) {
|
||||
body {
|
||||
grid-template-rows: max-content auto;
|
||||
grid-template-areas:
|
||||
"aside"
|
||||
"main";
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
#aside,
|
||||
#main {
|
||||
padding: 0;
|
||||
box-shadow: unset;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
#content-home,
|
||||
#content-birthdays,
|
||||
#content-view-profile,
|
||||
#content-edit-profile {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#content-home {
|
||||
form {
|
||||
input[type="submit"] {
|
||||
grid-column: 1/3;
|
||||
}
|
||||
@media only screen and (max-width: 1200px) {
|
||||
#header {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#title {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
|
||||
#main {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#footer,
|
||||
.content {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
#menu {
|
||||
width: auto;
|
||||
float: none;
|
||||
grid-template-columns: auto 50px;
|
||||
grid-template-areas:
|
||||
"title hamburger"
|
||||
"language language"
|
||||
"search search"
|
||||
"menu menu"
|
||||
"account account";
|
||||
|
||||
#hamburger {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
#main-menu,
|
||||
#account-area {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#search-area {
|
||||
justify-content: center;
|
||||
margin: auto;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
@use "errors";
|
||||
|
||||
#content-area {
|
||||
color: colors.$content-text;
|
||||
color: colors.$content-text;
|
||||
}
|
||||
|
||||
.content {
|
||||
max-width: 780px;
|
||||
width: 70vw;
|
||||
width: 50vw;
|
||||
margin: 20px;
|
||||
|
||||
h2 {
|
||||
|
@ -21,7 +21,7 @@
|
|||
input {
|
||||
min-height: 30px;
|
||||
padding: 5px;
|
||||
border: none;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
|
@ -35,11 +35,11 @@
|
|||
#content-home {
|
||||
form {
|
||||
display: grid;
|
||||
width: min(400px, 100%);
|
||||
width: 400px;
|
||||
margin: 0 auto;
|
||||
grid-template-columns: 30% 65%;
|
||||
gap: 5%;
|
||||
|
||||
grid-template-columns: 150px 250px;
|
||||
gap: 20px;
|
||||
|
||||
label {
|
||||
grid-column: 1;
|
||||
margin: 5px 0 0 0;
|
||||
|
@ -74,7 +74,7 @@
|
|||
// input[type="checkbox"] {
|
||||
// display: inline;
|
||||
// width: 1rem;
|
||||
// float: right;
|
||||
// float: right;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +82,7 @@
|
|||
|
||||
#content-view-profile {
|
||||
display: grid;
|
||||
grid-template-rows: min(150px, 45%) auto auto;
|
||||
grid-template-rows: 150px auto auto;
|
||||
grid-template-columns: 3fr 1fr;
|
||||
grid-template-areas:
|
||||
"header header"
|
||||
|
@ -111,7 +111,7 @@
|
|||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.photo {
|
||||
height: 150px;
|
||||
margin: 0 20px 0 0;
|
||||
|
@ -143,7 +143,7 @@
|
|||
padding: 10px;
|
||||
background-color: colors.$content-frame-background;
|
||||
box-shadow: 2px 2px 0 colors.$shadow;
|
||||
|
||||
|
||||
&.multi-entry ul.value {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
@ -151,29 +151,19 @@
|
|||
|
||||
li {
|
||||
padding: 0;
|
||||
display: grid;
|
||||
grid-template-areas: "type value";
|
||||
|
||||
.type {
|
||||
display: inline-block;
|
||||
margin: 0 1em 0 0;
|
||||
color: colors.$page-text-secondary;
|
||||
font-style: italic;
|
||||
grid-area: type;
|
||||
}
|
||||
|
||||
.value {
|
||||
display: inline-block;
|
||||
grid-area: value;
|
||||
text-align: right;
|
||||
float: right;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -195,7 +185,7 @@
|
|||
|
||||
#content-edit-profile {
|
||||
form {
|
||||
width: max(600px, 80%);
|
||||
width: 400px;
|
||||
margin: 0 auto;
|
||||
|
||||
.form-entry {
|
||||
|
@ -205,11 +195,11 @@
|
|||
padding: 10px;
|
||||
background-color: colors.$content-frame-background;
|
||||
box-shadow: 2px 2px 0 colors.$shadow;
|
||||
|
||||
|
||||
> * {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
label {
|
||||
margin: 0 0 5px 0;
|
||||
color: colors.$page-text-emph;
|
||||
|
@ -217,7 +207,7 @@
|
|||
font-style: italic;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
||||
input, select {
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -239,7 +229,6 @@
|
|||
"type-input value-input remove-button";
|
||||
column-gap: 10px;
|
||||
margin: 0 0 10px 0;
|
||||
align-items: center;
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
|
@ -248,21 +237,12 @@
|
|||
// Since the different labels and inputs are not obvious to identifiate using CSS selectors,
|
||||
// they are selected one after the other using their natural order in the DOM
|
||||
// TODO: make this more robust by giving proper class names to each sub-entry element
|
||||
input:nth-child(1) {
|
||||
grid-area: type-input;
|
||||
max-height: 30px;
|
||||
}
|
||||
input:nth-child(1) { grid-area: type-input; }
|
||||
input:nth-child(2) { grid-area: value-input; }
|
||||
textarea:nth-child(2) {
|
||||
grid-area: value-input;
|
||||
max-height: 35px;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.remove-button {
|
||||
grid-area: remove-button;
|
||||
min-height: 30px;
|
||||
max-height: 30px;
|
||||
min-width: 30px;
|
||||
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="18px" height="18px"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>');
|
||||
background-size: 80%;
|
||||
|
@ -281,14 +261,14 @@
|
|||
|
||||
&.erroneous {
|
||||
@extend %error-list-container;
|
||||
|
||||
|
||||
// TODO: this is not robust and should be replaced by better selectors
|
||||
// In case of error, increment the child indices
|
||||
// In case of error, increment the child indices
|
||||
// to take the additional list of errors (.errorlist)
|
||||
// prepended by Django to the sub-entry
|
||||
input:nth-child(2) { grid-area: type-input; }
|
||||
input:nth-child(3) { grid-area: value-input; }
|
||||
|
||||
|
||||
// Use a darker red color when the sub-entry background is already red
|
||||
.remove-button:hover,
|
||||
.remove-button:active {
|
||||
|
@ -301,7 +281,7 @@
|
|||
grid-area: errors;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.form-sub-entry-template {
|
||||
display: none;
|
||||
}
|
||||
|
@ -309,7 +289,7 @@
|
|||
.add-sub-entry-button {
|
||||
margin: 0 auto 0 auto;
|
||||
}
|
||||
|
||||
|
||||
.form-entry.checkbox {
|
||||
> * {
|
||||
display: inline;
|
||||
|
@ -330,7 +310,7 @@
|
|||
input[type="checkbox"] {
|
||||
display: block;
|
||||
width: auto;
|
||||
float: right;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -338,9 +318,7 @@
|
|||
#free-text-edit-form {
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
padding: 5px;
|
||||
resize: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -377,7 +355,7 @@
|
|||
// input[type="checkbox"] {
|
||||
// display: inline-block;
|
||||
// width: 1rem;
|
||||
// float: right;
|
||||
// float: right;
|
||||
// }
|
||||
|
||||
input[type="file"] {
|
||||
|
@ -385,4 +363,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
padding: 20px;
|
||||
color: colors.$page-text-secondary;
|
||||
text-align: center;
|
||||
|
||||
|
||||
.thanks {
|
||||
display: block;
|
||||
text-align: center;
|
||||
|
|
|
@ -12,23 +12,13 @@ $account-area-height: 120px;
|
|||
width: 320px;
|
||||
// height: 100%;
|
||||
grid-template-columns: auto;
|
||||
grid-template-rows: auto auto auto auto auto 15px;
|
||||
grid-template-rows: auto auto auto auto $account-area-height;
|
||||
grid-template-areas:
|
||||
"title"
|
||||
"language"
|
||||
"search"
|
||||
"menu"
|
||||
"account";
|
||||
|
||||
#hamburger {
|
||||
display: none;
|
||||
grid-area: hamburger;
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TS1VaHOwg4pChOlkoKuKoVShChVIrtOpgcukXNGlIUlwcBdeCgx+LVQcXZ10dXAVB8APEzc1J0UVK/F9SaBHjwXE/3t173L0DhGaVqWZPHFA1y8gkE2IuvyoGXyEggD6EEZeYqc+l0yl4jq97+Ph6F+NZ3uf+HGGlYDLAJxLPMt2wiDeIpzctnfM+cYSVJYX4nHjcoAsSP3JddvmNc8lhgWdGjGxmnjhCLJa6WO5iVjZU4iniqKJqlC/kXFY4b3FWq3XWvid/YaigrSxzneYIkljEEtIQIaOOCqqwEKNVI8VEhvYTHv5hx58ml0yuChg5FlCDCsnxg//B727N4uSEmxRKAIEX2/4YBYK7QKth29/Htt06AfzPwJXW8deawMwn6Y2OFj0CBraBi+uOJu8BlzvA0JMuGZIj+WkKxSLwfkbflAcGb4H+Nbe39j5OH4AsdZW6AQ4OgbESZa97vLu3u7d/z7T7+wEsq3KLN3ZkxAAAAAlwSFlzAAAA7AAAAOwBeShxvQAAADNJREFUWMPt1EERAEAIA7Ee/h0hjpPBg0RBp49NrnvTmc0Btf2AARVAipVQikGKpdgArvsZZAhH7c55ywAAAABJRU5ErkJggg==');
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
float: right;
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
#title {
|
||||
|
@ -78,7 +68,6 @@ $account-area-height: 120px;
|
|||
background-position: center;
|
||||
border: none;
|
||||
transition: 25ms ease-in-out;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
|
@ -92,18 +81,13 @@ $account-area-height: 120px;
|
|||
text-align: center;
|
||||
|
||||
.language {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: inline-block;
|
||||
background: none;
|
||||
border: none;
|
||||
// text-decoration: underline;
|
||||
color: colors.$page-text-secondary;
|
||||
}
|
||||
|
||||
.language:hover {
|
||||
color: colors.$main-menu-link;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.language::after,
|
||||
|
@ -169,7 +153,7 @@ $account-area-height: 120px;
|
|||
#account-area {
|
||||
grid-area: account;
|
||||
display: block;
|
||||
padding-top: 20px;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
|
||||
@extend %menu-link;
|
||||
|
@ -177,4 +161,4 @@ $account-area-height: 120px;
|
|||
.clipper {
|
||||
font-family: fonts.$monospace-fonts;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
@use "common";
|
||||
@use "header";
|
||||
@use "content";
|
||||
@use "footer";
|
||||
@use "common";
|
||||
@use "footer";
|
|
@ -1,414 +0,0 @@
|
|||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Hidenao Miyamoto
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
//== foundational variables ==//
|
||||
$black: hsl(0, 0%, 4%) !default;
|
||||
$white: hsl(0, 0%, 100%) !default;
|
||||
$light: hsl(0, 0%, 96%) !default; // white-ter
|
||||
$dark: hsl(0, 0%, 21%) !default; // grey-darker
|
||||
$link: hsl(217, 71%, 53%) !default; // blue
|
||||
|
||||
$grey-dark: lighten($black, 25%) !default;
|
||||
$grey-light: darken($light, 25%) !default;
|
||||
$grey-lighter: darken($light, 10%) !default;
|
||||
|
||||
//== datepicker variables ==//
|
||||
$dp-background-color: #301827 !default;
|
||||
$dp-border-color: #1f0e19 !default;
|
||||
$dp-border-radius: 0px !default;
|
||||
$dp-border-radius-small: 0px !default;
|
||||
$dp-line-height-base: 1.5 !default;
|
||||
$dp-font-size-normal: 1rem !default;
|
||||
$dp-font-size-small: 0.75rem !default;
|
||||
$dp-font-weight-semibold: 600 !default;
|
||||
$dp-font-weight-bold: 700 !default;
|
||||
$dp-dropdown-offset: 4px !default;
|
||||
$dp-dropdown-shadow: 0 2px 3px rgba($black, 0.1), 0 0 0 1px rgba($black, 0.1) !default;
|
||||
$dp-dropdown-z: 20 !default;
|
||||
|
||||
$dp-title-background-color: #1f0e19 !default;
|
||||
|
||||
$dp-cell-size-base: 2.25rem !default;
|
||||
$dp-cell-focus-background-color: darken(#301827, 5%) !default;
|
||||
$dp-cell-prevnext-color: hsl(0, 0%, 48%) !default; // grey
|
||||
$dp-cell-disabled-color: $grey-lighter !default;
|
||||
$dp-cell-selected-background-color: #3b1e31 !default;
|
||||
$dp-cell-selected-color: #ffdc00 !default; // link(blue)-invert
|
||||
$dp-cell-selected-font-weight: 600 !default;
|
||||
$dp-cell-today-background-color: #3b1e31 !default; // turquoise (primary)
|
||||
$dp-cell-today-color: #fff !default; // turquoise-invert
|
||||
$dp-cell-highlighted-background-color: #3b1e31 !default;
|
||||
$dp-range-start-end-background-color: $grey-light !default;
|
||||
$dp-range-start-end-color: $dp-cell-selected-color !default;
|
||||
$dp-range-background-color: $grey-lighter !default;
|
||||
$dp-range-today-background-color: $dp-cell-today-background-color !default;
|
||||
$dp-week-color: $grey-light !default;
|
||||
|
||||
$dp-footer-background-color: $light !default;
|
||||
|
||||
$dp-input-in-edit-border-color: darken($link, 5%) !default;
|
||||
$dp-input-in-edit-focus-box-shadow-size: 0 0 0.25em 0.25em !default;
|
||||
|
||||
//== non-configurable variables ==//
|
||||
$dp-cell-shrink-threshold: $dp-cell-size-base * 10; // = 8 * 1.25
|
||||
$dp-cell-shrinked-width: $dp-cell-size-base * 7 / 8;
|
||||
|
||||
//== mixins ==//
|
||||
@mixin dp-header-button-common {
|
||||
border-color: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@mixin dp-footer-button-common {
|
||||
margin: calc(0.375rem - 1px) 0.375rem;
|
||||
border-radius: $dp-border-radius-small;
|
||||
width: 100%;
|
||||
font-size: $dp-font-size-small;
|
||||
}
|
||||
|
||||
//== styles ==//
|
||||
.datepicker {
|
||||
display: none;
|
||||
|
||||
&.active {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.datepicker-dropdown {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: $dp-dropdown-z;
|
||||
padding-top: $dp-dropdown-offset;
|
||||
|
||||
&.datepicker-orient-top {
|
||||
padding-top: 0;
|
||||
padding-bottom: $dp-dropdown-offset;
|
||||
}
|
||||
}
|
||||
|
||||
.datepicker-picker {
|
||||
display: inline-block;
|
||||
border-radius: $dp-border-radius;
|
||||
background-color: $dp-background-color;
|
||||
|
||||
.datepicker-dropdown & {
|
||||
box-shadow: $dp-dropdown-shadow;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
flex: 1;
|
||||
border: 0;
|
||||
border-radius: $dp-border-radius;
|
||||
cursor: default;
|
||||
text-align: center;
|
||||
-webkit-touch-callout: none;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
.datepicker-main {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.datepicker-footer {
|
||||
box-shadow: inset 0 1px 1px rgba($black, 0.1);
|
||||
background-color: $dp-footer-background-color;
|
||||
}
|
||||
|
||||
%flex-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
%flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
%flex-basis-day {
|
||||
flex-basis: percentage(1 / 7);
|
||||
}
|
||||
|
||||
%flex-basis-month-year {
|
||||
flex-basis: 25%;
|
||||
}
|
||||
|
||||
%datepicker-cell-height {
|
||||
height: $dp-cell-size-base;
|
||||
line-height: $dp-cell-size-base;
|
||||
}
|
||||
|
||||
.datepicker-title {
|
||||
box-shadow: inset 0 -1px 1px rgba($black, 0.1);
|
||||
background-color: $dp-title-background-color;
|
||||
padding: 0.375rem 0.75rem;
|
||||
text-align: center;
|
||||
font-weight: $dp-font-weight-bold;
|
||||
}
|
||||
|
||||
.datepicker-controls {
|
||||
@extend %flex-container;
|
||||
|
||||
.datepicker-header & {
|
||||
padding: 2px 2px 0;
|
||||
}
|
||||
|
||||
@if mixin-exists(dp-button) {
|
||||
@include dp-button;
|
||||
} @else {
|
||||
.button {
|
||||
$button-color: $dark;
|
||||
$button-background-color: $white;
|
||||
|
||||
$button-border-color: $grey-lighter;
|
||||
$button-border-width: 1px;
|
||||
|
||||
$button-padding-vertical: calc(0.375em - #{$button-border-width});
|
||||
$button-padding-horizontal: 0.75em;
|
||||
|
||||
$button-hover-color: $dark; // link-hover
|
||||
$button-hover-border-color: $grey-light; // link-hover-border
|
||||
|
||||
$button-focus-color: $dark; // link-focus
|
||||
$button-focus-border-color: $link; // link-focus-border
|
||||
$button-focus-box-shadow-size: 0 0 0 0.125em;
|
||||
$button-focus-box-shadow-color: rgba($link, 0.25);
|
||||
|
||||
$button-active-color: $dark; // link-active
|
||||
$button-active-border-color: $grey-dark; // link-active-border
|
||||
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0;
|
||||
border: $button-border-width solid $button-border-color;
|
||||
border-radius: $dp-border-radius; // control-radius
|
||||
box-shadow: none;
|
||||
background-color: $button-background-color;
|
||||
cursor: pointer;
|
||||
padding: $button-padding-vertical $button-padding-horizontal;
|
||||
height: 2.25em; // control-height
|
||||
vertical-align: top;
|
||||
text-align: center;
|
||||
line-height: $dp-line-height-base; // control-line-height
|
||||
white-space: nowrap;
|
||||
color: $button-color;
|
||||
font-size: $dp-font-size-normal; // size-normal
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: $button-hover-border-color;
|
||||
color: $button-hover-color;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: $button-focus-border-color;
|
||||
color: $button-focus-color;
|
||||
|
||||
&:not(:active) {
|
||||
box-shadow: $button-focus-box-shadow-size $button-focus-box-shadow-color;
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
border-color: $button-active-border-color;
|
||||
color: $button-active-color;
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.datepicker-header & {
|
||||
@include dp-header-button-common;
|
||||
|
||||
&:hover {
|
||||
background-color: darken($white, 2.5%);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
&:not(:active) {
|
||||
box-shadow: 0 0 0 0.125em rgba($white, 0.25);
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: darken($white, 5%);
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.datepicker-footer & {
|
||||
@include dp-footer-button-common;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.view-switch {
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
.prev-btn,
|
||||
.next-btn {
|
||||
padding-right: 0.375rem;
|
||||
padding-left: 0.375rem;
|
||||
width: $dp-cell-size-base;
|
||||
|
||||
&.disabled {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.datepicker-view {
|
||||
@extend %flex-container;
|
||||
|
||||
.days-of-week {
|
||||
@extend %flex-container;
|
||||
}
|
||||
|
||||
.dow {
|
||||
@extend %flex-basis-day;
|
||||
height: $dp-font-size-normal * $dp-line-height-base;
|
||||
line-height: $dp-font-size-normal * $dp-line-height-base;
|
||||
font-size: ($dp-font-size-small + $dp-font-size-normal) / 2;
|
||||
font-weight: $dp-font-weight-bold;
|
||||
}
|
||||
|
||||
.week {
|
||||
@extend %datepicker-cell-height;
|
||||
width: $dp-cell-size-base;
|
||||
color: $dp-week-color;
|
||||
font-size: $dp-font-size-small;
|
||||
|
||||
@media (max-width: $dp-cell-shrink-threshold) {
|
||||
width: $dp-cell-shrinked-width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.datepicker-grid {
|
||||
@extend %flex-container;
|
||||
@extend %flex-wrap;
|
||||
|
||||
width: $dp-cell-size-base * 7;
|
||||
|
||||
@media (max-width: $dp-cell-shrink-threshold) {
|
||||
.calendar-weeks + .days & {
|
||||
width: $dp-cell-shrinked-width * 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.datepicker-cell {
|
||||
@extend %datepicker-cell-height;
|
||||
|
||||
&:not(.disabled):hover {
|
||||
background-color: darken($dp-background-color, 2.5%);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.focused:not(.selected) {
|
||||
background-color: $dp-cell-focus-background-color;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
&,
|
||||
&:hover {
|
||||
background-color: $dp-cell-selected-background-color;
|
||||
color: $dp-cell-selected-color;
|
||||
font-weight: $dp-cell-selected-font-weight;
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
color: $dp-cell-disabled-color;
|
||||
}
|
||||
|
||||
&.prev,
|
||||
&.next {
|
||||
&:not(.disabled) {
|
||||
color: $dp-cell-prevnext-color;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
color: darken($dp-cell-selected-color, 10%);
|
||||
}
|
||||
}
|
||||
|
||||
&.highlighted:not(.selected):not(.today) {
|
||||
border-radius: 0;
|
||||
background-color: $dp-cell-highlighted-background-color;
|
||||
|
||||
&:not(.disabled):hover {
|
||||
background-color: darken($dp-cell-highlighted-background-color, 2.5%);
|
||||
}
|
||||
|
||||
&.focused {
|
||||
background-color: $dp-cell-focus-background-color;
|
||||
}
|
||||
}
|
||||
|
||||
&.today {
|
||||
&:not(.selected) {
|
||||
background-color: $dp-cell-today-background-color;
|
||||
|
||||
&:not(.disabled) {
|
||||
color: $dp-cell-today-color;
|
||||
}
|
||||
}
|
||||
|
||||
&.focused:not(.selected) {
|
||||
background-color: darken($dp-cell-today-background-color, 2.5%);
|
||||
}
|
||||
}
|
||||
|
||||
.datepicker-view .days & {
|
||||
@extend %flex-basis-day;
|
||||
}
|
||||
|
||||
.datepicker-view.datepicker-grid & {
|
||||
@extend %flex-basis-month-year;
|
||||
height: $dp-cell-size-base * 2;
|
||||
line-height: $dp-cell-size-base * 2;
|
||||
}
|
||||
}
|
||||
|
||||
.datepicker-input.in-edit {
|
||||
border-color: $dp-input-in-edit-border-color;
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
box-shadow: $dp-input-in-edit-focus-box-shadow-size rgba($dp-input-in-edit-border-color, 0.2);
|
||||
}
|
||||
}
|
|
@ -1,49 +1,43 @@
|
|||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load staticfiles %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<meta content="width=device-width, initial-scale=1.0; maximum-scale=1.0; minimum-scale=1.0;" name="viewport">
|
||||
<meta charset="UTF-8">
|
||||
<title>
|
||||
{% block title_onglet %}{% trans "Annuaire des élèves de l'ENS" %}{% endblock %}
|
||||
</title>
|
||||
<link rel="stylesheet" type="text/css" href="{% static "fiches/css/normalize.css" %}" />
|
||||
<link rel="stylesheet" type="text/css" href="{% static "fiches/css/annuaire.css" %}" />
|
||||
<link rel="stylesheet" type="text/css" href="{% static "fiches/css/datepicker.css" %}" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="aside">
|
||||
<div id="menu">
|
||||
<h1 id="title">
|
||||
{% block title %} <a href='{% url "home" %}'>{% trans "Annuaire des élèves de l'ENS" %}</a>{% endblock %}
|
||||
</h1>
|
||||
<a id="hamburger" href="javascript:void(0);" onclick="toggleMenu()"></a>
|
||||
|
||||
<div id="language_switch">
|
||||
<form action="{% url 'set_language' %}" method="post">
|
||||
{% csrf_token %}
|
||||
{% get_current_language as lang %}
|
||||
<input name="next" type="hidden" value="{{ request.get_full_path }}">
|
||||
<button class="language {% if lang == "fr" %}current-language{% endif %}" name="language" value="fr">Français</button><button class="language {% if lang == "en" %}current-language{% endif %}" name="language" value="en">English</button>
|
||||
</form>
|
||||
<a class="language french {% if get_current_language == "fr" %}current-language{% endif %}" href="/fr{{ request.get_full_path|slice:'3:' }}">Français</a>
|
||||
<a class="language english {% if get_current_language == "en" %}current-language{% endif %}" href="/en{{ request.get_full_path|slice:'3:' }}">English</a>
|
||||
</div>
|
||||
|
||||
<form id="search-area" method="post" action='{% url "home" %}'>
|
||||
{% csrf_token %}
|
||||
<input id="id_name" name="name" type="search" placeholder="{% trans "Recherche Rapide" %}">
|
||||
<input id="id_name" name="name" type="search" placeholder="Recherche Rapide">
|
||||
<button type="submit">{% trans "Rechercher" %}</button>
|
||||
</form>
|
||||
|
||||
<h1 id="title">
|
||||
{% block title %} <a href='{% url "home" %}'>{% trans "Annuaire des élèves de l'ENS" %}</a>{% endblock %}
|
||||
</h1>
|
||||
|
||||
|
||||
<div id="main-menu">
|
||||
<nav>
|
||||
<a href='{% url "home" %}'>{% trans "Accueil" %}</a>
|
||||
<a href='{% url "fiche_modif" %}'>{% trans "Modifier sa fiche d'annuaire" %}</a>
|
||||
{% if user.is_authenticated %}
|
||||
<a href='{% url "fiche" request.user.profile.user %}'>{% trans "Consulter sa fiche d'annuaire" %}</a>
|
||||
<a href='{% url "fiche_modif" %}'>{% trans "Modifier sa fiche d'annuaire" %}</a>
|
||||
<a href='{% url "fiche_reset" %}'>{% trans "Réinitialiser sa fiche d'annuaire" %}</a>
|
||||
{% endif %}
|
||||
<a href='{% url "birthday" %}'>{% trans "Anniversaires à venir" %}</a>
|
||||
</nav>
|
||||
|
@ -52,9 +46,9 @@
|
|||
<div id="account-area">
|
||||
{% if user.is_authenticated %}
|
||||
{% blocktrans %}Connecté en tant que <span class="clipper">{{ user }}</span>{% endblocktrans %}<br />
|
||||
<a href='{% url "authens:logout" %}'>{% trans "Se déconnecter" %}</a>
|
||||
<a href='{% url "cas_ng_logout" %}'>{% trans "Se déconnecter" %}</a>
|
||||
{% else %}
|
||||
<a href='{% url "authens:login" %}'>{% trans "Se connecter" %}</a>
|
||||
<a href='{% url "cas_ng_login" %}'>{% trans "Se connecter" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -79,21 +73,6 @@
|
|||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function toggleMenu() {
|
||||
var menu = document.getElementById("main-menu");
|
||||
var account = document.getElementById("account-area");
|
||||
if (menu.style.display === "none") {
|
||||
menu.style.display = "grid"
|
||||
account.style.display = "grid"
|
||||
} else {
|
||||
menu.style.display = "none"
|
||||
account.style.display = "none"
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
{% block extra_js %}{% endblock %}
|
||||
</body>
|
||||
|
||||
{% block extra_js %}{% endblock %}
|
||||
</html>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "fiches/base.html" %}
|
||||
{% load i18n l10n %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
|
@ -27,7 +27,6 @@
|
|||
<span class="value">{{ profile.pronoun }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if profile.department.exists %}
|
||||
<div class="department">
|
||||
<span class="label">{% trans "Département" %}{{ profile.department.count|pluralize }}</span>
|
||||
|
@ -35,17 +34,13 @@
|
|||
<span class="value">{% for dep in profile.department.all %}{{ dep }}{% if not forloop.last %}, {% endif %}{% endfor %}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if profile.birth_date %}
|
||||
<div class="birthdate">
|
||||
<span class="label">{% trans "Date de naissance" %}</span>
|
||||
<span class="separator"></span>
|
||||
{% localize on %}
|
||||
<span class="value">{{ profile.birth_date }}</span>
|
||||
{% endlocalize %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if profile.thurne %}
|
||||
<div class="room">
|
||||
<span class="label">{% trans "Thurne" %}</span>
|
||||
|
@ -53,76 +48,56 @@
|
|||
<span class="value">{{ profile.thurne }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if profile.phone_set.exists %}
|
||||
<div class="phone multi-entry">
|
||||
<span class="label">{% trans "Téléphone" %}{{ profile.phone_set.count|pluralize }}</span>
|
||||
<span class="separator"></span>
|
||||
<ul class="value">
|
||||
{% for p in profile.phone_set.all %}
|
||||
<li><span class="type">{{ p.name }}</span><span class="value">{{ p.number }}</span></li>
|
||||
<li><span class="type">{{ p.name }}</span><span class="value">{{ p.number }}</span></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if profile.social_set.exists %}
|
||||
<div class="social multi-entry">
|
||||
<span class="label">{{ profile.social_set.count|pluralize:_("Réseau social,Réseaux sociaux") }}</span>
|
||||
<span class="separator"></span>
|
||||
<ul class="value">
|
||||
{% for p in profile.social_set.all %}
|
||||
<li><span class="type">{{ p.name }}</span><span class="value">{{ p.content }}</span></li>
|
||||
<li><span class="type">{{ p.name }}</span><span class="value">{{ p.content }}</span></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if profile.mail_set.exists %}
|
||||
<div class="mail multi-entry">
|
||||
<span class="label">{{ profile.mail_set.count|pluralize:_("Mail,Mails") }}</span>
|
||||
<span class="separator"></span>
|
||||
<ul class="value">
|
||||
{% for p in profile.mail_set.all %}
|
||||
<li><span class="type">{{ p.name }}</span><span class="value">{{ p.mail }}</span></li>
|
||||
<li><span class="type">{{ p.name }}</span><span class="value">{{ p.mail }}</span></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if profile.address_set.exists %}
|
||||
<div class="address multi-entry">
|
||||
<span class="label">{{ profile.address_set.count|pluralize:_("Adresse,Adresses") }}</span>
|
||||
<span class="separator"></span>
|
||||
<ul class="value">
|
||||
{% for p in profile.address_set.all %}
|
||||
<li><span class="type">{{ p.name }}</span><span class="value">{{ p.content|linebreaksbr }}</span></li>
|
||||
<li><span class="type">{{ p.name }}</span><span class="value">{{ p.content }}</span></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if profile.past_studies %}
|
||||
<div class="studies">
|
||||
<span class="label">{% trans "Études passées" %}</span>
|
||||
<span class="separator"></span>
|
||||
<span class="value">{{ profile.past_studies|linebreaksbr }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if profile.experiences %}
|
||||
<div class="studies">
|
||||
<span class="label">{% trans "Expériences passées" %}</span>
|
||||
<span class="separator"></span>
|
||||
<span class="value">{{ profile.experiences|linebreaksbr }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if profile.text_field %}
|
||||
<div class="free-text">
|
||||
<span class="label">{% trans "Champ libre" %}</span>
|
||||
<p class="value">{{ profile.text_field|linebreaksbr }}</p>
|
||||
<p class="value">{{ profile.text_field }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{% extends "fiches/base.html" %}
|
||||
{% load i18n static %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
|
@ -20,10 +21,6 @@
|
|||
<label for="id_pronoun">{% trans "Pronom(s) utilisé(s) :" %}</label>
|
||||
{{ form.pronoun }}
|
||||
</div>
|
||||
<div class="form-entry">
|
||||
<label for="id_birth_date">{% trans "Date de naissance :" %}</label>
|
||||
{{ form.birth_date }}
|
||||
</div>
|
||||
<div class="form-entry">
|
||||
<label for="id_picture">{% trans "Photo :" %}</label>
|
||||
<div id="photo-edit-form" class="wide-form-entry">
|
||||
|
@ -32,6 +29,8 @@
|
|||
<a href="{{ form.instance.picture.url }}">
|
||||
<img src="{{ form.instance.picture.url }}">
|
||||
</a>
|
||||
{% else %}
|
||||
<img>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="photo-edit-controls">
|
||||
|
@ -57,38 +56,46 @@
|
|||
{{ form.promotion }}
|
||||
</div>
|
||||
<div class="form-entry">
|
||||
<label for="id_past_studies">{% trans "Études passées :" %}</label>
|
||||
<div id="free-text-edit-form" class="wide-form-entry">
|
||||
{{ form.past_studies }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-entry">
|
||||
<label for="id_experiences">{% trans "Expériences :" %}</label>
|
||||
<div id="free-text-edit-form" class="wide-form-entry">
|
||||
{{ form.experiences }}
|
||||
</div>
|
||||
<label for="id_birth_date">{% trans "Date de naissance :" %}</label>
|
||||
{{ form.birth_date }}
|
||||
</div>
|
||||
<div class="form-entry">
|
||||
<label for="id_thurne">{% trans "Thurne :" %}</label>
|
||||
{{ form.thurne }}
|
||||
</div>
|
||||
<div class="form-entry multi-entry" data-type-placeholder="{% trans "Personnel" %}" data-value-placeholder="{% trans "0612345678" %}">
|
||||
<label for="id_phone">{% trans "Numéro(s) de téléphone :" %}</label>
|
||||
<div
|
||||
class="form-entry multi-entry"
|
||||
data-type-placeholder="{% trans "Personnel" %}"
|
||||
data-value-placeholder="{% trans "0612345678" %}"
|
||||
>
|
||||
<label for="id_phone">{% trans "Numéro(s) de téléphone :" %}</label>
|
||||
{% trans "Ajouter un numéro" as add_number %}
|
||||
{% include "fiches/multientry.html" with formset=phone_form new_entry_text=add_number %}
|
||||
</div>
|
||||
<div class="form-entry multi-entry" data-type-placeholder="{% trans "InstaTok" %}" data-value-placeholder="{% trans "mon_profil_instatok" %}">
|
||||
<div
|
||||
class="form-entry multi-entry"
|
||||
data-type-placeholder="{% trans "InstaTok" %}"
|
||||
data-value-placeholder="{% trans "mon_profil_instatok" %}"
|
||||
>
|
||||
<label for="id_social">{% trans "Réseaux sociaux :" %}</label>
|
||||
{% trans "Ajouter un réseau social" as add_social %}
|
||||
{% include "fiches/multientry.html" with formset=social_form new_entry_text=add_social %}
|
||||
</div>
|
||||
<div class="form-entry multi-entry" data-type-placeholder="{% trans "Professionelle" %}" data-value-placeholder="{% trans "moi@ens.fr" %}">
|
||||
<label for="id_mail">{% trans "Mail(s) :" %}</label>
|
||||
<div
|
||||
class="form-entry multi-entry"
|
||||
data-type-placeholder="{% trans "Professionelle" %}"
|
||||
data-value-placeholder="{% trans "moi@ens.fr" %}"
|
||||
>
|
||||
<label for="id_mail">{% trans "Mail(s):" %}</label>
|
||||
{% trans "Ajouter un email" as add_mail %}
|
||||
{% include "fiches/multientry.html" with formset=mail_form new_entry_text=add_mail %}
|
||||
</div>
|
||||
<div class="form-entry multi-entry" data-type-placeholder="{% trans "Bureau" %}" data-value-placeholder="{% trans "45 rue d'Ulm" %}">
|
||||
<label for="id_address">{% trans "Adresse(s) :" %}</label>
|
||||
<div
|
||||
class="form-entry multi-entry"
|
||||
data-type-placeholder="{% trans "Bureau" %}"
|
||||
data-value-placeholder="{% trans "45 rue d'Ulm" %}"
|
||||
>
|
||||
<label for="id_address">{% trans "Adresse(s):" %}</label>
|
||||
{% trans "Ajouter une adresse" as add_address %}
|
||||
{% include "fiches/multientry.html" with formset=address_form new_entry_text=add_address %}
|
||||
</div>
|
||||
|
@ -113,14 +120,5 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="{% static "fiches/js/forms.js" %}"></script>
|
||||
<script src="{% static "fiches/js/datepicker.min.js" %}"></script>
|
||||
<script>
|
||||
const elem = document.querySelector('input[name="birth_date"]');
|
||||
const datepicker = new Datepicker(elem, {
|
||||
format: "yyyy-mm-dd",
|
||||
defaultViewDate: "{{ default_birth_date }}",
|
||||
});
|
||||
|
||||
</script>
|
||||
<script type="text/javascript" src="{% static "fiches/js/forms.js" %}"></script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<label for="id_year">{% trans "Promotion :" %}</label>
|
||||
{{ form.year }}
|
||||
|
||||
<label for="id_department">{% trans "Départment :" %}</label>
|
||||
<label for="id_department">{% trans "Department :" %}</label>
|
||||
{{ form.department }}
|
||||
<input type="submit" value="{% trans "Recherche" %}">
|
||||
</form>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{{ formset.non_field_errors }}
|
||||
{{ formset.non_field_errors }}
|
||||
{{ formset.management_form }}
|
||||
{% for form in formset %}
|
||||
{{ form.non_field_errors }}
|
||||
|
@ -10,16 +10,16 @@
|
|||
{% for field in form.hidden_fields %}
|
||||
{{field}}
|
||||
{% endfor %}
|
||||
<button type=button class="remove-button"></button>
|
||||
<button type=button" class="remove-button"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="form-sub-entry-template">
|
||||
{% for field in formset.empty_form.visible_fields %}
|
||||
{{field}}
|
||||
{% endfor %}
|
||||
{% for field in formset.empty_form.hidden_fields %}
|
||||
{{field}}
|
||||
{% endfor %}
|
||||
{% for field in form.visible_fields %}
|
||||
{{field}}
|
||||
{% endfor %}
|
||||
{% for field in form.hidden_fields %}
|
||||
{{field}}
|
||||
{% endfor %}
|
||||
<button type="button" class="remove-button"></button>
|
||||
</div>
|
||||
<button type="button" class="add-sub-entry-button">{{ new_entry_text }}</button>
|
|
@ -1,19 +0,0 @@
|
|||
{% extends "fiches/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
|
||||
<div class="content" id="content-reset">
|
||||
<h2>{% trans "Réinitialiser ma fiche annuaire" %}</h2>
|
||||
<p id="warning">{% trans "Votre fiche annuaire sera réinitialisée. Votre nom, département et promotion seront récupérés de l'annuaire de l'administration." %}</p>
|
||||
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
<input type="submit" name="reset" id="reset" value="{% trans "Réinitialiser" %}">
|
||||
<input type="submit" name="abort" id="abort" value="{% trans "Annuler" %}">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
|
@ -1,11 +1,7 @@
|
|||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path("", views.HomeView.as_view(), name="home"),
|
||||
path("fiche/_edit", views.EditView.as_view(), name="fiche_modif"),
|
||||
path("fiche/_reset", views.ResetView.as_view(), name="fiche_reset"),
|
||||
path("fiche/<slug:slug>", views.FicheView.as_view(), name="fiche"),
|
||||
path("birthday", views.BirthdayView.as_view(), name="birthday"),
|
||||
path("edit", views.EditView.as_view(), name="fiche_modif"),
|
||||
path("<user>", views.FicheView.as_view(), name="fiche"),
|
||||
]
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
from .management.commands._ldap import ClipperLDAP
|
||||
|
||||
|
||||
def get_ldap_infos(clipper_login):
|
||||
ldap = ClipperLDAP()
|
||||
try:
|
||||
res = ldap.search("(uid={})".format(clipper_login))
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
if not res:
|
||||
return None
|
||||
|
||||
if len(res) != 1:
|
||||
raise RuntimeError("LDAP returned too many results: {}".format(res))
|
||||
(res,) = res
|
||||
promo, dept = ldap.parse_dept(ldap.extract_ldap_info(res, "homeDirectory"))
|
||||
|
||||
return {
|
||||
"name": ldap.extract_ldap_info(res, "cn"),
|
||||
"promo": promo,
|
||||
"dept": dept,
|
||||
}
|
177
fiches/views.py
177
fiches/views.py
|
@ -1,34 +1,30 @@
|
|||
from datetime import date, timedelta
|
||||
|
||||
from django.shortcuts import render
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.core.mail import send_mail
|
||||
from django.db.models import DateTimeField, Q, Value
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.template.loader import render_to_string
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from fiches.models import Profile, Phone, Social, Mail, Address
|
||||
from fiches.forms import ProfileForm, SearchForm, PhoneFormSet, SocialFormSet, MailFormSet, AddressFormSet
|
||||
from django.forms import formset_factory
|
||||
from django.forms.models import model_to_dict
|
||||
from django.urls import reverse
|
||||
from django.db.models import Q
|
||||
from django.utils import timezone
|
||||
from django.utils.decorators import method_decorator
|
||||
from datetime import timedelta
|
||||
from django.core.mail import send_mail
|
||||
from django.template.loader import render_to_string
|
||||
from django.views.generic.detail import DetailView
|
||||
from django.views.generic.edit import FormView, UpdateView
|
||||
from django.views.generic.list import ListView
|
||||
|
||||
from fiches.forms import (
|
||||
AddressFormSet,
|
||||
MailFormSet,
|
||||
PhoneFormSet,
|
||||
ProfileForm,
|
||||
SearchForm,
|
||||
SocialFormSet,
|
||||
)
|
||||
from fiches.models import Department, Profile
|
||||
from fiches.utils import get_ldap_infos
|
||||
from django.views.generic.edit import FormView, UpdateView
|
||||
from django.http import HttpResponseRedirect
|
||||
|
||||
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
class FicheView(DetailView):
|
||||
model = Profile
|
||||
template_name = "fiches/fiche.html"
|
||||
slug_field = "user__username"
|
||||
|
||||
def get_object(self):
|
||||
return get_object_or_404(Profile, user__username=self.kwargs.get("user"))
|
||||
|
||||
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
|
@ -46,14 +42,11 @@ class EditView(UpdateView):
|
|||
mail_form = MailFormSet(instance=self.object)
|
||||
address_form = AddressFormSet(instance=self.object)
|
||||
return self.render_to_response(
|
||||
self.get_context_data(
|
||||
form=form,
|
||||
phone_form=phone_form,
|
||||
social_form=social_form,
|
||||
mail_form=mail_form,
|
||||
address_form=address_form,
|
||||
)
|
||||
)
|
||||
self.get_context_data(form=form,
|
||||
phone_form=phone_form,
|
||||
social_form=social_form,
|
||||
mail_form=mail_form,
|
||||
address_form=address_form))
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
|
@ -63,21 +56,12 @@ class EditView(UpdateView):
|
|||
social_form = SocialFormSet(self.request.POST, instance=self.object)
|
||||
mail_form = MailFormSet(self.request.POST, instance=self.object)
|
||||
address_form = AddressFormSet(self.request.POST, instance=self.object)
|
||||
if (
|
||||
form.is_valid()
|
||||
and phone_form.is_valid()
|
||||
and social_form.is_valid()
|
||||
and mail_form.is_valid()
|
||||
and address_form.is_valid()
|
||||
):
|
||||
return self.form_valid(
|
||||
form, phone_form, social_form, mail_form, address_form
|
||||
)
|
||||
if (form.is_valid() and phone_form.is_valid() and social_form.is_valid()
|
||||
and mail_form.is_valid() and address_form.is_valid()):
|
||||
return self.form_valid(form, phone_form, social_form, mail_form, address_form)
|
||||
else:
|
||||
return self.form_invalid(
|
||||
form, phone_form, social_form, mail_form, address_form
|
||||
)
|
||||
|
||||
return self.form_invalid(form, phone_form, social_form, mail_form, address_form)
|
||||
|
||||
def form_valid(self, form, phone_form, social_form, mail_form, address_form):
|
||||
self.object = form.save()
|
||||
phone_form.save()
|
||||
|
@ -85,34 +69,21 @@ class EditView(UpdateView):
|
|||
mail_form.save()
|
||||
address_form.save()
|
||||
send_mail(
|
||||
"Fiche annuaire modifée",
|
||||
render_to_string(
|
||||
"fiches/mail/mail_modif.txt", {"profile": self.get_object()}
|
||||
),
|
||||
"klub-dev@ens.psl.eu",
|
||||
["{}@clipper.ens.psl.eu".format(self.get_object().user.username)],
|
||||
fail_silently=False,
|
||||
)
|
||||
"Fiche annuaire modifée",
|
||||
render_to_string("fiches/mail/mail_modif.txt", {"profile": self.get_object()}),
|
||||
"klub-dev@ens.psl.eu",
|
||||
["{}@clipper.ens.psl.eu".format(self.get_object().user.username)],
|
||||
fail_silently=False,
|
||||
)
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
def form_invalid(self, form, phone_form, social_form, mail_form, address_form):
|
||||
return self.render_to_response(
|
||||
self.get_context_data(
|
||||
form=form,
|
||||
phone_form=phone_form,
|
||||
social_form=social_form,
|
||||
mail_form=mail_form,
|
||||
address_form=address_form,
|
||||
)
|
||||
)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
today = date.today()
|
||||
context["default_birth_date"] = today.replace(
|
||||
year=(today.year - 20)
|
||||
).isoformat()
|
||||
return context
|
||||
self.get_context_data(form=form,
|
||||
phone_form=phone_form,
|
||||
social_form=social_form,
|
||||
mail_form=mail_form,
|
||||
address_form=address_form))
|
||||
|
||||
def get_object(self):
|
||||
return self.request.user.profile
|
||||
|
@ -128,21 +99,10 @@ class HomeView(FormView):
|
|||
form_class = SearchForm
|
||||
|
||||
def form_valid(self, form):
|
||||
name = form.cleaned_data["name"]
|
||||
promotion = form.cleaned_data["year"]
|
||||
depts = form.cleaned_data["department"]
|
||||
result = Profile.objects.filter(
|
||||
Q(full_name__icontains=name)
|
||||
| Q(nickname__icontains=name)
|
||||
| Q(user__username__icontains=name)
|
||||
)
|
||||
|
||||
if depts:
|
||||
result = result.filter(department__in=depts)
|
||||
|
||||
if promotion:
|
||||
result = result.filter(promotion=promotion)
|
||||
|
||||
Q(full_name__icontains=form.cleaned_data["name"])
|
||||
| Q(nickname__icontains=form.cleaned_data["name"])
|
||||
)
|
||||
return self.render_to_response(self.get_context_data(result=result))
|
||||
|
||||
|
||||
|
@ -151,60 +111,19 @@ class BirthdayView(ListView):
|
|||
model = Profile
|
||||
template_name = "fiches/birthday.html"
|
||||
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
today = timezone.now()
|
||||
context["result"] = list(
|
||||
Profile.objects.filter(
|
||||
birth_date__day=today.day, birth_date__month=today.month
|
||||
).annotate(day=Value(today, output_field=DateTimeField()))
|
||||
context['result'] = list(
|
||||
Profile.objects.filter(birth_date__day=today.day, birth_date__month=today.month)
|
||||
)
|
||||
for _ in range(1, 7):
|
||||
for i in range(1, 7):
|
||||
today = today + timedelta(days=1)
|
||||
context["result"] += list(
|
||||
context['result'] += list(
|
||||
Profile.objects.filter(
|
||||
birth_date__day=today.day, birth_date__month=today.month
|
||||
).annotate(day=Value(today, output_field=DateTimeField()))
|
||||
)
|
||||
)
|
||||
return context
|
||||
|
||||
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
class ResetView(UpdateView):
|
||||
model = Profile
|
||||
template_name = "fiches/reset.html"
|
||||
fields = []
|
||||
success_url = reverse_lazy("fiche_modif")
|
||||
|
||||
def get_object(self):
|
||||
return self.request.user.profile
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
if "reset" in request.POST:
|
||||
# On réinitialise le profil
|
||||
profile = self.get_object()
|
||||
base_infos = get_ldap_infos(profile.user.cas_account.cas_login)
|
||||
|
||||
# On supprime les trucs inutiles
|
||||
profile.phone_set.all().delete()
|
||||
profile.social_set.all().delete()
|
||||
profile.mail_set.all().delete()
|
||||
profile.address_set.all().delete()
|
||||
profile.nickname = ""
|
||||
profile.pronoun = ""
|
||||
profile.birth_date = None
|
||||
profile.past_studies = ""
|
||||
profile.experiences = ""
|
||||
profile.thurne = ""
|
||||
profile.text_field = ""
|
||||
profile.picture.delete()
|
||||
|
||||
# On réinitialise avec les infos du LDAP
|
||||
if base_infos is not None:
|
||||
profile.full_name = base_infos["name"]
|
||||
profile.department.clear()
|
||||
profile.department.add(Department.objects.get(name=base_infos["dept"]))
|
||||
profile.promotion = base_infos["promo"]
|
||||
profile.save()
|
||||
|
||||
return HttpResponseRedirect(self.success_url)
|
||||
return context
|
Binary file not shown.
|
@ -1,398 +0,0 @@
|
|||
# Annuaire translations
|
||||
# Copyright (C) 2021 Klub Dev ENS
|
||||
# This file is distributed under the same license as the annuaire package.
|
||||
# Klub Dev ENS <klub-dev@ens.fr>, 2021.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-02-06 12:02+0000\n"
|
||||
"PO-Revision-Date: 2021-10-08 09:36+0200\n"
|
||||
"Last-Translator: Test Translator <test@translator>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: en\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Poedit 3.0\n"
|
||||
|
||||
#: fiches/forms.py:20
|
||||
msgid "Nom/Surnom"
|
||||
msgstr "Name/Nickname"
|
||||
|
||||
#: fiches/forms.py:21
|
||||
msgid "Promotion"
|
||||
msgstr "Entrance year"
|
||||
|
||||
#: fiches/forms.py:33
|
||||
msgid "Tous les champs sont vides"
|
||||
msgstr "All fields are empty"
|
||||
|
||||
#: fiches/models.py:11
|
||||
msgid "utilisateur"
|
||||
msgstr "user"
|
||||
|
||||
#: fiches/models.py:14
|
||||
msgid "nom"
|
||||
msgstr "name"
|
||||
|
||||
#: fiches/models.py:15
|
||||
msgid "surnom"
|
||||
msgstr "nickname"
|
||||
|
||||
#: fiches/models.py:17
|
||||
msgid "pronom(s) utilisé(s)"
|
||||
msgstr "pronoun(s)"
|
||||
|
||||
#: fiches/models.py:20
|
||||
msgid "photo"
|
||||
msgstr "photo"
|
||||
|
||||
#: fiches/models.py:22
|
||||
msgid "département"
|
||||
msgstr "department"
|
||||
|
||||
#: fiches/models.py:24
|
||||
msgid "promotion"
|
||||
msgstr "entrance year"
|
||||
|
||||
#: fiches/models.py:27
|
||||
msgid "date de naissance"
|
||||
msgstr "birth date"
|
||||
|
||||
#: fiches/models.py:29
|
||||
msgid "études passées"
|
||||
msgstr "past studies"
|
||||
|
||||
#: fiches/models.py:30
|
||||
msgid "expériences"
|
||||
msgstr "experiences"
|
||||
|
||||
#: fiches/models.py:31
|
||||
msgid "thurne"
|
||||
msgstr "room"
|
||||
|
||||
#: fiches/models.py:32
|
||||
msgid "champ libre"
|
||||
msgstr "free space"
|
||||
|
||||
#: fiches/models.py:34
|
||||
msgid "apparaître sur l'annuaire papier ?"
|
||||
msgstr "appear on the paper directory?"
|
||||
|
||||
#: fiches/models.py:37
|
||||
msgid "conserver la fiche annuaire ?"
|
||||
msgstr "keep the directory record?"
|
||||
|
||||
#: fiches/models.py:49
|
||||
msgid "nom du département"
|
||||
msgstr "department name"
|
||||
|
||||
#: fiches/models.py:58 fiches/models.py:69 fiches/models.py:80
|
||||
#: fiches/models.py:91
|
||||
msgid "profil"
|
||||
msgstr "profile"
|
||||
|
||||
#: fiches/models.py:60 fiches/models.py:71 fiches/models.py:82
|
||||
#: fiches/models.py:93
|
||||
msgid "type"
|
||||
msgstr "type"
|
||||
|
||||
#: fiches/models.py:61
|
||||
msgid "numéro"
|
||||
msgstr "number"
|
||||
|
||||
#: fiches/models.py:72
|
||||
msgid "contenu"
|
||||
msgstr "content"
|
||||
|
||||
#: fiches/models.py:83
|
||||
msgid "adresse mail"
|
||||
msgstr "e-mail"
|
||||
|
||||
#: fiches/models.py:94
|
||||
msgid "adresse"
|
||||
msgstr "address"
|
||||
|
||||
#: fiches/templates/fiches/base.html:10 fiches/templates/fiches/base.html:21
|
||||
msgid "Annuaire des élèves de l'ENS"
|
||||
msgstr "ENS student directory"
|
||||
|
||||
#: fiches/templates/fiches/base.html:36
|
||||
msgid "Recherche Rapide"
|
||||
msgstr "Quick Search"
|
||||
|
||||
#: fiches/templates/fiches/base.html:37
|
||||
msgid "Rechercher"
|
||||
msgstr "Search"
|
||||
|
||||
#: fiches/templates/fiches/base.html:42
|
||||
msgid "Accueil"
|
||||
msgstr "Home"
|
||||
|
||||
#: fiches/templates/fiches/base.html:44
|
||||
msgid "Consulter sa fiche d'annuaire"
|
||||
msgstr "View your directory record"
|
||||
|
||||
#: fiches/templates/fiches/base.html:45
|
||||
msgid "Modifier sa fiche d'annuaire"
|
||||
msgstr "Edit your directory record"
|
||||
|
||||
#: fiches/templates/fiches/base.html:46
|
||||
msgid "Réinitialiser sa fiche d'annuaire"
|
||||
msgstr "Reset your directory record"
|
||||
|
||||
#: fiches/templates/fiches/base.html:48
|
||||
msgid "Anniversaires à venir"
|
||||
msgstr "Upcoming birthdays"
|
||||
|
||||
#: fiches/templates/fiches/base.html:54
|
||||
#, python-format
|
||||
msgid "Connecté en tant que <span class=\"clipper\">%(user)s</span>"
|
||||
msgstr "Connected as <span class=\"clipper\">%(user)s</span>"
|
||||
|
||||
#: fiches/templates/fiches/base.html:55
|
||||
msgid "Se déconnecter"
|
||||
msgstr "Log out"
|
||||
|
||||
#: fiches/templates/fiches/base.html:57
|
||||
msgid "Se connecter"
|
||||
msgstr "Log in"
|
||||
|
||||
#: fiches/templates/fiches/base.html:73
|
||||
msgid "Crée par KDENS · Propulsé par Django"
|
||||
msgstr "Created by KDENS · Powered by Django"
|
||||
|
||||
#: fiches/templates/fiches/base.html:77
|
||||
msgid "Page des élèves"
|
||||
msgstr "Students' page"
|
||||
|
||||
#: fiches/templates/fiches/base.html:78
|
||||
msgid "Contacter l'équipe annuaire"
|
||||
msgstr "Contact the directory team"
|
||||
|
||||
#: fiches/templates/fiches/birthday.html:8
|
||||
msgid "Anniversaires"
|
||||
msgstr "Birthdays"
|
||||
|
||||
#: fiches/templates/fiches/birthday.html:15
|
||||
msgid "ans"
|
||||
msgstr "years"
|
||||
|
||||
#: fiches/templates/fiches/fiche.html:25
|
||||
msgid "Pronom(s) utilisé(s)"
|
||||
msgstr "Pronoun(s)"
|
||||
|
||||
#: fiches/templates/fiches/fiche.html:33
|
||||
msgid "Département"
|
||||
msgstr "Department"
|
||||
|
||||
#: fiches/templates/fiches/fiche.html:41
|
||||
msgid "Date de naissance"
|
||||
msgstr "Birth date"
|
||||
|
||||
#: fiches/templates/fiches/fiche.html:51
|
||||
msgid "Thurne"
|
||||
msgstr "Room"
|
||||
|
||||
#: fiches/templates/fiches/fiche.html:59
|
||||
msgid "Téléphone"
|
||||
msgstr "Phone number"
|
||||
|
||||
#: fiches/templates/fiches/fiche.html:71
|
||||
msgid "Réseau social,Réseaux sociaux"
|
||||
msgstr "Social network,Social networks"
|
||||
|
||||
#: fiches/templates/fiches/fiche.html:83
|
||||
msgid "Mail,Mails"
|
||||
msgstr "E-mail,E-mails"
|
||||
|
||||
#: fiches/templates/fiches/fiche.html:95
|
||||
msgid "Adresse,Adresses"
|
||||
msgstr "Address,Addresses"
|
||||
|
||||
#: fiches/templates/fiches/fiche.html:107
|
||||
msgid "Études passées"
|
||||
msgstr "Past studies"
|
||||
|
||||
#: fiches/templates/fiches/fiche.html:115
|
||||
msgid "Expériences passées"
|
||||
msgstr "Experiences"
|
||||
|
||||
#: fiches/templates/fiches/fiche.html:124
|
||||
msgid "Champ libre"
|
||||
msgstr "Free space"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:7
|
||||
msgid "Modifier ma page d'annuaire"
|
||||
msgstr "Edit my directory record"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:12
|
||||
msgid "Nom :"
|
||||
msgstr "Name:"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:16
|
||||
msgid "Surnom :"
|
||||
msgstr "Nickname:"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:20
|
||||
msgid "Pronom(s) utilisé(s) :"
|
||||
msgstr "Pronoun(s):"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:24
|
||||
msgid "Date de naissance :"
|
||||
msgstr "Birth date:"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:28
|
||||
msgid "Photo :"
|
||||
msgstr "Photo:"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:40
|
||||
msgid "Effacer (cochez la case) :"
|
||||
msgstr "Delete (check the box):"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:45
|
||||
msgid "Nouvelle photo :"
|
||||
msgstr "New photo:"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:52
|
||||
msgid "Département :"
|
||||
msgstr "Department:"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:56
|
||||
#: fiches/templates/fiches/home.html:14
|
||||
msgid "Promotion :"
|
||||
msgstr "Entrance year:"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:60
|
||||
msgid "Études passées :"
|
||||
msgstr "Past studies:"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:66
|
||||
msgid "Expériences :"
|
||||
msgstr "Experiences:"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:72
|
||||
msgid "Thurne :"
|
||||
msgstr "Room :"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:75
|
||||
msgid "Personnel"
|
||||
msgstr "Private"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:75
|
||||
msgid "0612345678"
|
||||
msgstr "0612345678"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:76
|
||||
msgid "Numéro(s) de téléphone :"
|
||||
msgstr "Phone number(s):"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:77
|
||||
msgid "Ajouter un numéro"
|
||||
msgstr "Add a phone number"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:80
|
||||
msgid "InstaTok"
|
||||
msgstr "InstaTok"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:80
|
||||
msgid "mon_profil_instatok"
|
||||
msgstr "my_instatok_profile"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:81
|
||||
msgid "Réseaux sociaux :"
|
||||
msgstr "Social networks:"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:82
|
||||
msgid "Ajouter un réseau social"
|
||||
msgstr "Add a social network"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:85
|
||||
msgid "Professionelle"
|
||||
msgstr "Professional"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:85
|
||||
msgid "moi@ens.fr"
|
||||
msgstr "me@ens.fr"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:86
|
||||
msgid "Mail(s) :"
|
||||
msgstr "E-mail(s):"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:87
|
||||
msgid "Ajouter un email"
|
||||
msgstr "Add an e-mail"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:90
|
||||
msgid "Bureau"
|
||||
msgstr "Office"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:90
|
||||
msgid "45 rue d'Ulm"
|
||||
msgstr "45 rue d'Ulm"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:91
|
||||
msgid "Adresse(s) :"
|
||||
msgstr "Address(es):"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:92
|
||||
msgid "Ajouter une adresse"
|
||||
msgstr "Add an address"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:96
|
||||
msgid "Champ libre :"
|
||||
msgstr "Free space:"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:102
|
||||
msgid "Apparaître sur l'annuaire papier ?"
|
||||
msgstr "Appear on the paper directory?"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:106
|
||||
msgid "Conserver la fiche annuaire ?"
|
||||
msgstr "Keep the directory record?"
|
||||
|
||||
#: fiches/templates/fiches/fiches_modif.html:109
|
||||
msgid "Enregistrer"
|
||||
msgstr "Save"
|
||||
|
||||
#: fiches/templates/fiches/home.html:8
|
||||
msgid "Chercher quelqu'un·e dans l'annuaire"
|
||||
msgstr "Search for someone in the directory"
|
||||
|
||||
#: fiches/templates/fiches/home.html:11
|
||||
msgid "Nom/Surnom :"
|
||||
msgstr "Name/Nickname:"
|
||||
|
||||
#: fiches/templates/fiches/home.html:17
|
||||
msgid "Départment :"
|
||||
msgstr "Department:"
|
||||
|
||||
#: fiches/templates/fiches/home.html:19
|
||||
msgid "Recherche"
|
||||
msgstr "Search"
|
||||
|
||||
#: fiches/templates/fiches/reset.html:8
|
||||
msgid "Réinitialiser ma fiche annuaire"
|
||||
msgstr "Reset your directory record"
|
||||
|
||||
#: fiches/templates/fiches/reset.html:9
|
||||
msgid ""
|
||||
"Votre fiche annuaire sera réinitialisée. Votre nom, département et promotion "
|
||||
"seront récupérés de l'annuaire de l'administration."
|
||||
msgstr ""
|
||||
"Your directory record will be reset. Your name, department and promotion "
|
||||
"will be copied from the administration directory."
|
||||
|
||||
#: fiches/templates/fiches/reset.html:13
|
||||
msgid "Réinitialiser"
|
||||
msgstr "Reset"
|
||||
|
||||
#: fiches/templates/fiches/reset.html:14
|
||||
msgid "Annuler"
|
||||
msgstr "Cancel"
|
||||
|
||||
#~ msgid "Department :"
|
||||
#~ msgstr "Department:"
|
|
@ -5,7 +5,7 @@ import sys
|
|||
|
||||
|
||||
def main():
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings")
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "annuaire.settings.local")
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
# Generated by npins. Do not modify; will be overwritten regularly
|
||||
let
|
||||
data = builtins.fromJSON (builtins.readFile ./sources.json);
|
||||
version = data.version;
|
||||
|
||||
mkSource =
|
||||
spec:
|
||||
assert spec ? type;
|
||||
let
|
||||
path =
|
||||
if spec.type == "Git" then
|
||||
mkGitSource spec
|
||||
else if spec.type == "GitRelease" then
|
||||
mkGitSource spec
|
||||
else if spec.type == "PyPi" then
|
||||
mkPyPiSource spec
|
||||
else if spec.type == "Channel" then
|
||||
mkChannelSource spec
|
||||
else
|
||||
builtins.throw "Unknown source type ${spec.type}";
|
||||
in
|
||||
spec // { outPath = path; };
|
||||
|
||||
mkGitSource =
|
||||
{
|
||||
repository,
|
||||
revision,
|
||||
url ? null,
|
||||
hash,
|
||||
branch ? null,
|
||||
...
|
||||
}:
|
||||
assert repository ? type;
|
||||
# At the moment, either it is a plain git repository (which has an url), or it is a GitHub/GitLab repository
|
||||
# In the latter case, there we will always be an url to the tarball
|
||||
if url != null then
|
||||
(builtins.fetchTarball {
|
||||
inherit url;
|
||||
sha256 = hash; # FIXME: check nix version & use SRI hashes
|
||||
})
|
||||
else
|
||||
assert repository.type == "Git";
|
||||
let
|
||||
urlToName =
|
||||
url: rev:
|
||||
let
|
||||
matched = builtins.match "^.*/([^/]*)(\\.git)?$" repository.url;
|
||||
|
||||
short = builtins.substring 0 7 rev;
|
||||
|
||||
appendShort = if (builtins.match "[a-f0-9]*" rev) != null then "-${short}" else "";
|
||||
in
|
||||
"${if matched == null then "source" else builtins.head matched}${appendShort}";
|
||||
name = urlToName repository.url revision;
|
||||
in
|
||||
builtins.fetchGit {
|
||||
url = repository.url;
|
||||
rev = revision;
|
||||
inherit name;
|
||||
# hash = hash;
|
||||
};
|
||||
|
||||
mkPyPiSource =
|
||||
{ url, hash, ... }:
|
||||
builtins.fetchurl {
|
||||
inherit url;
|
||||
sha256 = hash;
|
||||
};
|
||||
|
||||
mkChannelSource =
|
||||
{ url, hash, ... }:
|
||||
builtins.fetchTarball {
|
||||
inherit url;
|
||||
sha256 = hash;
|
||||
};
|
||||
in
|
||||
if version == 3 then
|
||||
builtins.mapAttrs (_: mkSource) data.pins
|
||||
else
|
||||
throw "Unsupported format version ${toString version} in sources.json. Try running `npins upgrade`"
|
|
@ -1,22 +0,0 @@
|
|||
{
|
||||
"pins": {
|
||||
"nix-pkgs": {
|
||||
"type": "Git",
|
||||
"repository": {
|
||||
"type": "Git",
|
||||
"url": "https://git.hubrecht.ovh/hubrecht/nix-pkgs"
|
||||
},
|
||||
"branch": "main",
|
||||
"revision": "3e731378f3984313ef902c5e5a49e002e6e2c27e",
|
||||
"url": null,
|
||||
"hash": "1vy2dj9fyy653w6idvi1r73s0nd2a332a1xkppddjip6rk0i030p"
|
||||
},
|
||||
"nixpkgs": {
|
||||
"type": "Channel",
|
||||
"name": "nixpkgs-unstable",
|
||||
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-24.11pre691017.b69de56fac8c/nixexprs.tar.xz",
|
||||
"hash": "0z32pj0lh5ng2a6cn0qfmka8cynnygckn5615mkaxq2aplkvgzx3"
|
||||
}
|
||||
},
|
||||
"version": 3
|
||||
}
|
1141
poetry.lock
generated
1141
poetry.lock
generated
File diff suppressed because it is too large
Load diff
86
provisioning/bootstrap.sh
Normal file
86
provisioning/bootstrap.sh
Normal file
|
@ -0,0 +1,86 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Stop if an error is encountered
|
||||
set -e
|
||||
|
||||
PROJECTNAME=$(basename $1)
|
||||
SETTINGS_MODULE="$PROJECTNAME.settings.vagrant"
|
||||
|
||||
# Configuration de la base de données. Le mot de passe est constant car c'est
|
||||
# pour une installation de dév locale qui ne sera accessible que depuis la
|
||||
# machine virtuelle.
|
||||
DBUSER=$PROJECTNAME
|
||||
DBNAME=$PROJECTNAME
|
||||
DBPASSWD="O1LxCADDA6Px5SiKvifjvdp3DSjfbp"
|
||||
|
||||
# Installation de paquets utiles.
|
||||
# Installe les paquets mentionnés dans `package.list`, en excluant les lignes
|
||||
# commençant par #.
|
||||
apt-get update && apt-get upgrade -y
|
||||
apt-get install -y $(awk '! /^ *#/' /vagrant/provisioning/package.list)
|
||||
|
||||
# Postgresql
|
||||
# On teste si la db existe déjà pour ne pas essayer de la recréer
|
||||
DB_EXISTS=$(sudo -u postgres psql -lqt | cut -d \| -f 1 | grep -cw $DBNAME || true)
|
||||
if [ $DB_EXISTS -eq 0 ]
|
||||
then
|
||||
sudo -u postgres createdb $DBNAME
|
||||
sudo -u postgres createuser -SdR $DBUSER
|
||||
sudo -u postgres psql -c "ALTER USER $DBUSER WITH PASSWORD '$DBPASSWD';"
|
||||
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DBNAME TO $DBUSER;"
|
||||
fi
|
||||
|
||||
|
||||
# Redis
|
||||
REDIS_PASSWD="dummy"
|
||||
redis-cli CONFIG SET requirepass $REDIS_PASSWD
|
||||
redis-cli -a $REDIS_PASSWD CONFIG REWRITE
|
||||
|
||||
# Contenu statique
|
||||
mkdir -p /srv/$PROJECTNAME/static
|
||||
ln -sf /vagrant/media /srv/$PROJECTNAME/media
|
||||
chown -R vagrant:www-data /srv/$PROJECTNAME
|
||||
|
||||
# Nginx
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
sed "s/\_\_PROJECTNAME__/$PROJECTNAME/g" /vagrant/provisioning/nginx.conf > /etc/nginx/sites-enabled/$PROJECTNAME.conf
|
||||
systemctl reload nginx
|
||||
|
||||
# Environnement virtuel python
|
||||
sudo -H -u vagrant python3 -m venv ~vagrant/venv
|
||||
sudo -H -u vagrant ~vagrant/venv/bin/pip install -U pip
|
||||
sudo -H -u vagrant ~vagrant/venv/bin/pip install -r /vagrant/requirements-prod.txt -r /vagrant/requirements-dev.txt
|
||||
|
||||
# Préparation de Django
|
||||
cd /vagrant
|
||||
sudo -H -u vagrant \
|
||||
DJANGO_SETTINGS_MODULE=$SETTINGS_MODULE \
|
||||
bash -c ". ~/venv/bin/activate && bash provisioning/prepare_django.sh"
|
||||
sudo -H -u vagrant /home/vagrant/venv/bin/python manage.py collectstatic --noinput --settings $SETTINGS_MODULE
|
||||
|
||||
# Mails
|
||||
mkdir -p /var/mail/django
|
||||
chown -R vagrant:www-data /var/mail/django
|
||||
|
||||
# Service files
|
||||
for file in /vagrant/provisioning/*.service
|
||||
do
|
||||
# failsafe si aucun fichier .service n'existe
|
||||
[ -f $file ] || break
|
||||
SERVICE=$(basename $file)
|
||||
|
||||
# On copie en remplaçant si nécessaire le template
|
||||
sed "s/\_\_PROJECTNAME__/$PROJECTNAME/g" $file > /etc/systemd/system/$SERVICE
|
||||
systemctl enable $SERVICE
|
||||
systemctl start $SERVICE
|
||||
done
|
||||
|
||||
# Mise en place du .bash_profile pour tout configurer lors du `vagrant ssh`
|
||||
cat >> ~vagrant/.bashrc <<EOF
|
||||
export DJANGO_SETTINGS_MODULE=$SETTINGS_MODULE
|
||||
|
||||
# Charge le virtualenv
|
||||
source ~/venv/bin/activate
|
||||
|
||||
cd /vagrant
|
||||
EOF
|
15
provisioning/gunicorn.service
Normal file
15
provisioning/gunicorn.service
Normal file
|
@ -0,0 +1,15 @@
|
|||
Description="Gunicorn"
|
||||
After=syslog.target
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=vagrant
|
||||
Group=vagrant
|
||||
TimeoutSec=300
|
||||
WorkingDirectory=/vagrant
|
||||
Environment="DJANGO_SETTINGS_MODULE=__PROJECTNAME__.settings.vagrant"
|
||||
ExecStart=/home/vagrant/venv/bin/gunicorn --bind=unix:/tmp/gunicorn.sock __PROJECTNAME__.wsgi:application
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
49
provisioning/nginx.conf
Normal file
49
provisioning/nginx.conf
Normal file
|
@ -0,0 +1,49 @@
|
|||
upstream app_server {
|
||||
# fail_timeout=0 means we always retry an upstream even if it failed
|
||||
# to return a good HTTP response
|
||||
|
||||
# for UNIX domain socket setups
|
||||
server unix:/tmp/gunicorn.sock fail_timeout=0;
|
||||
|
||||
# for a TCP configuration
|
||||
# server 192.168.0.7:8000 fail_timeout=0;
|
||||
}
|
||||
|
||||
server {
|
||||
# use 'listen 80 deferred;' for Linux
|
||||
# use 'listen 80 accept_filter=httpready;' for FreeBSD
|
||||
listen 80 deferred;
|
||||
client_max_body_size 4G;
|
||||
|
||||
# set the correct host(s) for your site
|
||||
server_name localhost;
|
||||
|
||||
keepalive_timeout 5;
|
||||
|
||||
# path for static files
|
||||
root /srv/__PROJECTNAME__;
|
||||
|
||||
# Static files
|
||||
location /static/ {
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# Uploaded media
|
||||
location /media/ {
|
||||
access_log off;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-SSL-Client-Serial $ssl_client_serial;
|
||||
proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
|
||||
proxy_set_header X-SSL-Client-S-DN $ssl_client_s_dn;
|
||||
# we don't want nginx trying to do something clever with
|
||||
# redirects, we set the Host: header above already.
|
||||
proxy_redirect off;
|
||||
proxy_pass http://app_server;
|
||||
}
|
||||
}
|
6
provisioning/package.list
Normal file
6
provisioning/package.list
Normal file
|
@ -0,0 +1,6 @@
|
|||
python3-pip python3-dev python3-venv
|
||||
libpq-dev postgresql postgresql-contrib libjpeg-dev
|
||||
build-essential nginx git redis-server
|
||||
|
||||
# Needed for python-ldap
|
||||
libldap2-dev libsasl2-dev ldap-utils lcov
|
6
provisioning/prepare_django.sh
Normal file
6
provisioning/prepare_django.sh
Normal file
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Stop if an error is encountered.
|
||||
set -e
|
||||
|
||||
python manage.py migrate
|
|
@ -1,34 +0,0 @@
|
|||
[tool.poetry]
|
||||
name = "annuaire"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["Klub-Dev ENS <klub-dev@ens.fr>"]
|
||||
readme = "README.md"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.9"
|
||||
Django = "^3.2.0"
|
||||
Pillow = "^9.4.0"
|
||||
authens = "^0.1b4"
|
||||
python-dateutil = "^2.8.2"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
ipython = "^8.9.0"
|
||||
isort = "^5.12.0"
|
||||
flake8 = "^6.0.0"
|
||||
black = "^22.12.0"
|
||||
django-types = "^0.16.0"
|
||||
django-debug-toolbar = "^3.8.1"
|
||||
|
||||
[tool.poetry.group.prod.dependencies]
|
||||
python-ldap = "^3.4.3"
|
||||
psycopg2 = "^2.9.5"
|
||||
gunicorn = "^20.1.0"
|
||||
django-redis = "^5.2.0"
|
||||
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
|
@ -1,394 +1,6 @@
|
|||
appnope==0.1.3 ; python_version >= "3.9" and python_version < "4.0" and sys_platform == "darwin" \
|
||||
--hash=sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24 \
|
||||
--hash=sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e
|
||||
asgiref==3.6.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac \
|
||||
--hash=sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506
|
||||
asttokens==2.2.1 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:4622110b2a6f30b77e1473affaa97e711bc2f07d3f10848420ff1898edbe94f3 \
|
||||
--hash=sha256:6b0ac9e93fb0335014d382b8fa9b3afa7df546984258005da0b9e7095b3deb1c
|
||||
authens==0.1b4 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:3430c85d0c11c7ca78cba484d5dfd18e9ba3370a0f2abbdd14aafb8feb8807a5
|
||||
backcall==0.2.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e \
|
||||
--hash=sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255
|
||||
black==22.12.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320 \
|
||||
--hash=sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351 \
|
||||
--hash=sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350 \
|
||||
--hash=sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f \
|
||||
--hash=sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf \
|
||||
--hash=sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148 \
|
||||
--hash=sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4 \
|
||||
--hash=sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d \
|
||||
--hash=sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc \
|
||||
--hash=sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d \
|
||||
--hash=sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2 \
|
||||
--hash=sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f
|
||||
certifi==2022.12.7 ; python_version >= "3.9" and python_version < "4" \
|
||||
--hash=sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3 \
|
||||
--hash=sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18
|
||||
charset-normalizer==3.0.1 ; python_version >= "3.9" and python_version < "4" \
|
||||
--hash=sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b \
|
||||
--hash=sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42 \
|
||||
--hash=sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d \
|
||||
--hash=sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b \
|
||||
--hash=sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a \
|
||||
--hash=sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59 \
|
||||
--hash=sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154 \
|
||||
--hash=sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1 \
|
||||
--hash=sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c \
|
||||
--hash=sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a \
|
||||
--hash=sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d \
|
||||
--hash=sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6 \
|
||||
--hash=sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b \
|
||||
--hash=sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b \
|
||||
--hash=sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783 \
|
||||
--hash=sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5 \
|
||||
--hash=sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918 \
|
||||
--hash=sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555 \
|
||||
--hash=sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639 \
|
||||
--hash=sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786 \
|
||||
--hash=sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e \
|
||||
--hash=sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed \
|
||||
--hash=sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820 \
|
||||
--hash=sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8 \
|
||||
--hash=sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3 \
|
||||
--hash=sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541 \
|
||||
--hash=sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14 \
|
||||
--hash=sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be \
|
||||
--hash=sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e \
|
||||
--hash=sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76 \
|
||||
--hash=sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b \
|
||||
--hash=sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c \
|
||||
--hash=sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b \
|
||||
--hash=sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3 \
|
||||
--hash=sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc \
|
||||
--hash=sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6 \
|
||||
--hash=sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59 \
|
||||
--hash=sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4 \
|
||||
--hash=sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d \
|
||||
--hash=sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d \
|
||||
--hash=sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3 \
|
||||
--hash=sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a \
|
||||
--hash=sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea \
|
||||
--hash=sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6 \
|
||||
--hash=sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e \
|
||||
--hash=sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603 \
|
||||
--hash=sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24 \
|
||||
--hash=sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a \
|
||||
--hash=sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58 \
|
||||
--hash=sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678 \
|
||||
--hash=sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a \
|
||||
--hash=sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c \
|
||||
--hash=sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6 \
|
||||
--hash=sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18 \
|
||||
--hash=sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174 \
|
||||
--hash=sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317 \
|
||||
--hash=sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f \
|
||||
--hash=sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc \
|
||||
--hash=sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837 \
|
||||
--hash=sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41 \
|
||||
--hash=sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c \
|
||||
--hash=sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579 \
|
||||
--hash=sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753 \
|
||||
--hash=sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8 \
|
||||
--hash=sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291 \
|
||||
--hash=sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087 \
|
||||
--hash=sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866 \
|
||||
--hash=sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3 \
|
||||
--hash=sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d \
|
||||
--hash=sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1 \
|
||||
--hash=sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca \
|
||||
--hash=sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e \
|
||||
--hash=sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db \
|
||||
--hash=sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72 \
|
||||
--hash=sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d \
|
||||
--hash=sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc \
|
||||
--hash=sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539 \
|
||||
--hash=sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d \
|
||||
--hash=sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af \
|
||||
--hash=sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b \
|
||||
--hash=sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602 \
|
||||
--hash=sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f \
|
||||
--hash=sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478 \
|
||||
--hash=sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c \
|
||||
--hash=sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e \
|
||||
--hash=sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479 \
|
||||
--hash=sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7 \
|
||||
--hash=sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8
|
||||
click==8.1.3 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e \
|
||||
--hash=sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48
|
||||
colorama==0.4.6 ; python_version >= "3.9" and python_version < "4.0" and sys_platform == "win32" or python_version >= "3.9" and python_version < "4.0" and platform_system == "Windows" \
|
||||
--hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
|
||||
--hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
|
||||
decorator==5.1.1 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330 \
|
||||
--hash=sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186
|
||||
django-debug-toolbar==3.8.1 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:24ef1a7d44d25e60d7951e378454c6509bf536dce7e7d9d36e7c387db499bc27 \
|
||||
--hash=sha256:879f8a4672d41621c06a4d322dcffa630fc4df056cada6e417ed01db0e5e0478
|
||||
django-types==0.16.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:29d23af8b88fa1579b47418e92ffc1d89bfbdf11eab64d2ae006fa09e54fed9a \
|
||||
--hash=sha256:9df4e1936f304f309a59f4cee19a607e151282fc0d97238833a382fafc5052c8
|
||||
django==3.2.16 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:18ba8efa36b69cfcd4b670d0fa187c6fe7506596f0ababe580e16909bcdec121 \
|
||||
--hash=sha256:3adc285124244724a394fa9b9839cc8cd116faf7d159554c43ecdaa8cdf0b94d
|
||||
executing==1.2.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc \
|
||||
--hash=sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107
|
||||
flake8==6.0.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7 \
|
||||
--hash=sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181
|
||||
idna==3.4 ; python_version >= "3.9" and python_version < "4" \
|
||||
--hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \
|
||||
--hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2
|
||||
ipython==8.9.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:71618e82e6d59487bea059626e7c79fb4a5b760d1510d02fab1160db6fdfa1f7 \
|
||||
--hash=sha256:9c207b0ef2d276d1bfcfeb9a62804336abbe4b170574ea061500952319b1d78c
|
||||
isort==5.12.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504 \
|
||||
--hash=sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6
|
||||
jedi==0.18.2 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:203c1fd9d969ab8f2119ec0a3342e0b49910045abe6af0a3ae83a5764d54639e \
|
||||
--hash=sha256:bae794c30d07f6d910d32a7048af09b5a39ed740918da923c6b780790ebac612
|
||||
lxml==4.9.2 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:01d36c05f4afb8f7c20fd9ed5badca32a2029b93b1750f571ccc0b142531caf7 \
|
||||
--hash=sha256:04876580c050a8c5341d706dd464ff04fd597095cc8c023252566a8826505726 \
|
||||
--hash=sha256:05ca3f6abf5cf78fe053da9b1166e062ade3fa5d4f92b4ed688127ea7d7b1d03 \
|
||||
--hash=sha256:090c6543d3696cbe15b4ac6e175e576bcc3f1ccfbba970061b7300b0c15a2140 \
|
||||
--hash=sha256:0dc313ef231edf866912e9d8f5a042ddab56c752619e92dfd3a2c277e6a7299a \
|
||||
--hash=sha256:0f2b1e0d79180f344ff9f321327b005ca043a50ece8713de61d1cb383fb8ac05 \
|
||||
--hash=sha256:13598ecfbd2e86ea7ae45ec28a2a54fb87ee9b9fdb0f6d343297d8e548392c03 \
|
||||
--hash=sha256:16efd54337136e8cd72fb9485c368d91d77a47ee2d42b057564aae201257d419 \
|
||||
--hash=sha256:1ab8f1f932e8f82355e75dda5413a57612c6ea448069d4fb2e217e9a4bed13d4 \
|
||||
--hash=sha256:223f4232855ade399bd409331e6ca70fb5578efef22cf4069a6090acc0f53c0e \
|
||||
--hash=sha256:2455cfaeb7ac70338b3257f41e21f0724f4b5b0c0e7702da67ee6c3640835b67 \
|
||||
--hash=sha256:2899456259589aa38bfb018c364d6ae7b53c5c22d8e27d0ec7609c2a1ff78b50 \
|
||||
--hash=sha256:2a29ba94d065945944016b6b74e538bdb1751a1db6ffb80c9d3c2e40d6fa9894 \
|
||||
--hash=sha256:2a87fa548561d2f4643c99cd13131acb607ddabb70682dcf1dff5f71f781a4bf \
|
||||
--hash=sha256:2e430cd2824f05f2d4f687701144556646bae8f249fd60aa1e4c768ba7018947 \
|
||||
--hash=sha256:36c3c175d34652a35475a73762b545f4527aec044910a651d2bf50de9c3352b1 \
|
||||
--hash=sha256:3818b8e2c4b5148567e1b09ce739006acfaa44ce3156f8cbbc11062994b8e8dd \
|
||||
--hash=sha256:3ab9fa9d6dc2a7f29d7affdf3edebf6ece6fb28a6d80b14c3b2fb9d39b9322c3 \
|
||||
--hash=sha256:3efea981d956a6f7173b4659849f55081867cf897e719f57383698af6f618a92 \
|
||||
--hash=sha256:4c8f293f14abc8fd3e8e01c5bd86e6ed0b6ef71936ded5bf10fe7a5efefbaca3 \
|
||||
--hash=sha256:5344a43228767f53a9df6e5b253f8cdca7dfc7b7aeae52551958192f56d98457 \
|
||||
--hash=sha256:58bfa3aa19ca4c0f28c5dde0ff56c520fbac6f0daf4fac66ed4c8d2fb7f22e74 \
|
||||
--hash=sha256:5b4545b8a40478183ac06c073e81a5ce4cf01bf1734962577cf2bb569a5b3bbf \
|
||||
--hash=sha256:5f50a1c177e2fa3ee0667a5ab79fdc6b23086bc8b589d90b93b4bd17eb0e64d1 \
|
||||
--hash=sha256:63da2ccc0857c311d764e7d3d90f429c252e83b52d1f8f1d1fe55be26827d1f4 \
|
||||
--hash=sha256:6749649eecd6a9871cae297bffa4ee76f90b4504a2a2ab528d9ebe912b101975 \
|
||||
--hash=sha256:6804daeb7ef69e7b36f76caddb85cccd63d0c56dedb47555d2fc969e2af6a1a5 \
|
||||
--hash=sha256:689bb688a1db722485e4610a503e3e9210dcc20c520b45ac8f7533c837be76fe \
|
||||
--hash=sha256:699a9af7dffaf67deeae27b2112aa06b41c370d5e7633e0ee0aea2e0b6c211f7 \
|
||||
--hash=sha256:6b418afe5df18233fc6b6093deb82a32895b6bb0b1155c2cdb05203f583053f1 \
|
||||
--hash=sha256:76cf573e5a365e790396a5cc2b909812633409306c6531a6877c59061e42c4f2 \
|
||||
--hash=sha256:7b515674acfdcadb0eb5d00d8a709868173acece5cb0be3dd165950cbfdf5409 \
|
||||
--hash=sha256:7b770ed79542ed52c519119473898198761d78beb24b107acf3ad65deae61f1f \
|
||||
--hash=sha256:7d2278d59425777cfcb19735018d897ca8303abe67cc735f9f97177ceff8027f \
|
||||
--hash=sha256:7e91ee82f4199af8c43d8158024cbdff3d931df350252288f0d4ce656df7f3b5 \
|
||||
--hash=sha256:821b7f59b99551c69c85a6039c65b75f5683bdc63270fec660f75da67469ca24 \
|
||||
--hash=sha256:822068f85e12a6e292803e112ab876bc03ed1f03dddb80154c395f891ca6b31e \
|
||||
--hash=sha256:8340225bd5e7a701c0fa98284c849c9b9fc9238abf53a0ebd90900f25d39a4e4 \
|
||||
--hash=sha256:85cabf64adec449132e55616e7ca3e1000ab449d1d0f9d7f83146ed5bdcb6d8a \
|
||||
--hash=sha256:880bbbcbe2fca64e2f4d8e04db47bcdf504936fa2b33933efd945e1b429bea8c \
|
||||
--hash=sha256:8d0b4612b66ff5d62d03bcaa043bb018f74dfea51184e53f067e6fdcba4bd8de \
|
||||
--hash=sha256:8e20cb5a47247e383cf4ff523205060991021233ebd6f924bca927fcf25cf86f \
|
||||
--hash=sha256:925073b2fe14ab9b87e73f9a5fde6ce6392da430f3004d8b72cc86f746f5163b \
|
||||
--hash=sha256:998c7c41910666d2976928c38ea96a70d1aa43be6fe502f21a651e17483a43c5 \
|
||||
--hash=sha256:9b22c5c66f67ae00c0199f6055705bc3eb3fcb08d03d2ec4059a2b1b25ed48d7 \
|
||||
--hash=sha256:9f102706d0ca011de571de32c3247c6476b55bb6bc65a20f682f000b07a4852a \
|
||||
--hash=sha256:a08cff61517ee26cb56f1e949cca38caabe9ea9fbb4b1e10a805dc39844b7d5c \
|
||||
--hash=sha256:a0a336d6d3e8b234a3aae3c674873d8f0e720b76bc1d9416866c41cd9500ffb9 \
|
||||
--hash=sha256:a35f8b7fa99f90dd2f5dc5a9fa12332642f087a7641289ca6c40d6e1a2637d8e \
|
||||
--hash=sha256:a38486985ca49cfa574a507e7a2215c0c780fd1778bb6290c21193b7211702ab \
|
||||
--hash=sha256:a5da296eb617d18e497bcf0a5c528f5d3b18dadb3619fbdadf4ed2356ef8d941 \
|
||||
--hash=sha256:a6e441a86553c310258aca15d1c05903aaf4965b23f3bc2d55f200804e005ee5 \
|
||||
--hash=sha256:a82d05da00a58b8e4c0008edbc8a4b6ec5a4bc1e2ee0fb6ed157cf634ed7fa45 \
|
||||
--hash=sha256:ab323679b8b3030000f2be63e22cdeea5b47ee0abd2d6a1dc0c8103ddaa56cd7 \
|
||||
--hash=sha256:b1f42b6921d0e81b1bcb5e395bc091a70f41c4d4e55ba99c6da2b31626c44892 \
|
||||
--hash=sha256:b23e19989c355ca854276178a0463951a653309fb8e57ce674497f2d9f208746 \
|
||||
--hash=sha256:b264171e3143d842ded311b7dccd46ff9ef34247129ff5bf5066123c55c2431c \
|
||||
--hash=sha256:b26a29f0b7fc6f0897f043ca366142d2b609dc60756ee6e4e90b5f762c6adc53 \
|
||||
--hash=sha256:b64d891da92e232c36976c80ed7ebb383e3f148489796d8d31a5b6a677825efe \
|
||||
--hash=sha256:b9cc34af337a97d470040f99ba4282f6e6bac88407d021688a5d585e44a23184 \
|
||||
--hash=sha256:bc718cd47b765e790eecb74d044cc8d37d58562f6c314ee9484df26276d36a38 \
|
||||
--hash=sha256:be7292c55101e22f2a3d4d8913944cbea71eea90792bf914add27454a13905df \
|
||||
--hash=sha256:c83203addf554215463b59f6399835201999b5e48019dc17f182ed5ad87205c9 \
|
||||
--hash=sha256:c9ec3eaf616d67db0764b3bb983962b4f385a1f08304fd30c7283954e6a7869b \
|
||||
--hash=sha256:ca34efc80a29351897e18888c71c6aca4a359247c87e0b1c7ada14f0ab0c0fb2 \
|
||||
--hash=sha256:ca989b91cf3a3ba28930a9fc1e9aeafc2a395448641df1f387a2d394638943b0 \
|
||||
--hash=sha256:d02a5399126a53492415d4906ab0ad0375a5456cc05c3fc0fc4ca11771745cda \
|
||||
--hash=sha256:d17bc7c2ccf49c478c5bdd447594e82692c74222698cfc9b5daae7ae7e90743b \
|
||||
--hash=sha256:d5bf6545cd27aaa8a13033ce56354ed9e25ab0e4ac3b5392b763d8d04b08e0c5 \
|
||||
--hash=sha256:d6b430a9938a5a5d85fc107d852262ddcd48602c120e3dbb02137c83d212b380 \
|
||||
--hash=sha256:da248f93f0418a9e9d94b0080d7ebc407a9a5e6d0b57bb30db9b5cc28de1ad33 \
|
||||
--hash=sha256:da4dd7c9c50c059aba52b3524f84d7de956f7fef88f0bafcf4ad7dde94a064e8 \
|
||||
--hash=sha256:df0623dcf9668ad0445e0558a21211d4e9a149ea8f5666917c8eeec515f0a6d1 \
|
||||
--hash=sha256:e5168986b90a8d1f2f9dc1b841467c74221bd752537b99761a93d2d981e04889 \
|
||||
--hash=sha256:efa29c2fe6b4fdd32e8ef81c1528506895eca86e1d8c4657fda04c9b3786ddf9 \
|
||||
--hash=sha256:f1496ea22ca2c830cbcbd473de8f114a320da308438ae65abad6bab7867fe38f \
|
||||
--hash=sha256:f49e52d174375a7def9915c9f06ec4e569d235ad428f70751765f48d5926678c
|
||||
matplotlib-inline==0.1.6 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311 \
|
||||
--hash=sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304
|
||||
mccabe==0.7.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325 \
|
||||
--hash=sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e
|
||||
mypy-extensions==0.4.3 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d \
|
||||
--hash=sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8
|
||||
parso==0.8.3 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0 \
|
||||
--hash=sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75
|
||||
pathspec==0.11.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229 \
|
||||
--hash=sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc
|
||||
pexpect==4.8.0 ; python_version >= "3.9" and python_version < "4.0" and sys_platform != "win32" \
|
||||
--hash=sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937 \
|
||||
--hash=sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c
|
||||
pickleshare==0.7.5 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca \
|
||||
--hash=sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56
|
||||
pillow==9.4.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:013016af6b3a12a2f40b704677f8b51f72cb007dac785a9933d5c86a72a7fe33 \
|
||||
--hash=sha256:0845adc64fe9886db00f5ab68c4a8cd933ab749a87747555cec1c95acea64b0b \
|
||||
--hash=sha256:0884ba7b515163a1a05440a138adeb722b8a6ae2c2b33aea93ea3118dd3a899e \
|
||||
--hash=sha256:09b89ddc95c248ee788328528e6a2996e09eaccddeeb82a5356e92645733be35 \
|
||||
--hash=sha256:0dd4c681b82214b36273c18ca7ee87065a50e013112eea7d78c7a1b89a739153 \
|
||||
--hash=sha256:0e51f608da093e5d9038c592b5b575cadc12fd748af1479b5e858045fff955a9 \
|
||||
--hash=sha256:0f3269304c1a7ce82f1759c12ce731ef9b6e95b6df829dccd9fe42912cc48569 \
|
||||
--hash=sha256:16a8df99701f9095bea8a6c4b3197da105df6f74e6176c5b410bc2df2fd29a57 \
|
||||
--hash=sha256:19005a8e58b7c1796bc0167862b1f54a64d3b44ee5d48152b06bb861458bc0f8 \
|
||||
--hash=sha256:1b4b4e9dda4f4e4c4e6896f93e84a8f0bcca3b059de9ddf67dac3c334b1195e1 \
|
||||
--hash=sha256:28676836c7796805914b76b1837a40f76827ee0d5398f72f7dcc634bae7c6264 \
|
||||
--hash=sha256:2968c58feca624bb6c8502f9564dd187d0e1389964898f5e9e1fbc8533169157 \
|
||||
--hash=sha256:3f4cc516e0b264c8d4ccd6b6cbc69a07c6d582d8337df79be1e15a5056b258c9 \
|
||||
--hash=sha256:3fa1284762aacca6dc97474ee9c16f83990b8eeb6697f2ba17140d54b453e133 \
|
||||
--hash=sha256:43521ce2c4b865d385e78579a082b6ad1166ebed2b1a2293c3be1d68dd7ca3b9 \
|
||||
--hash=sha256:451f10ef963918e65b8869e17d67db5e2f4ab40e716ee6ce7129b0cde2876eab \
|
||||
--hash=sha256:46c259e87199041583658457372a183636ae8cd56dbf3f0755e0f376a7f9d0e6 \
|
||||
--hash=sha256:46f39cab8bbf4a384ba7cb0bc8bae7b7062b6a11cfac1ca4bc144dea90d4a9f5 \
|
||||
--hash=sha256:519e14e2c49fcf7616d6d2cfc5c70adae95682ae20f0395e9280db85e8d6c4df \
|
||||
--hash=sha256:53dcb50fbdc3fb2c55431a9b30caeb2f7027fcd2aeb501459464f0214200a503 \
|
||||
--hash=sha256:54614444887e0d3043557d9dbc697dbb16cfb5a35d672b7a0fcc1ed0cf1c600b \
|
||||
--hash=sha256:575d8912dca808edd9acd6f7795199332696d3469665ef26163cd090fa1f8bfa \
|
||||
--hash=sha256:5dd5a9c3091a0f414a963d427f920368e2b6a4c2f7527fdd82cde8ef0bc7a327 \
|
||||
--hash=sha256:5f532a2ad4d174eb73494e7397988e22bf427f91acc8e6ebf5bb10597b49c493 \
|
||||
--hash=sha256:60e7da3a3ad1812c128750fc1bc14a7ceeb8d29f77e0a2356a8fb2aa8925287d \
|
||||
--hash=sha256:653d7fb2df65efefbcbf81ef5fe5e5be931f1ee4332c2893ca638c9b11a409c4 \
|
||||
--hash=sha256:6663977496d616b618b6cfa43ec86e479ee62b942e1da76a2c3daa1c75933ef4 \
|
||||
--hash=sha256:6abfb51a82e919e3933eb137e17c4ae9c0475a25508ea88993bb59faf82f3b35 \
|
||||
--hash=sha256:6c6b1389ed66cdd174d040105123a5a1bc91d0aa7059c7261d20e583b6d8cbd2 \
|
||||
--hash=sha256:6d9dfb9959a3b0039ee06c1a1a90dc23bac3b430842dcb97908ddde05870601c \
|
||||
--hash=sha256:765cb54c0b8724a7c12c55146ae4647e0274a839fb6de7bcba841e04298e1011 \
|
||||
--hash=sha256:7a21222644ab69ddd9967cfe6f2bb420b460dae4289c9d40ff9a4896e7c35c9a \
|
||||
--hash=sha256:7ac7594397698f77bce84382929747130765f66406dc2cd8b4ab4da68ade4c6e \
|
||||
--hash=sha256:7cfc287da09f9d2a7ec146ee4d72d6ea1342e770d975e49a8621bf54eaa8f30f \
|
||||
--hash=sha256:83125753a60cfc8c412de5896d10a0a405e0bd88d0470ad82e0869ddf0cb3848 \
|
||||
--hash=sha256:847b114580c5cc9ebaf216dd8c8dbc6b00a3b7ab0131e173d7120e6deade1f57 \
|
||||
--hash=sha256:87708d78a14d56a990fbf4f9cb350b7d89ee8988705e58e39bdf4d82c149210f \
|
||||
--hash=sha256:8a2b5874d17e72dfb80d917213abd55d7e1ed2479f38f001f264f7ce7bae757c \
|
||||
--hash=sha256:8f127e7b028900421cad64f51f75c051b628db17fb00e099eb148761eed598c9 \
|
||||
--hash=sha256:94cdff45173b1919350601f82d61365e792895e3c3a3443cf99819e6fbf717a5 \
|
||||
--hash=sha256:99d92d148dd03fd19d16175b6d355cc1b01faf80dae93c6c3eb4163709edc0a9 \
|
||||
--hash=sha256:9a3049a10261d7f2b6514d35bbb7a4dfc3ece4c4de14ef5876c4b7a23a0e566d \
|
||||
--hash=sha256:9d9a62576b68cd90f7075876f4e8444487db5eeea0e4df3ba298ee38a8d067b0 \
|
||||
--hash=sha256:9e5f94742033898bfe84c93c831a6f552bb629448d4072dd312306bab3bd96f1 \
|
||||
--hash=sha256:a1c2d7780448eb93fbcc3789bf3916aa5720d942e37945f4056680317f1cd23e \
|
||||
--hash=sha256:a2e0f87144fcbbe54297cae708c5e7f9da21a4646523456b00cc956bd4c65815 \
|
||||
--hash=sha256:a4dfdae195335abb4e89cc9762b2edc524f3c6e80d647a9a81bf81e17e3fb6f0 \
|
||||
--hash=sha256:a96e6e23f2b79433390273eaf8cc94fec9c6370842e577ab10dabdcc7ea0a66b \
|
||||
--hash=sha256:aabdab8ec1e7ca7f1434d042bf8b1e92056245fb179790dc97ed040361f16bfd \
|
||||
--hash=sha256:b222090c455d6d1a64e6b7bb5f4035c4dff479e22455c9eaa1bdd4c75b52c80c \
|
||||
--hash=sha256:b52ff4f4e002f828ea6483faf4c4e8deea8d743cf801b74910243c58acc6eda3 \
|
||||
--hash=sha256:b70756ec9417c34e097f987b4d8c510975216ad26ba6e57ccb53bc758f490dab \
|
||||
--hash=sha256:b8c2f6eb0df979ee99433d8b3f6d193d9590f735cf12274c108bd954e30ca858 \
|
||||
--hash=sha256:b9b752ab91e78234941e44abdecc07f1f0d8f51fb62941d32995b8161f68cfe5 \
|
||||
--hash=sha256:ba6612b6548220ff5e9df85261bddc811a057b0b465a1226b39bfb8550616aee \
|
||||
--hash=sha256:bd752c5ff1b4a870b7661234694f24b1d2b9076b8bf337321a814c612665f343 \
|
||||
--hash=sha256:c3c4ed2ff6760e98d262e0cc9c9a7f7b8a9f61aa4d47c58835cdaf7b0b8811bb \
|
||||
--hash=sha256:c5c1362c14aee73f50143d74389b2c158707b4abce2cb055b7ad37ce60738d47 \
|
||||
--hash=sha256:cb362e3b0976dc994857391b776ddaa8c13c28a16f80ac6522c23d5257156bed \
|
||||
--hash=sha256:d197df5489004db87d90b918033edbeee0bd6df3848a204bca3ff0a903bef837 \
|
||||
--hash=sha256:d3b56206244dc8711f7e8b7d6cad4663917cd5b2d950799425076681e8766286 \
|
||||
--hash=sha256:d5b2f8a31bd43e0f18172d8ac82347c8f37ef3e0b414431157718aa234991b28 \
|
||||
--hash=sha256:d7081c084ceb58278dd3cf81f836bc818978c0ccc770cbbb202125ddabec6628 \
|
||||
--hash=sha256:db74f5562c09953b2c5f8ec4b7dfd3f5421f31811e97d1dbc0a7c93d6e3a24df \
|
||||
--hash=sha256:df41112ccce5d47770a0c13651479fbcd8793f34232a2dd9faeccb75eb5d0d0d \
|
||||
--hash=sha256:e1339790c083c5a4de48f688b4841f18df839eb3c9584a770cbd818b33e26d5d \
|
||||
--hash=sha256:e621b0246192d3b9cb1dc62c78cfa4c6f6d2ddc0ec207d43c0dedecb914f152a \
|
||||
--hash=sha256:e8c5cf126889a4de385c02a2c3d3aba4b00f70234bfddae82a5eaa3ee6d5e3e6 \
|
||||
--hash=sha256:e9d7747847c53a16a729b6ee5e737cf170f7a16611c143d95aa60a109a59c336 \
|
||||
--hash=sha256:eaef5d2de3c7e9b21f1e762f289d17b726c2239a42b11e25446abf82b26ac132 \
|
||||
--hash=sha256:ed3e4b4e1e6de75fdc16d3259098de7c6571b1a6cc863b1a49e7d3d53e036070 \
|
||||
--hash=sha256:ef21af928e807f10bf4141cad4746eee692a0dd3ff56cfb25fce076ec3cc8abe \
|
||||
--hash=sha256:f09598b416ba39a8f489c124447b007fe865f786a89dbfa48bb5cf395693132a \
|
||||
--hash=sha256:f0caf4a5dcf610d96c3bd32932bfac8aee61c96e60481c2a0ea58da435e25acd \
|
||||
--hash=sha256:f6e78171be3fb7941f9910ea15b4b14ec27725865a73c15277bc39f5ca4f8391 \
|
||||
--hash=sha256:f715c32e774a60a337b2bb8ad9839b4abf75b267a0f18806f6f4f5f1688c4b5a \
|
||||
--hash=sha256:fb5c1ad6bad98c57482236a21bf985ab0ef42bd51f7ad4e4538e89a997624e12
|
||||
platformdirs==2.6.2 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490 \
|
||||
--hash=sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2
|
||||
prompt-toolkit==3.0.36 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63 \
|
||||
--hash=sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305
|
||||
ptyprocess==0.7.0 ; python_version >= "3.9" and python_version < "4.0" and sys_platform != "win32" \
|
||||
--hash=sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35 \
|
||||
--hash=sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220
|
||||
pure-eval==0.2.2 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350 \
|
||||
--hash=sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3
|
||||
pyasn1-modules==0.2.8 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e \
|
||||
--hash=sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74
|
||||
pyasn1==0.4.8 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d \
|
||||
--hash=sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba
|
||||
pycodestyle==2.10.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053 \
|
||||
--hash=sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610
|
||||
pyflakes==3.0.1 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf \
|
||||
--hash=sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd
|
||||
pygments==2.14.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297 \
|
||||
--hash=sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717
|
||||
python-cas==1.6.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:2abc0dae93c3b14097999fb7062f23cd09bc9f4e33d93f03c0cc040bd71ed50e \
|
||||
--hash=sha256:b8f1dcb1b6c56b3ff4f86bbef47bcdfcf932061ccd4812ae35e3f63954dfdb28
|
||||
python-ldap==3.4.3 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:ab26c519a0ef2a443a2a10391fa3c5cb52d7871323399db949ebfaa9f25ee2a0
|
||||
pytz==2022.7.1 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0 \
|
||||
--hash=sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a
|
||||
requests==2.28.2 ; python_version >= "3.9" and python_version < "4" \
|
||||
--hash=sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa \
|
||||
--hash=sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf
|
||||
six==1.16.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
|
||||
--hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
|
||||
sqlparse==0.4.3 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:0323c0ec29cd52bceabc1b4d9d579e311f3e4961b98d174201d5622a23b85e34 \
|
||||
--hash=sha256:69ca804846bb114d2ec380e4360a8a340db83f0ccf3afceeb1404df028f57268
|
||||
stack-data==0.6.2 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:32d2dd0376772d01b6cb9fc996f3c8b57a357089dec328ed4b6553d037eaf815 \
|
||||
--hash=sha256:cbb2a53eb64e5785878201a97ed7c7b94883f48b87bfb0bbe8b623c74679e4a8
|
||||
tomli==2.0.1 ; python_version >= "3.9" and python_full_version < "3.11.0a7" \
|
||||
--hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \
|
||||
--hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f
|
||||
traitlets==5.9.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:9e6ec080259b9a5940c797d58b613b5e31441c2257b87c2e795c5228ae80d2d8 \
|
||||
--hash=sha256:f6cde21a9c68cf756af02035f72d5a723bf607e862e7be33ece505abf4a3bad9
|
||||
typing-extensions==4.4.0 ; python_version >= "3.9" and python_version < "3.10" \
|
||||
--hash=sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa \
|
||||
--hash=sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e
|
||||
urllib3==1.26.14 ; python_version >= "3.9" and python_version < "4" \
|
||||
--hash=sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72 \
|
||||
--hash=sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1
|
||||
wcwidth==0.2.6 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e \
|
||||
--hash=sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0
|
||||
-r requirements.txt
|
||||
django-debug-toolbar
|
||||
ipython
|
||||
black
|
||||
flake8
|
||||
isort
|
|
@ -1,314 +1,5 @@
|
|||
asgiref==3.6.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac \
|
||||
--hash=sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506
|
||||
async-timeout==4.0.2 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15 \
|
||||
--hash=sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c
|
||||
authens==0.1b4 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:3430c85d0c11c7ca78cba484d5dfd18e9ba3370a0f2abbdd14aafb8feb8807a5
|
||||
certifi==2022.12.7 ; python_version >= "3.9" and python_version < "4" \
|
||||
--hash=sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3 \
|
||||
--hash=sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18
|
||||
charset-normalizer==3.0.1 ; python_version >= "3.9" and python_version < "4" \
|
||||
--hash=sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b \
|
||||
--hash=sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42 \
|
||||
--hash=sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d \
|
||||
--hash=sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b \
|
||||
--hash=sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a \
|
||||
--hash=sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59 \
|
||||
--hash=sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154 \
|
||||
--hash=sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1 \
|
||||
--hash=sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c \
|
||||
--hash=sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a \
|
||||
--hash=sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d \
|
||||
--hash=sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6 \
|
||||
--hash=sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b \
|
||||
--hash=sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b \
|
||||
--hash=sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783 \
|
||||
--hash=sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5 \
|
||||
--hash=sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918 \
|
||||
--hash=sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555 \
|
||||
--hash=sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639 \
|
||||
--hash=sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786 \
|
||||
--hash=sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e \
|
||||
--hash=sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed \
|
||||
--hash=sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820 \
|
||||
--hash=sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8 \
|
||||
--hash=sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3 \
|
||||
--hash=sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541 \
|
||||
--hash=sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14 \
|
||||
--hash=sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be \
|
||||
--hash=sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e \
|
||||
--hash=sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76 \
|
||||
--hash=sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b \
|
||||
--hash=sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c \
|
||||
--hash=sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b \
|
||||
--hash=sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3 \
|
||||
--hash=sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc \
|
||||
--hash=sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6 \
|
||||
--hash=sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59 \
|
||||
--hash=sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4 \
|
||||
--hash=sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d \
|
||||
--hash=sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d \
|
||||
--hash=sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3 \
|
||||
--hash=sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a \
|
||||
--hash=sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea \
|
||||
--hash=sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6 \
|
||||
--hash=sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e \
|
||||
--hash=sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603 \
|
||||
--hash=sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24 \
|
||||
--hash=sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a \
|
||||
--hash=sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58 \
|
||||
--hash=sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678 \
|
||||
--hash=sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a \
|
||||
--hash=sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c \
|
||||
--hash=sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6 \
|
||||
--hash=sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18 \
|
||||
--hash=sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174 \
|
||||
--hash=sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317 \
|
||||
--hash=sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f \
|
||||
--hash=sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc \
|
||||
--hash=sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837 \
|
||||
--hash=sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41 \
|
||||
--hash=sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c \
|
||||
--hash=sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579 \
|
||||
--hash=sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753 \
|
||||
--hash=sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8 \
|
||||
--hash=sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291 \
|
||||
--hash=sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087 \
|
||||
--hash=sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866 \
|
||||
--hash=sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3 \
|
||||
--hash=sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d \
|
||||
--hash=sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1 \
|
||||
--hash=sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca \
|
||||
--hash=sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e \
|
||||
--hash=sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db \
|
||||
--hash=sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72 \
|
||||
--hash=sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d \
|
||||
--hash=sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc \
|
||||
--hash=sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539 \
|
||||
--hash=sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d \
|
||||
--hash=sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af \
|
||||
--hash=sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b \
|
||||
--hash=sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602 \
|
||||
--hash=sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f \
|
||||
--hash=sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478 \
|
||||
--hash=sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c \
|
||||
--hash=sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e \
|
||||
--hash=sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479 \
|
||||
--hash=sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7 \
|
||||
--hash=sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8
|
||||
django-redis==5.2.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:1d037dc02b11ad7aa11f655d26dac3fb1af32630f61ef4428860a2e29ff92026 \
|
||||
--hash=sha256:8a99e5582c79f894168f5865c52bd921213253b7fd64d16733ae4591564465de
|
||||
django==3.2.16 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:18ba8efa36b69cfcd4b670d0fa187c6fe7506596f0ababe580e16909bcdec121 \
|
||||
--hash=sha256:3adc285124244724a394fa9b9839cc8cd116faf7d159554c43ecdaa8cdf0b94d
|
||||
gunicorn==20.1.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e \
|
||||
--hash=sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8
|
||||
idna==3.4 ; python_version >= "3.9" and python_version < "4" \
|
||||
--hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \
|
||||
--hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2
|
||||
lxml==4.9.2 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:01d36c05f4afb8f7c20fd9ed5badca32a2029b93b1750f571ccc0b142531caf7 \
|
||||
--hash=sha256:04876580c050a8c5341d706dd464ff04fd597095cc8c023252566a8826505726 \
|
||||
--hash=sha256:05ca3f6abf5cf78fe053da9b1166e062ade3fa5d4f92b4ed688127ea7d7b1d03 \
|
||||
--hash=sha256:090c6543d3696cbe15b4ac6e175e576bcc3f1ccfbba970061b7300b0c15a2140 \
|
||||
--hash=sha256:0dc313ef231edf866912e9d8f5a042ddab56c752619e92dfd3a2c277e6a7299a \
|
||||
--hash=sha256:0f2b1e0d79180f344ff9f321327b005ca043a50ece8713de61d1cb383fb8ac05 \
|
||||
--hash=sha256:13598ecfbd2e86ea7ae45ec28a2a54fb87ee9b9fdb0f6d343297d8e548392c03 \
|
||||
--hash=sha256:16efd54337136e8cd72fb9485c368d91d77a47ee2d42b057564aae201257d419 \
|
||||
--hash=sha256:1ab8f1f932e8f82355e75dda5413a57612c6ea448069d4fb2e217e9a4bed13d4 \
|
||||
--hash=sha256:223f4232855ade399bd409331e6ca70fb5578efef22cf4069a6090acc0f53c0e \
|
||||
--hash=sha256:2455cfaeb7ac70338b3257f41e21f0724f4b5b0c0e7702da67ee6c3640835b67 \
|
||||
--hash=sha256:2899456259589aa38bfb018c364d6ae7b53c5c22d8e27d0ec7609c2a1ff78b50 \
|
||||
--hash=sha256:2a29ba94d065945944016b6b74e538bdb1751a1db6ffb80c9d3c2e40d6fa9894 \
|
||||
--hash=sha256:2a87fa548561d2f4643c99cd13131acb607ddabb70682dcf1dff5f71f781a4bf \
|
||||
--hash=sha256:2e430cd2824f05f2d4f687701144556646bae8f249fd60aa1e4c768ba7018947 \
|
||||
--hash=sha256:36c3c175d34652a35475a73762b545f4527aec044910a651d2bf50de9c3352b1 \
|
||||
--hash=sha256:3818b8e2c4b5148567e1b09ce739006acfaa44ce3156f8cbbc11062994b8e8dd \
|
||||
--hash=sha256:3ab9fa9d6dc2a7f29d7affdf3edebf6ece6fb28a6d80b14c3b2fb9d39b9322c3 \
|
||||
--hash=sha256:3efea981d956a6f7173b4659849f55081867cf897e719f57383698af6f618a92 \
|
||||
--hash=sha256:4c8f293f14abc8fd3e8e01c5bd86e6ed0b6ef71936ded5bf10fe7a5efefbaca3 \
|
||||
--hash=sha256:5344a43228767f53a9df6e5b253f8cdca7dfc7b7aeae52551958192f56d98457 \
|
||||
--hash=sha256:58bfa3aa19ca4c0f28c5dde0ff56c520fbac6f0daf4fac66ed4c8d2fb7f22e74 \
|
||||
--hash=sha256:5b4545b8a40478183ac06c073e81a5ce4cf01bf1734962577cf2bb569a5b3bbf \
|
||||
--hash=sha256:5f50a1c177e2fa3ee0667a5ab79fdc6b23086bc8b589d90b93b4bd17eb0e64d1 \
|
||||
--hash=sha256:63da2ccc0857c311d764e7d3d90f429c252e83b52d1f8f1d1fe55be26827d1f4 \
|
||||
--hash=sha256:6749649eecd6a9871cae297bffa4ee76f90b4504a2a2ab528d9ebe912b101975 \
|
||||
--hash=sha256:6804daeb7ef69e7b36f76caddb85cccd63d0c56dedb47555d2fc969e2af6a1a5 \
|
||||
--hash=sha256:689bb688a1db722485e4610a503e3e9210dcc20c520b45ac8f7533c837be76fe \
|
||||
--hash=sha256:699a9af7dffaf67deeae27b2112aa06b41c370d5e7633e0ee0aea2e0b6c211f7 \
|
||||
--hash=sha256:6b418afe5df18233fc6b6093deb82a32895b6bb0b1155c2cdb05203f583053f1 \
|
||||
--hash=sha256:76cf573e5a365e790396a5cc2b909812633409306c6531a6877c59061e42c4f2 \
|
||||
--hash=sha256:7b515674acfdcadb0eb5d00d8a709868173acece5cb0be3dd165950cbfdf5409 \
|
||||
--hash=sha256:7b770ed79542ed52c519119473898198761d78beb24b107acf3ad65deae61f1f \
|
||||
--hash=sha256:7d2278d59425777cfcb19735018d897ca8303abe67cc735f9f97177ceff8027f \
|
||||
--hash=sha256:7e91ee82f4199af8c43d8158024cbdff3d931df350252288f0d4ce656df7f3b5 \
|
||||
--hash=sha256:821b7f59b99551c69c85a6039c65b75f5683bdc63270fec660f75da67469ca24 \
|
||||
--hash=sha256:822068f85e12a6e292803e112ab876bc03ed1f03dddb80154c395f891ca6b31e \
|
||||
--hash=sha256:8340225bd5e7a701c0fa98284c849c9b9fc9238abf53a0ebd90900f25d39a4e4 \
|
||||
--hash=sha256:85cabf64adec449132e55616e7ca3e1000ab449d1d0f9d7f83146ed5bdcb6d8a \
|
||||
--hash=sha256:880bbbcbe2fca64e2f4d8e04db47bcdf504936fa2b33933efd945e1b429bea8c \
|
||||
--hash=sha256:8d0b4612b66ff5d62d03bcaa043bb018f74dfea51184e53f067e6fdcba4bd8de \
|
||||
--hash=sha256:8e20cb5a47247e383cf4ff523205060991021233ebd6f924bca927fcf25cf86f \
|
||||
--hash=sha256:925073b2fe14ab9b87e73f9a5fde6ce6392da430f3004d8b72cc86f746f5163b \
|
||||
--hash=sha256:998c7c41910666d2976928c38ea96a70d1aa43be6fe502f21a651e17483a43c5 \
|
||||
--hash=sha256:9b22c5c66f67ae00c0199f6055705bc3eb3fcb08d03d2ec4059a2b1b25ed48d7 \
|
||||
--hash=sha256:9f102706d0ca011de571de32c3247c6476b55bb6bc65a20f682f000b07a4852a \
|
||||
--hash=sha256:a08cff61517ee26cb56f1e949cca38caabe9ea9fbb4b1e10a805dc39844b7d5c \
|
||||
--hash=sha256:a0a336d6d3e8b234a3aae3c674873d8f0e720b76bc1d9416866c41cd9500ffb9 \
|
||||
--hash=sha256:a35f8b7fa99f90dd2f5dc5a9fa12332642f087a7641289ca6c40d6e1a2637d8e \
|
||||
--hash=sha256:a38486985ca49cfa574a507e7a2215c0c780fd1778bb6290c21193b7211702ab \
|
||||
--hash=sha256:a5da296eb617d18e497bcf0a5c528f5d3b18dadb3619fbdadf4ed2356ef8d941 \
|
||||
--hash=sha256:a6e441a86553c310258aca15d1c05903aaf4965b23f3bc2d55f200804e005ee5 \
|
||||
--hash=sha256:a82d05da00a58b8e4c0008edbc8a4b6ec5a4bc1e2ee0fb6ed157cf634ed7fa45 \
|
||||
--hash=sha256:ab323679b8b3030000f2be63e22cdeea5b47ee0abd2d6a1dc0c8103ddaa56cd7 \
|
||||
--hash=sha256:b1f42b6921d0e81b1bcb5e395bc091a70f41c4d4e55ba99c6da2b31626c44892 \
|
||||
--hash=sha256:b23e19989c355ca854276178a0463951a653309fb8e57ce674497f2d9f208746 \
|
||||
--hash=sha256:b264171e3143d842ded311b7dccd46ff9ef34247129ff5bf5066123c55c2431c \
|
||||
--hash=sha256:b26a29f0b7fc6f0897f043ca366142d2b609dc60756ee6e4e90b5f762c6adc53 \
|
||||
--hash=sha256:b64d891da92e232c36976c80ed7ebb383e3f148489796d8d31a5b6a677825efe \
|
||||
--hash=sha256:b9cc34af337a97d470040f99ba4282f6e6bac88407d021688a5d585e44a23184 \
|
||||
--hash=sha256:bc718cd47b765e790eecb74d044cc8d37d58562f6c314ee9484df26276d36a38 \
|
||||
--hash=sha256:be7292c55101e22f2a3d4d8913944cbea71eea90792bf914add27454a13905df \
|
||||
--hash=sha256:c83203addf554215463b59f6399835201999b5e48019dc17f182ed5ad87205c9 \
|
||||
--hash=sha256:c9ec3eaf616d67db0764b3bb983962b4f385a1f08304fd30c7283954e6a7869b \
|
||||
--hash=sha256:ca34efc80a29351897e18888c71c6aca4a359247c87e0b1c7ada14f0ab0c0fb2 \
|
||||
--hash=sha256:ca989b91cf3a3ba28930a9fc1e9aeafc2a395448641df1f387a2d394638943b0 \
|
||||
--hash=sha256:d02a5399126a53492415d4906ab0ad0375a5456cc05c3fc0fc4ca11771745cda \
|
||||
--hash=sha256:d17bc7c2ccf49c478c5bdd447594e82692c74222698cfc9b5daae7ae7e90743b \
|
||||
--hash=sha256:d5bf6545cd27aaa8a13033ce56354ed9e25ab0e4ac3b5392b763d8d04b08e0c5 \
|
||||
--hash=sha256:d6b430a9938a5a5d85fc107d852262ddcd48602c120e3dbb02137c83d212b380 \
|
||||
--hash=sha256:da248f93f0418a9e9d94b0080d7ebc407a9a5e6d0b57bb30db9b5cc28de1ad33 \
|
||||
--hash=sha256:da4dd7c9c50c059aba52b3524f84d7de956f7fef88f0bafcf4ad7dde94a064e8 \
|
||||
--hash=sha256:df0623dcf9668ad0445e0558a21211d4e9a149ea8f5666917c8eeec515f0a6d1 \
|
||||
--hash=sha256:e5168986b90a8d1f2f9dc1b841467c74221bd752537b99761a93d2d981e04889 \
|
||||
--hash=sha256:efa29c2fe6b4fdd32e8ef81c1528506895eca86e1d8c4657fda04c9b3786ddf9 \
|
||||
--hash=sha256:f1496ea22ca2c830cbcbd473de8f114a320da308438ae65abad6bab7867fe38f \
|
||||
--hash=sha256:f49e52d174375a7def9915c9f06ec4e569d235ad428f70751765f48d5926678c
|
||||
pillow==9.4.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:013016af6b3a12a2f40b704677f8b51f72cb007dac785a9933d5c86a72a7fe33 \
|
||||
--hash=sha256:0845adc64fe9886db00f5ab68c4a8cd933ab749a87747555cec1c95acea64b0b \
|
||||
--hash=sha256:0884ba7b515163a1a05440a138adeb722b8a6ae2c2b33aea93ea3118dd3a899e \
|
||||
--hash=sha256:09b89ddc95c248ee788328528e6a2996e09eaccddeeb82a5356e92645733be35 \
|
||||
--hash=sha256:0dd4c681b82214b36273c18ca7ee87065a50e013112eea7d78c7a1b89a739153 \
|
||||
--hash=sha256:0e51f608da093e5d9038c592b5b575cadc12fd748af1479b5e858045fff955a9 \
|
||||
--hash=sha256:0f3269304c1a7ce82f1759c12ce731ef9b6e95b6df829dccd9fe42912cc48569 \
|
||||
--hash=sha256:16a8df99701f9095bea8a6c4b3197da105df6f74e6176c5b410bc2df2fd29a57 \
|
||||
--hash=sha256:19005a8e58b7c1796bc0167862b1f54a64d3b44ee5d48152b06bb861458bc0f8 \
|
||||
--hash=sha256:1b4b4e9dda4f4e4c4e6896f93e84a8f0bcca3b059de9ddf67dac3c334b1195e1 \
|
||||
--hash=sha256:28676836c7796805914b76b1837a40f76827ee0d5398f72f7dcc634bae7c6264 \
|
||||
--hash=sha256:2968c58feca624bb6c8502f9564dd187d0e1389964898f5e9e1fbc8533169157 \
|
||||
--hash=sha256:3f4cc516e0b264c8d4ccd6b6cbc69a07c6d582d8337df79be1e15a5056b258c9 \
|
||||
--hash=sha256:3fa1284762aacca6dc97474ee9c16f83990b8eeb6697f2ba17140d54b453e133 \
|
||||
--hash=sha256:43521ce2c4b865d385e78579a082b6ad1166ebed2b1a2293c3be1d68dd7ca3b9 \
|
||||
--hash=sha256:451f10ef963918e65b8869e17d67db5e2f4ab40e716ee6ce7129b0cde2876eab \
|
||||
--hash=sha256:46c259e87199041583658457372a183636ae8cd56dbf3f0755e0f376a7f9d0e6 \
|
||||
--hash=sha256:46f39cab8bbf4a384ba7cb0bc8bae7b7062b6a11cfac1ca4bc144dea90d4a9f5 \
|
||||
--hash=sha256:519e14e2c49fcf7616d6d2cfc5c70adae95682ae20f0395e9280db85e8d6c4df \
|
||||
--hash=sha256:53dcb50fbdc3fb2c55431a9b30caeb2f7027fcd2aeb501459464f0214200a503 \
|
||||
--hash=sha256:54614444887e0d3043557d9dbc697dbb16cfb5a35d672b7a0fcc1ed0cf1c600b \
|
||||
--hash=sha256:575d8912dca808edd9acd6f7795199332696d3469665ef26163cd090fa1f8bfa \
|
||||
--hash=sha256:5dd5a9c3091a0f414a963d427f920368e2b6a4c2f7527fdd82cde8ef0bc7a327 \
|
||||
--hash=sha256:5f532a2ad4d174eb73494e7397988e22bf427f91acc8e6ebf5bb10597b49c493 \
|
||||
--hash=sha256:60e7da3a3ad1812c128750fc1bc14a7ceeb8d29f77e0a2356a8fb2aa8925287d \
|
||||
--hash=sha256:653d7fb2df65efefbcbf81ef5fe5e5be931f1ee4332c2893ca638c9b11a409c4 \
|
||||
--hash=sha256:6663977496d616b618b6cfa43ec86e479ee62b942e1da76a2c3daa1c75933ef4 \
|
||||
--hash=sha256:6abfb51a82e919e3933eb137e17c4ae9c0475a25508ea88993bb59faf82f3b35 \
|
||||
--hash=sha256:6c6b1389ed66cdd174d040105123a5a1bc91d0aa7059c7261d20e583b6d8cbd2 \
|
||||
--hash=sha256:6d9dfb9959a3b0039ee06c1a1a90dc23bac3b430842dcb97908ddde05870601c \
|
||||
--hash=sha256:765cb54c0b8724a7c12c55146ae4647e0274a839fb6de7bcba841e04298e1011 \
|
||||
--hash=sha256:7a21222644ab69ddd9967cfe6f2bb420b460dae4289c9d40ff9a4896e7c35c9a \
|
||||
--hash=sha256:7ac7594397698f77bce84382929747130765f66406dc2cd8b4ab4da68ade4c6e \
|
||||
--hash=sha256:7cfc287da09f9d2a7ec146ee4d72d6ea1342e770d975e49a8621bf54eaa8f30f \
|
||||
--hash=sha256:83125753a60cfc8c412de5896d10a0a405e0bd88d0470ad82e0869ddf0cb3848 \
|
||||
--hash=sha256:847b114580c5cc9ebaf216dd8c8dbc6b00a3b7ab0131e173d7120e6deade1f57 \
|
||||
--hash=sha256:87708d78a14d56a990fbf4f9cb350b7d89ee8988705e58e39bdf4d82c149210f \
|
||||
--hash=sha256:8a2b5874d17e72dfb80d917213abd55d7e1ed2479f38f001f264f7ce7bae757c \
|
||||
--hash=sha256:8f127e7b028900421cad64f51f75c051b628db17fb00e099eb148761eed598c9 \
|
||||
--hash=sha256:94cdff45173b1919350601f82d61365e792895e3c3a3443cf99819e6fbf717a5 \
|
||||
--hash=sha256:99d92d148dd03fd19d16175b6d355cc1b01faf80dae93c6c3eb4163709edc0a9 \
|
||||
--hash=sha256:9a3049a10261d7f2b6514d35bbb7a4dfc3ece4c4de14ef5876c4b7a23a0e566d \
|
||||
--hash=sha256:9d9a62576b68cd90f7075876f4e8444487db5eeea0e4df3ba298ee38a8d067b0 \
|
||||
--hash=sha256:9e5f94742033898bfe84c93c831a6f552bb629448d4072dd312306bab3bd96f1 \
|
||||
--hash=sha256:a1c2d7780448eb93fbcc3789bf3916aa5720d942e37945f4056680317f1cd23e \
|
||||
--hash=sha256:a2e0f87144fcbbe54297cae708c5e7f9da21a4646523456b00cc956bd4c65815 \
|
||||
--hash=sha256:a4dfdae195335abb4e89cc9762b2edc524f3c6e80d647a9a81bf81e17e3fb6f0 \
|
||||
--hash=sha256:a96e6e23f2b79433390273eaf8cc94fec9c6370842e577ab10dabdcc7ea0a66b \
|
||||
--hash=sha256:aabdab8ec1e7ca7f1434d042bf8b1e92056245fb179790dc97ed040361f16bfd \
|
||||
--hash=sha256:b222090c455d6d1a64e6b7bb5f4035c4dff479e22455c9eaa1bdd4c75b52c80c \
|
||||
--hash=sha256:b52ff4f4e002f828ea6483faf4c4e8deea8d743cf801b74910243c58acc6eda3 \
|
||||
--hash=sha256:b70756ec9417c34e097f987b4d8c510975216ad26ba6e57ccb53bc758f490dab \
|
||||
--hash=sha256:b8c2f6eb0df979ee99433d8b3f6d193d9590f735cf12274c108bd954e30ca858 \
|
||||
--hash=sha256:b9b752ab91e78234941e44abdecc07f1f0d8f51fb62941d32995b8161f68cfe5 \
|
||||
--hash=sha256:ba6612b6548220ff5e9df85261bddc811a057b0b465a1226b39bfb8550616aee \
|
||||
--hash=sha256:bd752c5ff1b4a870b7661234694f24b1d2b9076b8bf337321a814c612665f343 \
|
||||
--hash=sha256:c3c4ed2ff6760e98d262e0cc9c9a7f7b8a9f61aa4d47c58835cdaf7b0b8811bb \
|
||||
--hash=sha256:c5c1362c14aee73f50143d74389b2c158707b4abce2cb055b7ad37ce60738d47 \
|
||||
--hash=sha256:cb362e3b0976dc994857391b776ddaa8c13c28a16f80ac6522c23d5257156bed \
|
||||
--hash=sha256:d197df5489004db87d90b918033edbeee0bd6df3848a204bca3ff0a903bef837 \
|
||||
--hash=sha256:d3b56206244dc8711f7e8b7d6cad4663917cd5b2d950799425076681e8766286 \
|
||||
--hash=sha256:d5b2f8a31bd43e0f18172d8ac82347c8f37ef3e0b414431157718aa234991b28 \
|
||||
--hash=sha256:d7081c084ceb58278dd3cf81f836bc818978c0ccc770cbbb202125ddabec6628 \
|
||||
--hash=sha256:db74f5562c09953b2c5f8ec4b7dfd3f5421f31811e97d1dbc0a7c93d6e3a24df \
|
||||
--hash=sha256:df41112ccce5d47770a0c13651479fbcd8793f34232a2dd9faeccb75eb5d0d0d \
|
||||
--hash=sha256:e1339790c083c5a4de48f688b4841f18df839eb3c9584a770cbd818b33e26d5d \
|
||||
--hash=sha256:e621b0246192d3b9cb1dc62c78cfa4c6f6d2ddc0ec207d43c0dedecb914f152a \
|
||||
--hash=sha256:e8c5cf126889a4de385c02a2c3d3aba4b00f70234bfddae82a5eaa3ee6d5e3e6 \
|
||||
--hash=sha256:e9d7747847c53a16a729b6ee5e737cf170f7a16611c143d95aa60a109a59c336 \
|
||||
--hash=sha256:eaef5d2de3c7e9b21f1e762f289d17b726c2239a42b11e25446abf82b26ac132 \
|
||||
--hash=sha256:ed3e4b4e1e6de75fdc16d3259098de7c6571b1a6cc863b1a49e7d3d53e036070 \
|
||||
--hash=sha256:ef21af928e807f10bf4141cad4746eee692a0dd3ff56cfb25fce076ec3cc8abe \
|
||||
--hash=sha256:f09598b416ba39a8f489c124447b007fe865f786a89dbfa48bb5cf395693132a \
|
||||
--hash=sha256:f0caf4a5dcf610d96c3bd32932bfac8aee61c96e60481c2a0ea58da435e25acd \
|
||||
--hash=sha256:f6e78171be3fb7941f9910ea15b4b14ec27725865a73c15277bc39f5ca4f8391 \
|
||||
--hash=sha256:f715c32e774a60a337b2bb8ad9839b4abf75b267a0f18806f6f4f5f1688c4b5a \
|
||||
--hash=sha256:fb5c1ad6bad98c57482236a21bf985ab0ef42bd51f7ad4e4538e89a997624e12
|
||||
psycopg2==2.9.5 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:093e3894d2d3c592ab0945d9eba9d139c139664dcf83a1c440b8a7aa9bb21955 \
|
||||
--hash=sha256:190d51e8c1b25a47484e52a79638a8182451d6f6dff99f26ad9bd81e5359a0fa \
|
||||
--hash=sha256:1a5c7d7d577e0eabfcf15eb87d1e19314c8c4f0e722a301f98e0e3a65e238b4e \
|
||||
--hash=sha256:1e5a38aa85bd660c53947bd28aeaafb6a97d70423606f1ccb044a03a1203fe4a \
|
||||
--hash=sha256:322fd5fca0b1113677089d4ebd5222c964b1760e361f151cbb2706c4912112c5 \
|
||||
--hash=sha256:4cb9936316d88bfab614666eb9e32995e794ed0f8f6b3b718666c22819c1d7ee \
|
||||
--hash=sha256:920bf418000dd17669d2904472efeab2b20546efd0548139618f8fa305d1d7ad \
|
||||
--hash=sha256:922cc5f0b98a5f2b1ff481f5551b95cd04580fd6f0c72d9b22e6c0145a4840e0 \
|
||||
--hash=sha256:a5246d2e683a972e2187a8714b5c2cf8156c064629f9a9b1a873c1730d9e245a \
|
||||
--hash=sha256:b9ac1b0d8ecc49e05e4e182694f418d27f3aedcfca854ebd6c05bb1cffa10d6d \
|
||||
--hash=sha256:d3ef67e630b0de0779c42912fe2cbae3805ebaba30cda27fea2a3de650a9414f \
|
||||
--hash=sha256:f5b6320dbc3cf6cfb9f25308286f9f7ab464e65cfb105b64cc9c52831748ced2 \
|
||||
--hash=sha256:fc04dd5189b90d825509caa510f20d1d504761e78b8dfb95a0ede180f71d50e5
|
||||
pyasn1-modules==0.2.8 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e \
|
||||
--hash=sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74
|
||||
pyasn1==0.4.8 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d \
|
||||
--hash=sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba
|
||||
python-cas==1.6.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:2abc0dae93c3b14097999fb7062f23cd09bc9f4e33d93f03c0cc040bd71ed50e \
|
||||
--hash=sha256:b8f1dcb1b6c56b3ff4f86bbef47bcdfcf932061ccd4812ae35e3f63954dfdb28
|
||||
python-ldap==3.4.3 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:ab26c519a0ef2a443a2a10391fa3c5cb52d7871323399db949ebfaa9f25ee2a0
|
||||
pytz==2022.7.1 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0 \
|
||||
--hash=sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a
|
||||
redis==4.4.2 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:a010f6cb7378065040a02839c3f75c7e0fb37a87116fb4a95be82a95552776c7 \
|
||||
--hash=sha256:e6206448e2f8a432871d07d432c13ed6c2abcf6b74edb436c99752b1371be387
|
||||
requests==2.28.2 ; python_version >= "3.9" and python_version < "4" \
|
||||
--hash=sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa \
|
||||
--hash=sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf
|
||||
setuptools==67.0.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:883131c5b6efa70b9101c7ef30b2b7b780a4283d5fc1616383cdf22c83cbefe6 \
|
||||
--hash=sha256:9d790961ba6219e9ff7d9557622d2fe136816a264dd01d5997cfc057d804853d
|
||||
six==1.16.0 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
|
||||
--hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
|
||||
sqlparse==0.4.3 ; python_version >= "3.9" and python_version < "4.0" \
|
||||
--hash=sha256:0323c0ec29cd52bceabc1b4d9d579e311f3e4961b98d174201d5622a23b85e34 \
|
||||
--hash=sha256:69ca804846bb114d2ec380e4360a8a340db83f0ccf3afceeb1404df028f57268
|
||||
urllib3==1.26.14 ; python_version >= "3.9" and python_version < "4" \
|
||||
--hash=sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72 \
|
||||
--hash=sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1
|
||||
-r requirements.txt
|
||||
|
||||
psycopg2
|
||||
python-ldap
|
||||
gunicorn
|
3
requirements.txt
Normal file
3
requirements.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
django==2.2.*
|
||||
Pillow
|
||||
django_cas_ng
|
|
@ -1,2 +0,0 @@
|
|||
[flake8]
|
||||
max-line-length = 99
|
|
@ -1,2 +0,0 @@
|
|||
(import ./. { }).devShell
|
||||
|
242
transfert.py
242
transfert.py
|
@ -1,242 +0,0 @@
|
|||
import json
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
import django
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
print("\nTransfert des fiches annuaires :")
|
||||
|
||||
# Configuration
|
||||
print("Paramétrage de Django...", end=" ")
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "annuaire.settings.prod")
|
||||
django.setup()
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
from authens.models import CASAccount # noqa
|
||||
|
||||
from fiches.management.commands._ldap import ClipperLDAP # noqa
|
||||
from fiches.models import Address, Department, Mail, Phone, Profile # noqa
|
||||
|
||||
print("[ok]")
|
||||
|
||||
|
||||
# Utilitaires
|
||||
def parse_date(s):
|
||||
try:
|
||||
return datetime.strptime(s, "%Y-%m-%d").date()
|
||||
except (ValueError, TypeError):
|
||||
return None
|
||||
|
||||
|
||||
def get_text_field(data):
|
||||
text = ""
|
||||
if data["interests"]:
|
||||
text += "Mes intérêts :\n" + data["interests"] + "\n\n"
|
||||
if data["websites"]:
|
||||
text += "Mes pages web :\n\n" + data["websites"] + "\n\n"
|
||||
if data["comments"]:
|
||||
text += data["comments"] + "\n\n"
|
||||
return text
|
||||
|
||||
|
||||
def get_address(data):
|
||||
content = data["lines"] + "\n"
|
||||
if data["zip"] and data["city"]:
|
||||
content += data["zip"] + ", " + data["city"]
|
||||
elif data["zip"] or data["city"]:
|
||||
content += data["zip"] + data["city"]
|
||||
if data["country"]:
|
||||
content += ", " + data["country"]
|
||||
return content
|
||||
|
||||
|
||||
def parse_list(s):
|
||||
s = s or ""
|
||||
return [p for p in s.replace(",", ";").replace("\n", ";").split(";") if p]
|
||||
|
||||
|
||||
def create_phones(s, profile, numeros, name="Téléphone"):
|
||||
for p in parse_list(s):
|
||||
numeros.append(Phone(profile=profile, name=name, number=p))
|
||||
|
||||
|
||||
def create_mails(s, profile, mails, name="E-mail"):
|
||||
for m in parse_list(s):
|
||||
mails.append(Mail(profile=profile, name=name, mail=m))
|
||||
|
||||
|
||||
print("Récupération des comptes clipper...", end=" ")
|
||||
|
||||
ldap = ClipperLDAP()
|
||||
clippers = {c.uid: c for c in ldap.get_clipper_list(stdout=sys.stdout)}
|
||||
|
||||
print("[ok]")
|
||||
|
||||
|
||||
depts = {
|
||||
dept: Department.objects.get_or_create(name=dept)[0].id
|
||||
for dept in ldap.verbose_depts.values()
|
||||
}
|
||||
|
||||
users_to_create = []
|
||||
users = {}
|
||||
profiles_to_create = []
|
||||
dept_m2m_to_create = []
|
||||
|
||||
|
||||
print("Création des comptes...", end=" ")
|
||||
|
||||
for clipper in clippers.values():
|
||||
user = User(username=clipper.uid, email=clipper.email)
|
||||
users_to_create.append(user)
|
||||
|
||||
User.objects.bulk_create(users_to_create)
|
||||
users = {u.username: (u, clippers[u.username]) for u in User.objects.all()}
|
||||
|
||||
print("[ok]")
|
||||
|
||||
|
||||
print("Création des comptes Authens...", end=" ")
|
||||
|
||||
cas_accounts = []
|
||||
|
||||
for (u, c) in users.values():
|
||||
cas_accounts.append(CASAccount(user=u, cas_login=c.uid, entrance_year=c.year))
|
||||
|
||||
CASAccount.objects.bulk_create(cas_accounts)
|
||||
|
||||
print("[ok]")
|
||||
|
||||
|
||||
fiches = {}
|
||||
references = {}
|
||||
adresses = []
|
||||
devises = {}
|
||||
numeros = []
|
||||
mails = []
|
||||
fiches_pk = {}
|
||||
|
||||
print("Récupération des anciennes fiches :")
|
||||
|
||||
# On recrée les profils
|
||||
with open("old_fiches.json") as json_file:
|
||||
data = json.load(json_file)
|
||||
for obj in data:
|
||||
if obj["model"] == "annuaire.fiches2021":
|
||||
obj_data = obj["fields"]
|
||||
try:
|
||||
user, clipper = users[obj_data["login"]]
|
||||
fiches_pk[obj["pk"]] = user.username
|
||||
fiches[obj_data["login"]] = Profile(
|
||||
user=user,
|
||||
full_name=" ".join((obj_data["firstname"], obj_data["lastname"])),
|
||||
promotion=clipper.year,
|
||||
birth_date=parse_date((obj_data["birthdate"] or "")),
|
||||
past_studies=(
|
||||
(obj_data["past_studies"] or "")
|
||||
+ "\n"
|
||||
+ (obj_data["studies"] or "")
|
||||
).strip(),
|
||||
experiences=(obj_data["experiences"] or ""),
|
||||
thurne=(obj_data["room"] or ""),
|
||||
text_field=get_text_field(obj_data),
|
||||
)
|
||||
create_phones(obj_data["phones"], fiches[obj_data["login"]], numeros)
|
||||
create_phones(
|
||||
obj_data["mobiles"],
|
||||
fiches[obj_data["login"]],
|
||||
numeros,
|
||||
name="Portable",
|
||||
)
|
||||
create_mails(obj_data["emails"], fiches[obj_data["login"]], mails)
|
||||
dept_m2m_to_create.append(
|
||||
Profile.department.through(
|
||||
profile=fiches[obj_data["login"]],
|
||||
department_id=depts[clipper.dept],
|
||||
)
|
||||
)
|
||||
except KeyError:
|
||||
print(f"\tLogin inconnu : {obj_data['login']}")
|
||||
elif obj["model"] == "annuaire.lesreferences2021":
|
||||
try:
|
||||
username = fiches_pk[obj["pk"]]
|
||||
surnom = obj["fields"]["nickname"]
|
||||
if surnom:
|
||||
fiche = fiches[username]
|
||||
if fiche.nickname:
|
||||
fiche.nickname += ", "
|
||||
fiche.nickname += surnom
|
||||
except KeyError:
|
||||
pass
|
||||
elif obj["model"] == "annuaire.adresses2021":
|
||||
try:
|
||||
username = fiches_pk[obj["pk"]]
|
||||
obj_data = obj["fields"]
|
||||
fiche = fiches[username]
|
||||
a = get_address(obj_data).strip()
|
||||
if a:
|
||||
adresses.append(Address(profile=fiche, name="Adresse", content=a))
|
||||
except KeyError:
|
||||
pass
|
||||
elif obj["model"] == "annuaire.devises2021":
|
||||
try:
|
||||
username = fiches_pk[obj["pk"]]
|
||||
obj_data = obj["fields"]
|
||||
fiche = fiches[username]
|
||||
if obj_data["quote"]:
|
||||
fiche.text_field += f"\n{obj_data['quote']}"
|
||||
if obj_data["source"]:
|
||||
fiche.text_field += f" ({obj_data['source']})"
|
||||
if obj_data["author"]:
|
||||
fiche.text_field += f", ({obj_data['author']})"
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
print("Création des nouvelles fiches fiches...", end=" ")
|
||||
|
||||
Profile.objects.bulk_create(fiches.values())
|
||||
|
||||
profils = {p.user.username: p for p in Profile.objects.select_related("user")}
|
||||
|
||||
print("[ok]")
|
||||
|
||||
|
||||
print("Rattachement des départements...", end=" ")
|
||||
|
||||
for dept_m2m in dept_m2m_to_create:
|
||||
dept_m2m.profile = profils[dept_m2m.profile.user.username]
|
||||
Profile.department.through.objects.bulk_create(dept_m2m_to_create)
|
||||
|
||||
print("[ok]")
|
||||
|
||||
|
||||
print("Création des numéros de téléphone...", end=" ")
|
||||
|
||||
for p in numeros:
|
||||
p.profile = profils[p.profile.user.username]
|
||||
Phone.objects.bulk_create(numeros)
|
||||
|
||||
print("[ok]")
|
||||
|
||||
|
||||
print("Création des adresses mail...", end=" ")
|
||||
|
||||
for m in mails:
|
||||
m.profile = profils[m.profile.user.username]
|
||||
Mail.objects.bulk_create(mails)
|
||||
|
||||
print("[ok]")
|
||||
|
||||
|
||||
print("Création des adresses...", end=" ")
|
||||
|
||||
for a in adresses:
|
||||
a.profile = profils[a.profile.user.username]
|
||||
Address.objects.bulk_create(adresses)
|
||||
|
||||
print("[ok]")
|
Loading…
Add table
Reference in a new issue