Compare commits

...

1 commit

Author SHA1 Message Date
Martin Pépin
6232bf2de4
Remove unused imports 2019-10-05 00:57:32 +02:00
11 changed files with 97 additions and 104 deletions

View file

@ -2,12 +2,12 @@
from tastypie.resources import ModelResource
from tastypie.authentication import SessionAuthentication
from tastypie import fields, utils
from tastypie import fields
from django.contrib.gis import geos
from django.urls import reverse
from .models import Lieu, Stage, Normalien, StageMatiere
from .models import Lieu, Stage, Normalien
from .utils import approximate_distance
class EnScolariteAuthentication(SessionAuthentication):
@ -20,12 +20,12 @@ class EnScolariteAuthentication(SessionAuthentication):
class LieuResource(ModelResource):
#stages = fields.ToManyField("avisstage.api.StageResource",
# "stages", use_in="detail", full=True)
class Meta:
queryset = Lieu.objects.all()
resource_name = "lieu"
fields = ["nom", "ville", "pays", "coord", "type_lieu", "id"]
#login_required
authentication = SessionAuthentication()
@ -46,17 +46,17 @@ class LieuResource(ModelResource):
# Filtrer les lieux qui ont déjà des stages
if "has_stage" in filters:
orm_filters['stages__public'] = True
return orm_filters
# Custom apply filters pour ajouter le "distinct"
def apply_filters(self, request, applicable_filters):
return self.get_object_list(request).filter(**applicable_filters).distinct()
# Ajout d'informations
def dehydrate(self, bundle):
bundle = super(LieuResource, self).dehydrate(bundle)
obj = bundle.obj
bundle.data['coord'] = {'lat': float(obj.coord.y),
'lng': float(obj.coord.x)}
@ -65,12 +65,12 @@ class LieuResource(ModelResource):
if "lat" in bundle.request.GET and "lng" in bundle.request.GET:
bundle.data['distance'] = approximate_distance(
self.reference_point, bundle.obj.coord)
# Autres infos utiles
bundle.data["pays_nom"] = obj.get_pays_display()
bundle.data["type_lieu_nom"] = obj.type_lieu_fancy
# TODO use annotate?
bundle.data["num_stages"] = obj.stages.filter(public=True).count()
bundle.data["num_stages"] = obj.stages.filter(public=True).count()
return bundle
@ -94,7 +94,7 @@ class StageResource(ModelResource):
if "lieux" in filters:
flieux = map(int, filters['lieux'].split(','))
orm_filters['lieux__id__in'] = flieux
return orm_filters
# Informations à ajouter
@ -106,7 +106,7 @@ class StageResource(ModelResource):
bundle.data['auteur'] = obj.auteur.nom
bundle.data['thematiques'] = list(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
bundle.data['url'] = reverse("avisstage:stage", kwargs={"pk": obj.id});
return bundle
@ -115,7 +115,7 @@ class StageResource(ModelResource):
class AuteurResource(ModelResource):
stages = fields.ToManyField("avisstage.api.StageResource",
"stages", use_in="detail")
class Meta:
queryset = Normalien.objects.all()
resource_name = "profil"

View file

@ -1,7 +1,7 @@
from django_elasticsearch_dsl import DocType, Index, fields
from elasticsearch_dsl import analyzer, token_filter, tokenizer
from elasticsearch_dsl import analyzer, token_filter
from .models import Stage, AvisStage, AvisLieu
from .models import Stage
from .statics import PAYS_OPTIONS
PAYS_DICT = dict(PAYS_OPTIONS)
@ -11,7 +11,7 @@ stage.settings(
number_of_shards=1,
number_of_replicas=0
)
text_analyzer = analyzer(
'default',
tokenizer="standard",
@ -32,7 +32,7 @@ class StageDocument(DocType):
})
thematiques = fields.StringField()
matieres = fields.StringField()
class Meta:
model = Stage
fields = [
@ -65,11 +65,11 @@ class StageDocument(DocType):
def prepare_sujet(self, instance):
return instance.sujet.lower()
# Hook pour l'affichage des noms de pays
def prepare(self, instance):
data = super(StageDocument, self).prepare(instance)
for lieu in data['lieux']:
lieu['pays'] = PAYS_DICT[lieu['pays']].lower()
return data

View file

@ -5,7 +5,7 @@ from django.utils import timezone
import re
from .models import Normalien, Stage, Lieu, AvisLieu, AvisStage
from .models import Stage, Lieu, AvisLieu, AvisStage
from .widgets import LatLonField
# Sur-classe utile
@ -15,12 +15,12 @@ class HTMLTrimmerForm(forms.ModelForm):
leading_white = re.compile(r"^( \t\n)*(<p>(&nbsp;|[ \n\t]|<br[ /]*>)*</p>( \t\n)*)+?", re.IGNORECASE)
trailing_white = re.compile(r"(( \t\n)*<p>(&nbsp;|[ \n\t]|<br[ /]*>)*</p>)+?( \t\n)*$", re.IGNORECASE)
cleaned_data = super(HTMLTrimmerForm, self).clean()
for (fname, fval) in cleaned_data.items():
# Heuristique : les champs commençant par "avis_" sont des champs html
if fname[:5] == "avis_":
cleaned_data[fname] = leading_white.sub("", trailing_white.sub("", fval))
return cleaned_data
# Infos sur un stage
@ -48,17 +48,17 @@ class StageForm(forms.ModelForm):
if "request" in kwargs:
self.request = kwargs.pop("request")
super(StageForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
# Lors de la création : attribution à l'utilisateur connecté
if self.instance.id is None and hasattr(self, 'request'):
self.instance.auteur = self.request.user.profil
# Date de modification
self.instance.date_maj = timezone.now()
self.instance.update_stats(False)
stage = super(StageForm, self).save(commit=commit)
return stage
@ -97,7 +97,7 @@ class AvisLieuForm(HTMLTrimmerForm):
class LieuForm(forms.ModelForm):
coord = LatLonField()
id = forms.IntegerField(widget=forms.widgets.HiddenInput(), required=False)
class Meta:
model = Lieu
fields = ['id', 'nom', 'type_lieu', 'ville', 'pays', 'coord']
@ -106,4 +106,3 @@ class LieuForm(forms.ModelForm):
class FeedbackForm(forms.Form):
objet = forms.CharField(label="Objet", required=True)
message = forms.CharField(label="Message", required=True, widget=forms.widgets.Textarea())

View file

@ -1,7 +1,7 @@
#coding: utf-8
from django.core.management.base import BaseCommand, CommandError
from django.db.models import Count
from avisstage.models import Stage, Lieu
from django.core.management.base import BaseCommand
from avisstage.models import Lieu
class Command(BaseCommand):
help = 'Nettoie les stages à plusieurs lieux identiques'
@ -21,7 +21,7 @@ class Command(BaseCommand):
rundb = True
else:
print(u"Les modifications ne seront pas appliquées")
min_lieu = options.get('min_lieu', 0)
for lieu in Lieu.objects.filter(id__gte=min_lieu).order_by('-id'):

View file

@ -1,7 +1,8 @@
#coding: utf-8
from django.core.management.base import BaseCommand, CommandError
from django.core.management.base import BaseCommand
from django.db.models import Count
from avisstage.models import Stage, Lieu
from avisstage.models import Stage
class Command(BaseCommand):
help = 'Nettoie les stages à plusieurs lieux identiques'
@ -31,7 +32,7 @@ class Command(BaseCommand):
rundb = True
else:
print(u"Les modifications ne seront pas appliquées")
min_stage = options.get('min_stage', 0)
for stage in Stage.objects.annotate(c=Count("lieux"))\

View file

@ -1,7 +1,7 @@
#coding: utf-8
from django.core.management.base import BaseCommand, CommandError
from django.db.models import Count
from avisstage.models import Stage, Lieu
from django.core.management.base import BaseCommand
from avisstage.models import Lieu
class Command(BaseCommand):
help = 'Nettoie les stages à plusieurs lieux identiques'

View file

@ -9,13 +9,10 @@ from django.db import models
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.contrib.gis.db import models as geomodels
from django.template.defaultfilters import slugify
from django.forms.widgets import DateInput
from django.urls import reverse
from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.html import strip_tags
from taggit_autosuggest.managers import TaggableManager
from tinymce.models import HTMLField as RichTextField
@ -23,8 +20,6 @@ from tinymce.models import HTMLField as RichTextField
from .utils import choices_length
from .statics import DEPARTEMENTS_DEFAUT, PAYS_OPTIONS, TYPE_LIEU_OPTIONS, TYPE_STAGE_OPTIONS, TYPE_LIEU_DICT, TYPE_STAGE_DICT, NIVEAU_SCOL_OPTIONS, NIVEAU_SCOL_DICT
import ldap
#
# Profil Normalien (extension du modèle User)
#
@ -93,7 +88,7 @@ def create_user_profile(sender, instance, created, **kwargs):
if "department_code" in edata:
dep = dict(DEPARTEMENTS_DEFAUT).get(
edata["department_code"].lower(), '')
profil.promotion = "%s %s" % (dep, edata["entrance_year"])
profil.nom = edata.get("name", "")
profil.save()
@ -119,7 +114,7 @@ class Lieu(models.Model):
pays = models.CharField(u"Pays",
choices=PAYS_OPTIONS,
max_length=choices_length(PAYS_OPTIONS))
# Coordonnées
objects = geomodels.GeoManager() # Requis par GeoDjango
coord = geomodels.PointField(u"Coordonnées",
@ -137,11 +132,11 @@ class Lieu(models.Model):
def __str__(self):
return u"%s (%s)" % (self.nom, self.ville)
class Meta:
verbose_name = "Lieu"
verbose_name_plural = "Lieux"
#
# Matières des stages
#
@ -149,7 +144,7 @@ class Lieu(models.Model):
class StageMatiere(models.Model):
nom = models.CharField(u"Nom", max_length=30)
slug = models.SlugField()
class Meta:
verbose_name = "Matière des stages"
verbose_name_plural = "Matières des stages"
@ -228,7 +223,7 @@ class Stage(models.Model):
def get_absolute_url(self):
return reverse('avisstage:stage', self)
def __str__(self):
return u"%s (par %s)" % (self.sujet, self.auteur.user.username)
@ -242,16 +237,16 @@ class Stage(models.Model):
length += len(obj.les_plus.split())
length += len(obj.les_moins.split())
return length
if self.avis_stage:
self.len_avis_stage = get_len(self.avis_stage)
self.len_avis_lieux = 0
for avis in self.avislieu_set.all():
self.len_avis_lieux += get_len(avis)
if save:
self.save()
class Meta:
verbose_name = "Stage"

View file

@ -4,16 +4,15 @@ from allauth_ens.adapter import deprecate_clippers
from datetime import date
from django.test import TestCase
from django.urls import reverse
from django.conf import settings
from .models import User, Normalien, Lieu, Stage, StageMatiere, AvisLieu
from .models import User, Lieu, Stage, StageMatiere, AvisLieu
class ExperiENSTestCase(CASTestCase):
# Dummy database
def setUp(self):
self.u_conscrit = User.objects.create_user('conscrit',
'conscrit@ens.fr',
@ -28,7 +27,7 @@ class ExperiENSTestCase(CASTestCase):
provider="clipper",
uid="conscrit")
self.sa_conscrit.save()
self.u_archi = User.objects.create_user('archicube',
'archicube@ens.fr',
'archicube')
@ -50,7 +49,7 @@ class ExperiENSTestCase(CASTestCase):
self.matiere1.save()
self.matiere2 = StageMatiere(nom="Sortilège", slug="sortilege")
self.matiere2.save()
self.cstage1 = Stage(auteur=self.p_conscrit, sujet="Wingardium Leviosa",
date_debut=date(2000, 5, 10),
date_fin=date(2000, 8, 26),
@ -73,7 +72,7 @@ class ExperiENSTestCase(CASTestCase):
chapo="Trop nul")
alieu2.save()
self.astage1 = Stage(auteur=self.p_archi, sujet="Alohomora",
date_debut=date(1994, 5, 10),
date_fin=date(1994, 8, 26),
@ -109,10 +108,10 @@ class PublicViewsTest(ExperiENSTestCase):
def test_stage_visibility_public(self):
self.assertRedirectToLogin(reverse('avisstage:stage',
kwargs={'pk':self.cstage1.id}))
self.assertRedirectToLogin(reverse('avisstage:stage',
kwargs={'pk':self.cstage2.id}))
self.assertRedirectToLogin(reverse('avisstage:stage',
kwargs={'pk':self.astage1.id}))
@ -127,7 +126,7 @@ class PublicViewsTest(ExperiENSTestCase):
self.assertRedirectToLogin(reverse(
'avisstage:profil', kwargs={'username': self.u_archi.username}))
"""
Vérifie que la recherche n'est pas accessible hors connexion
"""
@ -151,27 +150,27 @@ class PublicViewsTest(ExperiENSTestCase):
"api_name": "v1"})
r = self.client.get(testurl)
self.assertEqual(r.status_code, 401)
testurl = reverse('avisstage:api_dispatch_list',
kwargs={"resource_name": "stage",
"api_name": "v1"})
r = self.client.get(testurl)
self.assertEqual(r.status_code, 401)
testurl = reverse('avisstage:api_dispatch_list',
kwargs={"resource_name": "profil",
"api_name": "v1"})
r = self.client.get(testurl)
self.assertEqual(r.status_code, 401)
"""
Vérifie que les pages d'édition ne sont pas accessible hors connexion
"""
def test_edit_visibility_public(self):
self.assertRedirectToLogin(reverse(
'avisstage:stage_edit', kwargs={'pk':self.cstage1.id}))
self.assertRedirectToLogin(reverse(
'avisstage:stage_edit', kwargs={'pk':self.astage1.id}))
@ -208,10 +207,10 @@ class ArchicubeViewsTest(ExperiENSTestCase):
def test_stage_visibility_archi(self):
self.assertPageNotFound(reverse('avisstage:stage',
kwargs={'pk':self.cstage1.id}))
self.assertPageNotFound(reverse('avisstage:stage',
kwargs={'pk':self.cstage2.id}))
testurl = reverse('avisstage:stage',
kwargs={'pk':self.astage1.id})
r = self.client.get(testurl)
@ -230,13 +229,13 @@ class ArchicubeViewsTest(ExperiENSTestCase):
r = self.client.get(testurl)
self.assertEqual(r.status_code, 200)
"""
Vérifie que la recherche n'est pas accessible
"""
def test_pages_visibility_archi(self):
self.assert403Archicubes(reverse('avisstage:recherche'))
self.assert403Archicubes(reverse('avisstage:recherche_resultats'))
self.assert403Archicubes(reverse('avisstage:stage_items'))
@ -260,20 +259,20 @@ class ArchicubeViewsTest(ExperiENSTestCase):
"api_name": "v1"})
r = self.client.get(testurl)
self.assertEqual(r.status_code, 200)
testurl = reverse('avisstage:api_dispatch_list',
kwargs={"resource_name": "stage",
"api_name": "v1"})
r = self.client.get(testurl)
self.assertEqual(r.status_code, 401)
testurl = reverse('avisstage:api_dispatch_list',
kwargs={"resource_name": "profil",
"api_name": "v1"})
r = self.client.get(testurl)
self.assertEqual(r.status_code, 401)
"""
Vérifie que le seul stage modifiable est le sien
"""
@ -296,11 +295,11 @@ class ArchicubeViewsTest(ExperiENSTestCase):
r = self.client.post(testurl, {"publier": True})
self.assertRedirects(r, reverse('avisstage:stage',
kwargs={"pk": self.astage1.id}))
testurl = reverse('avisstage:stage_ajout')
r = self.client.get(testurl)
self.assertEqual(r.status_code, 200)
testurl = reverse('avisstage:profil_edit')
r = self.client.get(testurl)
self.assertEqual(r.status_code, 200)
@ -311,14 +310,14 @@ class ArchicubeViewsTest(ExperiENSTestCase):
class DeprecatedArchicubeViewsTest(ArchicubeViewsTest):
def setUp(self):
super().setUp()
self.sa_archi = SocialAccount(user=self.u_archi,
provider="clipper",
uid="archicube")
self.sa_archi.save()
deprecate_clippers()
self.client.login(username='archicube', password='archicube')
@ -330,7 +329,7 @@ ACCÈS EN SCOLARITE
class ScolariteViewsTest(ExperiENSTestCase):
def setUp(self):
super().setUp()
self.u_vieuxcon = User.objects.create_user('vieuxcon',
'vieuxcon@ens.fr',
'vieuxcon')
@ -344,7 +343,7 @@ class ScolariteViewsTest(ExperiENSTestCase):
provider="clipper",
uid="vieuxcon")
self.sa_vieuxcon.save()
self.vstage1 = Stage(auteur=self.p_vieuxcon, sujet="Oubliettes",
date_debut=date(1998, 5, 10),
date_fin=date(1998, 8, 26),
@ -355,7 +354,7 @@ class ScolariteViewsTest(ExperiENSTestCase):
alieu1 = AvisLieu(stage=self.vstage1, lieu=self.lieu2,
chapo="Pas si mal")
alieu1.save()
self.client.login(username='vieuxcon', password='vieuxcon')
"""
@ -370,7 +369,7 @@ class ScolariteViewsTest(ExperiENSTestCase):
self.assertPageNotFound(reverse('avisstage:stage',
kwargs={'pk':self.cstage2.id}))
testurl = reverse('avisstage:stage',
kwargs={'pk':self.vstage1.id})
r = self.client.get(testurl)
@ -398,7 +397,7 @@ class ScolariteViewsTest(ExperiENSTestCase):
r = self.client.get(testurl)
self.assertEqual(r.status_code, 200)
"""
Vérifie que la recherche et les autres pages sont accessible
"""
@ -442,7 +441,7 @@ class ScolariteViewsTest(ExperiENSTestCase):
"api_name": "v1"})
r = self.client.get(testurl)
self.assertEqual(r.status_code, 200)
testurl = reverse('avisstage:api_dispatch_list',
kwargs={"resource_name": "stage",
"api_name": "v1"})
@ -450,14 +449,14 @@ class ScolariteViewsTest(ExperiENSTestCase):
self.assertEqual(r.status_code, 200)
self.assertContains(r, "Wingardium Leviosa") # Public
self.assertNotContains(r, "Avada Kedavra") # Brouillon
testurl = reverse('avisstage:api_dispatch_list',
kwargs={"resource_name": "profil",
"api_name": "v1"})
r = self.client.get(testurl)
self.assertEqual(r.status_code, 200)
"""
Vérifie que le seul stage modifiable est le sien
"""
@ -469,7 +468,7 @@ class ScolariteViewsTest(ExperiENSTestCase):
testurl = reverse('avisstage:stage_edit', kwargs={'pk':self.astage1.id})
r = self.client.get(testurl)
self.assertEqual(r.status_code, 403)
testurl = reverse('avisstage:stage_edit', kwargs={'pk':self.vstage1.id})
r = self.client.get(testurl)
self.assertEqual(r.status_code, 200)
@ -484,11 +483,11 @@ class ScolariteViewsTest(ExperiENSTestCase):
r = self.client.post(testurl, {"publier": True})
self.assertRedirects(r, reverse('avisstage:stage',
kwargs={"pk": self.vstage1.id}))
testurl = reverse('avisstage:stage_ajout')
r = self.client.get(testurl)
self.assertEqual(r.status_code, 200)
testurl = reverse('avisstage:profil_edit')
r = self.client.get(testurl)
self.assertEqual(r.status_code, 200)

View file

@ -1,6 +1,5 @@
# coding: utf-8
from allauth.socialaccount.models import SocialAccount
from functools import reduce
from math import cos, radians, sqrt

View file

@ -2,7 +2,7 @@
from django.shortcuts import render, redirect, get_object_or_404
from django.views.generic import DetailView, ListView
from django.views.generic import DetailView
from django.views.generic.edit import UpdateView, CreateView
from django import forms
from django.urls import reverse
@ -58,7 +58,7 @@ class ProfilView(LoginRequiredMixin, DetailView):
# Récupération du profil
def get_object(self):
# Restriction d'accès pour les archicubes
if (en_scolarite(self.request.user) or
self.kwargs.get('username') == self.request.user.username):
@ -76,7 +76,7 @@ class StageView(LoginRequiredMixin, DetailView):
# Restriction aux stages publics ou personnels
def get_queryset(self):
filtre = Q(auteur__user_id=self.request.user.id)
# Restriction d'accès pour les archicubes
if en_scolarite(self.request.user):
filtre |= Q(public=True)
@ -106,7 +106,7 @@ class ProfilEdit(LoginRequiredMixin, UpdateView):
# Limitation à son propre profil
def get_object(self):
return self.request.user.profil
def get_success_url(self):
return reverse('avisstage:perso')
@ -179,7 +179,7 @@ def manage_stage(request, pk=None):
@login_required
def save_lieu(request):
normalien = request.user.profil
if request.method == "POST":
pk = request.POST.get("id", None)
#print(request.POST)
@ -198,7 +198,7 @@ def save_lieu(request):
lieu = Lieu()
# Servira à bouger un peu le lieu
jitter = True
# Lecture des données
form = LieuForm(request.POST, instance=lieu)
@ -222,7 +222,7 @@ def save_lieu(request):
olieu.ville == lieu.ville and \
olieu.pays == lieu.pays:
return JsonResponse({"success": True, "id": olieu.id})
lieu.save()
return JsonResponse({"success": True, "id": lieu.id})
else:
@ -270,7 +270,7 @@ def publier_stage(request, pk):
stage.public = False
stage.save()
return redirect(reverse("avisstage:stage", kwargs={"pk": pk}))
#

View file

@ -9,7 +9,7 @@ from django.core.cache import cache
from django.core.paginator import Paginator
from django.db.models import Q, Case, When
from django.http import JsonResponse, HttpResponseBadRequest
from django.shortcuts import render, redirect, get_object_or_404
from django.shortcuts import render
import json
import logging
@ -32,7 +32,7 @@ class SearchForm(forms.Form):
sujet = forms.CharField(label=u'À propos de', required=False)
contexte = forms.CharField(label=u'Contexte (lieu, encadrant⋅e⋅s, structure)',
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)
@ -42,7 +42,7 @@ class SearchForm(forms.Form):
niveau_scol = forms.ChoiceField(label="Année d'étude", choices=([('', u'')]
+ list(NIVEAU_SCOL_OPTIONS)),
required=False)
type_lieu = forms.ChoiceField(label=u"Type de lieu d'accueil",
choices=([('', u'')]
+ list(TYPE_LIEU_OPTIONS)),
@ -105,7 +105,7 @@ def cherche(**kwargs):
| Q(matieres__nom__icontains=generique)
| Q(lieux__nom__icontains=generique))
# Autres champs -> non fonctionnels
# Autres champs -> non fonctionnels
if field_relevant("sujet") or field_relevant("contexte"):
raise NotImplementedError(
"ElasticSearch doit être activé pour ce type de recherche")
@ -113,7 +113,7 @@ def cherche(**kwargs):
#
# Filtres directs db
#
# Dates
if field_relevant('avant_annee', False):
dte = date(kwargs['avant_annee']+1, 1, 1)
@ -126,7 +126,7 @@ def cherche(**kwargs):
# Type de stage
if field_relevant('type_stage'):
filtres &= Q(type_stage=kwargs["type_stage"])
if field_relevant('niveau_scol'):
filtres &= Q(niveau_scol=kwargs["niveau_scol"])
@ -134,7 +134,7 @@ def cherche(**kwargs):
if field_relevant('type_lieu'):
filtres &= Q(lieux__type_lieu=kwargs["type_lieu"])
# Application
if USE_ELASTICSEARCH and use_dsl:
filtres &= Q(id__in=[s.meta.id for s in dsl.scan()])
@ -145,7 +145,7 @@ def cherche(**kwargs):
if not use_dsl:
kwargs['tri'] = '-date_maj'
if field_relevant('tri') and kwargs['tri'] in ['-date_maj']:
tri = kwargs['tri']
resultat = resultat.order_by(tri)
@ -223,7 +223,7 @@ def recherche_resultats(request):
if request.GET.get("format") == "json":
return JsonResponse({"stages": stages, "page": page,
"num_pages": paginator.num_pages})
template_name = 'avisstage/recherche/resultats.html'
if request.GET.get("format") == "raw":
template_name = 'avisstage/recherche/stage_items.html'