chore: Initial Commit

feat: Gestion des inscriptions [#1]
feat(frontend): Création des vues pour le paramétrage de l'école [#2]
feat: Gestion du login [#6]
fix: Correction lors de la migration des modèle [#8]
feat: Révision du menu principal [#9]
feat: Ajout d'un footer [#10]
feat: Création des dockers compose pour les environnements de
développement et de production [#12]
doc(ci): Mise en place de Husky et d'un suivi de version automatique [#14]
This commit is contained in:
Luc SORIGNET
2024-11-18 10:02:58 +01:00
committed by N3WT DE COMPET
commit af0cd1c840
228 changed files with 22694 additions and 0 deletions

View File

@ -0,0 +1,5 @@
from __future__ import absolute_import, unicode_literals
from .celery import app as celery_app
__all__ = ('celery_app',)
default_app_config = 'N3wtSchool.apps.N3wtSchoolConfig' # Assurer l'utilisation de la configuration d'application

View File

@ -0,0 +1,8 @@
# n3wtschool/apps.py
from django.apps import AppConfig
class N3wtSchoolConfig(AppConfig):
name = 'N3wtSchool'
def ready(self):
import N3wtSchool.signals

View File

@ -0,0 +1,16 @@
"""
ASGI config for N3wtSchool project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'N3wtSchool.settings')
application = get_asgi_application()

View File

@ -0,0 +1,86 @@
import logging
from django.db.models import Q
from GestionInscriptions.models import FicheInscription, Profil, Eleve
def getAllObjects(_objectName):
result = _objectName.objects.all()
if not result:
logging.warning("Aucun résultat n'a été trouvé - " + _objectName.__name__)
return result
def getObject(_objectName, _columnName, _value):
result=None
try :
result = _objectName.objects.get(**{_columnName: _value})
except _objectName.DoesNotExist:
logging.error("Aucun résultat n'a été trouvé - " + _objectName.__name__ + " (" + _columnName + "=" + str(_value) + ")")
return result
def getObjects(_objectName, _columnName, _value, _reverseCondition=False):
results=None
try :
results = _objectName.objects.filter(**{_columnName: _value}) if _reverseCondition == False else _objectName.objects.filter(~Q(**{_columnName: _value}))
except _objectName.DoesNotExist:
logging.error("Aucun résultat n'a été trouvé - " + _objectName.__name__ + " (" + _columnName + "=" + str(_value) + ")")
return results
def existsProfilInList(objectList, valueToCheck):
result = False
for objectInstance in objectList:
if objectInstance.email == valueToCheck:
result = True
return result
def getProfile(objectList, valueToCheck):
result = None
for objectInstance in objectList:
if objectInstance.email == valueToCheck:
result = objectInstance
return result
def getEleveByCodeFI(_codeFI):
eleve = None
ficheInscriptions_List=getAllObjects(FicheInscription)
for fi in ficheInscriptions_List:
if fi.codeLienInscription == _codeFI:
eleve = fi.eleve
return eleve
def getLastId(_object):
result = 1
try:
result = _object.objects.latest('id').id
except:
logging.warning("Aucun résultat n'a été trouvé - ")
return result
def searchObjects(_objectName, _searchTerm, _excludeState=None):
"""
Recherche générique sur les objets avec possibilité d'exclure certains états
_objectName: Classe du modèle
_searchTerm: Terme de recherche
_excludeState: État à exclure de la recherche (optionnel)
"""
try:
query = _objectName.objects.all()
# Si on a un état à exclure
if _excludeState is not None:
query = query.filter(etat__lt=_excludeState)
# Si on a un terme de recherche
if _searchTerm and _searchTerm.strip():
terms = _searchTerm.lower().strip().split()
for term in terms:
query = query.filter(
Q(eleve__nom__icontains=term) |
Q(eleve__prenom__icontains=term)
)
return query.order_by('eleve__nom', 'eleve__prenom')
except _objectName.DoesNotExist:
logging.error(f"Aucun résultat n'a été trouvé - {_objectName.__name__} (recherche: {_searchTerm})")
return None

View File

@ -0,0 +1,20 @@
from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
from django.apps import apps
import logging
# Définir le module de réglages de Django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'N3wtSchool.settings')
app = Celery('N3wtSchool')
# Lire les configurations de Celery depuis les réglages de Django
app.config_from_object('django.conf:settings', namespace='CELERY')
# Découverte automatique des tâches des apps Django
app.autodiscover_tasks(lambda: [n.name for n in apps.get_app_configs()])
# Configurer le logger global pour Celery
logger = logging.getLogger('celery')
logger.setLevel(logging.WARNING)

View File

@ -0,0 +1,31 @@
from typing import Final
WRONG_ID: Final = 1
INCOMPLETE: Final = 2
BAD_URL: Final = 3
ALREADY_EXISTS: Final = 4
DIFFERENT_PASWWORD: Final = 5
PROFIL_NOT_EXISTS: Final = 6
MESSAGE_REINIT_PASSWORD: Final = 7
EXPIRED_URL: Final = 8
PASSWORD_CHANGED: Final = 8
WRONG_MAIL_FORMAT: Final = 9
PROFIL_INACTIVE: Final = 10
MESSAGE_ACTIVATION_PROFILE: Final = 11
PROFIL_ACTIVE: Final = 12
returnMessage = {
WRONG_ID:'Identifiants invalides',
INCOMPLETE:'Renseignez les champs obligatoires',
BAD_URL:'Lien invalide : veuillez contacter l\'administrateur',
ALREADY_EXISTS: 'Profil déjà existant',
DIFFERENT_PASWWORD: 'Les mots de passe ne correspondent pas',
PROFIL_NOT_EXISTS: 'Aucun profil associé à cet utilisateur',
MESSAGE_REINIT_PASSWORD: 'Un mail a été envoyé à l\'adresse \'%s\'',
EXPIRED_URL:'L\'URL a expiré. Effectuer à nouveau la demande de réinitialisation de mot de passe : http://localhost:3000/password/reset?uuid=%s',
PASSWORD_CHANGED: 'Le mot de passe a été réinitialisé',
WRONG_MAIL_FORMAT: 'L\'adresse mail est mal formatée',
PROFIL_INACTIVE: 'Le profil n\'est pas actif',
MESSAGE_ACTIVATION_PROFILE: 'Votre profil a été activé avec succès',
PROFIL_ACTIVE: 'Le profil est déjà actif',
}

View File

@ -0,0 +1,10 @@
# redis_client.py
import redis
from django.conf import settings
# Configurer le client Redis
redis_client = redis.StrictRedis(
host=settings.REDIS_HOST,
port=settings.REDIS_PORT,
db=settings.REDIS_DB,
)

View File

@ -0,0 +1,14 @@
from io import BytesIO
from django.http import HttpResponse
from django.template.loader import get_template
from xhtml2pdf import pisa
def render_to_pdf(template_src, context_dict={}):
template = get_template(template_src)
html = template.render(context_dict)
result = BytesIO()
pdf = pisa.pisaDocument(BytesIO(html.encode("UTF-8")), result)
if pdf.err:
return HttpResponse("Invalid PDF", status_code=400, content_type='text/plain')
return HttpResponse(result.getvalue(), content_type='application/pdf')

View File

@ -0,0 +1,247 @@
"""
Django settings for N3wtSchool project.
Generated by 'django-admin startproject' using Django 5.0.4.
For more information on this file, see
https://docs.djangoproject.com/en/5.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.0/ref/settings/
"""
from pathlib import Path
import json
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
LOGIN_REDIRECT_URL = '/GestionInscriptions/fichesInscriptions'
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-afjm6kvigncxzx6jjjf(qb0n(*qvi#je79r=gqflcn007d_ve9'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['*']
# Application definition
INSTALLED_APPS = [
'GestionInscriptions.apps.GestioninscriptionsConfig',
'GestionLogin.apps.GestionloginConfig',
'GestionMessagerie.apps.GestionMessagerieConfig',
'GestionNotification.apps.GestionNotificationConfig',
'GestionEnseignants.apps.GestionenseignantsConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'corsheaders',
'django_celery_beat',
'N3wtSchool',
'drf_yasg',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', # Déplacez ici, avant CorsMiddleware
'corsheaders.middleware.CorsMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'N3wtSchool.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / "templates", BASE_DIR / "static/templates"],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://redis:6379',
}
}
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'
WSGI_APPLICATION = 'N3wtSchool.wsgi.application'
# Password validation
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {
'min_length': 6,
}
},
#{
# 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
#},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.0/howto/static-files/
DEBUG = True
STATIC_URL = 'static/'
STATICFILES_DIRS = [
BASE_DIR / 'static',
]
STATIC_ROOT = BASE_DIR / 'staticfiles'
# Default primary key field type
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
########################################################################
#################### Application Settings ##############################
########################################################################
with open('GestionInscriptions/Configuration/application.json', 'r') as f:
jsonObject = json.load(f)
DJANGO_SUPERUSER_PASSWORD='admin'
DJANGO_SUPERUSER_USERNAME='admin'
DJANGO_SUPERUSER_EMAIL='admin@n3wtschool.com'
EMAIL_HOST='smtp.gmail.com'
EMAIL_PORT=587
EMAIL_HOST_USER=jsonObject['mailFrom']
EMAIL_HOST_PASSWORD=jsonObject['password']
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_TLS = True
EMAIL_USE_SSL = False
EMAIL_INSCRIPTION_SUBJECT = '[N3WT-SCHOOL] Dossier Inscription'
EMAIL_INSCRIPTION_CORPUS = """Bonjour,
Afin de procéder à l'inscription de votre petit bout, vous trouverez ci-joint le lien vers la page d'authentification : http://localhost:3000/users/login
S'il s'agit de votre première connexion, veuillez procéder à l'activation de votre compte : http://localhost:3000/users/subscribe
identifiant = %s
Cordialement,
"""
EMAIL_RELANCE_SUBJECT = '[N3WT-SCHOOL] Relance - Dossier Inscription'
EMAIL_RELANCE_CORPUS = 'Bonjour,\nN\'ayant pas eu de retour de votre part, nous vous renvoyons le lien vers le formulaire d\'inscription : http://localhost:3000/users/login\nCordialement'
EMAIL_REINIT_SUBJECT = 'Réinitialisation du mot de passe'
EMAIL_REINIT_CORPUS = 'Bonjour,\nVous trouverez ci-joint le lien pour réinitialiser votre mot de passe : http://localhost:3000/users/password/reset?uuid=%s\nCordialement'
DOCUMENT_DIR = 'documents'
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_ALL_HEADERS = True
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000"
]
CSRF_TRUSTED_ORIGINS = [
"http://localhost:3000", # Front Next.js
"http://localhost:8080" # Insomnia
]
CSRF_COOKIE_HTTPONLY = False
CSRF_COOKIE_SECURE = False
CSRF_COOKIE_NAME = 'csrftoken'
USE_TZ = True
TZ_APPLI = 'Europe/Paris'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
"NAME": "school",
"USER": "postgres",
"PASSWORD": "postgres",
"HOST": "database",
"PORT": "5432",
}
}
AUTH_USER_MODEL = 'GestionLogin.Profil'
AUTHENTICATION_BACKENDS = ('GestionLogin.backends.EmailBackend', )
SILENCED_SYSTEM_CHECKS = ["auth.W004"]
EXPIRATION_URL_NB_DAYS = 7
EXPIRATION_DI_NB_DAYS = 20
DATE_FORMAT = '%d-%m-%Y %H:%M'
EXPIRATION_SESSION_NB_SEC = 10
NB_RESULT_PER_PAGE = 8
NB_MAX_PAGE = 100
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'GestionInscriptions.pagination.CustomPagination',
'PAGE_SIZE': NB_RESULT_PER_PAGE
}
CELERY_BROKER_URL = 'redis://redis:6379/0'
CELERY_RESULT_BACKEND = 'redis://redis:6379/0'
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TIMEZONE = 'Europe/Paris'
CELERY_BROKER_CONNECTION_RETRY_ON_STARTUP = True
URL_DJANGO = 'http://localhost:8080/'
REDIS_HOST = 'redis'
REDIS_PORT = 6379
REDIS_DB = 0
REDIS_PASSWORD = None
SECRET_KEY = 'QWQ8bYlCz1NpQ9G0vR5kxMnvWszfH2y3'

View File

@ -0,0 +1,21 @@
from django.db.models.signals import post_migrate
from django.dispatch import receiver
from django_celery_beat.models import IntervalSchedule, PeriodicTask
import json
@receiver(post_migrate)
def setup_periodic_tasks(sender, **kwargs):
schedule, created = IntervalSchedule.objects.get_or_create(
every=5,
period=IntervalSchedule.SECONDS,
)
# Déclarer la tâche périodique
PeriodicTask.objects.get_or_create(
interval=schedule, # Utiliser l'intervalle défini ci-dessus
name='Tâche périodique toutes les 5 secondes',
task='GestionInscriptions.tasks.check_for_signature_deadlines', # Remplacer par le nom de ta tâche
kwargs=json.dumps({}) # Si nécessaire, ajoute
)

View File

@ -0,0 +1,49 @@
"""
URL configuration for N3wtSchool project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import include, path, re_path
from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
schema_view = get_schema_view(
openapi.Info(
title="N3wtSchool API",
default_version='v1',
description="Documentation de l'API de N3wtSchool",
terms_of_service="https://www.google.com/policies/terms/",
contact=openapi.Contact(email="contact@example.com"),
license=openapi.License(name="BSD License"),
),
public=True,
permission_classes=(permissions.AllowAny,),
)
urlpatterns = [
path('admin/', admin.site.urls),
path("GestionInscriptions/", include(("GestionInscriptions.urls", 'GestionInscriptions'), namespace='GestionInscriptions')),
path("GestionLogin/", include(("GestionLogin.urls", 'GestionLogin'), namespace='GestionLogin')),
path("GestionMessagerie/", include(("GestionMessagerie.urls", 'GestionMessagerie'), namespace='GestionMessagerie')),
path("GestionNotification/", include(("GestionNotification.urls", 'GestionNotification'), namespace='GestionNotification')),
path("GestionEnseignants/", include(("GestionEnseignants.urls", 'GestionEnseignants'), namespace='GestionEnseignants')),
# Documentation Api
re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]

View File

@ -0,0 +1,16 @@
"""
WSGI config for N3wtSchool project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'N3wtSchool.settings')
application = get_wsgi_application()