Skip to content
Snippets Groups Projects
Unverified Commit 54c4c0e8 authored by Seon82's avatar Seon82 Committed by GitHub
Browse files

Inscription sur le site (#21)


* Password reset feature (#8)

* Add Django Rest auth module

* Try to make the send reset password email work

* Modified template mail for reset

* Add Django Rest auth module

* Try to make the send reset password email work

* Modified template mail for reset

* test

* Ajouté API student

* API GET sans authentification

* Added POST support

* POST API fix

* Fixed charfields length in wiki

* loosen security

* simplify API serialized data

* Implemented partial_update

* Added special teaching and nationality to serializer

* dependantsNumber to int

* Fixed factory imports

* Added write auth and minor serializer fix

* Added read permissions

* Read permissions that work?

* Actually added read permissions?

* Updated tests to pass travis

* Updated tests

* Fixed all factory tests

* Adapté les tests à la nouvelle structure de Student

* Created StaffUserFactory and finished fixing tests

* Update README.md

* Added classType

* Fixed migrations

* Added last modified year & filters in admin interface

Co-authored-by: default avatarchiahetcho <44137047+chiahetcho@users.noreply.github.com>
Co-authored-by: default avatarflorimondmanca <florimond.manca@gmail.com>
Co-authored-by: default avatarDylan Sechet <dylan.sechet@student-cs.fr>
Co-authored-by: default avatarArthur Guédon <arthur.guedon@student-cs.fr>
parent 8ddf8629
Branches
No related tags found
No related merge requests found
Showing
with 519 additions and 45 deletions
...@@ -50,13 +50,17 @@ Le site utilise une base de données SQL. Plusieurs technologies existent mais o ...@@ -50,13 +50,17 @@ Le site utilise une base de données SQL. Plusieurs technologies existent mais o
Après avoir installé PostgreSQL, démarrez le serveur en ouvrant pgAdmin, l'interface graphique qui sera installée en même temps que Postgres. Après avoir installé PostgreSQL, démarrez le serveur en ouvrant pgAdmin, l'interface graphique qui sera installée en même temps que Postgres.
- Créez une nouvelle base de données :
Dans le menu de gauche, dépliez `Servers (1)`, puis votre serveur (nommé `PostgreSQL XX` par défaut), puis faites un clic droit sur l'onglet `Databases` et créez une base de données nommée `oser_backend_db`.
### Installation du projet ### Installation du projet
- (Recommandé) Créez un environnement virtuel (ici appelé `env`) puis activez-le : - (Recommandé) Créez un environnement virtuel (appelé ici oser-back) à l'aide de conda et activez-le :
```bash ```bash
$ python -m venv env $ conda create -n oser-back
$ source env/bin/activate $ conda activate oser-back
``` ```
- Installez les dépendances : - Installez les dépendances :
...@@ -70,6 +74,7 @@ $ pip install -r requirements.txt ...@@ -70,6 +74,7 @@ $ pip install -r requirements.txt
```bash ```bash
$ python manage.py migrate $ python manage.py migrate
``` ```
(En cas d'erreur, les logs du serveur PostgreSQL sont disponibles dans : %PROGRAMFILES%\PostgreSQL\POSTGRESQL_VERSION_NUM\data\log)
Il ne vous reste plus qu'à lancer le serveur de développement : Il ne vous reste plus qu'à lancer le serveur de développement :
......
...@@ -26,7 +26,7 @@ router.register('users', users_views.UserViewSet) ...@@ -26,7 +26,7 @@ router.register('users', users_views.UserViewSet)
# Profiles views # Profiles views
router.register('tutors', profiles_views.TutorViewSet) router.register('tutors', profiles_views.TutorViewSet)
router.register('students', profiles_views.StudentViewSet) router.register('students', profiles_views.StudentViewSet, base_name='student')
# Register views # Register views
router.register('registrations', register_views.RegistrationViewSet) router.register('registrations', register_views.RegistrationViewSet)
......
...@@ -5,7 +5,7 @@ import factory.django ...@@ -5,7 +5,7 @@ import factory.django
from . import models from . import models
class DocumentFactory(factory.DjangoModelFactory): class DocumentFactory(factory.django.DjangoModelFactory):
"""Document object factory.""" """Document object factory."""
class Meta: # noqa class Meta: # noqa
...@@ -15,7 +15,7 @@ class DocumentFactory(factory.DjangoModelFactory): ...@@ -15,7 +15,7 @@ class DocumentFactory(factory.DjangoModelFactory):
content = factory.Faker('text', max_nb_chars=1000, locale='fr') content = factory.Faker('text', max_nb_chars=1000, locale='fr')
class AddressFactory(factory.DjangoModelFactory): class AddressFactory(factory.django.DjangoModelFactory):
"""Address object factory.""" """Address object factory."""
class Meta: # noqa class Meta: # noqa
......
...@@ -38,7 +38,7 @@ DJANGO_APPS = [ ...@@ -38,7 +38,7 @@ DJANGO_APPS = [
'whitenoise.runserver_nostatic', 'whitenoise.runserver_nostatic',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'django.forms', 'django.forms',
'django.contrib.sites', 'django.contrib.sites'
] ]
THIRD_PARTY_APPS = [ THIRD_PARTY_APPS = [
......
...@@ -43,7 +43,7 @@ class TutorAdmin(ProfileAdminMixin, admin.ModelAdmin,ExportCsvMixin): ...@@ -43,7 +43,7 @@ class TutorAdmin(ProfileAdminMixin, admin.ModelAdmin,ExportCsvMixin):
@admin.register(Student) @admin.register(Student)
class StudentAdmin(ProfileAdminMixin, admin.ModelAdmin,ExportCsvMixin): class StudentAdmin(ProfileAdminMixin, admin.ModelAdmin,ExportCsvMixin):
"""Student admin panel.""" """Student admin panel."""
list_filter = ('school', 'year')
class Meta: # noqa class Meta: # noqa
model = Student model = Student
actions = ["export_as_csv"] actions = ["export_as_csv"]
\ No newline at end of file
...@@ -12,7 +12,7 @@ from users.factory import UserFactory ...@@ -12,7 +12,7 @@ from users.factory import UserFactory
from . import models from . import models
class StudentFactory(factory.DjangoModelFactory): class StudentFactory(factory.django.DjangoModelFactory):
"""Student object factory.""" """Student object factory."""
class Meta: # noqa class Meta: # noqa
...@@ -24,7 +24,7 @@ class StudentFactory(factory.DjangoModelFactory): ...@@ -24,7 +24,7 @@ class StudentFactory(factory.DjangoModelFactory):
_this_year = datetime.today().year _this_year = datetime.today().year
class TutorFactory(factory.DjangoModelFactory): class TutorFactory(factory.django.DjangoModelFactory):
"""Tutor object factory.""" """Tutor object factory."""
class Meta: # noqa class Meta: # noqa
......
# Generated by Django 2.2 on 2020-09-13 22:57
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('profiles', '0002_auto_20180911_1938'),
]
operations = [
migrations.AddField(
model_name='student',
name='addressNumber',
field=models.IntegerField(blank=True, null=True, verbose_name='numéro de rue'),
),
migrations.AddField(
model_name='student',
name='city',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='nom de ville'),
),
migrations.AddField(
model_name='student',
name='dependantsNumber',
field=models.CharField(blank=True, max_length=12, null=True, verbose_name="numero d'urgence"),
),
migrations.AddField(
model_name='student',
name='fatherActivity',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name='metier du pere'),
),
migrations.AddField(
model_name='student',
name='gender',
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='genre'),
),
migrations.AddField(
model_name='student',
name='grade',
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='nom du niveau de la classe'),
),
migrations.AddField(
model_name='student',
name='motherActivity',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name='metier de la mere'),
),
migrations.AddField(
model_name='student',
name='parentsEmail',
field=models.EmailField(blank=True, max_length=20, null=True, verbose_name='adresse mail parentale'),
),
migrations.AddField(
model_name='student',
name='parentsPhone',
field=models.CharField(blank=True, max_length=12, null=True, verbose_name='numéro de téléphone parental'),
),
migrations.AddField(
model_name='student',
name='parentsStatus',
field=models.CharField(blank=True, max_length=30, null=True, verbose_name='statut des parents'),
),
migrations.AddField(
model_name='student',
name='personnalPhone',
field=models.CharField(blank=True, max_length=12, null=True, verbose_name='numéro de téléphone personnel'),
),
migrations.AddField(
model_name='student',
name='scholarship',
field=models.BooleanField(blank=True, null=True, verbose_name='boursier'),
),
migrations.AddField(
model_name='student',
name='school',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name="nom de l'ecole"),
),
migrations.AddField(
model_name='student',
name='street',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name='nom de rue'),
),
migrations.AlterField(
model_name='tutor',
name='promotion',
field=models.IntegerField(choices=[(2023, '2023'), (2022, '2022'), (2021, '2021'), (2020, '2020'), (2019, '2019')], default=2023),
),
]
# Generated by Django 2.2 on 2020-09-15 16:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('profiles', '0003_auto_20200914_0057'),
]
operations = [
migrations.AlterField(
model_name='student',
name='dependantsNumber',
field=models.CharField(blank=True, max_length=12, null=True, verbose_name="numéro d'urgence"),
),
migrations.AlterField(
model_name='student',
name='fatherActivity',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name='métier du père'),
),
migrations.AlterField(
model_name='student',
name='grade',
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='niveau de la classe'),
),
migrations.AlterField(
model_name='student',
name='motherActivity',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name='métier de la mère'),
),
migrations.AlterField(
model_name='student',
name='scholarship',
field=models.NullBooleanField(verbose_name='boursier'),
),
migrations.AlterField(
model_name='student',
name='school',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name="nom de l'école"),
),
]
# Generated by Django 2.2 on 2020-09-18 11:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('profiles', '0003_auto_20200914_0057'),
]
operations = [
migrations.AlterField(
model_name='student',
name='dependantsNumber',
field=models.CharField(blank=True, max_length=12, null=True, verbose_name="numéro d'urgence"),
),
migrations.AlterField(
model_name='student',
name='fatherActivity',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name='métier du père'),
),
migrations.AlterField(
model_name='student',
name='grade',
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='niveau de la classe'),
),
migrations.AlterField(
model_name='student',
name='motherActivity',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name='métier de la mère'),
),
migrations.AlterField(
model_name='student',
name='scholarship',
field=models.NullBooleanField(verbose_name='boursier'),
),
migrations.AlterField(
model_name='student',
name='school',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name="nom de l'école"),
),
]
# Generated by Django 2.2 on 2020-09-18 16:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('profiles', '0004_auto_20200918_1320'),
]
operations = [
migrations.AlterField(
model_name='student',
name='scholarship',
field=models.CharField(blank=True, max_length=10, null=True, verbose_name='boursier'),
),
]
# Generated by Django 2.2 on 2020-09-18 16:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('profiles', '0005_auto_20200918_1805'),
]
operations = [
migrations.AddField(
model_name='student',
name='nationality',
field=models.CharField(blank=True, max_length=30, null=True, verbose_name='nationalité'),
),
migrations.AddField(
model_name='student',
name='specialTeaching',
field=models.CharField(blank=True, max_length=30, null=True, verbose_name='enseignement de spécialité'),
),
migrations.AddField(
model_name='student',
name='zipCode',
field=models.CharField(blank=True, max_length=10, null=True, verbose_name='code postal'),
),
]
# Generated by Django 2.2 on 2020-09-18 16:24
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('profiles', '0006_auto_20200918_1818'),
('profiles', '0004_auto_20200915_1827'),
]
operations = [
]
# Generated by Django 2.2 on 2020-09-18 17:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('profiles', '0007_merge_20200918_1824'),
]
operations = [
migrations.AlterField(
model_name='student',
name='scholarship',
field=models.CharField(blank=True, max_length=10, null=True, verbose_name='boursier'),
),
]
# Generated by Django 2.2 on 2020-09-23 11:29
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('profiles', '0008_auto_20200918_1913'),
]
operations = [
migrations.AlterField(
model_name='student',
name='dependantsNumber',
field=models.IntegerField(blank=True, null=True, verbose_name='nombre de personnes à charge'),
),
migrations.AlterField(
model_name='student',
name='nationality',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='nationalité'),
),
migrations.AlterField(
model_name='student',
name='parentsEmail',
field=models.EmailField(blank=True, max_length=70, null=True, verbose_name='adresse mail parentale'),
),
migrations.AlterField(
model_name='student',
name='parentsStatus',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name='statut des parents'),
),
migrations.AlterField(
model_name='student',
name='scholarship',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='boursier'),
),
migrations.AlterField(
model_name='student',
name='specialTeaching',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='enseignement de spécialité'),
),
]
# Generated by Django 2.2 on 2020-09-23 12:44
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('profiles', '0009_auto_20200923_1329'),
]
operations = [
migrations.RenameField(
model_name='student',
old_name='personnalPhone',
new_name='personalPhone',
),
]
# Generated by Django 2.2 on 2020-09-24 16:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('profiles', '0010_auto_20200923_1444'),
]
operations = [
migrations.AddField(
model_name='student',
name='classType',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='général/techno'),
),
]
# Generated by Django 2.2 on 2020-09-25 12:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('profiles', '0011_student_classtype'),
]
operations = [
migrations.AddField(
model_name='student',
name='year',
field=models.CharField(blank=True, max_length=10, null=True, verbose_name='année'),
),
]
...@@ -4,7 +4,7 @@ from django.db import models ...@@ -4,7 +4,7 @@ from django.db import models
from django.shortcuts import reverse from django.shortcuts import reverse
from dry_rest_permissions.generics import authenticated_users from dry_rest_permissions.generics import authenticated_users
from .utils import get_promotion_range from .utils import get_promotion_range
from datetime import datetime
class ProfileMixin: class ProfileMixin:
"""Mixin with common functionnality for profiles.""" """Mixin with common functionnality for profiles."""
...@@ -52,6 +52,144 @@ class Student(ProfileMixin, models.Model): ...@@ -52,6 +52,144 @@ class Student(ProfileMixin, models.Model):
related_name='student', related_name='student',
) )
classType = models.CharField(max_length=50,
null=True,
blank=True,
verbose_name="général/techno",
)
nationality = models.CharField(max_length=50,
null=True,
blank=True,
verbose_name="nationalité",
)
specialTeaching = models.CharField(max_length=50,
null=True,
blank=True,
verbose_name="enseignement de spécialité",
)
zipCode = models.CharField(max_length=10,
null=True,
blank=True,
verbose_name="code postal",
)
gender = models.CharField(max_length=20,
null=True,
blank=True,
verbose_name="genre",
)
addressNumber = models.IntegerField(
null=True,
blank=True,
verbose_name="numéro de rue"
)
street = models.CharField(max_length=70,
null=True,
blank=True,
verbose_name="nom de rue"
)
city = models.CharField(max_length=50,
null=True,
blank=True,
verbose_name="nom de ville"
)
personalPhone = models.CharField(max_length=12,
null=True,
blank=True,
verbose_name="numéro de téléphone personnel"
)
parentsPhone = models.CharField(max_length=12,
null=True,
blank=True,
verbose_name="numéro de téléphone parental"
)
parentsEmail = models.EmailField(max_length=70,
null=True,
blank=True,
verbose_name="adresse mail parentale"
)
school = models.CharField(max_length=70,
null=True,
blank=True,
verbose_name="établissement"
)
grade = models.CharField(max_length=20,
null=True,
blank=True,
verbose_name="niveau de la classe"
)
scholarship = models.CharField(max_length=50,
null=True,
blank=True,
verbose_name="boursier"
)
fatherActivity = models.CharField(max_length=70,
null=True,
blank=True,
verbose_name="métier du père"
)
motherActivity = models.CharField(max_length=70,
null=True,
blank=True,
verbose_name="métier de la mère"
)
parentsStatus = models.CharField(max_length=70,
null=True,
blank=True,
verbose_name="statut des parents"
)
dependantsNumber = models.IntegerField(
null=True,
blank=True,
verbose_name="nombre de personnes à charge"
)
year = models.CharField(max_length=10,
null=True,
blank=True,
verbose_name="année"
)
@staticmethod
def has_write_permission(request):
return True
def has_object_write_permission(self, request):
return request.user == self.user
def save(self, *args, **kwargs):
"""Updates the year field based on the last modified date"""
date_now = datetime.now()
if date_now.month>=9:
self.year = f"{date_now.year}/{date_now.year+1}"
else:
self.year = f"{date_now.year-1}/{date_now.year}"
return super(Student,self).save(*args, **kwargs)
class Meta: # noqa class Meta: # noqa
verbose_name = 'lycéen' verbose_name = 'lycéen'
......
...@@ -31,11 +31,11 @@ class StudentSerializer(serializers.HyperlinkedModelSerializer): ...@@ -31,11 +31,11 @@ class StudentSerializer(serializers.HyperlinkedModelSerializer):
read_only=True) read_only=True)
registration = StudentRegistrationSerializer() registration = StudentRegistrationSerializer()
class Meta: # noqa class Meta: # noqa
model = Student model = Student
fields = ( fields = (
'user_id', 'user', 'registration', 'visits', 'url', 'user_id', 'user', 'url', 'registration', 'visits', 'gender', 'addressNumber', 'street', 'city', 'personalPhone', 'parentsPhone', 'parentsEmail', 'school', 'grade', 'scholarship', 'fatherActivity', 'motherActivity', 'parentsStatus', 'dependantsNumber', 'specialTeaching', 'nationality', 'zipCode', 'classType')
)
extra_kwargs = { extra_kwargs = {
'url': {'view_name': 'api:student-detail'}, 'url': {'view_name': 'api:student-detail'},
} }
...@@ -5,6 +5,7 @@ from dry_rest_permissions.generics import DRYPermissions ...@@ -5,6 +5,7 @@ from dry_rest_permissions.generics import DRYPermissions
from rest_framework import viewsets from rest_framework import viewsets
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
from visits.serializers import VisitSerializer from visits.serializers import VisitSerializer
...@@ -23,40 +24,21 @@ class TutorViewSet(viewsets.ReadOnlyModelViewSet): ...@@ -23,40 +24,21 @@ class TutorViewSet(viewsets.ReadOnlyModelViewSet):
permission_classes = (DRYPermissions,) permission_classes = (DRYPermissions,)
class StudentViewSet(viewsets.ReadOnlyModelViewSet): class StudentViewSet(viewsets.ModelViewSet):
"""API endpoint that allows students to be viewed. """API endpoint that allows students to be viewed, and profiles to be updated."""
list: def get_serializer(self, *args, **kwargs):
kwargs['partial'] = True
return super(StudentViewSet, self).get_serializer(*args, **kwargs)
### Example response def get_queryset(self):
user = self.request.user
if user.is_staff:
return Student.objects.all()
else:
return Student.objects.filter(user_id = user.id)
List of results from `retrieve` (see the example response for `retrieve`).
retrieve:
### Example response
{
"user_id": 4,
"user": {
"id": 4,
"email": "charles.dumont@example.net",
"profile_type": null,
"first_name": "",
"last_name": "",
"url": "http://localhost:8000/api/users/4/"
},
"registration": {
"id": 3,
"submitted": "2018-05-05T14:15:10.998206+02:00",
"validated": false
},
"visits": [],
"url": "http://localhost:8000/api/students/2/"
}
"""
queryset = Student.objects.all()
serializer_class = StudentSerializer serializer_class = StudentSerializer
permission_classes = (DRYPermissions,) permission_classes = (DRYPermissions,)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment