Reformatage #29
25 changed files with 2360 additions and 1158 deletions
|
@ -5,29 +5,36 @@ from avisstage.models import *
|
||||||
|
|
||||||
import authens.models as authmod
|
import authens.models as authmod
|
||||||
|
|
||||||
|
|
||||||
class NormalienInline(admin.StackedInline):
|
class NormalienInline(admin.StackedInline):
|
||||||
model = Normalien
|
model = Normalien
|
||||||
inline_classes = ("collapse open",)
|
inline_classes = ("collapse open",)
|
||||||
|
|
||||||
|
|
||||||
class UserAdmin(UserAdmin):
|
class UserAdmin(UserAdmin):
|
||||||
inlines = (NormalienInline,)
|
inlines = (NormalienInline,)
|
||||||
|
|
||||||
|
|
||||||
class AvisLieuInline(admin.StackedInline):
|
class AvisLieuInline(admin.StackedInline):
|
||||||
model = AvisLieu
|
model = AvisLieu
|
||||||
inline_classes = ("collapse open",)
|
inline_classes = ("collapse open",)
|
||||||
extra = 0
|
extra = 0
|
||||||
|
|
||||||
|
|
||||||
class AvisStageInline(admin.StackedInline):
|
class AvisStageInline(admin.StackedInline):
|
||||||
model = AvisStage
|
model = AvisStage
|
||||||
inline_classes = ("collapse open",)
|
inline_classes = ("collapse open",)
|
||||||
extra = 0
|
extra = 0
|
||||||
|
|
||||||
|
|
||||||
class StageAdmin(admin.ModelAdmin):
|
class StageAdmin(admin.ModelAdmin):
|
||||||
inlines = (AvisLieuInline, AvisStageInline)
|
inlines = (AvisLieuInline, AvisStageInline)
|
||||||
|
|
||||||
|
|
||||||
class StageMatiereAdmin(admin.ModelAdmin):
|
class StageMatiereAdmin(admin.ModelAdmin):
|
||||||
model = StageMatiere
|
model = StageMatiere
|
||||||
prepopulated_fields = {"slug": ('nom',)}
|
prepopulated_fields = {"slug": ("nom",)}
|
||||||
|
|
||||||
|
|
||||||
admin.site.unregister(User)
|
admin.site.unregister(User)
|
||||||
admin.site.register(User, UserAdmin)
|
admin.site.register(User, UserAdmin)
|
||||||
|
|
|
@ -10,12 +10,14 @@ from django.urls import reverse
|
||||||
from .models import Lieu, Stage, Normalien, StageMatiere
|
from .models import Lieu, Stage, Normalien, StageMatiere
|
||||||
from .utils import approximate_distance
|
from .utils import approximate_distance
|
||||||
|
|
||||||
|
|
||||||
class EnScolariteAuthentication(SessionAuthentication):
|
class EnScolariteAuthentication(SessionAuthentication):
|
||||||
def is_authenticated(self, request, **kwargs):
|
def is_authenticated(self, request, **kwargs):
|
||||||
if super().is_authenticated(request, **kwargs):
|
if super().is_authenticated(request, **kwargs):
|
||||||
return request.user.profil.en_scolarite
|
return request.user.profil.en_scolarite
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
# API principale pour les lieux
|
# API principale pour les lieux
|
||||||
class LieuResource(ModelResource):
|
class LieuResource(ModelResource):
|
||||||
# stages = fields.ToManyField("avisstage.api.StageResource",
|
# stages = fields.ToManyField("avisstage.api.StageResource",
|
||||||
|
@ -37,15 +39,15 @@ class LieuResource(ModelResource):
|
||||||
|
|
||||||
# Trouver les lieux à proximités d'un point donné
|
# Trouver les lieux à proximités d'un point donné
|
||||||
if "lng" in filters and "lat" in filters:
|
if "lng" in filters and "lat" in filters:
|
||||||
lat = float(filters['lat'])
|
lat = float(filters["lat"])
|
||||||
lng = float(filters['lng'])
|
lng = float(filters["lng"])
|
||||||
pt = geos.Point((lng, lat), srid=4326)
|
pt = geos.Point((lng, lat), srid=4326)
|
||||||
self.reference_point = pt
|
self.reference_point = pt
|
||||||
orm_filters['coord__distance_lte'] = (pt, 10000)
|
orm_filters["coord__distance_lte"] = (pt, 10000)
|
||||||
|
|
||||||
# Filtrer les lieux qui ont déjà des stages
|
# Filtrer les lieux qui ont déjà des stages
|
||||||
if "has_stage" in filters:
|
if "has_stage" in filters:
|
||||||
orm_filters['stages__public'] = True
|
orm_filters["stages__public"] = True
|
||||||
|
|
||||||
return orm_filters
|
return orm_filters
|
||||||
|
|
||||||
|
@ -58,13 +60,13 @@ class LieuResource(ModelResource):
|
||||||
bundle = super(LieuResource, self).dehydrate(bundle)
|
bundle = super(LieuResource, self).dehydrate(bundle)
|
||||||
|
|
||||||
obj = bundle.obj
|
obj = bundle.obj
|
||||||
bundle.data['coord'] = {'lat': float(obj.coord.y),
|
bundle.data["coord"] = {"lat": float(obj.coord.y), "lng": float(obj.coord.x)}
|
||||||
'lng': float(obj.coord.x)}
|
|
||||||
|
|
||||||
# Distance au point recherché
|
# Distance au point recherché
|
||||||
if "lat" in bundle.request.GET and "lng" in bundle.request.GET:
|
if "lat" in bundle.request.GET and "lng" in bundle.request.GET:
|
||||||
bundle.data['distance'] = approximate_distance(
|
bundle.data["distance"] = approximate_distance(
|
||||||
self.reference_point, bundle.obj.coord)
|
self.reference_point, bundle.obj.coord
|
||||||
|
)
|
||||||
|
|
||||||
# Autres infos utiles
|
# Autres infos utiles
|
||||||
bundle.data["pays_nom"] = obj.get_pays_display()
|
bundle.data["pays_nom"] = obj.get_pays_display()
|
||||||
|
@ -74,6 +76,7 @@ class LieuResource(ModelResource):
|
||||||
|
|
||||||
return bundle
|
return bundle
|
||||||
|
|
||||||
|
|
||||||
# API sur un stage
|
# API sur un stage
|
||||||
class StageResource(ModelResource):
|
class StageResource(ModelResource):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -92,8 +95,8 @@ class StageResource(ModelResource):
|
||||||
|
|
||||||
# Récupération des stages à un lieu donné
|
# Récupération des stages à un lieu donné
|
||||||
if "lieux" in filters:
|
if "lieux" in filters:
|
||||||
flieux = map(int, filters['lieux'].split(','))
|
flieux = map(int, filters["lieux"].split(","))
|
||||||
orm_filters['lieux__id__in'] = flieux
|
orm_filters["lieux__id__in"] = flieux
|
||||||
|
|
||||||
return orm_filters
|
return orm_filters
|
||||||
|
|
||||||
|
@ -103,18 +106,22 @@ class StageResource(ModelResource):
|
||||||
obj = bundle.obj
|
obj = bundle.obj
|
||||||
|
|
||||||
# Affichage des manytomany en condensé
|
# Affichage des manytomany en condensé
|
||||||
bundle.data['auteur'] = obj.auteur.nom
|
bundle.data["auteur"] = obj.auteur.nom
|
||||||
bundle.data['thematiques'] = list(obj.thematiques.all().values_list("name", flat=True))
|
bundle.data["thematiques"] = list(
|
||||||
bundle.data['matieres'] = list(obj.matieres.all().values_list("nom", flat=True))
|
obj.thematiques.all().values_list("name", flat=True)
|
||||||
|
)
|
||||||
|
bundle.data["matieres"] = list(obj.matieres.all().values_list("nom", flat=True))
|
||||||
|
|
||||||
# Adresse de la fiche de stage
|
# Adresse de la fiche de stage
|
||||||
bundle.data['url'] = reverse("avisstage:stage", kwargs={"pk": obj.id});
|
bundle.data["url"] = reverse("avisstage:stage", kwargs={"pk": obj.id})
|
||||||
return bundle
|
return bundle
|
||||||
|
|
||||||
|
|
||||||
# Auteurs des fiches (TODO supprimer ?)
|
# Auteurs des fiches (TODO supprimer ?)
|
||||||
class AuteurResource(ModelResource):
|
class AuteurResource(ModelResource):
|
||||||
stages = fields.ToManyField("avisstage.api.StageResource",
|
stages = fields.ToManyField(
|
||||||
"stages", use_in="detail")
|
"avisstage.api.StageResource", "stages", use_in="detail"
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
queryset = Normalien.objects.all()
|
queryset = Normalien.objects.all()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
class AvisstageConfig(AppConfig):
|
class AvisstageConfig(AppConfig):
|
||||||
name = 'avisstage'
|
name = "avisstage"
|
||||||
|
|
|
@ -3,10 +3,12 @@ from functools import wraps
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
|
|
||||||
|
|
||||||
def en_scolarite_required(view_func):
|
def en_scolarite_required(view_func):
|
||||||
@wraps(view_func)
|
@wraps(view_func)
|
||||||
def _wrapped_view(request, *args, **kwargs):
|
def _wrapped_view(request, *args, **kwargs):
|
||||||
if request.user.profil.en_scolarite:
|
if request.user.profil.en_scolarite:
|
||||||
return view_func(request, *args, **kwargs)
|
return view_func(request, *args, **kwargs)
|
||||||
return redirect(reverse("avisstage:403-archicubes"))
|
return redirect(reverse("avisstage:403-archicubes"))
|
||||||
|
|
||||||
return _wrapped_view
|
return _wrapped_view
|
||||||
|
|
|
@ -6,47 +6,56 @@ from .statics import PAYS_OPTIONS
|
||||||
|
|
||||||
PAYS_DICT = dict(PAYS_OPTIONS)
|
PAYS_DICT = dict(PAYS_OPTIONS)
|
||||||
|
|
||||||
stage = Index('stages')
|
stage = Index("stages")
|
||||||
stage.settings(
|
stage.settings(number_of_shards=1, number_of_replicas=0)
|
||||||
number_of_shards=1,
|
|
||||||
number_of_replicas=0
|
|
||||||
)
|
|
||||||
|
|
||||||
text_analyzer = analyzer(
|
text_analyzer = analyzer(
|
||||||
'default',
|
"default",
|
||||||
tokenizer="standard",
|
tokenizer="standard",
|
||||||
filter=['lowercase', 'standard', 'asciifolding',
|
filter=[
|
||||||
|
"lowercase",
|
||||||
|
"standard",
|
||||||
|
"asciifolding",
|
||||||
token_filter("frstop", type="stop", stopwords="_french_"),
|
token_filter("frstop", type="stop", stopwords="_french_"),
|
||||||
token_filter("frsnow", type="snowball", language="French")])
|
token_filter("frsnow", type="snowball", language="French"),
|
||||||
|
],
|
||||||
|
)
|
||||||
stage.analyzer(text_analyzer)
|
stage.analyzer(text_analyzer)
|
||||||
|
|
||||||
|
|
||||||
@stage.doc_type
|
@stage.doc_type
|
||||||
class StageDocument(DocType):
|
class StageDocument(DocType):
|
||||||
lieux = fields.ObjectField(properties={
|
lieux = fields.ObjectField(
|
||||||
'nom': fields.StringField(),
|
properties={
|
||||||
'ville': fields.StringField(),
|
"nom": fields.StringField(),
|
||||||
'pays': fields.StringField(),
|
"ville": fields.StringField(),
|
||||||
})
|
"pays": fields.StringField(),
|
||||||
auteur = fields.ObjectField(properties={
|
}
|
||||||
'nom': fields.StringField(),
|
)
|
||||||
})
|
auteur = fields.ObjectField(
|
||||||
|
properties={
|
||||||
|
"nom": fields.StringField(),
|
||||||
|
}
|
||||||
|
)
|
||||||
thematiques = fields.StringField()
|
thematiques = fields.StringField()
|
||||||
matieres = fields.StringField()
|
matieres = fields.StringField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Stage
|
model = Stage
|
||||||
fields = [
|
fields = [
|
||||||
'sujet',
|
"sujet",
|
||||||
'encadrants',
|
"encadrants",
|
||||||
'type_stage',
|
"type_stage",
|
||||||
'niveau_scol',
|
"niveau_scol",
|
||||||
'structure',
|
"structure",
|
||||||
'date_debut',
|
"date_debut",
|
||||||
'date_fin'
|
"date_fin",
|
||||||
]
|
]
|
||||||
|
|
||||||
def prepare_thematiques(self, instance):
|
def prepare_thematiques(self, instance):
|
||||||
return ", ".join(instance.thematiques.all().values_list("name", flat=True)).lower()
|
return ", ".join(
|
||||||
|
instance.thematiques.all().values_list("name", flat=True)
|
||||||
|
).lower()
|
||||||
|
|
||||||
def prepare_matieres(self, instance):
|
def prepare_matieres(self, instance):
|
||||||
return ", ".join(instance.matieres.all().values_list("nom", flat=True)).lower()
|
return ", ".join(instance.matieres.all().values_list("nom", flat=True)).lower()
|
||||||
|
@ -70,6 +79,6 @@ class StageDocument(DocType):
|
||||||
def prepare(self, instance):
|
def prepare(self, instance):
|
||||||
data = super(StageDocument, self).prepare(instance)
|
data = super(StageDocument, self).prepare(instance)
|
||||||
|
|
||||||
for lieu in data['lieux']:
|
for lieu in data["lieux"]:
|
||||||
lieu['pays'] = PAYS_DICT[lieu['pays']].lower()
|
lieu["pays"] = PAYS_DICT[lieu["pays"]].lower()
|
||||||
return data
|
return data
|
||||||
|
|
|
@ -15,32 +15,52 @@ from .widgets import LatLonField
|
||||||
class HTMLTrimmerForm(forms.ModelForm):
|
class HTMLTrimmerForm(forms.ModelForm):
|
||||||
def clean(self):
|
def clean(self):
|
||||||
# Suppression des espaces blanc avant et après le texte pour les champs html
|
# Suppression des espaces blanc avant et après le texte pour les champs html
|
||||||
leading_white = re.compile(r"^( \t\n)*(<p>( |[ \n\t]|<br[ /]*>)*</p>( \t\n)*)+?", re.IGNORECASE)
|
leading_white = re.compile(
|
||||||
trailing_white = re.compile(r"(( \t\n)*<p>( |[ \n\t]|<br[ /]*>)*</p>)+?( \t\n)*$", re.IGNORECASE)
|
r"^( \t\n)*(<p>( |[ \n\t]|<br[ /]*>)*</p>( \t\n)*)+?", re.IGNORECASE
|
||||||
|
)
|
||||||
|
trailing_white = re.compile(
|
||||||
|
r"(( \t\n)*<p>( |[ \n\t]|<br[ /]*>)*</p>)+?( \t\n)*$", re.IGNORECASE
|
||||||
|
)
|
||||||
cleaned_data = super(HTMLTrimmerForm, self).clean()
|
cleaned_data = super(HTMLTrimmerForm, self).clean()
|
||||||
|
|
||||||
for (fname, fval) in cleaned_data.items():
|
for (fname, fval) in cleaned_data.items():
|
||||||
# Heuristique : les champs commençant par "avis_" sont des champs html
|
# Heuristique : les champs commençant par "avis_" sont des champs html
|
||||||
if fname[:5] == "avis_":
|
if fname[:5] == "avis_":
|
||||||
cleaned_data[fname] = leading_white.sub("", trailing_white.sub("", fval))
|
cleaned_data[fname] = leading_white.sub(
|
||||||
|
"", trailing_white.sub("", fval)
|
||||||
|
)
|
||||||
|
|
||||||
return cleaned_data
|
return cleaned_data
|
||||||
|
|
||||||
|
|
||||||
# Infos sur un stage
|
# Infos sur un stage
|
||||||
class StageForm(forms.ModelForm):
|
class StageForm(forms.ModelForm):
|
||||||
date_widget = forms.DateInput(attrs={"class":"datepicker",
|
date_widget = forms.DateInput(
|
||||||
"placeholder":"JJ/MM/AAAA"})
|
attrs={"class": "datepicker", "placeholder": "JJ/MM/AAAA"}
|
||||||
date_debut = forms.DateField(label="Date de début",
|
)
|
||||||
input_formats=["%d/%m/%Y"], widget=date_widget)
|
date_debut = forms.DateField(
|
||||||
date_fin = forms.DateField(label="Date de fin",
|
label="Date de début", input_formats=["%d/%m/%Y"], widget=date_widget
|
||||||
input_formats=["%d/%m/%Y"], widget=date_widget)
|
)
|
||||||
|
date_fin = forms.DateField(
|
||||||
|
label="Date de fin", input_formats=["%d/%m/%Y"], widget=date_widget
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Stage
|
model = Stage
|
||||||
fields = ['sujet', 'date_debut', 'date_fin', 'type_stage', 'niveau_scol', 'thematiques', 'matieres', 'structure', 'encadrants']
|
fields = [
|
||||||
|
"sujet",
|
||||||
|
"date_debut",
|
||||||
|
"date_fin",
|
||||||
|
"type_stage",
|
||||||
|
"niveau_scol",
|
||||||
|
"thematiques",
|
||||||
|
"matieres",
|
||||||
|
"structure",
|
||||||
|
"encadrants",
|
||||||
|
]
|
||||||
help_texts = {
|
help_texts = {
|
||||||
"thematiques": "Mettez une virgule pour valider votre thématique si la suggestion ne correspond pas ou si elle n'existe pas encore",
|
"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)"
|
"structure": "Nom de l'équipe, du laboratoire, de la startup... (si le lieu ne suffit pas)",
|
||||||
}
|
}
|
||||||
labels = {
|
labels = {
|
||||||
"date_debut": "Date de début",
|
"date_debut": "Date de début",
|
||||||
|
@ -54,7 +74,7 @@ class StageForm(forms.ModelForm):
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
# Lors de la création : attribution à l'utilisateur connecté
|
# Lors de la création : attribution à l'utilisateur connecté
|
||||||
if self.instance.id is None and hasattr(self, 'request'):
|
if self.instance.id is None and hasattr(self, "request"):
|
||||||
self.instance.auteur = self.request.user.profil
|
self.instance.auteur = self.request.user.profil
|
||||||
|
|
||||||
# Date de modification
|
# Date de modification
|
||||||
|
@ -65,13 +85,22 @@ class StageForm(forms.ModelForm):
|
||||||
stage = super(StageForm, self).save(commit=commit)
|
stage = super(StageForm, self).save(commit=commit)
|
||||||
return stage
|
return stage
|
||||||
|
|
||||||
|
|
||||||
# Sous-formulaire des avis sur le stage
|
# Sous-formulaire des avis sur le stage
|
||||||
class AvisStageForm(HTMLTrimmerForm):
|
class AvisStageForm(HTMLTrimmerForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = AvisStage
|
model = AvisStage
|
||||||
fields = ['chapo', 'avis_sujet', 'avis_ambiance', 'avis_admin', 'avis_prestage', 'les_plus', 'les_moins']
|
fields = [
|
||||||
|
"chapo",
|
||||||
|
"avis_sujet",
|
||||||
|
"avis_ambiance",
|
||||||
|
"avis_admin",
|
||||||
|
"avis_prestage",
|
||||||
|
"les_plus",
|
||||||
|
"les_moins",
|
||||||
|
]
|
||||||
help_texts = {
|
help_texts = {
|
||||||
"chapo": "\"Trop long, pas lu\" : une accroche résumant ce que vous avez pensé de ce séjour",
|
"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_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_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_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 ?",
|
||||||
|
@ -80,21 +109,29 @@ class AvisStageForm(HTMLTrimmerForm):
|
||||||
"les_moins": "Ce qui aurait pu être mieux",
|
"les_moins": "Ce qui aurait pu être mieux",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class AvisLieuForm(HTMLTrimmerForm):
|
class AvisLieuForm(HTMLTrimmerForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = AvisLieu
|
model = AvisLieu
|
||||||
fields = ['lieu', 'chapo', 'avis_lieustage', 'avis_pratique', 'avis_tourisme', 'les_plus', 'les_moins']
|
fields = [
|
||||||
|
"lieu",
|
||||||
|
"chapo",
|
||||||
|
"avis_lieustage",
|
||||||
|
"avis_pratique",
|
||||||
|
"avis_tourisme",
|
||||||
|
"les_plus",
|
||||||
|
"les_moins",
|
||||||
|
]
|
||||||
help_texts = {
|
help_texts = {
|
||||||
"chapo": "\"Trop long, pas lu\" : une accroche résumant ce que vous avez pensé de cet endroit",
|
"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_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_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 ?",
|
"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_plus": "Les meilleures raisons de partir à cet endroit",
|
||||||
"les_moins": "Ce qui vous a gêné ou manqué là-bas",
|
"les_moins": "Ce qui vous a gêné ou manqué là-bas",
|
||||||
}
|
}
|
||||||
widgets = {
|
widgets = {"lieu": forms.HiddenInput(attrs={"class": "lieu-hidden"})}
|
||||||
"lieu": forms.HiddenInput(attrs={"class":"lieu-hidden"})
|
|
||||||
}
|
|
||||||
|
|
||||||
# Création d'un nouveau lieu
|
# Création d'un nouveau lieu
|
||||||
class LieuForm(forms.ModelForm):
|
class LieuForm(forms.ModelForm):
|
||||||
|
@ -103,25 +140,31 @@ class LieuForm(forms.ModelForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Lieu
|
model = Lieu
|
||||||
fields = ['id', 'nom', 'type_lieu', 'ville', 'pays', 'coord']
|
fields = ["id", "nom", "type_lieu", "ville", "pays", "coord"]
|
||||||
|
|
||||||
|
|
||||||
# Widget de feedback
|
# Widget de feedback
|
||||||
class FeedbackForm(forms.Form):
|
class FeedbackForm(forms.Form):
|
||||||
objet = forms.CharField(label="Objet", required=True)
|
objet = forms.CharField(label="Objet", required=True)
|
||||||
message = forms.CharField(label="Message", required=True, widget=forms.widgets.Textarea())
|
message = forms.CharField(
|
||||||
|
label="Message", required=True, widget=forms.widgets.Textarea()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Nouvelle adresse mail
|
# Nouvelle adresse mail
|
||||||
class AdresseEmailForm(forms.Form):
|
class AdresseEmailForm(forms.Form):
|
||||||
def __init__(self, _user, **kwargs):
|
def __init__(self, _user, **kwargs):
|
||||||
self._user = _user
|
self._user = _user
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
email = forms.EmailField(widget=forms.widgets.EmailInput(attrs={"placeholder": "Nouvelle adresse"}))
|
|
||||||
|
email = forms.EmailField(
|
||||||
|
widget=forms.widgets.EmailInput(attrs={"placeholder": "Nouvelle adresse"})
|
||||||
|
)
|
||||||
|
|
||||||
def clean_email(self):
|
def clean_email(self):
|
||||||
email = self.cleaned_data["email"]
|
email = self.cleaned_data["email"]
|
||||||
if EmailAddress.objects.filter(user=self._user, email=email).exists():
|
if EmailAddress.objects.filter(user=self._user, email=email).exists():
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError("Cette adresse est déjà associée à ce compte")
|
||||||
"Cette adresse est déjà associée à ce compte")
|
|
||||||
return email
|
return email
|
||||||
|
|
||||||
|
|
||||||
|
@ -131,7 +174,10 @@ def _unicode_ci_compare(s1, s2):
|
||||||
recommended algorithm from Unicode Technical Report 36, section
|
recommended algorithm from Unicode Technical Report 36, section
|
||||||
2.11.2(B)(2).
|
2.11.2(B)(2).
|
||||||
"""
|
"""
|
||||||
return unicodedata.normalize('NFKC', s1).casefold() == unicodedata.normalize('NFKC', s2).casefold()
|
return (
|
||||||
|
unicodedata.normalize("NFKC", s1).casefold()
|
||||||
|
== unicodedata.normalize("NFKC", s2).casefold()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# (Ré)initialisation du mot de passe
|
# (Ré)initialisation du mot de passe
|
||||||
|
@ -139,11 +185,14 @@ class ReinitMdpForm(PasswordResetForm):
|
||||||
def get_users(self, email):
|
def get_users(self, email):
|
||||||
"""Override default method to allow unusable passwords"""
|
"""Override default method to allow unusable passwords"""
|
||||||
email_field_name = User.get_email_field_name()
|
email_field_name = User.get_email_field_name()
|
||||||
active_users = User._default_manager.filter(**{
|
active_users = User._default_manager.filter(
|
||||||
'%s__iexact' % email_field_name: email,
|
**{
|
||||||
'is_active': True,
|
"%s__iexact" % email_field_name: email,
|
||||||
})
|
"is_active": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
return (
|
return (
|
||||||
u for u in active_users
|
u
|
||||||
|
for u in active_users
|
||||||
if _unicode_ci_compare(email, getattr(u, email_field_name))
|
if _unicode_ci_compare(email, getattr(u, email_field_name))
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,41 +3,58 @@ from django.core.management.base import BaseCommand, CommandError
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from avisstage.models import Stage, Lieu
|
from avisstage.models import Stage, Lieu
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = 'Nettoie les stages à plusieurs lieux identiques'
|
help = "Nettoie les stages à plusieurs lieux identiques"
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument('min_lieu', nargs='?', default=0, type=int)
|
parser.add_argument("min_lieu", nargs="?", default=0, type=int)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--apply',
|
"--apply",
|
||||||
action='store_true',
|
action="store_true",
|
||||||
default=False,
|
default=False,
|
||||||
help='Applies the modifications',
|
help="Applies the modifications",
|
||||||
)
|
)
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
rundb = False
|
rundb = False
|
||||||
if options.get('apply', False):
|
if options.get("apply", False):
|
||||||
rundb = True
|
rundb = True
|
||||||
else:
|
else:
|
||||||
print("Les modifications ne seront pas appliquées")
|
print("Les modifications ne seront pas appliquées")
|
||||||
|
|
||||||
min_lieu = options.get('min_lieu', 0)
|
min_lieu = options.get("min_lieu", 0)
|
||||||
|
|
||||||
for lieu in Lieu.objects.filter(id__gte=min_lieu).order_by('-id'):
|
for lieu in Lieu.objects.filter(id__gte=min_lieu).order_by("-id"):
|
||||||
lproches = Lieu.objects.filter(id__lt=lieu.id, coord__distance_lte=(lieu.coord, 5))
|
lproches = Lieu.objects.filter(
|
||||||
|
id__lt=lieu.id, coord__distance_lte=(lieu.coord, 5)
|
||||||
|
)
|
||||||
if len(lproches) == 0:
|
if len(lproches) == 0:
|
||||||
continue
|
continue
|
||||||
print("Doublons possibles pour %s (id=%d, %d avis) :" % (lieu, lieu.id, lieu.avislieu_set.count()))
|
print(
|
||||||
|
"Doublons possibles pour %s (id=%d, %d avis) :"
|
||||||
|
% (lieu, lieu.id, lieu.avislieu_set.count())
|
||||||
|
)
|
||||||
for plieu in lproches:
|
for plieu in lproches:
|
||||||
pprint = " > %s (id=%d, %d avis)" % (plieu, plieu.id, plieu.avislieu_set.count())
|
pprint = " > %s (id=%d, %d avis)" % (
|
||||||
if plieu.nom == lieu.nom and plieu.ville == lieu.ville and plieu.type_lieu == lieu.type_lieu:
|
plieu,
|
||||||
print("%s %s" % (pprint, self.style.SUCCESS(u'-> Suppression')))
|
plieu.id,
|
||||||
|
plieu.avislieu_set.count(),
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
plieu.nom == lieu.nom
|
||||||
|
and plieu.ville == lieu.ville
|
||||||
|
and plieu.type_lieu == lieu.type_lieu
|
||||||
|
):
|
||||||
|
print("%s %s" % (pprint, self.style.SUCCESS(u"-> Suppression")))
|
||||||
if rundb:
|
if rundb:
|
||||||
for avis in plieu.avislieu_set.all():
|
for avis in plieu.avislieu_set.all():
|
||||||
avis.lieu = lieu
|
avis.lieu = lieu
|
||||||
avis.save()
|
avis.save()
|
||||||
plieu.delete()
|
plieu.delete()
|
||||||
else:
|
else:
|
||||||
print("%s %s" % (pprint, self.style.WARNING(u'-> À supprimer manuellement')))
|
print(
|
||||||
self.stdout.write(self.style.SUCCESS(u'Nettoyage des lieux effectué'))
|
"%s %s"
|
||||||
|
% (pprint, self.style.WARNING(u"-> À supprimer manuellement"))
|
||||||
|
)
|
||||||
|
self.stdout.write(self.style.SUCCESS(u"Nettoyage des lieux effectué"))
|
||||||
|
|
|
@ -3,16 +3,17 @@ from django.core.management.base import BaseCommand, CommandError
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from avisstage.models import Stage, Lieu
|
from avisstage.models import Stage, Lieu
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = 'Nettoie les stages à plusieurs lieux identiques'
|
help = "Nettoie les stages à plusieurs lieux identiques"
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument('min_stage', nargs='?', default=0, type=int)
|
parser.add_argument("min_stage", nargs="?", default=0, type=int)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--apply',
|
"--apply",
|
||||||
action='store_true',
|
action="store_true",
|
||||||
default=False,
|
default=False,
|
||||||
help='Applies the modifications',
|
help="Applies the modifications",
|
||||||
)
|
)
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
|
@ -27,15 +28,16 @@ class Command(BaseCommand):
|
||||||
return length
|
return length
|
||||||
|
|
||||||
rundb = False
|
rundb = False
|
||||||
if options.get('apply', False):
|
if options.get("apply", False):
|
||||||
rundb = True
|
rundb = True
|
||||||
else:
|
else:
|
||||||
print("Les modifications ne seront pas appliquées")
|
print("Les modifications ne seront pas appliquées")
|
||||||
|
|
||||||
min_stage = options.get('min_stage', 0)
|
min_stage = options.get("min_stage", 0)
|
||||||
|
|
||||||
for stage in Stage.objects.annotate(c=Count("lieux"))\
|
for stage in Stage.objects.annotate(c=Count("lieux")).filter(
|
||||||
.filter(c__gte=2, id__gte=min_stage):
|
c__gte=2, id__gte=min_stage
|
||||||
|
):
|
||||||
lieuset = {}
|
lieuset = {}
|
||||||
todel = []
|
todel = []
|
||||||
problems = []
|
problems = []
|
||||||
|
@ -54,13 +56,17 @@ class Command(BaseCommand):
|
||||||
if len(todel) > 0:
|
if len(todel) > 0:
|
||||||
print("Doublons détectés dans %s" % (stage,))
|
print("Doublons détectés dans %s" % (stage,))
|
||||||
for avis, alen in todel:
|
for avis, alen in todel:
|
||||||
print(" > Suppression de l'avis sur %s de %d mots" % \
|
print(
|
||||||
(avis.lieu, alen))
|
" > Suppression de l'avis sur %s de %d mots" % (avis.lieu, alen)
|
||||||
|
)
|
||||||
if rundb:
|
if rundb:
|
||||||
avis.delete()
|
avis.delete()
|
||||||
if len(problems) > 0:
|
if len(problems) > 0:
|
||||||
self.stdout.write(self.style.WARNING("Réparation impossible de %s (id=%d)" % (stage, stage.id)))
|
self.stdout.write(
|
||||||
|
self.style.WARNING(
|
||||||
|
"Réparation impossible de %s (id=%d)" % (stage, stage.id)
|
||||||
|
)
|
||||||
|
)
|
||||||
for avis, alen in problems:
|
for avis, alen in problems:
|
||||||
print(" > Avis sur %s de %d mots" % \
|
print(" > Avis sur %s de %d mots" % (avis.lieu, alen))
|
||||||
(avis.lieu, alen))
|
self.stdout.write(self.style.SUCCESS(u"Nettoyage des stages effectué"))
|
||||||
self.stdout.write(self.style.SUCCESS(u'Nettoyage des stages effectué'))
|
|
||||||
|
|
|
@ -3,33 +3,40 @@ from django.core.management.base import BaseCommand, CommandError
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from avisstage.models import Stage, Lieu
|
from avisstage.models import Stage, Lieu
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = 'Nettoie les stages à plusieurs lieux identiques'
|
help = "Nettoie les stages à plusieurs lieux identiques"
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument('del_lieu', type=int, help='Lieu à supprimer')
|
parser.add_argument("del_lieu", type=int, help="Lieu à supprimer")
|
||||||
parser.add_argument('repl_lieu', type=int, help='Lieu le remplaçant')
|
parser.add_argument("repl_lieu", type=int, help="Lieu le remplaçant")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--apply',
|
"--apply",
|
||||||
action='store_true',
|
action="store_true",
|
||||||
default=False,
|
default=False,
|
||||||
help='Applies the modifications',
|
help="Applies the modifications",
|
||||||
)
|
)
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
rundb = False
|
rundb = False
|
||||||
if options.get('apply', False):
|
if options.get("apply", False):
|
||||||
rundb = True
|
rundb = True
|
||||||
else:
|
else:
|
||||||
print("Les modifications ne seront pas appliquées")
|
print("Les modifications ne seront pas appliquées")
|
||||||
|
|
||||||
plieu = Lieu.objects.get(id=options['del_lieu'])
|
plieu = Lieu.objects.get(id=options["del_lieu"])
|
||||||
lieu = Lieu.objects.get(id=options['repl_lieu'])
|
lieu = Lieu.objects.get(id=options["repl_lieu"])
|
||||||
print("Suppression de %s (id=%d, %d avis)" % (plieu, plieu.id, plieu.avislieu_set.count()))
|
print(
|
||||||
print("Remplacement par %s (id=%d, %d avis)" % (lieu, lieu.id, lieu.avislieu_set.count()))
|
"Suppression de %s (id=%d, %d avis)"
|
||||||
|
% (plieu, plieu.id, plieu.avislieu_set.count())
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"Remplacement par %s (id=%d, %d avis)"
|
||||||
|
% (lieu, lieu.id, lieu.avislieu_set.count())
|
||||||
|
)
|
||||||
if rundb:
|
if rundb:
|
||||||
for avis in plieu.avislieu_set.all():
|
for avis in plieu.avislieu_set.all():
|
||||||
avis.lieu = lieu
|
avis.lieu = lieu
|
||||||
avis.save()
|
avis.save()
|
||||||
plieu.delete()
|
plieu.delete()
|
||||||
self.stdout.write(self.style.SUCCESS(u'Terminé'))
|
self.stdout.write(self.style.SUCCESS(u"Terminé"))
|
||||||
|
|
|
@ -4,6 +4,7 @@ from avisstage.models import Normalien
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = 'Réinitialise les statuts "en scolarité" de tout le monde'
|
help = 'Réinitialise les statuts "en scolarité" de tout le monde'
|
||||||
|
|
||||||
|
@ -13,4 +14,4 @@ class Command(BaseCommand):
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
old_conn = timezone.now() - timedelta(days=365)
|
old_conn = timezone.now() - timedelta(days=365)
|
||||||
Normalien.objects.all().update(last_cas_connect=t)
|
Normalien.objects.all().update(last_cas_connect=t)
|
||||||
self.stdout.write(self.style.SUCCESS(u'Terminé'))
|
self.stdout.write(self.style.SUCCESS(u"Terminé"))
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -9,23 +9,29 @@ import tinymce.models
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('avisstage', '0001_initial'),
|
("avisstage", "0001_initial"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='avisstage',
|
model_name="avisstage",
|
||||||
name='avis_prestage',
|
name="avis_prestage",
|
||||||
field=tinymce.models.HTMLField(blank=True, default='', verbose_name='Avant le stage'),
|
field=tinymce.models.HTMLField(
|
||||||
|
blank=True, default="", verbose_name="Avant le stage"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='stage',
|
model_name="stage",
|
||||||
name='len_avis_lieux',
|
name="len_avis_lieux",
|
||||||
field=models.IntegerField(default=0, verbose_name='Longueur des avis de lieu'),
|
field=models.IntegerField(
|
||||||
|
default=0, verbose_name="Longueur des avis de lieu"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='stage',
|
model_name="stage",
|
||||||
name='len_avis_stage',
|
name="len_avis_stage",
|
||||||
field=models.IntegerField(default=0, verbose_name='Longueur des avis de stage'),
|
field=models.IntegerField(
|
||||||
|
default=0, verbose_name="Longueur des avis de stage"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -3,17 +3,18 @@ from django.db import migrations
|
||||||
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
|
||||||
def forwards(apps, schema_editor):
|
def forwards(apps, schema_editor):
|
||||||
User = apps.get_model('auth', 'User')
|
User = apps.get_model("auth", "User")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
CASAccount = apps.get_model('authens', 'CASAccount')
|
CASAccount = apps.get_model("authens", "CASAccount")
|
||||||
except LookupError:
|
except LookupError:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
SocialAccount = apps.get_model('socialaccount', 'SocialAccount')
|
SocialAccount = apps.get_model("socialaccount", "SocialAccount")
|
||||||
OldEmailAddress = apps.get_model('account', 'EmailAddress')
|
OldEmailAddress = apps.get_model("account", "EmailAddress")
|
||||||
except LookupError:
|
except LookupError:
|
||||||
# Allauth not installed
|
# Allauth not installed
|
||||||
# Simply create CAS accounts for every profile
|
# Simply create CAS accounts for every profile
|
||||||
|
@ -25,29 +26,26 @@ def forwards(apps, schema_editor):
|
||||||
if ldap_info:
|
if ldap_info:
|
||||||
entrance_year = ldap_info["entrance_year"]
|
entrance_year = ldap_info["entrance_year"]
|
||||||
CASAccount.objects.create(
|
CASAccount.objects.create(
|
||||||
user=user, cas_login=user.username,
|
user=user, cas_login=user.username, entrance_year=entrance_year
|
||||||
entrance_year=entrance_year
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for user in User.objects.all():
|
for user in User.objects.all():
|
||||||
migrate_user(user)
|
migrate_user(user)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
NewEmailAddress = apps.get_model("simple_email_confirmation", "EmailAddress")
|
||||||
NewEmailAddress = apps.get_model('simple_email_confirmation',
|
|
||||||
'EmailAddress')
|
|
||||||
from simple_email_confirmation.models import EmailAddressManager
|
from simple_email_confirmation.models import EmailAddressManager
|
||||||
|
|
||||||
# Transfer from allauth to authens
|
# Transfer from allauth to authens
|
||||||
# Assumes usernames have the format <clipper>@<promo>
|
# Assumes usernames have the format <clipper>@<promo>
|
||||||
# Assumes no clashing clipper accounts have ever been found
|
# Assumes no clashing clipper accounts have ever been found
|
||||||
oldusers = (
|
oldusers = User.objects.all().prefetch_related(
|
||||||
User.objects.all().prefetch_related(
|
"emailaddress_set", "socialaccount_set"
|
||||||
"emailaddress_set", "socialaccount_set")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
is_ens_mail = lambda mail: (
|
is_ens_mail = lambda mail: (
|
||||||
mail is not None and (mail.endswith("ens.fr") or mail.endswith("ens.psl.eu")))
|
mail is not None and (mail.endswith("ens.fr") or mail.endswith("ens.psl.eu"))
|
||||||
|
)
|
||||||
new_conns = []
|
new_conns = []
|
||||||
new_mails = []
|
new_mails = []
|
||||||
|
|
||||||
|
@ -56,14 +54,14 @@ def forwards(apps, schema_editor):
|
||||||
addresses = user.emailaddress_set.all()
|
addresses = user.emailaddress_set.all()
|
||||||
for addr in addresses:
|
for addr in addresses:
|
||||||
newaddr = NewEmailAddress(
|
newaddr = NewEmailAddress(
|
||||||
user=user, email=addr.email,
|
user=user,
|
||||||
|
email=addr.email,
|
||||||
set_at=timezone.now(),
|
set_at=timezone.now(),
|
||||||
confirmed_at=(timezone.now() if addr.verified else None),
|
confirmed_at=(timezone.now() if addr.verified else None),
|
||||||
key=EmailAddressManager().generate_key(),
|
key=EmailAddressManager().generate_key(),
|
||||||
)
|
)
|
||||||
if addr.primary and user.email != addr.email:
|
if addr.primary and user.email != addr.email:
|
||||||
print("Adresse principale inconsistante",
|
print("Adresse principale inconsistante", user.email, addr.email)
|
||||||
user.email, addr.email)
|
|
||||||
new_mails.append(newaddr)
|
new_mails.append(newaddr)
|
||||||
|
|
||||||
# Create new CASAccount connexion
|
# Create new CASAccount connexion
|
||||||
|
@ -78,30 +76,33 @@ def forwards(apps, schema_editor):
|
||||||
print(user.username)
|
print(user.username)
|
||||||
continue
|
continue
|
||||||
entrance_year = saccount.extra_data.get(
|
entrance_year = saccount.extra_data.get(
|
||||||
"entrance_year", user.username.split("@")[1])
|
"entrance_year", user.username.split("@")[1]
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
entrance_year = 2000 + int(entrance_year)
|
entrance_year = 2000 + int(entrance_year)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
print(entrance_year)
|
print(entrance_year)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
new_conns.append(CASAccount(user=user, cas_login=clipper,
|
new_conns.append(
|
||||||
entrance_year=int(entrance_year)))
|
CASAccount(user=user, cas_login=clipper, entrance_year=int(entrance_year))
|
||||||
|
)
|
||||||
|
|
||||||
NewEmailAddress.objects.bulk_create(new_mails)
|
NewEmailAddress.objects.bulk_create(new_mails)
|
||||||
CASAccount.objects.bulk_create(new_conns)
|
CASAccount.objects.bulk_create(new_conns)
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(forwards, migrations.RunPython.noop),
|
migrations.RunPython(forwards, migrations.RunPython.noop),
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('avisstage', '0003_auto_20210117_1208'),
|
("avisstage", "0003_auto_20210117_1208"),
|
||||||
('authens', '0002_old_cas_account'),
|
("authens", "0002_old_cas_account"),
|
||||||
]
|
]
|
||||||
|
|
||||||
if global_apps.is_installed('allauth'):
|
if global_apps.is_installed("allauth"):
|
||||||
dependencies.append(('socialaccount', '0003_extra_data_default_dict'))
|
dependencies.append(("socialaccount", "0003_extra_data_default_dict"))
|
||||||
|
|
||||||
if global_apps.is_installed('simple_email_confirmation'):
|
if global_apps.is_installed("simple_email_confirmation"):
|
||||||
dependencies.append(('simple_email_confirmation', '0001_initial'))
|
dependencies.append(("simple_email_confirmation", "0001_initial"))
|
||||||
|
|
|
@ -6,13 +6,13 @@ from django.db import migrations, models
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('avisstage', '0004_allauth_to_authens'),
|
("avisstage", "0004_allauth_to_authens"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='normalien',
|
model_name="normalien",
|
||||||
name='en_scolarite',
|
name="en_scolarite",
|
||||||
field=models.BooleanField(blank=True, default=False),
|
field=models.BooleanField(blank=True, default=False),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,26 +7,30 @@ from django.db import migrations, models
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('avisstage', '0005_normalien_en_scolarite'),
|
("avisstage", "0005_normalien_en_scolarite"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RemoveField(
|
migrations.RemoveField(
|
||||||
model_name='normalien',
|
model_name="normalien",
|
||||||
name='en_scolarite',
|
name="en_scolarite",
|
||||||
),
|
),
|
||||||
migrations.RemoveField(
|
migrations.RemoveField(
|
||||||
model_name='normalien',
|
model_name="normalien",
|
||||||
name='mail',
|
name="mail",
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='normalien',
|
model_name="normalien",
|
||||||
name='last_cas_login',
|
name="last_cas_login",
|
||||||
field=models.DateField(default=avisstage.models._default_cas_login),
|
field=models.DateField(default=avisstage.models._default_cas_login),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='normalien',
|
model_name="normalien",
|
||||||
name='contactez_moi',
|
name="contactez_moi",
|
||||||
field=models.BooleanField(default=True, help_text='Affiche votre adresse e-mail principale sur votre profil public', verbose_name='Inviter les visiteurs à me contacter'),
|
field=models.BooleanField(
|
||||||
|
default=True,
|
||||||
|
help_text="Affiche votre adresse e-mail principale sur votre profil public",
|
||||||
|
verbose_name="Inviter les visiteurs à me contacter",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -20,27 +20,39 @@ from datetime import timedelta
|
||||||
|
|
||||||
from .utils import choices_length, is_email_ens
|
from .utils import choices_length, is_email_ens
|
||||||
from .statics import (
|
from .statics import (
|
||||||
DEPARTEMENTS_DEFAUT, PAYS_OPTIONS, TYPE_LIEU_OPTIONS, TYPE_STAGE_OPTIONS, TYPE_LIEU_DICT,
|
DEPARTEMENTS_DEFAUT,
|
||||||
TYPE_STAGE_DICT, NIVEAU_SCOL_OPTIONS, NIVEAU_SCOL_DICT
|
PAYS_OPTIONS,
|
||||||
|
TYPE_LIEU_OPTIONS,
|
||||||
|
TYPE_STAGE_OPTIONS,
|
||||||
|
TYPE_LIEU_DICT,
|
||||||
|
TYPE_STAGE_DICT,
|
||||||
|
NIVEAU_SCOL_OPTIONS,
|
||||||
|
NIVEAU_SCOL_DICT,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _default_cas_login():
|
def _default_cas_login():
|
||||||
return (timezone.now() - timedelta(days=365)).date()
|
return (timezone.now() - timedelta(days=365)).date()
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Profil Normalien (extension du modèle User)
|
# Profil Normalien (extension du modèle User)
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
class Normalien(models.Model):
|
class Normalien(models.Model):
|
||||||
user = models.OneToOneField(User, related_name="profil",
|
user = models.OneToOneField(
|
||||||
on_delete=models.SET_NULL, null=True)
|
User, related_name="profil", on_delete=models.SET_NULL, null=True
|
||||||
|
)
|
||||||
|
|
||||||
# Infos spécifiques
|
# Infos spécifiques
|
||||||
nom = models.CharField("Nom complet", max_length=255, blank=True)
|
nom = models.CharField("Nom complet", max_length=255, blank=True)
|
||||||
promotion = models.CharField("Promotion", max_length=40, blank=True)
|
promotion = models.CharField("Promotion", max_length=40, blank=True)
|
||||||
contactez_moi = models.BooleanField(
|
contactez_moi = models.BooleanField(
|
||||||
"Inviter les visiteurs à me contacter",
|
"Inviter les visiteurs à me contacter",
|
||||||
default=True, help_text="Affiche votre adresse e-mail principale sur votre profil public")
|
default=True,
|
||||||
|
help_text="Affiche votre adresse e-mail principale sur votre profil public",
|
||||||
|
)
|
||||||
bio = models.TextField("À propos de moi", blank=True, default="")
|
bio = models.TextField("À propos de moi", blank=True, default="")
|
||||||
last_cas_login = models.DateField(default=_default_cas_login)
|
last_cas_login = models.DateField(default=_default_cas_login)
|
||||||
|
|
||||||
|
@ -53,12 +65,11 @@ class Normalien(models.Model):
|
||||||
|
|
||||||
# Liste des stages publiés
|
# Liste des stages publiés
|
||||||
def stages_publics(self):
|
def stages_publics(self):
|
||||||
return self.stages.filter(public=True).order_by('-date_debut')
|
return self.stages.filter(public=True).order_by("-date_debut")
|
||||||
|
|
||||||
def has_nonENS_email(self):
|
def has_nonENS_email(self):
|
||||||
return (
|
return (
|
||||||
self.user.email_address_set
|
self.user.email_address_set.exclude(confirmed_at__isnull=True)
|
||||||
.exclude(confirmed_at__isnull=True)
|
|
||||||
.exclude(email__endswith="ens.fr")
|
.exclude(email__endswith="ens.fr")
|
||||||
.exclude(email__endswith="ens.psl.eu")
|
.exclude(email__endswith="ens.psl.eu")
|
||||||
.exists()
|
.exists()
|
||||||
|
@ -77,6 +88,7 @@ class Normalien(models.Model):
|
||||||
def preferred_email(self):
|
def preferred_email(self):
|
||||||
return self.user.email
|
return self.user.email
|
||||||
|
|
||||||
|
|
||||||
# Hook à la création d'un nouvel utilisateur : information de base
|
# Hook à la création d'un nouvel utilisateur : information de base
|
||||||
def create_basic_user_profile(sender, instance, created, **kwargs):
|
def create_basic_user_profile(sender, instance, created, **kwargs):
|
||||||
if created:
|
if created:
|
||||||
|
@ -89,6 +101,7 @@ def create_basic_user_profile(sender, instance, created, **kwargs):
|
||||||
profil.promotion = instance.username.split("@")[1]
|
profil.promotion = instance.username.split("@")[1]
|
||||||
profil.save()
|
profil.save()
|
||||||
|
|
||||||
|
|
||||||
post_save.connect(create_basic_user_profile, sender=User)
|
post_save.connect(create_basic_user_profile, sender=User)
|
||||||
|
|
||||||
# Hook d'authENS : information du CAS
|
# Hook d'authENS : information du CAS
|
||||||
|
@ -114,33 +127,33 @@ def handle_cas_connection(sender, instance, created, cas_login, attributes, **kw
|
||||||
profil.nom = attributes.get("name", "")
|
profil.nom = attributes.get("name", "")
|
||||||
profil.save()
|
profil.save()
|
||||||
|
|
||||||
|
|
||||||
post_cas_connect.connect(handle_cas_connection, sender=User)
|
post_cas_connect.connect(handle_cas_connection, sender=User)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Lieu de stage
|
# Lieu de stage
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
class Lieu(models.Model):
|
class Lieu(models.Model):
|
||||||
# Général
|
# Général
|
||||||
nom = models.CharField("Nom de l'institution d'accueil",
|
nom = models.CharField("Nom de l'institution d'accueil", max_length=250)
|
||||||
max_length=250)
|
type_lieu = models.CharField(
|
||||||
type_lieu = models.CharField("Type de structure d'accueil",
|
"Type de structure d'accueil",
|
||||||
default="universite",
|
default="universite",
|
||||||
choices=TYPE_LIEU_OPTIONS,
|
choices=TYPE_LIEU_OPTIONS,
|
||||||
max_length=choices_length(TYPE_LIEU_OPTIONS))
|
max_length=choices_length(TYPE_LIEU_OPTIONS),
|
||||||
|
)
|
||||||
|
|
||||||
# Infos géographiques
|
# Infos géographiques
|
||||||
ville = models.CharField("Ville",
|
ville = models.CharField("Ville", max_length=200)
|
||||||
max_length=200)
|
pays = models.CharField(
|
||||||
pays = models.CharField("Pays",
|
"Pays", choices=PAYS_OPTIONS, max_length=choices_length(PAYS_OPTIONS)
|
||||||
choices=PAYS_OPTIONS,
|
)
|
||||||
max_length=choices_length(PAYS_OPTIONS))
|
|
||||||
|
|
||||||
# Coordonnées
|
# Coordonnées
|
||||||
# objects = geomodels.GeoManager() # Requis par GeoDjango
|
# objects = geomodels.GeoManager() # Requis par GeoDjango
|
||||||
coord = geomodels.PointField("Coordonnées",
|
coord = geomodels.PointField("Coordonnées", geography=True, srid=4326)
|
||||||
geography=True,
|
|
||||||
srid = 4326)
|
|
||||||
|
|
||||||
# Type du lieu en plus joli
|
# Type du lieu en plus joli
|
||||||
@property
|
@property
|
||||||
|
@ -158,10 +171,12 @@ class Lieu(models.Model):
|
||||||
verbose_name = "Lieu"
|
verbose_name = "Lieu"
|
||||||
verbose_name_plural = "Lieux"
|
verbose_name_plural = "Lieux"
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Matières des stages
|
# Matières des stages
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
class StageMatiere(models.Model):
|
class StageMatiere(models.Model):
|
||||||
nom = models.CharField("Nom", max_length=30)
|
nom = models.CharField("Nom", max_length=30)
|
||||||
slug = models.SlugField()
|
slug = models.SlugField()
|
||||||
|
@ -173,14 +188,17 @@ class StageMatiere(models.Model):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.nom
|
return self.nom
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Un stage
|
# Un stage
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
class Stage(models.Model):
|
class Stage(models.Model):
|
||||||
# Misc
|
# Misc
|
||||||
auteur = models.ForeignKey(Normalien, related_name="stages",
|
auteur = models.ForeignKey(
|
||||||
on_delete=models.SET_NULL, null=True)
|
Normalien, related_name="stages", on_delete=models.SET_NULL, null=True
|
||||||
|
)
|
||||||
public = models.BooleanField("Visible publiquement", default=False)
|
public = models.BooleanField("Visible publiquement", default=False)
|
||||||
date_creation = models.DateTimeField("Créé le", default=timezone.now)
|
date_creation = models.DateTimeField("Créé le", default=timezone.now)
|
||||||
date_maj = models.DateTimeField("Mis à jour le", default=timezone.now)
|
date_maj = models.DateTimeField("Mis à jour le", default=timezone.now)
|
||||||
|
@ -193,29 +211,36 @@ class Stage(models.Model):
|
||||||
date_debut = models.DateField("Date de début", null=True)
|
date_debut = models.DateField("Date de début", null=True)
|
||||||
date_fin = models.DateField("Date de fin", null=True)
|
date_fin = models.DateField("Date de fin", null=True)
|
||||||
|
|
||||||
type_stage = models.CharField("Type",
|
type_stage = models.CharField(
|
||||||
|
"Type",
|
||||||
default="stage",
|
default="stage",
|
||||||
choices=TYPE_STAGE_OPTIONS,
|
choices=TYPE_STAGE_OPTIONS,
|
||||||
max_length=choices_length(TYPE_STAGE_OPTIONS))
|
max_length=choices_length(TYPE_STAGE_OPTIONS),
|
||||||
niveau_scol = models.CharField("Année de scolarité",
|
)
|
||||||
|
niveau_scol = models.CharField(
|
||||||
|
"Année de scolarité",
|
||||||
default="",
|
default="",
|
||||||
choices=NIVEAU_SCOL_OPTIONS,
|
choices=NIVEAU_SCOL_OPTIONS,
|
||||||
max_length=choices_length(NIVEAU_SCOL_OPTIONS),
|
max_length=choices_length(NIVEAU_SCOL_OPTIONS),
|
||||||
blank=True)
|
blank=True,
|
||||||
|
)
|
||||||
|
|
||||||
thematiques = TaggableManager("Thématiques", blank=True)
|
thematiques = TaggableManager("Thématiques", blank=True)
|
||||||
matieres = models.ManyToManyField(StageMatiere, verbose_name="Matière(s)", related_name="stages")
|
matieres = models.ManyToManyField(
|
||||||
|
StageMatiere, verbose_name="Matière(s)", related_name="stages"
|
||||||
|
)
|
||||||
encadrants = models.CharField("Encadrant⋅e⋅s", max_length=500, blank=True)
|
encadrants = models.CharField("Encadrant⋅e⋅s", max_length=500, blank=True)
|
||||||
structure = models.CharField("Structure d'accueil", max_length=500, blank=True)
|
structure = models.CharField("Structure d'accueil", max_length=500, blank=True)
|
||||||
|
|
||||||
# Avis
|
# Avis
|
||||||
lieux = models.ManyToManyField(Lieu, related_name="stages",
|
lieux = models.ManyToManyField(
|
||||||
through="AvisLieu", blank=True)
|
Lieu, related_name="stages", through="AvisLieu", blank=True
|
||||||
|
)
|
||||||
|
|
||||||
# Affichage des avis ordonnés
|
# Affichage des avis ordonnés
|
||||||
@property
|
@property
|
||||||
def avis_lieux(self):
|
def avis_lieux(self):
|
||||||
return self.avislieu_set.order_by('order')
|
return self.avislieu_set.order_by("order")
|
||||||
|
|
||||||
# Shortcut pour affichage rapide
|
# Shortcut pour affichage rapide
|
||||||
@property
|
@property
|
||||||
|
@ -229,6 +254,7 @@ class Stage(models.Model):
|
||||||
@property
|
@property
|
||||||
def type_stage_fancy(self):
|
def type_stage_fancy(self):
|
||||||
return TYPE_STAGE_DICT.get(self.type_stage, ("stage", False))[0]
|
return TYPE_STAGE_DICT.get(self.type_stage, ("stage", False))[0]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type_stage_fem(self):
|
def type_stage_fem(self):
|
||||||
return TYPE_STAGE_DICT.get(self.type_stage, ("stage", False))[1]
|
return TYPE_STAGE_DICT.get(self.type_stage, ("stage", False))[1]
|
||||||
|
@ -244,7 +270,7 @@ class Stage(models.Model):
|
||||||
return self.lieux.all()
|
return self.lieux.all()
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('avisstage:stage', self)
|
return reverse("avisstage:stage", self)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s (par %s)" % (self.sujet, self.auteur.user.username)
|
return "%s (par %s)" % (self.sujet, self.auteur.user.username)
|
||||||
|
@ -272,13 +298,16 @@ class Stage(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "Stage"
|
verbose_name = "Stage"
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Les avis
|
# Les avis
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
class AvisStage(models.Model):
|
class AvisStage(models.Model):
|
||||||
stage = models.OneToOneField(Stage, related_name="avis_stage",
|
stage = models.OneToOneField(
|
||||||
on_delete=models.CASCADE)
|
Stage, related_name="avis_stage", on_delete=models.CASCADE
|
||||||
|
)
|
||||||
|
|
||||||
chapo = models.TextField("En quelques mots", blank=True)
|
chapo = models.TextField("En quelques mots", blank=True)
|
||||||
avis_ambiance = RichTextField("L'ambiance de travail", blank=True)
|
avis_ambiance = RichTextField("L'ambiance de travail", blank=True)
|
||||||
|
@ -290,14 +319,19 @@ class AvisStage(models.Model):
|
||||||
les_moins = models.TextField("Les moins de cette expérience", blank=True)
|
les_moins = models.TextField("Les moins de cette expérience", blank=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Avis sur {%s} par %s" % (self.stage.sujet, self.stage.auteur.user.username)
|
return "Avis sur {%s} par %s" % (
|
||||||
|
self.stage.sujet,
|
||||||
|
self.stage.auteur.user.username,
|
||||||
|
)
|
||||||
|
|
||||||
# Liste des champs d'avis, couplés à leur nom (pour l'affichage)
|
# Liste des champs d'avis, couplés à leur nom (pour l'affichage)
|
||||||
@property
|
@property
|
||||||
def avis_all(self):
|
def avis_all(self):
|
||||||
fields = ['avis_sujet', 'avis_ambiance', 'avis_admin', 'avis_prestage']
|
fields = ["avis_sujet", "avis_ambiance", "avis_admin", "avis_prestage"]
|
||||||
return [(AvisStage._meta.get_field(field).verbose_name,
|
return [
|
||||||
getattr(self, field, '')) for field in fields]
|
(AvisStage._meta.get_field(field).verbose_name, getattr(self, field, ""))
|
||||||
|
for field in fields
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class AvisLieu(models.Model):
|
class AvisLieu(models.Model):
|
||||||
|
@ -307,8 +341,7 @@ class AvisLieu(models.Model):
|
||||||
|
|
||||||
chapo = models.TextField("En quelques mots", blank=True)
|
chapo = models.TextField("En quelques mots", blank=True)
|
||||||
avis_lieustage = RichTextField("Les lieux de travail", blank=True)
|
avis_lieustage = RichTextField("Les lieux de travail", blank=True)
|
||||||
avis_pratique = RichTextField("S'installer - conseils pratiques",
|
avis_pratique = RichTextField("S'installer - conseils pratiques", blank=True)
|
||||||
blank=True)
|
|
||||||
avis_tourisme = RichTextField("Dans les parages", blank=True)
|
avis_tourisme = RichTextField("Dans les parages", blank=True)
|
||||||
|
|
||||||
les_plus = models.TextField("Les plus du lieu", blank=True)
|
les_plus = models.TextField("Les plus du lieu", blank=True)
|
||||||
|
@ -324,6 +357,8 @@ class AvisLieu(models.Model):
|
||||||
# Liste des champs d'avis, couplés à leur nom (pour l'affichage)
|
# Liste des champs d'avis, couplés à leur nom (pour l'affichage)
|
||||||
@property
|
@property
|
||||||
def avis_all(self):
|
def avis_all(self):
|
||||||
fields = ['avis_lieustage', 'avis_pratique', 'avis_tourisme']
|
fields = ["avis_lieustage", "avis_pratique", "avis_tourisme"]
|
||||||
return [(AvisLieu._meta.get_field(field).verbose_name,
|
return [
|
||||||
getattr(self, field, '')) for field in fields]
|
(AvisLieu._meta.get_field(field).verbose_name, getattr(self, field, ""))
|
||||||
|
for field in fields
|
||||||
|
]
|
||||||
|
|
|
@ -1,67 +1,76 @@
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
DEPARTEMENTS_DEFAUT = (
|
DEPARTEMENTS_DEFAUT = (
|
||||||
('phy', u'Physique'),
|
("phy", u"Physique"),
|
||||||
('maths', u'Maths'),
|
("maths", u"Maths"),
|
||||||
('bio', u'Biologie'),
|
("bio", u"Biologie"),
|
||||||
('chimie', u'Chimie'),
|
("chimie", u"Chimie"),
|
||||||
('geol', u'Géosciences'),
|
("geol", u"Géosciences"),
|
||||||
('dec', u'DEC'),
|
("dec", u"DEC"),
|
||||||
('info', u'Informatique'),
|
("info", u"Informatique"),
|
||||||
('litt', u'Littéraire'),
|
("litt", u"Littéraire"),
|
||||||
('guests', u'Pensionnaires étrangers'),
|
("guests", u"Pensionnaires étrangers"),
|
||||||
('pei', u'PEI'),
|
("pei", u"PEI"),
|
||||||
)
|
)
|
||||||
|
|
||||||
TYPE_STAGE_OPTIONS = (
|
TYPE_STAGE_OPTIONS = (
|
||||||
(u'Recherche :', (
|
(
|
||||||
('recherche', "Stage académique"),
|
u"Recherche :",
|
||||||
('recherche_autre', "Stage non-académique"),
|
(
|
||||||
('sejour_dri', "Séjour de recherche DRI"),
|
("recherche", "Stage académique"),
|
||||||
)),
|
("recherche_autre", "Stage non-académique"),
|
||||||
(u'Stage sans visée de recherche :', (
|
("sejour_dri", "Séjour de recherche DRI"),
|
||||||
('pro', "Stage en entreprise"),
|
),
|
||||||
('admin', "Stage en admin./ONG/orga. internationale"),
|
),
|
||||||
)),
|
(
|
||||||
(u'Enseignement :', (
|
u"Stage sans visée de recherche :",
|
||||||
('lectorat', "Lectorat DRI"),
|
(
|
||||||
('autre_teach', "Autre expérience d'enseignement"),
|
("pro", "Stage en entreprise"),
|
||||||
)),
|
("admin", "Stage en admin./ONG/orga. internationale"),
|
||||||
('autre', "Autre"),
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
u"Enseignement :",
|
||||||
|
(
|
||||||
|
("lectorat", "Lectorat DRI"),
|
||||||
|
("autre_teach", "Autre expérience d'enseignement"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("autre", "Autre"),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Dictionnaire des type de stage (et de leur genre, True=féminin)
|
# Dictionnaire des type de stage (et de leur genre, True=féminin)
|
||||||
TYPE_STAGE_DICT = {
|
TYPE_STAGE_DICT = {
|
||||||
'recherche': ("stage de recherche académique", False),
|
"recherche": ("stage de recherche académique", False),
|
||||||
'recherche_autre': ("stage de recherche non-académique", False),
|
"recherche_autre": ("stage de recherche non-académique", False),
|
||||||
'sejour_dri': ("séjour de recherche DRI", False),
|
"sejour_dri": ("séjour de recherche DRI", False),
|
||||||
'pro': ("stage en entreprise sans visée de recherche", False),
|
"pro": ("stage en entreprise sans visée de recherche", False),
|
||||||
'admin': ("stage en administration, ONG ou organisation internationale", False),
|
"admin": ("stage en administration, ONG ou organisation internationale", False),
|
||||||
'lectorat': ("lectorat DRI", False),
|
"lectorat": ("lectorat DRI", False),
|
||||||
'autre_teach': ("expérience de recherche", True),
|
"autre_teach": ("expérience de recherche", True),
|
||||||
'autre': ("expérience", True),
|
"autre": ("expérience", True),
|
||||||
}
|
}
|
||||||
|
|
||||||
TYPE_LIEU_OPTIONS = (
|
TYPE_LIEU_OPTIONS = (
|
||||||
('universite', "Université"),
|
("universite", "Université"),
|
||||||
('entreprise', "Entreprise"),
|
("entreprise", "Entreprise"),
|
||||||
('centrerecherche', "Centre de recherche"),
|
("centrerecherche", "Centre de recherche"),
|
||||||
('administration', "Administration"),
|
("administration", "Administration"),
|
||||||
('autre', "Autre"),
|
("autre", "Autre"),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Place du stage dans le cursus
|
# Place du stage dans le cursus
|
||||||
|
|
||||||
NIVEAU_SCOL_OPTIONS = (
|
NIVEAU_SCOL_OPTIONS = (
|
||||||
('L3', "Licence 3"),
|
("L3", "Licence 3"),
|
||||||
('M1', "Master 1"),
|
("M1", "Master 1"),
|
||||||
('M2', "Master 2"),
|
("M2", "Master 2"),
|
||||||
('DOC', "Pré-doctorat"),
|
("DOC", "Pré-doctorat"),
|
||||||
('CST', "Césure"),
|
("CST", "Césure"),
|
||||||
('BLA', "Année blanche"),
|
("BLA", "Année blanche"),
|
||||||
('VAC', "Vacances scolaires"),
|
("VAC", "Vacances scolaires"),
|
||||||
('MIT', "Mi-temps en parallèle des études"),
|
("MIT", "Mi-temps en parallèle des études"),
|
||||||
('', "Autre"),
|
("", "Autre"),
|
||||||
)
|
)
|
||||||
|
|
||||||
NIVEAU_SCOL_DICT = {
|
NIVEAU_SCOL_DICT = {
|
||||||
|
@ -78,11 +87,11 @@ NIVEAU_SCOL_DICT = {
|
||||||
|
|
||||||
# Dictionnaire des noms de lieux (et de leur genre, True=féminin)
|
# Dictionnaire des noms de lieux (et de leur genre, True=féminin)
|
||||||
TYPE_LIEU_DICT = {
|
TYPE_LIEU_DICT = {
|
||||||
'universite': ("université", True),
|
"universite": ("université", True),
|
||||||
'entreprise': ("entreprise", True),
|
"entreprise": ("entreprise", True),
|
||||||
'centrerecherche': ("centre de recherche", False),
|
"centrerecherche": ("centre de recherche", False),
|
||||||
'administration': ("administration", True),
|
"administration": ("administration", True),
|
||||||
'autre': ("lieu", False),
|
"autre": ("lieu", False),
|
||||||
}
|
}
|
||||||
|
|
||||||
PAYS_OPTIONS = (
|
PAYS_OPTIONS = (
|
||||||
|
|
|
@ -6,23 +6,27 @@ import re
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
@register.inclusion_tag('avisstage/templatetags/widget_lieu.html')
|
|
||||||
|
@register.inclusion_tag("avisstage/templatetags/widget_lieu.html")
|
||||||
def lieu_widget():
|
def lieu_widget():
|
||||||
form = LieuForm()
|
form = LieuForm()
|
||||||
return {"form": form}
|
return {"form": form}
|
||||||
|
|
||||||
@register.inclusion_tag('avisstage/templatetags/widget_feedback.html')
|
|
||||||
|
@register.inclusion_tag("avisstage/templatetags/widget_feedback.html")
|
||||||
def feedback_widget():
|
def feedback_widget():
|
||||||
form = FeedbackForm()
|
form = FeedbackForm()
|
||||||
return {"form": form}
|
return {"form": form}
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def typonazisme(value):
|
def typonazisme(value):
|
||||||
value = re.sub(r'(\w)\s*([?!:])', u'\\1 \\2', value)
|
value = re.sub(r"(\w)\s*([?!:])", u"\\1 \\2", value)
|
||||||
value = re.sub(r'(\w)\s*([,.])', u'\\1\\2', value)
|
value = re.sub(r"(\w)\s*([,.])", u"\\1\\2", value)
|
||||||
value = re.sub(r'([?!:,.])(\w)', u'\\1 \\2', value)
|
value = re.sub(r"([?!:,.])(\w)", u"\\1 \\2", value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def avis_len(value):
|
def avis_len(value):
|
||||||
if value < 5:
|
if value < 5:
|
||||||
|
@ -32,6 +36,7 @@ def avis_len(value):
|
||||||
else:
|
else:
|
||||||
return "long"
|
return "long"
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def url_replace(request, field, value):
|
def url_replace(request, field, value):
|
||||||
dict_ = request.GET.copy()
|
dict_ = request.GET.copy()
|
||||||
|
|
|
@ -12,14 +12,15 @@ from unittest import mock
|
||||||
|
|
||||||
from .models import User, Normalien, Lieu, Stage, StageMatiere, AvisLieu
|
from .models import User, Normalien, Lieu, Stage, StageMatiere, AvisLieu
|
||||||
|
|
||||||
|
|
||||||
class ExperiENSTestCase(TestCase):
|
class ExperiENSTestCase(TestCase):
|
||||||
|
|
||||||
# Dummy database
|
# Dummy database
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.u_conscrit = User.objects.create_user('conscrit',
|
self.u_conscrit = User.objects.create_user(
|
||||||
'conscrit@ens.fr',
|
"conscrit", "conscrit@ens.fr", "conscrit"
|
||||||
'conscrit')
|
)
|
||||||
self.p_conscrit = self.u_conscrit.profil
|
self.p_conscrit = self.u_conscrit.profil
|
||||||
self.p_conscrit.nom = "Petit conscrit"
|
self.p_conscrit.nom = "Petit conscrit"
|
||||||
self.p_conscrit.promotion = "Serpentard 2020"
|
self.p_conscrit.promotion = "Serpentard 2020"
|
||||||
|
@ -33,21 +34,29 @@ class ExperiENSTestCase(TestCase):
|
||||||
)
|
)
|
||||||
self.sa_conscrit.save()
|
self.sa_conscrit.save()
|
||||||
|
|
||||||
self.u_archi = User.objects.create_user('archicube',
|
self.u_archi = User.objects.create_user(
|
||||||
'archicube@ens.fr',
|
"archicube", "archicube@ens.fr", "archicube"
|
||||||
'archicube')
|
)
|
||||||
self.p_archi = self.u_archi.profil
|
self.p_archi = self.u_archi.profil
|
||||||
self.p_archi.nom = "Vieil archicube"
|
self.p_archi.nom = "Vieil archicube"
|
||||||
self.p_archi.promotion = "Gryffondor 2014"
|
self.p_archi.promotion = "Gryffondor 2014"
|
||||||
self.p_archi.bio = "Je suis un vieil archicube"
|
self.p_archi.bio = "Je suis un vieil archicube"
|
||||||
|
|
||||||
self.lieu1 = Lieu(nom="Beaux-Bâtons", type_lieu="universite",
|
self.lieu1 = Lieu(
|
||||||
ville="Brocéliande", pays="FR",
|
nom="Beaux-Bâtons",
|
||||||
coord="POINT(-1.63971 48.116382)")
|
type_lieu="universite",
|
||||||
|
ville="Brocéliande",
|
||||||
|
pays="FR",
|
||||||
|
coord="POINT(-1.63971 48.116382)",
|
||||||
|
)
|
||||||
self.lieu1.save()
|
self.lieu1.save()
|
||||||
self.lieu2 = Lieu(nom="Durmstrang", type_lieu="universite",
|
self.lieu2 = Lieu(
|
||||||
ville="Edimbourg", pays="GB",
|
nom="Durmstrang",
|
||||||
coord="POINT(56.32153 -1.259715)")
|
type_lieu="universite",
|
||||||
|
ville="Edimbourg",
|
||||||
|
pays="GB",
|
||||||
|
coord="POINT(56.32153 -1.259715)",
|
||||||
|
)
|
||||||
self.lieu2.save()
|
self.lieu2.save()
|
||||||
|
|
||||||
self.matiere1 = StageMatiere(nom="Arithmancie", slug="arithmancie")
|
self.matiere1 = StageMatiere(nom="Arithmancie", slug="arithmancie")
|
||||||
|
@ -55,38 +64,46 @@ class ExperiENSTestCase(TestCase):
|
||||||
self.matiere2 = StageMatiere(nom="Sortilège", slug="sortilege")
|
self.matiere2 = StageMatiere(nom="Sortilège", slug="sortilege")
|
||||||
self.matiere2.save()
|
self.matiere2.save()
|
||||||
|
|
||||||
self.cstage1 = Stage(auteur=self.p_conscrit, sujet="Wingardium Leviosa",
|
self.cstage1 = Stage(
|
||||||
|
auteur=self.p_conscrit,
|
||||||
|
sujet="Wingardium Leviosa",
|
||||||
date_debut=date(2020, 5, 10),
|
date_debut=date(2020, 5, 10),
|
||||||
date_fin=date(2020, 8, 26),
|
date_fin=date(2020, 8, 26),
|
||||||
type_stage="recherche",
|
type_stage="recherche",
|
||||||
niveau_scol="M1", public=True)
|
niveau_scol="M1",
|
||||||
|
public=True,
|
||||||
|
)
|
||||||
self.cstage1.save()
|
self.cstage1.save()
|
||||||
self.cstage1.matieres.add(self.matiere1)
|
self.cstage1.matieres.add(self.matiere1)
|
||||||
alieu1 = AvisLieu(stage=self.cstage1, lieu=self.lieu1,
|
alieu1 = AvisLieu(stage=self.cstage1, lieu=self.lieu1, chapo="Trop bien")
|
||||||
chapo="Trop bien")
|
|
||||||
alieu1.save()
|
alieu1.save()
|
||||||
|
|
||||||
self.cstage2 = Stage(auteur=self.p_conscrit, sujet="Avada Kedavra",
|
self.cstage2 = Stage(
|
||||||
|
auteur=self.p_conscrit,
|
||||||
|
sujet="Avada Kedavra",
|
||||||
date_debut=date(2021, 5, 10),
|
date_debut=date(2021, 5, 10),
|
||||||
date_fin=date(2021, 8, 26),
|
date_fin=date(2021, 8, 26),
|
||||||
type_stage="sejour_dri",
|
type_stage="sejour_dri",
|
||||||
niveau_scol="M2", public=False)
|
niveau_scol="M2",
|
||||||
|
public=False,
|
||||||
|
)
|
||||||
self.cstage2.save()
|
self.cstage2.save()
|
||||||
self.cstage2.matieres.add(self.matiere2)
|
self.cstage2.matieres.add(self.matiere2)
|
||||||
alieu2 = AvisLieu(stage=self.cstage2, lieu=self.lieu2,
|
alieu2 = AvisLieu(stage=self.cstage2, lieu=self.lieu2, chapo="Trop nul")
|
||||||
chapo="Trop nul")
|
|
||||||
alieu2.save()
|
alieu2.save()
|
||||||
|
|
||||||
|
self.astage1 = Stage(
|
||||||
self.astage1 = Stage(auteur=self.p_archi, sujet="Alohomora",
|
auteur=self.p_archi,
|
||||||
|
sujet="Alohomora",
|
||||||
date_debut=date(2014, 5, 10),
|
date_debut=date(2014, 5, 10),
|
||||||
date_fin=date(2014, 8, 26),
|
date_fin=date(2014, 8, 26),
|
||||||
type_stage="recherche",
|
type_stage="recherche",
|
||||||
niveau_scol="M2", public=True)
|
niveau_scol="M2",
|
||||||
|
public=True,
|
||||||
|
)
|
||||||
self.astage1.save()
|
self.astage1.save()
|
||||||
self.astage1.matieres.add(self.matiere2)
|
self.astage1.matieres.add(self.matiere2)
|
||||||
alieu3 = AvisLieu(stage=self.astage1, lieu=self.lieu1,
|
alieu3 = AvisLieu(stage=self.astage1, lieu=self.lieu1, chapo="Trop moyen")
|
||||||
chapo="Trop moyen")
|
|
||||||
alieu3.save()
|
alieu3.save()
|
||||||
|
|
||||||
def assertRedirectToLogin(self, testurl):
|
def assertRedirectToLogin(self, testurl):
|
||||||
|
@ -98,215 +115,231 @@ class ExperiENSTestCase(TestCase):
|
||||||
self.assertEqual(r.status_code, 404)
|
self.assertEqual(r.status_code, 404)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
ACCÈS PUBLIC
|
ACCÈS PUBLIC
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class PublicViewsTest(ExperiENSTestCase):
|
class PublicViewsTest(ExperiENSTestCase):
|
||||||
"""
|
"""
|
||||||
Vérifie que les fiches de stages ne sont pas visibles hors connexion
|
Vérifie que les fiches de stages ne sont pas visibles hors connexion
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def test_stage_visibility_public(self):
|
def test_stage_visibility_public(self):
|
||||||
self.assertRedirectToLogin(reverse('avisstage:stage',
|
self.assertRedirectToLogin(
|
||||||
kwargs={'pk':self.cstage1.id}))
|
reverse("avisstage:stage", kwargs={"pk": self.cstage1.id})
|
||||||
|
)
|
||||||
|
|
||||||
self.assertRedirectToLogin(reverse('avisstage:stage',
|
self.assertRedirectToLogin(
|
||||||
kwargs={'pk':self.cstage2.id}))
|
reverse("avisstage:stage", kwargs={"pk": self.cstage2.id})
|
||||||
|
)
|
||||||
self.assertRedirectToLogin(reverse('avisstage:stage',
|
|
||||||
kwargs={'pk':self.astage1.id}))
|
|
||||||
|
|
||||||
|
self.assertRedirectToLogin(
|
||||||
|
reverse("avisstage:stage", kwargs={"pk": self.astage1.id})
|
||||||
|
)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Vérifie que les profils de normaliens ne sont pas visibles hors connexion
|
Vérifie que les profils de normaliens ne sont pas visibles hors connexion
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def test_profil_visibility_public(self):
|
def test_profil_visibility_public(self):
|
||||||
self.assertRedirectToLogin(reverse(
|
self.assertRedirectToLogin(
|
||||||
'avisstage:profil', kwargs={'username': self.u_conscrit.username}))
|
reverse("avisstage:profil", kwargs={"username": self.u_conscrit.username})
|
||||||
|
)
|
||||||
self.assertRedirectToLogin(reverse(
|
|
||||||
'avisstage:profil', kwargs={'username': self.u_archi.username}))
|
|
||||||
|
|
||||||
|
self.assertRedirectToLogin(
|
||||||
|
reverse("avisstage:profil", kwargs={"username": self.u_archi.username})
|
||||||
|
)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Vérifie que la recherche n'est pas accessible hors connexion
|
Vérifie que la recherche n'est pas accessible hors connexion
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def test_pages_visibility_public(self):
|
def test_pages_visibility_public(self):
|
||||||
self.assertRedirectToLogin(reverse('avisstage:recherche'))
|
self.assertRedirectToLogin(reverse("avisstage:recherche"))
|
||||||
|
|
||||||
self.assertRedirectToLogin(reverse('avisstage:recherche_resultats'))
|
self.assertRedirectToLogin(reverse("avisstage:recherche_resultats"))
|
||||||
|
|
||||||
self.assertRedirectToLogin(reverse('avisstage:stage_items'))
|
self.assertRedirectToLogin(reverse("avisstage:stage_items"))
|
||||||
|
|
||||||
self.assertRedirectToLogin(reverse('avisstage:feedback'))
|
self.assertRedirectToLogin(reverse("avisstage:feedback"))
|
||||||
|
|
||||||
self.assertRedirectToLogin(reverse('avisstage:moderation'))
|
self.assertRedirectToLogin(reverse("avisstage:moderation"))
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Vérifie que l'API n'est pas accessible hors connexion
|
Vérifie que l'API n'est pas accessible hors connexion
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def test_api_visibility_public(self):
|
def test_api_visibility_public(self):
|
||||||
testurl = reverse('avisstage:api_dispatch_list',
|
testurl = reverse(
|
||||||
kwargs={"resource_name": "lieu",
|
"avisstage:api_dispatch_list",
|
||||||
"api_name": "v1"})
|
kwargs={"resource_name": "lieu", "api_name": "v1"},
|
||||||
|
)
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 401)
|
self.assertEqual(r.status_code, 401)
|
||||||
|
|
||||||
testurl = reverse('avisstage:api_dispatch_list',
|
testurl = reverse(
|
||||||
kwargs={"resource_name": "stage",
|
"avisstage:api_dispatch_list",
|
||||||
"api_name": "v1"})
|
kwargs={"resource_name": "stage", "api_name": "v1"},
|
||||||
|
)
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 401)
|
self.assertEqual(r.status_code, 401)
|
||||||
|
|
||||||
testurl = reverse('avisstage:api_dispatch_list',
|
testurl = reverse(
|
||||||
kwargs={"resource_name": "profil",
|
"avisstage:api_dispatch_list",
|
||||||
"api_name": "v1"})
|
kwargs={"resource_name": "profil", "api_name": "v1"},
|
||||||
|
)
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 401)
|
self.assertEqual(r.status_code, 401)
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Vérifie que les pages d'édition ne sont pas accessible hors connexion
|
Vérifie que les pages d'édition ne sont pas accessible hors connexion
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def test_edit_visibility_public(self):
|
def test_edit_visibility_public(self):
|
||||||
self.assertRedirectToLogin(reverse(
|
self.assertRedirectToLogin(
|
||||||
'avisstage:stage_edit', kwargs={'pk':self.cstage1.id}))
|
reverse("avisstage:stage_edit", kwargs={"pk": self.cstage1.id})
|
||||||
|
)
|
||||||
|
|
||||||
self.assertRedirectToLogin(reverse(
|
self.assertRedirectToLogin(
|
||||||
'avisstage:stage_edit', kwargs={'pk':self.astage1.id}))
|
reverse("avisstage:stage_edit", kwargs={"pk": self.astage1.id})
|
||||||
|
)
|
||||||
self.assertRedirectToLogin(reverse(
|
|
||||||
'avisstage:stage_publication', kwargs={'pk':self.cstage1.id}))
|
|
||||||
|
|
||||||
self.assertRedirectToLogin(reverse(
|
|
||||||
'avisstage:stage_publication', kwargs={'pk':self.astage1.id}))
|
|
||||||
|
|
||||||
self.assertRedirectToLogin(reverse('avisstage:stage_ajout'))
|
|
||||||
|
|
||||||
self.assertRedirectToLogin(reverse('avisstage:profil_edit'))
|
|
||||||
|
|
||||||
|
self.assertRedirectToLogin(
|
||||||
|
reverse("avisstage:stage_publication", kwargs={"pk": self.cstage1.id})
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertRedirectToLogin(
|
||||||
|
reverse("avisstage:stage_publication", kwargs={"pk": self.astage1.id})
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertRedirectToLogin(reverse("avisstage:stage_ajout"))
|
||||||
|
|
||||||
|
self.assertRedirectToLogin(reverse("avisstage:profil_edit"))
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
ACCÈS ARCHICUBE
|
ACCÈS ARCHICUBE
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class ArchicubeViewsTest(ExperiENSTestCase):
|
class ArchicubeViewsTest(ExperiENSTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
# Connexion with password
|
# Connexion with password
|
||||||
self.client.login(username='archicube', password='archicube')
|
self.client.login(username="archicube", password="archicube")
|
||||||
|
|
||||||
def assert403Archicubes(self, testurl):
|
def assert403Archicubes(self, testurl):
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
return self.assertRedirects(r, reverse('avisstage:403-archicubes'))
|
return self.assertRedirects(r, reverse("avisstage:403-archicubes"))
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Vérifie que les seules fiches de stages visibles sont les siennes
|
Vérifie que les seules fiches de stages visibles sont les siennes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def test_stage_visibility_archi(self):
|
def test_stage_visibility_archi(self):
|
||||||
self.assertPageNotFound(reverse('avisstage:stage',
|
self.assertPageNotFound(
|
||||||
kwargs={'pk':self.cstage1.id}))
|
reverse("avisstage:stage", kwargs={"pk": self.cstage1.id})
|
||||||
|
)
|
||||||
|
|
||||||
self.assertPageNotFound(reverse('avisstage:stage',
|
self.assertPageNotFound(
|
||||||
kwargs={'pk':self.cstage2.id}))
|
reverse("avisstage:stage", kwargs={"pk": self.cstage2.id})
|
||||||
|
)
|
||||||
|
|
||||||
testurl = reverse('avisstage:stage',
|
testurl = reverse("avisstage:stage", kwargs={"pk": self.astage1.id})
|
||||||
kwargs={'pk':self.astage1.id})
|
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Vérifie que le seul profil visible est le sien
|
Vérifie que le seul profil visible est le sien
|
||||||
"""
|
"""
|
||||||
def test_profil_visibility_archi(self):
|
|
||||||
self.assertPageNotFound(reverse(
|
|
||||||
'avisstage:profil', kwargs={'username': self.u_conscrit.username}))
|
|
||||||
|
|
||||||
testurl = reverse('avisstage:profil',
|
def test_profil_visibility_archi(self):
|
||||||
kwargs={'username': self.u_archi.username})
|
self.assertPageNotFound(
|
||||||
|
reverse("avisstage:profil", kwargs={"username": self.u_conscrit.username})
|
||||||
|
)
|
||||||
|
|
||||||
|
testurl = reverse(
|
||||||
|
"avisstage:profil", kwargs={"username": self.u_archi.username}
|
||||||
|
)
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Vérifie que la recherche n'est pas accessible
|
Vérifie que la recherche n'est pas accessible
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def test_pages_visibility_archi(self):
|
def test_pages_visibility_archi(self):
|
||||||
self.assert403Archicubes(reverse('avisstage:recherche'))
|
self.assert403Archicubes(reverse("avisstage:recherche"))
|
||||||
|
|
||||||
self.assert403Archicubes(reverse('avisstage:recherche_resultats'))
|
self.assert403Archicubes(reverse("avisstage:recherche_resultats"))
|
||||||
|
|
||||||
self.assert403Archicubes(reverse('avisstage:stage_items'))
|
self.assert403Archicubes(reverse("avisstage:stage_items"))
|
||||||
|
|
||||||
testurl = reverse('avisstage:feedback')
|
testurl = reverse("avisstage:feedback")
|
||||||
r = self.client.post(testurl, {"objet": "Contact",
|
r = self.client.post(
|
||||||
"message": "Ceci est un texte"})
|
testurl, {"objet": "Contact", "message": "Ceci est un texte"}
|
||||||
self.assertRedirects(r, reverse('avisstage:index'))
|
)
|
||||||
|
self.assertRedirects(r, reverse("avisstage:index"))
|
||||||
|
|
||||||
testurl = reverse('avisstage:moderation')
|
testurl = reverse("avisstage:moderation")
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertRedirects(r, reverse('admin:login')+"?next="+testurl)
|
self.assertRedirects(r, reverse("admin:login") + "?next=" + testurl)
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Vérifie que la seule API accessible est celle des lieux
|
Vérifie que la seule API accessible est celle des lieux
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def test_api_visibility_archi(self):
|
def test_api_visibility_archi(self):
|
||||||
testurl = reverse('avisstage:api_dispatch_list',
|
testurl = reverse(
|
||||||
kwargs={"resource_name": "lieu",
|
"avisstage:api_dispatch_list",
|
||||||
"api_name": "v1"})
|
kwargs={"resource_name": "lieu", "api_name": "v1"},
|
||||||
|
)
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
testurl = reverse('avisstage:api_dispatch_list',
|
testurl = reverse(
|
||||||
kwargs={"resource_name": "stage",
|
"avisstage:api_dispatch_list",
|
||||||
"api_name": "v1"})
|
kwargs={"resource_name": "stage", "api_name": "v1"},
|
||||||
|
)
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 401)
|
self.assertEqual(r.status_code, 401)
|
||||||
|
|
||||||
testurl = reverse('avisstage:api_dispatch_list',
|
testurl = reverse(
|
||||||
kwargs={"resource_name": "profil",
|
"avisstage:api_dispatch_list",
|
||||||
"api_name": "v1"})
|
kwargs={"resource_name": "profil", "api_name": "v1"},
|
||||||
|
)
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 401)
|
self.assertEqual(r.status_code, 401)
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Vérifie que le seul stage modifiable est le sien
|
Vérifie que le seul stage modifiable est le sien
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def test_edit_visibility_archi(self):
|
def test_edit_visibility_archi(self):
|
||||||
testurl = reverse('avisstage:stage_edit', kwargs={'pk':self.cstage1.id})
|
testurl = reverse("avisstage:stage_edit", kwargs={"pk": self.cstage1.id})
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 403)
|
self.assertEqual(r.status_code, 403)
|
||||||
|
|
||||||
testurl = reverse('avisstage:stage_edit', kwargs={'pk':self.astage1.id})
|
testurl = reverse("avisstage:stage_edit", kwargs={"pk": self.astage1.id})
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
testurl = reverse('avisstage:stage_publication',
|
testurl = reverse("avisstage:stage_publication", kwargs={"pk": self.cstage1.id})
|
||||||
kwargs={'pk':self.cstage1.id})
|
|
||||||
r = self.client.post(testurl, {"publier": True})
|
r = self.client.post(testurl, {"publier": True})
|
||||||
self.assertEqual(r.status_code, 403)
|
self.assertEqual(r.status_code, 403)
|
||||||
|
|
||||||
testurl = reverse('avisstage:stage_publication',
|
testurl = reverse("avisstage:stage_publication", kwargs={"pk": self.astage1.id})
|
||||||
kwargs={'pk':self.astage1.id})
|
|
||||||
r = self.client.post(testurl, {"publier": True})
|
r = self.client.post(testurl, {"publier": True})
|
||||||
self.assertRedirects(r, reverse('avisstage:stage',
|
self.assertRedirects(
|
||||||
kwargs={"pk": self.astage1.id}))
|
r, reverse("avisstage:stage", kwargs={"pk": self.astage1.id})
|
||||||
|
)
|
||||||
|
|
||||||
testurl = reverse('avisstage:stage_ajout')
|
testurl = reverse("avisstage:stage_ajout")
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
testurl = reverse('avisstage:profil_edit')
|
testurl = reverse("avisstage:profil_edit")
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
@ -337,14 +370,14 @@ class DeprecatedArchicubeViewsTest(ArchicubeViewsTest):
|
||||||
self.p_archi.save()
|
self.p_archi.save()
|
||||||
|
|
||||||
# New connexion with password
|
# New connexion with password
|
||||||
self.client.login(username='archicube', password='archicube')
|
self.client.login(username="archicube", password="archicube")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
ACCÈS EN SCOLARITE
|
ACCÈS EN SCOLARITE
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class ScolariteViewsTest(ExperiENSTestCase):
|
class ScolariteViewsTest(ExperiENSTestCase):
|
||||||
@mock.patch("authens.backends.get_cas_client")
|
@mock.patch("authens.backends.get_cas_client")
|
||||||
def setUp(self, mock_cas_client):
|
def setUp(self, mock_cas_client):
|
||||||
|
@ -354,9 +387,7 @@ class ScolariteViewsTest(ExperiENSTestCase):
|
||||||
mock_cas_client.return_value = fake_cas_client
|
mock_cas_client.return_value = fake_cas_client
|
||||||
|
|
||||||
self.u_vieuxcon = User.objects.create_user(
|
self.u_vieuxcon = User.objects.create_user(
|
||||||
'vieuxcon',
|
"vieuxcon", "vieuxcon@ens.fr", "vieuxcon"
|
||||||
'vieuxcon@ens.fr',
|
|
||||||
'vieuxcon'
|
|
||||||
)
|
)
|
||||||
self.p_vieuxcon = self.u_vieuxcon.profil
|
self.p_vieuxcon = self.u_vieuxcon.profil
|
||||||
self.p_vieuxcon.nom = "Vieux con"
|
self.p_vieuxcon.nom = "Vieux con"
|
||||||
|
@ -371,15 +402,18 @@ class ScolariteViewsTest(ExperiENSTestCase):
|
||||||
)
|
)
|
||||||
self.sa_vieuxcon.save()
|
self.sa_vieuxcon.save()
|
||||||
|
|
||||||
self.vstage1 = Stage(auteur=self.p_vieuxcon, sujet="Oubliettes",
|
self.vstage1 = Stage(
|
||||||
|
auteur=self.p_vieuxcon,
|
||||||
|
sujet="Oubliettes",
|
||||||
date_debut=date(2018, 5, 10),
|
date_debut=date(2018, 5, 10),
|
||||||
date_fin=date(2018, 8, 26),
|
date_fin=date(2018, 8, 26),
|
||||||
type_stage="recherche",
|
type_stage="recherche",
|
||||||
niveau_scol="M1", public=False)
|
niveau_scol="M1",
|
||||||
|
public=False,
|
||||||
|
)
|
||||||
self.vstage1.save()
|
self.vstage1.save()
|
||||||
self.vstage1.matieres.add(self.matiere2)
|
self.vstage1.matieres.add(self.matiere2)
|
||||||
alieu1 = AvisLieu(stage=self.vstage1, lieu=self.lieu2,
|
alieu1 = AvisLieu(stage=self.vstage1, lieu=self.lieu2, chapo="Pas si mal")
|
||||||
chapo="Pas si mal")
|
|
||||||
alieu1.save()
|
alieu1.save()
|
||||||
|
|
||||||
# Connexion through CAS
|
# Connexion through CAS
|
||||||
|
@ -389,134 +423,143 @@ class ScolariteViewsTest(ExperiENSTestCase):
|
||||||
Vérifie que les seules fiches de stages visibles sont les siennes ou celles
|
Vérifie que les seules fiches de stages visibles sont les siennes ou celles
|
||||||
publiques
|
publiques
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def test_stage_visibility_scolarite(self):
|
def test_stage_visibility_scolarite(self):
|
||||||
testurl = reverse('avisstage:stage',
|
testurl = reverse("avisstage:stage", kwargs={"pk": self.cstage1.id})
|
||||||
kwargs={'pk':self.cstage1.id})
|
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
self.assertPageNotFound(reverse('avisstage:stage',
|
self.assertPageNotFound(
|
||||||
kwargs={'pk':self.cstage2.id}))
|
reverse("avisstage:stage", kwargs={"pk": self.cstage2.id})
|
||||||
|
)
|
||||||
|
|
||||||
testurl = reverse('avisstage:stage',
|
testurl = reverse("avisstage:stage", kwargs={"pk": self.vstage1.id})
|
||||||
kwargs={'pk':self.vstage1.id})
|
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Vérifie que tous les profils sont visibles
|
Vérifie que tous les profils sont visibles
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def test_profil_visibility_scolarite(self):
|
def test_profil_visibility_scolarite(self):
|
||||||
testurl = reverse('avisstage:profil',
|
testurl = reverse(
|
||||||
kwargs={'username': self.u_conscrit.username})
|
"avisstage:profil", kwargs={"username": self.u_conscrit.username}
|
||||||
|
)
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
self.assertContains(r, "Wingardium Leviosa") # Public
|
self.assertContains(r, "Wingardium Leviosa") # Public
|
||||||
self.assertNotContains(r, "Avada Kedavra") # Brouillon
|
self.assertNotContains(r, "Avada Kedavra") # Brouillon
|
||||||
|
|
||||||
testurl = reverse('avisstage:profil',
|
testurl = reverse(
|
||||||
kwargs={'username': self.u_archi.username})
|
"avisstage:profil", kwargs={"username": self.u_archi.username}
|
||||||
|
)
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
testurl = reverse('avisstage:profil',
|
testurl = reverse(
|
||||||
kwargs={'username': self.u_vieuxcon.username})
|
"avisstage:profil", kwargs={"username": self.u_vieuxcon.username}
|
||||||
|
)
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Vérifie que la recherche et les autres pages sont accessibles
|
Vérifie que la recherche et les autres pages sont accessibles
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def test_pages_visibility_scolarite(self):
|
def test_pages_visibility_scolarite(self):
|
||||||
testurl = reverse('avisstage:recherche')
|
testurl = reverse("avisstage:recherche")
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
testurl = reverse('avisstage:recherche_resultats')
|
testurl = reverse("avisstage:recherche_resultats")
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
self.assertContains(r, "Wingardium Leviosa") # Public
|
self.assertContains(r, "Wingardium Leviosa") # Public
|
||||||
self.assertNotContains(r, "Avada Kedavra") # Brouillon
|
self.assertNotContains(r, "Avada Kedavra") # Brouillon
|
||||||
|
|
||||||
testurl = reverse('avisstage:stage_items') + "?ids=" \
|
testurl = (
|
||||||
+ ";".join(("%d" % k.id) for k in [self.cstage1,
|
reverse("avisstage:stage_items")
|
||||||
self.cstage2,
|
+ "?ids="
|
||||||
self.astage1])
|
+ ";".join(
|
||||||
|
("%d" % k.id) for k in [self.cstage1, self.cstage2, self.astage1]
|
||||||
|
)
|
||||||
|
)
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
self.assertContains(r, "Wingardium Leviosa") # Public
|
self.assertContains(r, "Wingardium Leviosa") # Public
|
||||||
self.assertNotContains(r, "Avada Kedavra") # Brouillon
|
self.assertNotContains(r, "Avada Kedavra") # Brouillon
|
||||||
|
|
||||||
testurl = reverse('avisstage:feedback')
|
testurl = reverse("avisstage:feedback")
|
||||||
r = self.client.post(testurl, {"objet": "Contact",
|
r = self.client.post(
|
||||||
"message": "Ceci est un texte"})
|
testurl, {"objet": "Contact", "message": "Ceci est un texte"}
|
||||||
self.assertRedirects(r, reverse('avisstage:index'))
|
)
|
||||||
|
self.assertRedirects(r, reverse("avisstage:index"))
|
||||||
|
|
||||||
testurl = reverse('avisstage:moderation')
|
testurl = reverse("avisstage:moderation")
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertRedirects(r, reverse('admin:login')+"?next="+testurl)
|
self.assertRedirects(r, reverse("admin:login") + "?next=" + testurl)
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Vérifie que toutes les API sont accessibles et qu'elles ne montrent que les
|
Vérifie que toutes les API sont accessibles et qu'elles ne montrent que les
|
||||||
stages publics
|
stages publics
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def test_api_visibility_scolarite(self):
|
def test_api_visibility_scolarite(self):
|
||||||
testurl = reverse('avisstage:api_dispatch_list',
|
testurl = reverse(
|
||||||
kwargs={"resource_name": "lieu",
|
"avisstage:api_dispatch_list",
|
||||||
"api_name": "v1"})
|
kwargs={"resource_name": "lieu", "api_name": "v1"},
|
||||||
|
)
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
testurl = reverse('avisstage:api_dispatch_list',
|
testurl = reverse(
|
||||||
kwargs={"resource_name": "stage",
|
"avisstage:api_dispatch_list",
|
||||||
"api_name": "v1"})
|
kwargs={"resource_name": "stage", "api_name": "v1"},
|
||||||
|
)
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
self.assertContains(r, "Wingardium Leviosa") # Public
|
self.assertContains(r, "Wingardium Leviosa") # Public
|
||||||
self.assertNotContains(r, "Avada Kedavra") # Brouillon
|
self.assertNotContains(r, "Avada Kedavra") # Brouillon
|
||||||
|
|
||||||
testurl = reverse('avisstage:api_dispatch_list',
|
testurl = reverse(
|
||||||
kwargs={"resource_name": "profil",
|
"avisstage:api_dispatch_list",
|
||||||
"api_name": "v1"})
|
kwargs={"resource_name": "profil", "api_name": "v1"},
|
||||||
|
)
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Vérifie que le seul stage modifiable est le sien
|
Vérifie que le seul stage modifiable est le sien
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def test_edit_visibility_scolarite(self):
|
def test_edit_visibility_scolarite(self):
|
||||||
testurl = reverse('avisstage:stage_edit', kwargs={'pk':self.cstage1.id})
|
testurl = reverse("avisstage:stage_edit", kwargs={"pk": self.cstage1.id})
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 403)
|
self.assertEqual(r.status_code, 403)
|
||||||
|
|
||||||
testurl = reverse('avisstage:stage_edit', kwargs={'pk':self.astage1.id})
|
testurl = reverse("avisstage:stage_edit", kwargs={"pk": self.astage1.id})
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 403)
|
self.assertEqual(r.status_code, 403)
|
||||||
|
|
||||||
testurl = reverse('avisstage:stage_edit', kwargs={'pk':self.vstage1.id})
|
testurl = reverse("avisstage:stage_edit", kwargs={"pk": self.vstage1.id})
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
testurl = reverse('avisstage:stage_publication',
|
testurl = reverse("avisstage:stage_publication", kwargs={"pk": self.cstage1.id})
|
||||||
kwargs={'pk':self.cstage1.id})
|
|
||||||
r = self.client.post(testurl, {"publier": True})
|
r = self.client.post(testurl, {"publier": True})
|
||||||
self.assertEqual(r.status_code, 403)
|
self.assertEqual(r.status_code, 403)
|
||||||
|
|
||||||
testurl = reverse('avisstage:stage_publication',
|
testurl = reverse("avisstage:stage_publication", kwargs={"pk": self.vstage1.id})
|
||||||
kwargs={'pk':self.vstage1.id})
|
|
||||||
r = self.client.post(testurl, {"publier": True})
|
r = self.client.post(testurl, {"publier": True})
|
||||||
self.assertRedirects(r, reverse('avisstage:stage',
|
self.assertRedirects(
|
||||||
kwargs={"pk": self.vstage1.id}))
|
r, reverse("avisstage:stage", kwargs={"pk": self.vstage1.id})
|
||||||
|
)
|
||||||
|
|
||||||
testurl = reverse('avisstage:stage_ajout')
|
testurl = reverse("avisstage:stage_ajout")
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
testurl = reverse('avisstage:profil_edit')
|
testurl = reverse("avisstage:profil_edit")
|
||||||
r = self.client.get(testurl)
|
r = self.client.get(testurl)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
|
|
@ -2,50 +2,62 @@ from django.urls import include, path
|
||||||
from . import views, api
|
from . import views, api
|
||||||
from tastypie.api import Api
|
from tastypie.api import Api
|
||||||
|
|
||||||
v1_api = Api(api_name='v1')
|
v1_api = Api(api_name="v1")
|
||||||
v1_api.register(api.LieuResource())
|
v1_api.register(api.LieuResource())
|
||||||
v1_api.register(api.StageResource())
|
v1_api.register(api.StageResource())
|
||||||
v1_api.register(api.AuteurResource())
|
v1_api.register(api.AuteurResource())
|
||||||
|
|
||||||
app_name = "avisstage"
|
app_name = "avisstage"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', views.index, name='index'),
|
path("", views.index, name="index"),
|
||||||
path('perso/', views.perso, name='perso'),
|
path("perso/", views.perso, name="perso"),
|
||||||
path('faq/', views.faq, name='faq'),
|
path("faq/", views.faq, name="faq"),
|
||||||
path('stage/nouveau/', views.manage_stage, name='stage_ajout'),
|
path("stage/nouveau/", views.manage_stage, name="stage_ajout"),
|
||||||
path('stage/<int:pk>/', views.StageView.as_view(), name='stage'),
|
path("stage/<int:pk>/", views.StageView.as_view(), name="stage"),
|
||||||
path('stage/<int:pk>/edit/', views.manage_stage, name='stage_edit'),
|
path("stage/<int:pk>/edit/", views.manage_stage, name="stage_edit"),
|
||||||
path('stage/<int:pk>/publication/', views.publier_stage,
|
path("stage/<int:pk>/publication/", views.publier_stage, name="stage_publication"),
|
||||||
name='stage_publication'),
|
path("403/archicubes/", views.archicubes_interdits, name="403-archicubes"),
|
||||||
path('403/archicubes/', views.archicubes_interdits,
|
path("lieu/save/", views.save_lieu, name="lieu_ajout"),
|
||||||
name='403-archicubes'),
|
path("profil/show/<str:username>/", views.ProfilView.as_view(), name="profil"),
|
||||||
|
path("profil/edit/", views.ProfilEdit.as_view(), name="profil_edit"),
|
||||||
path('lieu/save/', views.save_lieu, name='lieu_ajout'),
|
path("profil/parametres/", views.MesParametres.as_view(), name="parametres"),
|
||||||
path('profil/show/<str:username>/', views.ProfilView.as_view(),
|
path(
|
||||||
name='profil'),
|
"profil/emails/<str:email>/aconfirmer/",
|
||||||
path('profil/edit/', views.ProfilEdit.as_view(), name='profil_edit'),
|
views.AdresseAConfirmer.as_view(),
|
||||||
path('profil/parametres/', views.MesParametres.as_view(), name='parametres'),
|
name="emails_aconfirmer",
|
||||||
path('profil/emails/<str:email>/aconfirmer/',
|
),
|
||||||
views.AdresseAConfirmer.as_view(), name="emails_aconfirmer"),
|
path(
|
||||||
path('profil/emails/<str:email>/supprime/', views.SupprimeAdresse.as_view(),
|
"profil/emails/<str:email>/supprime/",
|
||||||
name="emails_supprime"),
|
views.SupprimeAdresse.as_view(),
|
||||||
path('profil/emails/<str:email>/reconfirme/',
|
name="emails_supprime",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"profil/emails/<str:email>/reconfirme/",
|
||||||
views.ReConfirmeAdresse.as_view(),
|
views.ReConfirmeAdresse.as_view(),
|
||||||
name="emails_reconfirme"),
|
name="emails_reconfirme",
|
||||||
path('profil/emails/<str:email>/principal/',
|
),
|
||||||
views.RendAdressePrincipale.as_view(), name="emails_principal"),
|
path(
|
||||||
path('profil/emails/confirme/<str:key>/', views.ConfirmeAdresse.as_view(),
|
"profil/emails/<str:email>/principal/",
|
||||||
name="emails_confirme"),
|
views.RendAdressePrincipale.as_view(),
|
||||||
path('profil/mdp/demande/',
|
name="emails_principal",
|
||||||
views.EnvoieLienMotDePasse.as_view(), name="mdp_demande"),
|
),
|
||||||
path('profil/mdp/<str:uidb64>/<str:token>/',
|
path(
|
||||||
views.DefinirMotDePasse.as_view(), name="mdp_edit"),
|
"profil/emails/confirme/<str:key>/",
|
||||||
|
views.ConfirmeAdresse.as_view(),
|
||||||
path('recherche/', views.recherche, name='recherche'),
|
name="emails_confirme",
|
||||||
path('recherche/resultats/', views.recherche_resultats,
|
),
|
||||||
name='recherche_resultats'),
|
path(
|
||||||
path('recherche/items/', views.stage_items, name='stage_items'),
|
"profil/mdp/demande/", views.EnvoieLienMotDePasse.as_view(), name="mdp_demande"
|
||||||
path('feedback/', views.feedback, name='feedback'),
|
),
|
||||||
path('moderation/', views.statistiques, name='moderation'),
|
path(
|
||||||
path('api/', include(v1_api.urls)),
|
"profil/mdp/<str:uidb64>/<str:token>/",
|
||||||
|
views.DefinirMotDePasse.as_view(),
|
||||||
|
name="mdp_edit",
|
||||||
|
),
|
||||||
|
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)),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,21 +1,25 @@
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from math import cos, radians, sqrt
|
from math import cos, radians, sqrt
|
||||||
|
|
||||||
|
|
||||||
def choices_length(choices):
|
def choices_length(choices):
|
||||||
return reduce(lambda m, choice: max(m, len(choice[0])), choices, 0)
|
return reduce(lambda m, choice: max(m, len(choice[0])), choices, 0)
|
||||||
|
|
||||||
|
|
||||||
def en_scolarite(user):
|
def en_scolarite(user):
|
||||||
return user.profil.en_scolarite
|
return user.profil.en_scolarite
|
||||||
|
|
||||||
|
|
||||||
def approximate_distance(a, b):
|
def approximate_distance(a, b):
|
||||||
lat_a = radians(a.y)
|
lat_a = radians(a.y)
|
||||||
lat_b = radians(b.y)
|
lat_b = radians(b.y)
|
||||||
dlon = radians(b.x - a.x)
|
dlon = radians(b.x - a.x)
|
||||||
dlon = dlon * cos((lat_a + lat_b) / 2)
|
dlon = dlon * cos((lat_a + lat_b) / 2)
|
||||||
dlat = (lat_a - lat_b)
|
dlat = lat_a - lat_b
|
||||||
distance = 6371000 * sqrt(dlon * dlon + dlat * dlat)
|
distance = 6371000 * sqrt(dlon * dlon + dlat * dlat)
|
||||||
return distance
|
return distance
|
||||||
|
|
||||||
|
|
||||||
def is_email_ens(mail, none=False):
|
def is_email_ens(mail, none=False):
|
||||||
if mail is None:
|
if mail is None:
|
||||||
return none
|
return none
|
||||||
|
|
|
@ -3,8 +3,14 @@
|
||||||
from django.shortcuts import render, redirect, get_object_or_404
|
from django.shortcuts import render, redirect, get_object_or_404
|
||||||
|
|
||||||
from django.views.generic import (
|
from django.views.generic import (
|
||||||
DetailView, ListView, UpdateView, CreateView, TemplateView, DeleteView,
|
DetailView,
|
||||||
FormView, View
|
ListView,
|
||||||
|
UpdateView,
|
||||||
|
CreateView,
|
||||||
|
TemplateView,
|
||||||
|
DeleteView,
|
||||||
|
FormView,
|
||||||
|
View,
|
||||||
)
|
)
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
from django import forms
|
from django import forms
|
||||||
|
@ -24,8 +30,13 @@ from simple_email_confirmation.models import EmailAddress
|
||||||
|
|
||||||
from .models import Normalien, Stage, Lieu, AvisLieu, AvisStage
|
from .models import Normalien, Stage, Lieu, AvisLieu, AvisStage
|
||||||
from .forms import (
|
from .forms import (
|
||||||
StageForm, LieuForm, AvisStageForm, AvisLieuForm, FeedbackForm, AdresseEmailForm,
|
StageForm,
|
||||||
ReinitMdpForm
|
LieuForm,
|
||||||
|
AvisStageForm,
|
||||||
|
AvisLieuForm,
|
||||||
|
FeedbackForm,
|
||||||
|
AdresseEmailForm,
|
||||||
|
ReinitMdpForm,
|
||||||
)
|
)
|
||||||
from .utils import en_scolarite
|
from .utils import en_scolarite
|
||||||
|
|
||||||
|
@ -40,8 +51,8 @@ import random, math
|
||||||
# Page d'accueil
|
# Page d'accueil
|
||||||
def index(request):
|
def index(request):
|
||||||
num_stages = Stage.objects.filter(public=True).count()
|
num_stages = Stage.objects.filter(public=True).count()
|
||||||
return render(request, 'avisstage/index.html',
|
return render(request, "avisstage/index.html", {"num_stages": num_stages})
|
||||||
{"num_stages": num_stages})
|
|
||||||
|
|
||||||
# Espace personnel
|
# Espace personnel
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -53,35 +64,41 @@ def perso(request):
|
||||||
profil, created = Normalien.objects.get_or_create(user=request.user)
|
profil, created = Normalien.objects.get_or_create(user=request.user)
|
||||||
profil.save()
|
profil.save()
|
||||||
|
|
||||||
return render(request, 'avisstage/perso.html')
|
return render(request, "avisstage/perso.html")
|
||||||
|
|
||||||
|
|
||||||
# 403 Archicubes
|
# 403 Archicubes
|
||||||
@login_required
|
@login_required
|
||||||
def archicubes_interdits(request):
|
def archicubes_interdits(request):
|
||||||
return render(request, 'avisstage/403-archicubes.html')
|
return render(request, "avisstage/403-archicubes.html")
|
||||||
|
|
||||||
|
|
||||||
# Profil
|
# Profil
|
||||||
# login_required
|
# login_required
|
||||||
class ProfilView(LoginRequiredMixin, DetailView):
|
class ProfilView(LoginRequiredMixin, DetailView):
|
||||||
model = Normalien
|
model = Normalien
|
||||||
template_name = 'avisstage/detail/profil.html'
|
template_name = "avisstage/detail/profil.html"
|
||||||
|
|
||||||
# Récupération du profil
|
# Récupération du profil
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
|
|
||||||
# Restriction d'accès pour les archicubes
|
# Restriction d'accès pour les archicubes
|
||||||
if (en_scolarite(self.request.user) or
|
if (
|
||||||
self.kwargs.get('username') == self.request.user.username):
|
en_scolarite(self.request.user)
|
||||||
|
or self.kwargs.get("username") == self.request.user.username
|
||||||
|
):
|
||||||
return get_object_or_404(
|
return get_object_or_404(
|
||||||
Normalien, user__username=self.kwargs.get('username'))
|
Normalien, user__username=self.kwargs.get("username")
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
|
|
||||||
# Stage
|
# Stage
|
||||||
# login_required
|
# login_required
|
||||||
class StageView(LoginRequiredMixin, DetailView):
|
class StageView(LoginRequiredMixin, DetailView):
|
||||||
model = Stage
|
model = Stage
|
||||||
template_name = 'avisstage/detail/stage.html'
|
template_name = "avisstage/detail/stage.html"
|
||||||
|
|
||||||
# Restriction aux stages publics ou personnels
|
# Restriction aux stages publics ou personnels
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
@ -95,12 +112,14 @@ class StageView(LoginRequiredMixin, DetailView):
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs):
|
def get_context_data(self, *args, **kwargs):
|
||||||
context = super().get_context_data(*args, **kwargs)
|
context = super().get_context_data(*args, **kwargs)
|
||||||
context['MAPBOX_API_KEY'] = settings.MAPBOX_API_KEY
|
context["MAPBOX_API_KEY"] = settings.MAPBOX_API_KEY
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
# FAQ
|
# FAQ
|
||||||
def faq(request):
|
def faq(request):
|
||||||
return render(request, 'avisstage/faq.html')
|
return render(request, "avisstage/faq.html")
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# EDITION
|
# EDITION
|
||||||
|
@ -110,15 +129,16 @@ def faq(request):
|
||||||
# login_required
|
# login_required
|
||||||
class ProfilEdit(LoginRequiredMixin, UpdateView):
|
class ProfilEdit(LoginRequiredMixin, UpdateView):
|
||||||
model = Normalien
|
model = Normalien
|
||||||
fields = ['nom', 'promotion', 'contactez_moi', 'bio']
|
fields = ["nom", "promotion", "contactez_moi", "bio"]
|
||||||
template_name = 'avisstage/formulaires/profil.html'
|
template_name = "avisstage/formulaires/profil.html"
|
||||||
|
|
||||||
# Limitation à son propre profil
|
# Limitation à son propre profil
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
return self.request.user.profil
|
return self.request.user.profil
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('avisstage:perso')
|
return reverse("avisstage:perso")
|
||||||
|
|
||||||
|
|
||||||
# Stage
|
# Stage
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -129,8 +149,9 @@ def manage_stage(request, pk=None):
|
||||||
stage = Stage(auteur=request.user.profil)
|
stage = Stage(auteur=request.user.profil)
|
||||||
avis_stage = AvisStage(stage=stage)
|
avis_stage = AvisStage(stage=stage)
|
||||||
c_del = False
|
c_del = False
|
||||||
last_creation = Stage.objects.filter(auteur=request.user.profil)\
|
last_creation = Stage.objects.filter(auteur=request.user.profil).order_by(
|
||||||
.order_by("-date_creation")[:1]
|
"-date_creation"
|
||||||
|
)[:1]
|
||||||
if len(last_creation) != 0:
|
if len(last_creation) != 0:
|
||||||
last_maj = last_creation[0].date_creation
|
last_maj = last_creation[0].date_creation
|
||||||
else:
|
else:
|
||||||
|
@ -144,20 +165,25 @@ def manage_stage(request, pk=None):
|
||||||
|
|
||||||
# Formset pour les avis des lieux
|
# Formset pour les avis des lieux
|
||||||
AvisLieuFormSet = forms.inlineformset_factory(
|
AvisLieuFormSet = forms.inlineformset_factory(
|
||||||
Stage, AvisLieu, form=AvisLieuForm, can_delete=c_del, extra=0)
|
Stage, AvisLieu, form=AvisLieuForm, can_delete=c_del, extra=0
|
||||||
|
)
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
# Lecture des données
|
# Lecture des données
|
||||||
form = StageForm(request.POST, request=request, instance=stage, prefix="stage")
|
form = StageForm(request.POST, request=request, instance=stage, prefix="stage")
|
||||||
avis_stage_form = AvisStageForm(request.POST,
|
avis_stage_form = AvisStageForm(
|
||||||
instance=avis_stage, prefix="avis")
|
request.POST, instance=avis_stage, prefix="avis"
|
||||||
avis_lieu_formset = AvisLieuFormSet(request.POST, instance=stage,
|
)
|
||||||
prefix="lieux")
|
avis_lieu_formset = AvisLieuFormSet(
|
||||||
|
request.POST, instance=stage, prefix="lieux"
|
||||||
|
)
|
||||||
|
|
||||||
# Validation et enregistrement
|
# Validation et enregistrement
|
||||||
if (form.is_valid() and
|
if (
|
||||||
avis_stage_form.is_valid() and
|
form.is_valid()
|
||||||
avis_lieu_formset.is_valid()):
|
and avis_stage_form.is_valid()
|
||||||
|
and avis_lieu_formset.is_valid()
|
||||||
|
):
|
||||||
stage = form.save()
|
stage = form.save()
|
||||||
avis_stage_form.instance.stage = stage
|
avis_stage_form.instance.stage = stage
|
||||||
avis_stage_form.save()
|
avis_stage_form.save()
|
||||||
|
@ -165,22 +191,31 @@ def manage_stage(request, pk=None):
|
||||||
# print(request.POST)
|
# print(request.POST)
|
||||||
if "continuer" in request.POST:
|
if "continuer" in request.POST:
|
||||||
if pk is None:
|
if pk is None:
|
||||||
return redirect(reverse('avisstage:stage_edit',kwargs={'pk':stage.id}))
|
return redirect(
|
||||||
|
reverse("avisstage:stage_edit", kwargs={"pk": stage.id})
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return redirect(reverse('avisstage:stage',
|
return redirect(reverse("avisstage:stage", kwargs={"pk": stage.id}))
|
||||||
kwargs={'pk':stage.id}))
|
|
||||||
else:
|
else:
|
||||||
form = StageForm(instance=stage, prefix="stage")
|
form = StageForm(instance=stage, prefix="stage")
|
||||||
avis_stage_form = AvisStageForm(instance=avis_stage, prefix="avis")
|
avis_stage_form = AvisStageForm(instance=avis_stage, prefix="avis")
|
||||||
avis_lieu_formset = AvisLieuFormSet(instance=stage, prefix="lieux")
|
avis_lieu_formset = AvisLieuFormSet(instance=stage, prefix="lieux")
|
||||||
|
|
||||||
# Affichage du formulaire
|
# Affichage du formulaire
|
||||||
return render(request, "avisstage/formulaires/stage.html",
|
return render(
|
||||||
{'form': form, 'avis_stage_form': avis_stage_form,
|
request,
|
||||||
'avis_lieu_formset': avis_lieu_formset,
|
"avisstage/formulaires/stage.html",
|
||||||
'creation': pk is None, "last_maj": last_maj,
|
{
|
||||||
'GOOGLE_API_KEY': settings.GOOGLE_API_KEY,
|
"form": form,
|
||||||
'MAPBOX_API_KEY': settings.MAPBOX_API_KEY})
|
"avis_stage_form": avis_stage_form,
|
||||||
|
"avis_lieu_formset": avis_lieu_formset,
|
||||||
|
"creation": pk is None,
|
||||||
|
"last_maj": last_maj,
|
||||||
|
"GOOGLE_API_KEY": settings.GOOGLE_API_KEY,
|
||||||
|
"MAPBOX_API_KEY": settings.MAPBOX_API_KEY,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Ajout d'un lieu de stage
|
# Ajout d'un lieu de stage
|
||||||
# login_required
|
# login_required
|
||||||
|
@ -194,7 +229,7 @@ def save_lieu(request):
|
||||||
pk = request.POST.get("id", None)
|
pk = request.POST.get("id", None)
|
||||||
# print(request.POST)
|
# print(request.POST)
|
||||||
jitter = False
|
jitter = False
|
||||||
if pk is None or pk == '':
|
if pk is None or pk == "":
|
||||||
lieu = Lieu()
|
lieu = Lieu()
|
||||||
else:
|
else:
|
||||||
# Modification du lieu
|
# Modification du lieu
|
||||||
|
@ -217,51 +252,54 @@ def save_lieu(request):
|
||||||
lieu = form.save(commit=False)
|
lieu = form.save(commit=False)
|
||||||
if jitter:
|
if jitter:
|
||||||
cdx, cdy = lieu.coord.get_coords()
|
cdx, cdy = lieu.coord.get_coords()
|
||||||
ang = random.random() * 6.29;
|
ang = random.random() * 6.29
|
||||||
rad = (random.random() + 0.5) * 3e-4
|
rad = (random.random() + 0.5) * 3e-4
|
||||||
cdx += math.cos(ang) * rad;
|
cdx += math.cos(ang) * rad
|
||||||
cdy += math.sin(ang) * rad;
|
cdy += math.sin(ang) * rad
|
||||||
lieu.coord.set_coords((cdx, cdy))
|
lieu.coord.set_coords((cdx, cdy))
|
||||||
lieu.save()
|
lieu.save()
|
||||||
|
|
||||||
# Élimination des doublons
|
# Élimination des doublons
|
||||||
if pk is None or pk == "":
|
if pk is None or pk == "":
|
||||||
olieux = Lieu.objects.filter(nom=lieu.nom, coord__distance_lte=(lieu.coord, 10))
|
olieux = Lieu.objects.filter(
|
||||||
|
nom=lieu.nom, coord__distance_lte=(lieu.coord, 10)
|
||||||
|
)
|
||||||
for olieu in olieux:
|
for olieu in olieux:
|
||||||
if olieu.type_lieu == lieu.type_lieu and \
|
if (
|
||||||
olieu.ville == lieu.ville and \
|
olieu.type_lieu == lieu.type_lieu
|
||||||
olieu.pays == lieu.pays:
|
and olieu.ville == lieu.ville
|
||||||
|
and olieu.pays == lieu.pays
|
||||||
|
):
|
||||||
return JsonResponse({"success": True, "id": olieu.id})
|
return JsonResponse({"success": True, "id": olieu.id})
|
||||||
|
|
||||||
lieu.save()
|
lieu.save()
|
||||||
return JsonResponse({"success": True, "id": lieu.id})
|
return JsonResponse({"success": True, "id": lieu.id})
|
||||||
else:
|
else:
|
||||||
return JsonResponse({"success": False,
|
return JsonResponse({"success": False, "errors": form.errors})
|
||||||
"errors": form.errors})
|
|
||||||
else:
|
else:
|
||||||
return JsonResponse({"erreur": "Aucune donnée POST"})
|
return JsonResponse({"erreur": "Aucune donnée POST"})
|
||||||
|
|
||||||
|
|
||||||
class LieuAjout(LoginRequiredMixin, CreateView):
|
class LieuAjout(LoginRequiredMixin, CreateView):
|
||||||
model = Lieu
|
model = Lieu
|
||||||
form_class = LieuForm
|
form_class = LieuForm
|
||||||
template_name = 'avisstage/formulaires/lieu.html'
|
template_name = "avisstage/formulaires/lieu.html"
|
||||||
|
|
||||||
# Retourne d'un JSON si requête AJAX
|
# Retourne d'un JSON si requête AJAX
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
if self.request.GET.get("format", "") == "json":
|
if self.request.GET.get("format", "") == "json":
|
||||||
self.object = form.save()
|
self.object = form.save()
|
||||||
return JsonResponse({"success": True,
|
return JsonResponse({"success": True, "id": self.object.id})
|
||||||
"id": self.object.id})
|
|
||||||
else:
|
else:
|
||||||
super(LieuAjout, self).form_valid(form)
|
super(LieuAjout, self).form_valid(form)
|
||||||
|
|
||||||
def form_invalid(self, form):
|
def form_invalid(self, form):
|
||||||
if self.request.GET.get("format", "") == "json":
|
if self.request.GET.get("format", "") == "json":
|
||||||
return JsonResponse({"success": False,
|
return JsonResponse({"success": False, "errors": form.errors})
|
||||||
"errors": form.errors})
|
|
||||||
else:
|
else:
|
||||||
super(LieuAjout, self).form_valid(form)
|
super(LieuAjout, self).form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
# Passage d'un stage en mode public
|
# Passage d'un stage en mode public
|
||||||
@login_required
|
@login_required
|
||||||
def publier_stage(request, pk):
|
def publier_stage(request, pk):
|
||||||
|
@ -283,24 +321,25 @@ def publier_stage(request, pk):
|
||||||
|
|
||||||
return redirect(reverse("avisstage:stage", kwargs={"pk": pk}))
|
return redirect(reverse("avisstage:stage", kwargs={"pk": pk}))
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# FEEDBACK
|
# FEEDBACK
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def feedback(request):
|
def feedback(request):
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = FeedbackForm(request.POST)
|
form = FeedbackForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
objet = form.cleaned_data['objet']
|
objet = form.cleaned_data["objet"]
|
||||||
header = "[From : %s <%s>]\n" % (request.user,
|
header = "[From : %s <%s>]\n" % (request.user, request.user.email)
|
||||||
request.user.email)
|
message = header + form.cleaned_data["message"]
|
||||||
message = header + form.cleaned_data['message']
|
|
||||||
send_mail(
|
send_mail(
|
||||||
"[experiENS] " + objet,
|
"[experiENS] " + objet,
|
||||||
message,
|
message,
|
||||||
request.user.email,
|
request.user.email,
|
||||||
['robin.champenois@ens.fr'],
|
["robin.champenois@ens.fr"],
|
||||||
fail_silently=False,
|
fail_silently=False,
|
||||||
)
|
)
|
||||||
if request.GET.get("format", None) == "json":
|
if request.GET.get("format", None) == "json":
|
||||||
|
@ -308,8 +347,7 @@ def feedback(request):
|
||||||
return redirect(reverse("avisstage:index"))
|
return redirect(reverse("avisstage:index"))
|
||||||
else:
|
else:
|
||||||
if request.GET.get("format", None) == "json":
|
if request.GET.get("format", None) == "json":
|
||||||
return JsonResponse({"success": False,
|
return JsonResponse({"success": False, "errors": form.errors})
|
||||||
"errors": form.errors})
|
|
||||||
else:
|
else:
|
||||||
form = FeedbackForm()
|
form = FeedbackForm()
|
||||||
raise Http404()
|
raise Http404()
|
||||||
|
@ -319,45 +357,76 @@ def feedback(request):
|
||||||
# STATISTIQUES
|
# STATISTIQUES
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@staff_member_required
|
@staff_member_required
|
||||||
def statistiques(request):
|
def statistiques(request):
|
||||||
nstages = Stage.objects.count()
|
nstages = Stage.objects.count()
|
||||||
npubstages = Stage.objects.filter(public=True).count()
|
npubstages = Stage.objects.filter(public=True).count()
|
||||||
nbymatiere_raw = Stage.objects.values('matieres__nom', 'public').annotate(scount=Count('matieres__nom'))
|
nbymatiere_raw = Stage.objects.values("matieres__nom", "public").annotate(
|
||||||
|
scount=Count("matieres__nom")
|
||||||
|
)
|
||||||
nbymatiere = defaultdict(dict)
|
nbymatiere = defaultdict(dict)
|
||||||
for npm in nbymatiere_raw:
|
for npm in nbymatiere_raw:
|
||||||
nbymatiere[npm["matieres__nom"]]["publics" if npm["public"] else "drafts"] = npm["scount"]
|
nbymatiere[npm["matieres__nom"]][
|
||||||
|
"publics" if npm["public"] else "drafts"
|
||||||
|
] = npm["scount"]
|
||||||
for mat, npm in nbymatiere.items():
|
for mat, npm in nbymatiere.items():
|
||||||
npm["matiere"] = mat
|
npm["matiere"] = mat
|
||||||
nbymatiere = sorted(list(nbymatiere.values()), key=lambda npm: -npm.get("publics", 0))
|
nbymatiere = sorted(
|
||||||
nbylength = [("Vide", Stage.objects.filter(len_avis_stage__lt=5).count(),
|
list(nbymatiere.values()), key=lambda npm: -npm.get("publics", 0)
|
||||||
Stage.objects.filter(len_avis_lieux__lt=5).count()),
|
)
|
||||||
("Court", Stage.objects.filter(len_avis_stage__lt=30, len_avis_stage__gt=4).count(),
|
nbylength = [
|
||||||
Stage.objects.filter(len_avis_lieux__lt=30, len_avis_lieux__gt=4).count()),
|
(
|
||||||
("Moyen", Stage.objects.filter(len_avis_stage__lt=100, len_avis_stage__gt=29).count(),
|
"Vide",
|
||||||
Stage.objects.filter(len_avis_lieux__lt=100, len_avis_lieux__gt=29).count()),
|
Stage.objects.filter(len_avis_stage__lt=5).count(),
|
||||||
("Long", Stage.objects.filter(len_avis_stage__gt=99).count(),
|
Stage.objects.filter(len_avis_lieux__lt=5).count(),
|
||||||
Stage.objects.filter(len_avis_lieux__gt=99).count())]
|
),
|
||||||
|
(
|
||||||
|
"Court",
|
||||||
|
Stage.objects.filter(len_avis_stage__lt=30, len_avis_stage__gt=4).count(),
|
||||||
|
Stage.objects.filter(len_avis_lieux__lt=30, len_avis_lieux__gt=4).count(),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Moyen",
|
||||||
|
Stage.objects.filter(len_avis_stage__lt=100, len_avis_stage__gt=29).count(),
|
||||||
|
Stage.objects.filter(len_avis_lieux__lt=100, len_avis_lieux__gt=29).count(),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Long",
|
||||||
|
Stage.objects.filter(len_avis_stage__gt=99).count(),
|
||||||
|
Stage.objects.filter(len_avis_lieux__gt=99).count(),
|
||||||
|
),
|
||||||
|
]
|
||||||
nusers = Normalien.objects.count()
|
nusers = Normalien.objects.count()
|
||||||
nauts = Normalien.objects.filter(stages__isnull=False).distinct().count()
|
nauts = Normalien.objects.filter(stages__isnull=False).distinct().count()
|
||||||
nbyaut = Counter(Normalien.objects.filter(stages__isnull=False).annotate(scount=Count('stages')).values_list('scount', flat="True")).items()
|
nbyaut = Counter(
|
||||||
|
Normalien.objects.filter(stages__isnull=False)
|
||||||
|
.annotate(scount=Count("stages"))
|
||||||
|
.values_list("scount", flat="True")
|
||||||
|
).items()
|
||||||
nlieux = Lieu.objects.filter(stages__isnull=False).distinct().count()
|
nlieux = Lieu.objects.filter(stages__isnull=False).distinct().count()
|
||||||
return render(request, 'avisstage/moderation/statistiques.html',
|
return render(
|
||||||
{'num_stages': nstages,
|
request,
|
||||||
'num_stages_pub': npubstages,
|
"avisstage/moderation/statistiques.html",
|
||||||
'num_par_matiere': nbymatiere,
|
{
|
||||||
'num_users': nusers,
|
"num_stages": nstages,
|
||||||
'num_auteurs': nauts,
|
"num_stages_pub": npubstages,
|
||||||
'num_par_auteur': nbyaut,
|
"num_par_matiere": nbymatiere,
|
||||||
'num_lieux_utiles': nlieux,
|
"num_users": nusers,
|
||||||
'num_par_longueur': nbylength,
|
"num_auteurs": nauts,
|
||||||
})
|
"num_par_auteur": nbyaut,
|
||||||
|
"num_lieux_utiles": nlieux,
|
||||||
|
"num_par_longueur": nbylength,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Compte
|
# Compte
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
class MesAdressesMixin(LoginRequiredMixin):
|
class MesAdressesMixin(LoginRequiredMixin):
|
||||||
slug_url_kwarg = "email"
|
slug_url_kwarg = "email"
|
||||||
slug_field = "email"
|
slug_field = "email"
|
||||||
|
@ -369,9 +438,11 @@ class MesAdressesMixin(LoginRequiredMixin):
|
||||||
qs = qs.filter(confirmed_at__isnull=False)
|
qs = qs.filter(confirmed_at__isnull=False)
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
|
|
||||||
def _send_confirm_mail(email, request):
|
def _send_confirm_mail(email, request):
|
||||||
confirm_url = request.build_absolute_uri(
|
confirm_url = request.build_absolute_uri(
|
||||||
reverse("avisstage:emails_confirme", kwargs={"key": email.key}))
|
reverse("avisstage:emails_confirme", kwargs={"key": email.key})
|
||||||
|
)
|
||||||
send_mail(
|
send_mail(
|
||||||
"[ExperiENS] Confirmez votre adresse a-mail",
|
"[ExperiENS] Confirmez votre adresse a-mail",
|
||||||
"""Bonjour,
|
"""Bonjour,
|
||||||
|
@ -383,13 +454,17 @@ Pour la vérifier, merci de cliquer sur le lien suivant, ou de copier l'adresse
|
||||||
{confirm_url}
|
{confirm_url}
|
||||||
|
|
||||||
Cordialement,
|
Cordialement,
|
||||||
L'équipe ExperiENS""".format(confirm_url=confirm_url),
|
L'équipe ExperiENS""".format(
|
||||||
'experiens-nepasrepondre@eleves.ens.fr',
|
confirm_url=confirm_url
|
||||||
|
),
|
||||||
|
"experiens-nepasrepondre@eleves.ens.fr",
|
||||||
[email.email],
|
[email.email],
|
||||||
fail_silently=False,
|
fail_silently=False,
|
||||||
)
|
)
|
||||||
return redirect(reverse("avisstage:emails_aconfirmer",
|
return redirect(
|
||||||
kwargs={"email": email.email}))
|
reverse("avisstage:emails_aconfirmer", kwargs={"email": email.email})
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class MesParametres(LoginRequiredMixin, FormView):
|
class MesParametres(LoginRequiredMixin, FormView):
|
||||||
model = EmailAddress
|
model = EmailAddress
|
||||||
|
@ -403,9 +478,11 @@ class MesParametres(LoginRequiredMixin, FormView):
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
new = EmailAddress.objects.create_unconfirmed(
|
new = EmailAddress.objects.create_unconfirmed(
|
||||||
form.cleaned_data["email"], self.request.user)
|
form.cleaned_data["email"], self.request.user
|
||||||
|
)
|
||||||
return _send_confirm_mail(new, self.request)
|
return _send_confirm_mail(new, self.request)
|
||||||
|
|
||||||
|
|
||||||
class RendAdressePrincipale(MesAdressesMixin, SingleObjectMixin, View):
|
class RendAdressePrincipale(MesAdressesMixin, SingleObjectMixin, View):
|
||||||
model = EmailAddress
|
model = EmailAddress
|
||||||
confirmed_only = True
|
confirmed_only = True
|
||||||
|
@ -417,10 +494,12 @@ class RendAdressePrincipale(MesAdressesMixin, SingleObjectMixin, View):
|
||||||
self.request.user.save()
|
self.request.user.save()
|
||||||
return redirect(reverse("avisstage:parametres"))
|
return redirect(reverse("avisstage:parametres"))
|
||||||
|
|
||||||
|
|
||||||
class AdresseAConfirmer(MesAdressesMixin, DetailView):
|
class AdresseAConfirmer(MesAdressesMixin, DetailView):
|
||||||
model = EmailAddress
|
model = EmailAddress
|
||||||
template_name = "avisstage/compte/aconfirmer.html"
|
template_name = "avisstage/compte/aconfirmer.html"
|
||||||
|
|
||||||
|
|
||||||
class ReConfirmeAdresse(MesAdressesMixin, DetailView):
|
class ReConfirmeAdresse(MesAdressesMixin, DetailView):
|
||||||
model = EmailAddress
|
model = EmailAddress
|
||||||
|
|
||||||
|
@ -430,18 +509,23 @@ class ReConfirmeAdresse(MesAdressesMixin, DetailView):
|
||||||
return _send_confirm_mail(email, self.request)
|
return _send_confirm_mail(email, self.request)
|
||||||
return redirect(reverse("avisstage:parametres"))
|
return redirect(reverse("avisstage:parametres"))
|
||||||
|
|
||||||
|
|
||||||
class ConfirmeAdresse(LoginRequiredMixin, View):
|
class ConfirmeAdresse(LoginRequiredMixin, View):
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
email = EmailAddress.objects.confirm(self.kwargs["key"],
|
email = EmailAddress.objects.confirm(
|
||||||
self.request.user, True)
|
self.kwargs["key"], self.request.user, True
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise Http404()
|
raise Http404()
|
||||||
messages.add_message(
|
messages.add_message(
|
||||||
self.request, messages.SUCCESS,
|
self.request,
|
||||||
"L'adresse email {email} a bien été confirmée".format(email=email.email))
|
messages.SUCCESS,
|
||||||
|
"L'adresse email {email} a bien été confirmée".format(email=email.email),
|
||||||
|
)
|
||||||
return redirect(reverse("avisstage:parametres"))
|
return redirect(reverse("avisstage:parametres"))
|
||||||
|
|
||||||
|
|
||||||
class SupprimeAdresse(MesAdressesMixin, DeleteView):
|
class SupprimeAdresse(MesAdressesMixin, DeleteView):
|
||||||
model = EmailAddress
|
model = EmailAddress
|
||||||
template_name = "avisstage/compte/email_supprime.html"
|
template_name = "avisstage/compte/email_supprime.html"
|
||||||
|
@ -451,21 +535,26 @@ class SupprimeAdresse(MesAdressesMixin, DeleteView):
|
||||||
qs = super().get_queryset(*args, **kwargs)
|
qs = super().get_queryset(*args, **kwargs)
|
||||||
return qs.exclude(email=self.request.user.email)
|
return qs.exclude(email=self.request.user.email)
|
||||||
|
|
||||||
|
|
||||||
class EnvoieLienMotDePasse(LoginRequiredMixin, View):
|
class EnvoieLienMotDePasse(LoginRequiredMixin, View):
|
||||||
def post(self, *args, **kwargs):
|
def post(self, *args, **kwargs):
|
||||||
form = ReinitMdpForm({"email": self.request.user.email})
|
form = ReinitMdpForm({"email": self.request.user.email})
|
||||||
form.is_valid()
|
form.is_valid()
|
||||||
form.save(
|
form.save(
|
||||||
email_template_name = 'avisstage/mails/reinit_mdp.html',
|
email_template_name="avisstage/mails/reinit_mdp.html",
|
||||||
from_email = 'experiens-nepasrepondre@eleves.ens.fr',
|
from_email="experiens-nepasrepondre@eleves.ens.fr",
|
||||||
subject_template_name = 'avisstage/mails/reinit_mdp.txt',
|
subject_template_name="avisstage/mails/reinit_mdp.txt",
|
||||||
)
|
)
|
||||||
messages.add_message(
|
messages.add_message(
|
||||||
self.request, messages.INFO,
|
self.request,
|
||||||
"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)
|
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
|
||||||
|
),
|
||||||
)
|
)
|
||||||
return redirect(reverse("avisstage:parametres"))
|
return redirect(reverse("avisstage:parametres"))
|
||||||
|
|
||||||
|
|
||||||
class DefinirMotDePasse(PasswordResetConfirmView):
|
class DefinirMotDePasse(PasswordResetConfirmView):
|
||||||
template_name = "avisstage/compte/edit_mdp.html"
|
template_name = "avisstage/compte/edit_mdp.html"
|
||||||
success_url = reverse_lazy("avisstage:perso")
|
success_url = reverse_lazy("avisstage:perso")
|
||||||
|
@ -475,4 +564,3 @@ class DefinirMotDePasse(PasswordResetConfirmView):
|
||||||
if self.request.user.is_authenticated and user != self.request.user:
|
if self.request.user.is_authenticated and user != self.request.user:
|
||||||
raise Http404("Ce token n'est pas valide pour votre compte")
|
raise Http404("Ce token n'est pas valide pour votre compte")
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
|
@ -29,28 +29,36 @@ logger = logging.getLogger("recherche")
|
||||||
# Recherche
|
# Recherche
|
||||||
class SearchForm(forms.Form):
|
class SearchForm(forms.Form):
|
||||||
generique = forms.CharField(required=False)
|
generique = forms.CharField(required=False)
|
||||||
sujet = forms.CharField(label=u'À propos de', required=False)
|
sujet = forms.CharField(label=u"À propos de", required=False)
|
||||||
contexte = forms.CharField(label=u'Contexte (lieu, encadrant⋅e⋅s, structure)',
|
contexte = forms.CharField(
|
||||||
required=False)
|
label=u"Contexte (lieu, encadrant⋅e⋅s, structure)", required=False
|
||||||
|
)
|
||||||
|
|
||||||
apres_annee = forms.IntegerField(label=u'Après cette année', required=False)
|
apres_annee = forms.IntegerField(label=u"Après cette année", required=False)
|
||||||
avant_annee = forms.IntegerField(label=u'Avant cette année', required=False)
|
avant_annee = forms.IntegerField(label=u"Avant cette année", required=False)
|
||||||
|
|
||||||
type_stage = forms.ChoiceField(label="Type de stage", choices=([('', u'')]
|
type_stage = forms.ChoiceField(
|
||||||
+ list(TYPE_STAGE_OPTIONS)),
|
label="Type de stage",
|
||||||
required=False)
|
choices=([("", u"")] + list(TYPE_STAGE_OPTIONS)),
|
||||||
niveau_scol = forms.ChoiceField(label="Année d'étude", choices=([('', u'')]
|
required=False,
|
||||||
+ list(NIVEAU_SCOL_OPTIONS)),
|
)
|
||||||
required=False)
|
niveau_scol = forms.ChoiceField(
|
||||||
|
label="Année d'étude",
|
||||||
|
choices=([("", u"")] + list(NIVEAU_SCOL_OPTIONS)),
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
type_lieu = forms.ChoiceField(label="Type de lieu d'accueil",
|
type_lieu = forms.ChoiceField(
|
||||||
choices=([('', u'')]
|
label="Type de lieu d'accueil",
|
||||||
+ list(TYPE_LIEU_OPTIONS)),
|
choices=([("", u"")] + list(TYPE_LIEU_OPTIONS)),
|
||||||
required=False)
|
required=False,
|
||||||
tri = forms.ChoiceField(label=u'Tri par',
|
)
|
||||||
choices=[('pertinence', u'Pertinence'),
|
tri = forms.ChoiceField(
|
||||||
('-date_maj',u'Dernière mise à jour')],
|
label=u"Tri par",
|
||||||
required=False, initial='pertinence')
|
choices=[("pertinence", u"Pertinence"), ("-date_maj", u"Dernière mise à jour")],
|
||||||
|
required=False,
|
||||||
|
initial="pertinence",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def cherche(**kwargs):
|
def cherche(**kwargs):
|
||||||
|
@ -58,9 +66,11 @@ def cherche(**kwargs):
|
||||||
use_dsl = False
|
use_dsl = False
|
||||||
|
|
||||||
def field_relevant(field, test_string=True):
|
def field_relevant(field, test_string=True):
|
||||||
return field in kwargs and \
|
return (
|
||||||
kwargs[field] is not None and \
|
field in kwargs
|
||||||
((not test_string) or kwargs[field].strip() != '')
|
and kwargs[field] is not None
|
||||||
|
and ((not test_string) or kwargs[field].strip() != "")
|
||||||
|
)
|
||||||
|
|
||||||
if USE_ELASTICSEARCH:
|
if USE_ELASTICSEARCH:
|
||||||
dsl = StageDocument.search()
|
dsl = StageDocument.search()
|
||||||
|
@ -73,26 +83,34 @@ def cherche(**kwargs):
|
||||||
if field_relevant("generique"):
|
if field_relevant("generique"):
|
||||||
# print("Filtre generique", kwargs['generique'])
|
# print("Filtre generique", kwargs['generique'])
|
||||||
dsl = dsl.query(
|
dsl = dsl.query(
|
||||||
"match",
|
"match", _all={"query": kwargs["generique"], "fuzziness": "auto"}
|
||||||
_all={"query": kwargs["generique"],
|
)
|
||||||
"fuzziness": "auto"})
|
|
||||||
use_dsl = True
|
use_dsl = True
|
||||||
|
|
||||||
# Sujet -> Recherche dan les noms de sujets et les thématiques
|
# Sujet -> Recherche dan les noms de sujets et les thématiques
|
||||||
if field_relevant("sujet"):
|
if field_relevant("sujet"):
|
||||||
dsl = dsl.query("multi_match",
|
dsl = dsl.query(
|
||||||
|
"multi_match",
|
||||||
query=kwargs["sujet"],
|
query=kwargs["sujet"],
|
||||||
fields = ['sujet^2', 'thematiques', 'matieres'],
|
fields=["sujet^2", "thematiques", "matieres"],
|
||||||
fuzziness = "auto")
|
fuzziness="auto",
|
||||||
|
)
|
||||||
use_dsl = True
|
use_dsl = True
|
||||||
|
|
||||||
# Contexte -> Encadrants, structure, lieu
|
# Contexte -> Encadrants, structure, lieu
|
||||||
if field_relevant("contexte"):
|
if field_relevant("contexte"):
|
||||||
dsl = dsl.query("multi_match",
|
dsl = dsl.query(
|
||||||
|
"multi_match",
|
||||||
query=kwargs["contexte"],
|
query=kwargs["contexte"],
|
||||||
fields = ['encadrants', 'structure^2',
|
fields=[
|
||||||
'lieux.nom', 'lieux.pays', 'lieux.ville'],
|
"encadrants",
|
||||||
fuzziness = "auto")
|
"structure^2",
|
||||||
|
"lieux.nom",
|
||||||
|
"lieux.pays",
|
||||||
|
"lieux.ville",
|
||||||
|
],
|
||||||
|
fuzziness="auto",
|
||||||
|
)
|
||||||
use_dsl = True
|
use_dsl = True
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -100,71 +118,74 @@ def cherche(**kwargs):
|
||||||
# recherche en base de données
|
# recherche en base de données
|
||||||
if field_relevant("generique"):
|
if field_relevant("generique"):
|
||||||
generique = kwargs["generique"]
|
generique = kwargs["generique"]
|
||||||
filtres = (Q(sujet__icontains=generique)
|
filtres = (
|
||||||
|
Q(sujet__icontains=generique)
|
||||||
| Q(thematiques__name__icontains=generique)
|
| Q(thematiques__name__icontains=generique)
|
||||||
| Q(matieres__nom__icontains=generique)
|
| Q(matieres__nom__icontains=generique)
|
||||||
| Q(lieux__nom__icontains=generique))
|
| Q(lieux__nom__icontains=generique)
|
||||||
|
)
|
||||||
|
|
||||||
# Autres champs -> non fonctionnels
|
# Autres champs -> non fonctionnels
|
||||||
if field_relevant("sujet") or field_relevant("contexte"):
|
if field_relevant("sujet") or field_relevant("contexte"):
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
"ElasticSearch doit être activé pour ce type de recherche")
|
"ElasticSearch doit être activé pour ce type de recherche"
|
||||||
|
)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Filtres directs db
|
# Filtres directs db
|
||||||
#
|
#
|
||||||
|
|
||||||
# Dates
|
# Dates
|
||||||
if field_relevant('avant_annee', False):
|
if field_relevant("avant_annee", False):
|
||||||
dte = date(kwargs['avant_annee']+1, 1, 1)
|
dte = date(kwargs["avant_annee"] + 1, 1, 1)
|
||||||
filtres &= Q(date_fin__lt=dte)
|
filtres &= Q(date_fin__lt=dte)
|
||||||
|
|
||||||
if field_relevant('apres_annee', False):
|
if field_relevant("apres_annee", False):
|
||||||
dte = date(kwargs['apres_annee'], 1, 1)
|
dte = date(kwargs["apres_annee"], 1, 1)
|
||||||
filtres &= Q(date_debut__gte=dte)
|
filtres &= Q(date_debut__gte=dte)
|
||||||
|
|
||||||
# Type de stage
|
# Type de stage
|
||||||
if field_relevant('type_stage'):
|
if field_relevant("type_stage"):
|
||||||
filtres &= Q(type_stage=kwargs["type_stage"])
|
filtres &= Q(type_stage=kwargs["type_stage"])
|
||||||
|
|
||||||
if field_relevant('niveau_scol'):
|
if field_relevant("niveau_scol"):
|
||||||
filtres &= Q(niveau_scol=kwargs["niveau_scol"])
|
filtres &= Q(niveau_scol=kwargs["niveau_scol"])
|
||||||
|
|
||||||
# Type de lieu
|
# Type de lieu
|
||||||
if field_relevant('type_lieu'):
|
if field_relevant("type_lieu"):
|
||||||
filtres &= Q(lieux__type_lieu=kwargs["type_lieu"])
|
filtres &= Q(lieux__type_lieu=kwargs["type_lieu"])
|
||||||
|
|
||||||
|
|
||||||
# Application
|
# Application
|
||||||
if USE_ELASTICSEARCH and use_dsl:
|
if USE_ELASTICSEARCH and use_dsl:
|
||||||
filtres &= Q(id__in=[s.meta.id for s in dsl.scan()])
|
filtres &= Q(id__in=[s.meta.id for s in dsl.scan()])
|
||||||
|
|
||||||
# print(filtres)
|
# print(filtres)
|
||||||
resultat = Stage.objects.filter(filtres)
|
resultat = Stage.objects.filter(filtres)
|
||||||
tri = 'pertinence'
|
tri = "pertinence"
|
||||||
|
|
||||||
if not use_dsl:
|
if not use_dsl:
|
||||||
kwargs['tri'] = '-date_maj'
|
kwargs["tri"] = "-date_maj"
|
||||||
|
|
||||||
if field_relevant('tri') and kwargs['tri'] in ['-date_maj']:
|
if field_relevant("tri") and kwargs["tri"] in ["-date_maj"]:
|
||||||
tri = kwargs['tri']
|
tri = kwargs["tri"]
|
||||||
resultat = resultat.order_by(tri)
|
resultat = resultat.order_by(tri)
|
||||||
|
|
||||||
return resultat, tri
|
return resultat, tri
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@en_scolarite_required
|
@en_scolarite_required
|
||||||
def recherche(request):
|
def recherche(request):
|
||||||
form = SearchForm()
|
form = SearchForm()
|
||||||
return render(request, 'avisstage/recherche/recherche.html',
|
return render(request, "avisstage/recherche/recherche.html", {"form": form})
|
||||||
{"form": form})
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@en_scolarite_required
|
@en_scolarite_required
|
||||||
def recherche_resultats(request):
|
def recherche_resultats(request):
|
||||||
stages = []
|
stages = []
|
||||||
tri = ''
|
tri = ""
|
||||||
vue = 'vue-liste'
|
vue = "vue-liste"
|
||||||
lieux = []
|
lieux = []
|
||||||
stageids = []
|
stageids = []
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
|
@ -174,17 +195,22 @@ def recherche_resultats(request):
|
||||||
search_args = form.cleaned_data
|
search_args = form.cleaned_data
|
||||||
|
|
||||||
# Gestion du cache
|
# Gestion du cache
|
||||||
lsearch_args = {key: val for key, val in search_args.items()
|
lsearch_args = {
|
||||||
if val != "" and val is not None}
|
key: val
|
||||||
|
for key, val in search_args.items()
|
||||||
|
if val != "" and val is not None
|
||||||
|
}
|
||||||
cache_key = json.dumps(lsearch_args, sort_keys=True)
|
cache_key = json.dumps(lsearch_args, sort_keys=True)
|
||||||
cached = cache.get(cache_key)
|
cached = cache.get(cache_key)
|
||||||
if cached is None:
|
if cached is None:
|
||||||
# Requête effective
|
# Requête effective
|
||||||
stages, tri = cherche(**search_args)
|
stages, tri = cherche(**search_args)
|
||||||
stageids = list(stages.values_list('id', flat=True))
|
stageids = list(stages.values_list("id", flat=True))
|
||||||
lieux = [[stageid, lieuid] for (stageid, lieuid)
|
lieux = [
|
||||||
in stages.values_list('id', 'lieux')
|
[stageid, lieuid]
|
||||||
if lieuid is not None]
|
for (stageid, lieuid) in stages.values_list("id", "lieux")
|
||||||
|
if lieuid is not None
|
||||||
|
]
|
||||||
|
|
||||||
# Sauvegarde dans le cache
|
# Sauvegarde dans le cache
|
||||||
to_cache = {"stages": stageids, "lieux": lieux, "tri": tri}
|
to_cache = {"stages": stageids, "lieux": lieux, "tri": tri}
|
||||||
|
@ -205,42 +231,55 @@ def recherche_resultats(request):
|
||||||
stageids = []
|
stageids = []
|
||||||
|
|
||||||
if cached is None:
|
if cached is None:
|
||||||
stages = stages[max(0, stageids.start_index()-1):
|
stages = stages[
|
||||||
stageids.end_index()]
|
max(0, stageids.start_index() - 1) : stageids.end_index()
|
||||||
|
]
|
||||||
else:
|
else:
|
||||||
orderer = Case(*[When(pk=pk, then=pos)
|
orderer = Case(
|
||||||
for pos, pk in enumerate(stageids)])
|
*[When(pk=pk, then=pos) for pos, pk in enumerate(stageids)]
|
||||||
|
)
|
||||||
stages = Stage.objects.filter(id__in=stageids).order_by(orderer)
|
stages = Stage.objects.filter(id__in=stageids).order_by(orderer)
|
||||||
|
|
||||||
stages = stages.prefetch_related('lieux', 'auteur',
|
stages = stages.prefetch_related(
|
||||||
'matieres', 'thematiques')
|
"lieux", "auteur", "matieres", "thematiques"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
form = SearchForm()
|
form = SearchForm()
|
||||||
if stages:
|
if stages:
|
||||||
vue = 'vue-hybride'
|
vue = "vue-hybride"
|
||||||
|
|
||||||
# Version JSON pour recherche dynamique
|
# Version JSON pour recherche dynamique
|
||||||
if request.GET.get("format") == "json":
|
if request.GET.get("format") == "json":
|
||||||
return JsonResponse({"stages": stages, "page": page,
|
return JsonResponse(
|
||||||
"num_pages": paginator.num_pages})
|
{"stages": stages, "page": page, "num_pages": paginator.num_pages}
|
||||||
|
)
|
||||||
|
|
||||||
template_name = 'avisstage/recherche/resultats.html'
|
template_name = "avisstage/recherche/resultats.html"
|
||||||
if request.GET.get("format") == "raw":
|
if request.GET.get("format") == "raw":
|
||||||
template_name = 'avisstage/recherche/stage_items.html'
|
template_name = "avisstage/recherche/stage_items.html"
|
||||||
return render(request, template_name,
|
return render(
|
||||||
{"form": form, "stages": stages, "paginator": stageids,
|
request,
|
||||||
"tri": tri, "vue": vue, "lieux": lieux,
|
template_name,
|
||||||
"MAPBOX_API_KEY": settings.MAPBOX_API_KEY})
|
{
|
||||||
|
"form": form,
|
||||||
|
"stages": stages,
|
||||||
|
"paginator": stageids,
|
||||||
|
"tri": tri,
|
||||||
|
"vue": vue,
|
||||||
|
"lieux": lieux,
|
||||||
|
"MAPBOX_API_KEY": settings.MAPBOX_API_KEY,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@en_scolarite_required
|
@en_scolarite_required
|
||||||
def stage_items(request):
|
def stage_items(request):
|
||||||
try:
|
try:
|
||||||
stageids = [int(a) for a in request.GET.get("ids", "").split(';')]
|
stageids = [int(a) for a in request.GET.get("ids", "").split(";")]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return HttpResponseBadRequest("Paramètre incorrect")
|
return HttpResponseBadRequest("Paramètre incorrect")
|
||||||
stages = Stage.objects.filter(id__in=stageids)\
|
stages = Stage.objects.filter(id__in=stageids).prefetch_related(
|
||||||
.prefetch_related('lieux', 'auteur',
|
"lieux", "auteur", "matieres", "thematiques"
|
||||||
'matieres', 'thematiques')
|
)
|
||||||
return render(request, 'avisstage/recherche/stage_items.html',
|
return render(request, "avisstage/recherche/stage_items.html", {"stages": stages})
|
||||||
{"stages": stages})
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
|
|
||||||
|
|
||||||
class LatLonWidget(forms.MultiWidget):
|
class LatLonWidget(forms.MultiWidget):
|
||||||
"""
|
"""
|
||||||
A Widget that splits Point input into two latitude/longitude boxes.
|
A Widget that splits Point input into two latitude/longitude boxes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, attrs=None, date_format=None, time_format=None):
|
def __init__(self, attrs=None, date_format=None, time_format=None):
|
||||||
widgets = (forms.HiddenInput(attrs=attrs),
|
widgets = (forms.HiddenInput(attrs=attrs), forms.HiddenInput(attrs=attrs))
|
||||||
forms.HiddenInput(attrs=attrs))
|
|
||||||
super(LatLonWidget, self).__init__(widgets, attrs)
|
super(LatLonWidget, self).__init__(widgets, attrs)
|
||||||
|
|
||||||
def decompress(self, value):
|
def decompress(self, value):
|
||||||
|
@ -23,13 +23,15 @@ class LatLonField(forms.MultiValueField):
|
||||||
srid = 4326
|
srid = 4326
|
||||||
|
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid_latitude' : (u'Entrez une latitude valide.'),
|
"invalid_latitude": (u"Entrez une latitude valide."),
|
||||||
'invalid_longitude' : (u'Entrez une longitude valide.'),
|
"invalid_longitude": (u"Entrez une longitude valide."),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
fields = (forms.FloatField(min_value=-90, max_value=90),
|
fields = (
|
||||||
forms.FloatField(min_value=-180, max_value=180))
|
forms.FloatField(min_value=-90, max_value=90),
|
||||||
|
forms.FloatField(min_value=-180, max_value=180),
|
||||||
|
)
|
||||||
super(LatLonField, self).__init__(fields, *args, **kwargs)
|
super(LatLonField, self).__init__(fields, *args, **kwargs)
|
||||||
|
|
||||||
def compress(self, data_list):
|
def compress(self, data_list):
|
||||||
|
@ -37,11 +39,11 @@ class LatLonField(forms.MultiValueField):
|
||||||
# Raise a validation error if latitude or longitude is empty
|
# Raise a validation error if latitude or longitude is empty
|
||||||
# (possible if LatLongField has required=False).
|
# (possible if LatLongField has required=False).
|
||||||
if data_list[0] in validators.EMPTY_VALUES:
|
if data_list[0] in validators.EMPTY_VALUES:
|
||||||
raise forms.ValidationError(self.error_messages['invalid_latitude'])
|
raise forms.ValidationError(self.error_messages["invalid_latitude"])
|
||||||
if data_list[1] in validators.EMPTY_VALUES:
|
if data_list[1] in validators.EMPTY_VALUES:
|
||||||
raise forms.ValidationError(self.error_messages['invalid_longitude'])
|
raise forms.ValidationError(self.error_messages["invalid_longitude"])
|
||||||
# SRID=4326;POINT(1.12345789 1.123456789)
|
# SRID=4326;POINT(1.12345789 1.123456789)
|
||||||
srid_str = 'SRID=%d'%self.srid
|
srid_str = "SRID=%d" % self.srid
|
||||||
point_str = 'POINT(%f %f)'%tuple(reversed(data_list))
|
point_str = "POINT(%f %f)" % tuple(reversed(data_list))
|
||||||
return ';'.join([srid_str, point_str])
|
return ";".join([srid_str, point_str])
|
||||||
return None
|
return None
|
||||||
|
|
Loading…
Reference in a new issue