Compare commits

..

19 commits

Author SHA1 Message Date
aa949f84b2 De retour pour les u-strings 2021-02-07 00:59:09 +01:00
b6d1ee6aa8 On enlève coding utf-8 2021-02-07 00:57:42 +01:00
2c92f35a75 black & isort 2021-02-07 00:45:46 +01:00
5d3f840de1 isort 2021-02-07 00:44:58 +01:00
9ddf4d0c6d black 2021-02-07 00:41:22 +01:00
56bc3a701e On remplace les u-strings 2021-02-07 00:40:55 +01:00
Robin Champenois
f7ac3313c8 Commentaires Tom bis 2021-02-06 22:48:03 +01:00
Robin Champenois
f48570bdaf Carte édition lieu 2021-02-06 18:58:56 +01:00
Robin Champenois
778c4cde72 Remarques Tom 2021-02-06 18:55:52 +01:00
Robin Champenois
de47d9033a Upgrade requirements 2021-01-31 23:48:56 +01:00
Robin Champenois
bc9fd155e6 Fix map & settings 2021-01-31 23:48:46 +01:00
Robin Champenois
04efa743f6 Corrige les tests 2021-01-31 23:25:44 +01:00
Robin Champenois
126bce9be3 Gestion automatique de la scolarité 2021-01-31 22:53:59 +01:00
Robin Champenois
9004c802eb Meilleur test has_nonENS_mail 2021-01-24 23:42:13 +01:00
Robin Champenois
90aa558896 Gestion des adresses et du mot de passe 2021-01-24 23:11:10 +01:00
Robin Champenois
4e683f62e1 Move to django_simple_email_confirmation 2021-01-23 21:17:52 +01:00
Robin Champenois
5644ea9290 Intégration AuthENS 2021-01-17 23:48:40 +01:00
Robin Champenois
79eb294ce5 AuthENS : migration depuis allauth 2021-01-17 21:15:52 +01:00
Evarin
72f28d185c Upgrade django2.2 2021-01-17 12:14:11 +01:00
33 changed files with 313 additions and 588 deletions

View file

@ -1 +0,0 @@
insecure-secret-key

1
.envrc
View file

@ -1 +0,0 @@
use nix

4
.gitignore vendored
View file

@ -108,6 +108,6 @@ test.py
.#*
*.sqlite3
.sass-cache
/static/
settings.py
secrets.py
.direnv
.pre-commit-config.yaml

View file

@ -1,242 +0,0 @@
"""
Django settings for the experiENS project
"""
from pathlib import Path
from loadcredential import Credentials
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
credentials = Credentials(env_prefix="EXPERIENS_")
# 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", [])
SITE_ID = 1
###
# ElasticSearch configuration
USE_ELASTICSEARCH = credentials.get_json("USE_ELASTICSEARCH", False)
ELASTICSEARCH_DSL = credentials.get_json(
"ELASTICSEARCH_DSL", {"default": {"hosts": "127.0.0.1:9200"}}
)
###
# Libraries configuration
GDAL_LIBRARY_PATH = credentials.get("GDAL_LIBRARY_PATH")
GEOS_LIBRARY_PATH = credentials.get("GEOS_LIBRARY_PATH")
###
# List the installed applications
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django.contrib.gis",
"django.contrib.sites",
*(["django_elasticsearch_dsl"] if USE_ELASTICSEARCH else []),
"simple_email_confirmation",
"authens",
"tastypie",
"braces",
"tinymce",
"taggit",
"taggit_autosuggest",
"avisstage",
]
###
# 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.contrib.auth.context_processors.auth",
"django.template.context_processors.debug",
"django.template.context_processors.i18n",
"django.template.context_processors.media",
"django.template.context_processors.static",
"django.template.context_processors.tz",
"django.template.context_processors.request",
"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.contrib.gis.db.backends.spatialite",
"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")),
]
###
# Authentication configuration
AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.ModelBackend",
"experiENS.auth.ENSCASBackend",
]
CAS_SERVER_URL = "https://cas.eleves.ens.fr/" # SPI CAS
AUTHENS_USE_OLDCAS = False
LOGIN_URL = reverse_lazy("authens:login")
LOGOUT_URL = reverse_lazy("authens:logout")
LOGIN_REDIRECT_URL = reverse_lazy("avisstage:perso")
LOGOUT_REDIRECT_URL = reverse_lazy("avisstage:index")
AUTH_PASSWORD_VALIDATORS = [
{"NAME": f"django.contrib.auth.password_validation.{v}"}
for v in [
"UserAttributeSimilarityValidator",
"MinimumLengthValidator",
"CommonPasswordValidator",
"NumericPasswordValidator",
]
]
###
# Logging configuration
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"handlers": {
"file": {
"level": "INFO",
"class": "logging.FileHandler",
"filename": credentials.get(
"RECHERCHE_LOG_FILE", BASE_DIR / "recherche.log"
),
},
},
"loggers": {
"recherche": {
"handlers": ["file"],
"level": "INFO",
"propagate": True,
},
},
}
###
# LDAP configuration
CLIPPER_LDAP_SERVER = credentials.get("CLIPPER_LDAP_SERVER", "ldaps://localhost:636")
# Development settings
if DEBUG:
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
INSTALLED_APPS += [
"debug_toolbar",
]
MIDDLEWARE = ["debug_toolbar.middleware.DebugToolbarMiddleware", *MIDDLEWARE]

View file

@ -4,7 +4,7 @@ from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from avisstage.models import AvisLieu, AvisStage, Lieu, Normalien, Stage, StageMatiere
from avisstage.models import *
class NormalienInline(admin.StackedInline):

View file

@ -1,11 +1,11 @@
from tastypie import fields
from tastypie import fields, utils
from tastypie.authentication import SessionAuthentication
from tastypie.resources import ModelResource
from django.contrib.gis import geos
from django.urls import reverse
from .models import Lieu, Normalien, Stage
from .models import Lieu, Normalien, Stage, StageMatiere
from .utils import approximate_distance

View file

@ -1,7 +1,7 @@
from django_elasticsearch_dsl import Document, Index, fields
from elasticsearch_dsl import analyzer, token_filter
from django_elasticsearch_dsl import DocType, Index, fields
from elasticsearch_dsl import analyzer, token_filter, tokenizer
from .models import Stage
from .models import AvisLieu, AvisStage, Stage
from .statics import PAYS_OPTIONS
PAYS_DICT = dict(PAYS_OPTIONS)
@ -14,6 +14,7 @@ text_analyzer = analyzer(
tokenizer="standard",
filter=[
"lowercase",
"standard",
"asciifolding",
token_filter("frstop", type="stop", stopwords="_french_"),
token_filter("frsnow", type="snowball", language="French"),
@ -23,23 +24,23 @@ stage.analyzer(text_analyzer)
@stage.doc_type
class StageDocument(Document):
class StageDocument(DocType):
lieux = fields.ObjectField(
properties={
"nom": fields.TextField(),
"ville": fields.TextField(),
"pays": fields.TextField(),
"nom": fields.StringField(),
"ville": fields.StringField(),
"pays": fields.StringField(),
}
)
auteur = fields.ObjectField(
properties={
"nom": fields.TextField(),
"nom": fields.StringField(),
}
)
thematiques = fields.TextField()
matieres = fields.TextField()
thematiques = fields.StringField()
matieres = fields.StringField()
class Django:
class Meta:
model = Stage
fields = [
"sujet",

View file

@ -7,7 +7,7 @@ from django import forms
from django.contrib.auth.forms import PasswordResetForm
from django.utils import timezone
from .models import AvisLieu, AvisStage, Lieu, Stage, User
from .models import AvisLieu, AvisStage, Lieu, Normalien, Stage, User
from .widgets import LatLonField
@ -59,10 +59,8 @@ class StageForm(forms.ModelForm):
"encadrants",
]
help_texts = {
"thematiques": "Mettez une virgule pour valider votre thématique si la suggestion ne "
"correspond pas ou si elle n'existe pas encore",
"structure": "Nom de l'équipe, du laboratoire, de la startup... (si le lieu ne suffit "
"pas)",
"thematiques": "Mettez une virgule pour valider votre thématique si la suggestion ne correspond pas ou si elle n'existe pas encore",
"structure": "Nom de l'équipe, du laboratoire, de la startup... (si le lieu ne suffit pas)",
}
labels = {
"date_debut": "Date de début",
@ -102,30 +100,11 @@ class AvisStageForm(HTMLTrimmerForm):
"les_moins",
]
help_texts = {
"chapo": (
'"Trop long, pas lu" : une accroche résumant ce que vous avez pensé de ce séjour'
),
"avis_ambiance": (
"Avez-vous passé un bon moment à ce travail ? Étiez-vous assez guidé·e ? "
"Aviez-vous un bon contact avec vos encadrant·e·s ? Y avait-il une bonne "
"ambiance dans l'équipe ?"
),
"avis_sujet": (
"Quelle était votre mission ? Qu'en avez-vous retiré ? Le travail "
"correspondait-il à vos attentes ? Était-ce à votre niveau, trop dur, "
"trop facile ?"
),
"avis_admin": (
"Avez-vous commencé votre travail à la date prévue ? Était-ce compliqué "
"d'obtenir les documents nécessaires (visa, contrats, etc) ? L'administration "
"de l'établissement vous a-t-elle aidé·e ? Étiez-vous rémunéré·e ?"
),
"avis_prestage": (
"Comment avez-vous trouvé où aller pour cette expérience ? À quel moment "
"avez-vous commencé à chercher ? Avez-vous eu des entretiens pour obtenir "
"votre place ? Avez-vous eu d'autres pistes, pourquoi avez-vous choisi "
"cette option ?"
),
"chapo": '"Trop long, pas lu" : une accroche résumant ce que vous avez pensé de ce séjour',
"avis_ambiance": "Avez-vous passé un bon moment à ce travail ? Étiez-vous assez guidé⋅e ? Aviez-vous un bon contact avec vos encadrant⋅e⋅s ? Y avait-il une bonne ambiance dans l'équipe ?",
"avis_sujet": "Quelle était votre mission ? Qu'en avez-vous retiré ? Le travail correspondait-il à vos attentes ? Était-ce à votre niveau, trop dur, trop facile ?",
"avis_admin": "Avez-vous commencé votre travail à la date prévue ? Était-ce compliqué d'obtenir les documents nécessaires (visa, contrats, etc) ? L'administration de l'établissement vous a-t-elle aidé⋅e ? Étiez-vous rémunéré⋅e ?",
"avis_prestage": "Comment avez-vous trouvé où aller pour cette expérience ? À quel moment avez-vous commencé à chercher ? Avez-vous eu des entretiens pour obtenir votre place ? Avez-vous eu d'autres pistes, pourquoi avez-vous choisi cette option ?",
"les_plus": "Les principaux points positifs de cette expérience",
"les_moins": "Ce qui aurait pu être mieux",
}
@ -144,21 +123,10 @@ class AvisLieuForm(HTMLTrimmerForm):
"les_moins",
]
help_texts = {
"chapo": (
'"Trop long, pas lu" : une accroche résumant ce que vous avez pensé de cet endroit'
),
"avis_lieustage": (
"Qu'avez-vous pensé des lieux où vous travailliez ? Les bâtiments "
"étaient-ils modernes ? Était-il agréable d'y travailler ?"
),
"avis_pratique": (
"Avez-vous eu du mal à trouver un logement ? Y-a-t-il des choses que vous avez "
"apprises sur place qu'il vous aurait été utile de savoir avant de partir ?"
),
"avis_tourisme": (
"Y-a-t-il des lieux à visiter dans cette zone ? Avez-vous pratiqué "
"des activités sportives ? Est-il facile de faire des rencontres ?"
),
"chapo": '"Trop long, pas lu" : une accroche résumant ce que vous avez pensé de cet endroit',
"avis_lieustage": "Qu'avez-vous pensé des lieux où vous travailliez ? Les bâtiments étaient-ils modernes ? Était-il agréable d'y travailler ?",
"avis_pratique": "Avez-vous eu du mal à trouver un logement ? Y-a-t-il des choses que vous avez apprises sur place qu'il vous aurait été utile de savoir avant de partir ?",
"avis_tourisme": "Y-a-t-il des lieux à visiter dans cette zone ? Avez-vous pratiqué des activités sportives ? Est-il facile de faire des rencontres ?",
"les_plus": "Les meilleures raisons de partir à cet endroit",
"les_moins": "Ce qui vous a gêné ou manqué là-bas",
}

View file

@ -1,6 +1,7 @@
from django.core.management.base import BaseCommand
from django.core.management.base import BaseCommand, CommandError
from django.db.models import Count
from avisstage.models import Lieu
from avisstage.models import Lieu, Stage
class Command(BaseCommand):

View file

@ -1,7 +1,7 @@
from django.core.management.base import BaseCommand
from django.core.management.base import BaseCommand, CommandError
from django.db.models import Count
from avisstage.models import Stage
from avisstage.models import Lieu, Stage
class Command(BaseCommand):

View file

@ -1,6 +1,7 @@
from django.core.management.base import BaseCommand
from django.core.management.base import BaseCommand, CommandError
from django.db.models import Count
from avisstage.models import Lieu
from avisstage.models import Lieu, Stage
class Command(BaseCommand):

View file

@ -1,6 +1,6 @@
from datetime import timedelta
from django.core.management.base import BaseCommand
from django.core.management.base import BaseCommand, CommandError
from django.utils import timezone
from avisstage.models import Normalien
@ -13,6 +13,6 @@ class Command(BaseCommand):
return
def handle(self, *args, **options):
t = timezone.now() - timedelta(days=365)
old_conn = timezone.now() - timedelta(days=365)
Normalien.objects.all().update(last_cas_connect=t)
self.stdout.write(self.style.SUCCESS("Terminé"))

View file

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2017-06-20 17:45
from __future__ import unicode_literals
import taggit_autosuggest.managers
import tinymce.models

View file

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-10-02 20:43
from __future__ import unicode_literals
import tinymce.models

View file

@ -1,5 +1,6 @@
from datetime import timedelta
from authens.models import CASAccount
from authens.signals import post_cas_connect
from taggit_autosuggest.managers import TaggableManager
from tinymce.models import HTMLField as RichTextField
@ -8,9 +9,12 @@ from django.contrib.auth.models import User
from django.contrib.gis.db import models as geomodels
from django.db import models
from django.db.models.signals import post_save
from django.forms.widgets import DateInput
from django.template.defaultfilters import slugify
from django.urls import reverse
from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.html import strip_tags
from .statics import (
DEPARTEMENTS_DEFAUT,
@ -22,7 +26,7 @@ from .statics import (
TYPE_STAGE_DICT,
TYPE_STAGE_OPTIONS,
)
from .utils import choices_length
from .utils import choices_length, is_email_ens
def _default_cas_login():
@ -98,7 +102,6 @@ def create_basic_user_profile(sender, instance, created, **kwargs):
post_save.connect(create_basic_user_profile, sender=User)
# Hook d'authENS : information du CAS
def handle_cas_connection(sender, instance, created, cas_login, attributes, **kwargs):
profil, created = Normalien.objects.get_or_create(user=instance)

View file

@ -1,4 +1,3 @@
# coding: utf-8
import re
from django import template

View file

@ -9,11 +9,10 @@ from django.test import TestCase
from django.urls import reverse
from django.utils import timezone
from .models import AvisLieu, Lieu, Stage, StageMatiere, User
from .models import AvisLieu, Lieu, Normalien, Stage, StageMatiere, User
class ExperiENSTestCase(TestCase):
# Dummy database
def setUp(self):

View file

@ -2,7 +2,7 @@ from tastypie.api import Api
from django.urls import include, path
from . import api, views, views_search
from . import api, views
v1_api = Api(api_name="v1")
v1_api.register(api.LieuResource())
@ -56,13 +56,9 @@ urlpatterns = [
views.DefinirMotDePasse.as_view(),
name="mdp_edit",
),
path("recherche/", views_search.recherche, name="recherche"),
path(
"recherche/resultats/",
views_search.recherche_resultats,
name="recherche_resultats",
),
path("recherche/items/", views_search.stage_items, name="stage_items"),
path("recherche/", views.recherche, name="recherche"),
path("recherche/resultats/", views.recherche_resultats, name="recherche_resultats"),
path("recherche/items/", views.stage_items, name="stage_items"),
path("feedback/", views.feedback, name="feedback"),
path("moderation/", views.statistiques, name="moderation"),
path("api/", include(v1_api.urls)),

View file

@ -10,6 +10,7 @@ from django.conf import settings
from django.contrib import messages
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required
from django.contrib.auth.tokens import default_token_generator
from django.contrib.auth.views import PasswordResetConfirmView
from django.core.mail import send_mail
from django.db.models import Count, Q
@ -21,6 +22,8 @@ from django.views.generic import (
DeleteView,
DetailView,
FormView,
ListView,
TemplateView,
UpdateView,
View,
)
@ -37,12 +40,12 @@ from .forms import (
)
from .models import AvisLieu, AvisStage, Lieu, Normalien, Stage
from .utils import en_scolarite
from .views_search import *
#
# LECTURE
#
# Page d'accueil
def index(request):
num_stages = Stage.objects.filter(public=True).count()
@ -233,8 +236,7 @@ def save_lieu(request):
# On regarde si les stages associés à ce lieu "appartiennent" tous à l'utilisateur
not_same_user = lieu.stages.exclude(auteur=normalien).count()
# Si d'autres personnes ont un stage à cet endroit,
# on crée un nouveau lieu, un peu à côté
# Si d'autres personnes ont un stage à cet endroit, on crée un nouveau lieu, un peu à côté
if not_same_user > 0:
lieu = Lieu()
# Servira à bouger un peu le lieu
@ -512,7 +514,7 @@ class ConfirmeAdresse(LoginRequiredMixin, View):
email = EmailAddress.objects.confirm(
self.kwargs["key"], self.request.user, True
)
except Exception:
except Exception as e:
raise Http404()
messages.add_message(
self.request,
@ -544,10 +546,9 @@ class EnvoieLienMotDePasse(LoginRequiredMixin, View):
messages.add_message(
self.request,
messages.INFO,
(
"Un mail a été envoyé à {email}. Merci de vérifier vos indésirables "
"si vous ne le recevez pas bientôt"
).format(email=self.request.user.email),
"Un mail a été envoyé à {email}. Merci de vérifier vos indésirables si vous ne le recevez pas bientôt".format(
email=self.request.user.email
),
)
return redirect(reverse("avisstage:parametres"))

View file

@ -6,10 +6,10 @@ from django import forms
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.cache import cache
from django.core.paginator import InvalidPage, Paginator
from django.core.paginator import Paginator
from django.db.models import Case, Q, When
from django.http import HttpResponseBadRequest, JsonResponse
from django.shortcuts import render
from django.shortcuts import get_object_or_404, redirect, render
USE_ELASTICSEARCH = getattr(settings, "USE_ELASTICSEARCH", True)
@ -22,13 +22,12 @@ from .statics import NIVEAU_SCOL_OPTIONS, TYPE_LIEU_OPTIONS, TYPE_STAGE_OPTIONS
logger = logging.getLogger("recherche")
# Recherche
class SearchForm(forms.Form):
generique = forms.CharField(required=False)
sujet = forms.CharField(label="À propos de", required=False)
contexte = forms.CharField(
label="Contexte (lieu, encadrant·e·s, structure)", required=False
label="Contexte (lieu, encadrant⋅e⋅s, structure)", required=False
)
apres_annee = forms.IntegerField(label="Après cette année", required=False)
@ -80,20 +79,7 @@ def cherche(**kwargs):
if field_relevant("generique"):
# print("Filtre generique", kwargs['generique'])
dsl = dsl.query(
"multi_match",
query=kwargs["generique"],
fuzziness="auto",
fields=[
"sujet^3",
"encadrants",
"type_stage",
"niveau_scol",
"structure",
"lieux.*^2",
"auteur.nom^2",
"thematiques^2",
"matieres",
],
"match", _all={"query": kwargs["generique"], "fuzziness": "auto"}
)
use_dsl = True
@ -147,11 +133,11 @@ def cherche(**kwargs):
# Dates
if field_relevant("avant_annee", False):
dte = date(min(2100, kwargs["avant_annee"]) + 1, 1, 1)
dte = date(kwargs["avant_annee"] + 1, 1, 1)
filtres &= Q(date_fin__lt=dte)
if field_relevant("apres_annee", False):
dte = date(max(2000, kwargs["apres_annee"]), 1, 1)
dte = date(kwargs["apres_annee"], 1, 1)
filtres &= Q(date_debut__gte=dte)
# Type de stage
@ -165,29 +151,19 @@ def cherche(**kwargs):
if field_relevant("type_lieu"):
filtres &= Q(lieux__type_lieu=kwargs["type_lieu"])
# Tri
# Application
if USE_ELASTICSEARCH and use_dsl:
filtres &= Q(id__in=[s.meta.id for s in dsl.scan()])
# print(filtres)
resultat = Stage.objects.filter(filtres)
tri = "pertinence"
if not use_dsl:
kwargs["tri"] = "-date_maj"
if field_relevant("tri") and kwargs["tri"] in ["-date_maj"]:
tri = kwargs["tri"]
if not use_dsl:
tri = "-date_maj"
# Application
resultat = Stage.objects.filter(filtres).distinct()
if USE_ELASTICSEARCH and use_dsl:
dsl_res = [s.meta.id for s in dsl.scan()]
resultat = resultat.filter(id__in=dsl_res)
if tri == "pertinence":
resultat = resultat.order_by(
Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(dsl_res)])
)
else:
resultat = resultat.order_by(tri)
else:
resultat = resultat.order_by(tri)
return resultat, tri

View file

@ -1,94 +0,0 @@
{
sources ? import ./npins,
pkgs ? import sources.nixpkgs { },
}:
let
nix-pkgs = import sources.nix-pkgs { inherit pkgs; };
check = (import sources.git-hooks).run {
src = ./.;
hooks = {
# Python hooks
black = {
enable = true;
stages = [ "pre-push" ];
};
isort = {
enable = true;
stages = [ "pre-push" ];
};
ruff = {
enable = true;
stages = [ "pre-push" ];
};
# Misc Hooks
commitizen.enable = true;
};
};
python3 = pkgs.python3.override {
packageOverrides = _: _: {
inherit (nix-pkgs)
authens
django-braces
django-elasticsearch-dsl
django-simple-email-confirmation
django-taggit-autosuggest
django-tinymce
loadcredential
spatialite
;
};
};
in
{
devShell = pkgs.mkShell {
name = "annuaire.dev";
packages = [
(python3.withPackages (ps: [
ps.authens
ps.django
ps.django-braces
ps.django-elasticsearch-dsl
ps.django-simple-email-confirmation
ps.django-taggit
ps.django-taggit-autosuggest
ps.django-tastypie
ps.django-tinymce
ps.loadcredential
# Dev packages
ps.django-debug-toolbar
ps.django-stubs
ps.spatialite
]))
];
env = {
DJANGO_SETTINGS_MODULE = "app.settings";
CREDENTIALS_DIRECTORY = builtins.toString ./.credentials;
EXPERIENS_DEBUG = builtins.toJSON true;
EXPERIENS_STATIC_ROOT = builtins.toString ./.static;
EXPERIENS_GDAL_LIBRARY_PATH = "${pkgs.gdal}/lib/libgdal.so";
EXPERIENS_GEOS_LIBRARY_PATH = "${pkgs.geos}/lib/libgeos_c.so";
};
shellHook = ''
${check.shellHook}
if [ ! -d .static ]; then
mkdir .static
fi
'';
};
}

143
experiENS/settings_base.py Normal file
View file

@ -0,0 +1,143 @@
"""
Django settings for experiENS project.
For more information on this file, see
https://docs.djangoproject.com/en/1.7/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.7/ref/settings/
"""
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
from django.urls import reverse_lazy
from .secrets import GOOGLE_API_KEY, MAPBOX_API_KEY, SECRET_KEY
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django.contrib.gis",
"django.contrib.sites",
"django_elasticsearch_dsl",
#'allauth', # Uncomment that part when you
#'allauth.account', # apply migration
#'allauth.socialaccount', # Allauth -> AuthENS
"simple_email_confirmation",
"authens",
"tastypie",
"braces",
"tinymce",
"taggit",
"taggit_autosuggest",
"avisstage",
]
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",
)
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [
# insert your TEMPLATE_DIRS here
],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.contrib.auth.context_processors.auth",
"django.template.context_processors.debug",
"django.template.context_processors.i18n",
"django.template.context_processors.media",
"django.template.context_processors.static",
"django.template.context_processors.tz",
"django.template.context_processors.request",
"django.contrib.messages.context_processors.messages",
],
},
},
]
ROOT_URLCONF = "experiENS.urls"
WSGI_APPLICATION = "experiENS.wsgi.application"
# Database
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases
# Internationalization
# https://docs.djangoproject.com/en/1.7/topics/i18n/
LANGUAGE_CODE = "fr"
TIME_ZONE = "Europe/Paris"
USE_I18N = True
USE_L10N = True
USE_TZ = True
SITE_ID = 1
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.7/howto/static-files/
STATIC_URL = "/static/"
AUTHENTICATION_BACKENDS = (
"django.contrib.auth.backends.ModelBackend",
"experiENS.auth.ENSCASBackend",
)
CAS_SERVER_URL = "https://cas.eleves.ens.fr/" # SPI CAS
AUTHENS_USE_OLDCAS = False
LOGIN_URL = reverse_lazy("authens:login")
LOGOUT_URL = reverse_lazy("authens:logout")
LOGIN_REDIRECT_URL = reverse_lazy("avisstage:perso")
LOGOUT_REDIRECT_URL = reverse_lazy("avisstage:index")
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"handlers": {
"file": {
"level": "INFO",
"class": "logging.FileHandler",
"filename": os.path.join(BASE_DIR, "recherche.log"),
},
},
"loggers": {
"recherche": {
"handlers": ["file"],
"level": "INFO",
"propagate": True,
},
},
}

42
experiENS/settings_dev.py Normal file
View file

@ -0,0 +1,42 @@
from .settings_base import *
DEBUG = True
DATABASES = {
"default": {
"ENGINE": "django.contrib.gis.db.backends.spatialite",
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
}
}
USE_DEBUG_TOOLBAR = False
if USE_DEBUG_TOOLBAR:
INSTALLED_APPS += [
"debug_toolbar",
]
MIDDLEWARE = ("debug_toolbar.middleware.DebugToolbarMiddleware",) + MIDDLEWARE
INTERNAL_IPS = ["127.0.0.1"]
SPATIALITE_LIBRARY_PATH = "mod_spatialite"
STATIC_ROOT = "/home/evarin/Bureau/experiENS/static/"
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
STATIC_URL = "/experiens/static/"
ELASTICSEARCH_DSL = {
"default": {"hosts": "localhost:9200"},
}
CLIPPER_LDAP_SERVER = "ldaps://localhost:636"
# Changer à True pour développer avec ES
USE_ELASTICSEARCH = False
if not USE_ELASTICSEARCH:
INSTALLED_APPS.remove("django_elasticsearch_dsl")

View file

@ -0,0 +1,52 @@
import os
import sys
from django.core.urlresolvers import reverse_lazy
from .settings_base import *
PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
BASE_DIR = os.path.dirname(PROJECT_DIR)
DEBUG = False
ALLOWED_HOSTS = ["www.eleves.ens.fr"]
ADMINS = (("Robin Champenois", "champeno@clipper.ens.fr"),)
ADMIN_LOGINS = [
"champeno",
]
SERVER_EMAIL = "experiens@www.eleves.ens.fr"
ROOT_URL = "/experiens/"
WSGI_APPLICATION = "experiENS.wsgi.application"
DATABASES = {
"default": {
"ENGINE": "django.contrib.gis.db.backends.postgis",
"NAME": "experiens",
"USER": "experiens",
"PASSWORD": "",
"HOST": "",
"PORT": "5432",
}
}
STATIC_URL = ROOT_URL + "static/"
MEDIA_URL = ROOT_URL + "media/"
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
EMAIL_HOST = "nef.ens.fr"
ELASTICSEARCH_DSL = {
"default": {"hosts": "127.0.0.1:9200"},
}
CLIPPER_LDAP_SERVER = "ldaps://ldap.spi.ens.fr:636"
DEFAULT_FROM_EMAIL = "experiens-no-reply@www.eleves.ens.fr"

View file

@ -9,8 +9,8 @@ https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "experiENS.settings")
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings")
application = get_wsgi_application()

0
manage.py Executable file → Normal file
View file

View file

@ -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`"

View file

@ -1,34 +0,0 @@
{
"pins": {
"git-hooks": {
"type": "Git",
"repository": {
"type": "GitHub",
"owner": "cachix",
"repo": "git-hooks.nix"
},
"branch": "master",
"revision": "3c3e88f0f544d6bb54329832616af7eb971b6be6",
"url": "https://github.com/cachix/git-hooks.nix/archive/3c3e88f0f544d6bb54329832616af7eb971b6be6.tar.gz",
"hash": "04pwjz423iq2nkazkys905gvsm5j39722ngavrnx42b8msr5k555"
},
"nix-pkgs": {
"type": "Git",
"repository": {
"type": "Git",
"url": "https://git.hubrecht.ovh/hubrecht/nix-pkgs"
},
"branch": "main",
"revision": "024f0d09d4ff1a62e11f5fdd74f2d00d0a77da5c",
"url": null,
"hash": "0abpyf4pclslg24wmwl3q6y8x5fmhq9winpgkpbb99yw2815j2iz"
},
"nixpkgs": {
"type": "Channel",
"name": "nixpkgs-unstable",
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-24.11pre694416.ccc0c2126893/nixexprs.tar.xz",
"hash": "0cn1z4wzps8nfqxzr6l5mbn81adcqy2cy2ic70z13fhzicmxfsbx"
}
},
"version": 3
}

View file

@ -1,7 +1,7 @@
[flake8]
max-line-length = 99
exclude = .git, *.pyc, __pycache__, migrations
extend-ignore = E231, E203, E402
extend-ignore = E231, E203
[isort]
profile = black

View file

@ -1 +0,0 @@
(import ./. { }).devShell