From 54c4c0e83d3051a5be3d5622cc98f3c989b19a4a Mon Sep 17 00:00:00 2001
From: Seon82 <46298009+Seon82@users.noreply.github.com>
Date: Wed, 30 Sep 2020 17:11:59 +0200
Subject: [PATCH] Inscription sur le site (#21)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* 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: chiahetcho <44137047+chiahetcho@users.noreply.github.com>
Co-authored-by: florimondmanca <florimond.manca@gmail.com>
Co-authored-by: Dylan Sechet <dylan.sechet@student-cs.fr>
Co-authored-by: Arthur Guédon <arthur.guedon@student-cs.fr>
---
 README.md                                     |  11 +-
 api/urls.py                                   |   2 +-
 core/factory.py                               |   4 +-
 oser_backend/settings/common.py               |   6 +-
 profiles/admin.py                             |   2 +-
 profiles/factory.py                           |   4 +-
 .../migrations/0003_auto_20200914_0057.py     |  88 +++++++++++
 .../migrations/0004_auto_20200915_1827.py     |  43 ++++++
 .../migrations/0004_auto_20200918_1320.py     |  43 ++++++
 .../migrations/0005_auto_20200918_1805.py     |  18 +++
 .../migrations/0006_auto_20200918_1818.py     |  28 ++++
 .../migrations/0007_merge_20200918_1824.py    |  14 ++
 .../migrations/0008_auto_20200918_1913.py     |  18 +++
 .../migrations/0009_auto_20200923_1329.py     |  43 ++++++
 .../migrations/0010_auto_20200923_1444.py     |  18 +++
 profiles/migrations/0011_student_classtype.py |  18 +++
 profiles/migrations/0012_student_year.py      |  18 +++
 profiles/models.py                            | 140 +++++++++++++++++-
 profiles/serializers.py                       |   4 +-
 profiles/views.py                             |  42 ++----
 projects/factory.py                           |   8 +-
 register/factory.py                           |   2 +-
 test                                          |   0
 tests/test_profiles/test_student.py           |   5 +-
 tests/test_profiles/test_student_api.py       |   2 +-
 users/factory.py                              |   6 +-
 visits/factory.py                             |   6 +-
 27 files changed, 536 insertions(+), 57 deletions(-)
 create mode 100644 profiles/migrations/0003_auto_20200914_0057.py
 create mode 100644 profiles/migrations/0004_auto_20200915_1827.py
 create mode 100644 profiles/migrations/0004_auto_20200918_1320.py
 create mode 100644 profiles/migrations/0005_auto_20200918_1805.py
 create mode 100644 profiles/migrations/0006_auto_20200918_1818.py
 create mode 100644 profiles/migrations/0007_merge_20200918_1824.py
 create mode 100644 profiles/migrations/0008_auto_20200918_1913.py
 create mode 100644 profiles/migrations/0009_auto_20200923_1329.py
 create mode 100644 profiles/migrations/0010_auto_20200923_1444.py
 create mode 100644 profiles/migrations/0011_student_classtype.py
 create mode 100644 profiles/migrations/0012_student_year.py
 create mode 100644 test

diff --git a/README.md b/README.md
index f5660a8..1b64c6d 100644
--- a/README.md
+++ b/README.md
@@ -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.
 
+- 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
 
-- (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
-$ python -m venv env
-$ source env/bin/activate
+$ conda create -n oser-back
+$ conda activate oser-back
 ```
 
 - Installez les dépendances :
@@ -70,6 +74,7 @@ $ pip install -r requirements.txt
 ```bash
 $ 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 :
 
diff --git a/api/urls.py b/api/urls.py
index dbd3f62..46497be 100644
--- a/api/urls.py
+++ b/api/urls.py
@@ -26,7 +26,7 @@ router.register('users', users_views.UserViewSet)
 
 # Profiles views
 router.register('tutors', profiles_views.TutorViewSet)
-router.register('students', profiles_views.StudentViewSet)
+router.register('students', profiles_views.StudentViewSet, base_name='student')
 
 # Register views
 router.register('registrations', register_views.RegistrationViewSet)
diff --git a/core/factory.py b/core/factory.py
index a812eed..7167327 100644
--- a/core/factory.py
+++ b/core/factory.py
@@ -5,7 +5,7 @@ import factory.django
 from . import models
 
 
-class DocumentFactory(factory.DjangoModelFactory):
+class DocumentFactory(factory.django.DjangoModelFactory):
     """Document object factory."""
 
     class Meta:  # noqa
@@ -15,7 +15,7 @@ class DocumentFactory(factory.DjangoModelFactory):
     content = factory.Faker('text', max_nb_chars=1000, locale='fr')
 
 
-class AddressFactory(factory.DjangoModelFactory):
+class AddressFactory(factory.django.DjangoModelFactory):
     """Address object factory."""
 
     class Meta:  # noqa
diff --git a/oser_backend/settings/common.py b/oser_backend/settings/common.py
index eca12cc..9579af4 100644
--- a/oser_backend/settings/common.py
+++ b/oser_backend/settings/common.py
@@ -38,7 +38,7 @@ DJANGO_APPS = [
     'whitenoise.runserver_nostatic',
     'django.contrib.staticfiles',
     'django.forms',
-    'django.contrib.sites',
+    'django.contrib.sites'
 ]
 
 THIRD_PARTY_APPS = [
@@ -236,7 +236,7 @@ LOGGING = {
 # Connect custom PasswordResetSerializer to override default
 
 REST_AUTH_SERIALIZERS = {
-    'PASSWORD_RESET_SERIALIZER': 
+    'PASSWORD_RESET_SERIALIZER':
         'oser_backend.serializers.PasswordResetSerializer',
 }
 
@@ -246,7 +246,7 @@ DEFAULT_FROM_EMAIL = "admin@oser-cs.fr"
 EMAIL_BACKEND = 'sendgrid_backend.SendgridBackend'
 SENDGRID_API_KEY = os.getenv('SENDGRID_API_KEY')
 
-# Sendgrid configuration 
+# Sendgrid configuration
 
 EMAIL_HOST = 'smtp.sendgrid.net'
 EMAIL_HOST_USER = 'apikey'
diff --git a/profiles/admin.py b/profiles/admin.py
index 4f00aa1..63f6e66 100644
--- a/profiles/admin.py
+++ b/profiles/admin.py
@@ -43,7 +43,7 @@ class TutorAdmin(ProfileAdminMixin, admin.ModelAdmin,ExportCsvMixin):
 @admin.register(Student)
 class StudentAdmin(ProfileAdminMixin, admin.ModelAdmin,ExportCsvMixin):
     """Student admin panel."""
-
+    list_filter = ('school', 'year')
     class Meta:  # noqa
         model = Student
     actions = ["export_as_csv"]
\ No newline at end of file
diff --git a/profiles/factory.py b/profiles/factory.py
index 886b5dd..6b8b4ff 100644
--- a/profiles/factory.py
+++ b/profiles/factory.py
@@ -12,7 +12,7 @@ from users.factory import UserFactory
 from . import models
 
 
-class StudentFactory(factory.DjangoModelFactory):
+class StudentFactory(factory.django.DjangoModelFactory):
     """Student object factory."""
 
     class Meta:  # noqa
@@ -24,7 +24,7 @@ class StudentFactory(factory.DjangoModelFactory):
 _this_year = datetime.today().year
 
 
-class TutorFactory(factory.DjangoModelFactory):
+class TutorFactory(factory.django.DjangoModelFactory):
     """Tutor object factory."""
 
     class Meta:  # noqa
diff --git a/profiles/migrations/0003_auto_20200914_0057.py b/profiles/migrations/0003_auto_20200914_0057.py
new file mode 100644
index 0000000..ebe33bd
--- /dev/null
+++ b/profiles/migrations/0003_auto_20200914_0057.py
@@ -0,0 +1,88 @@
+# 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),
+        ),
+    ]
diff --git a/profiles/migrations/0004_auto_20200915_1827.py b/profiles/migrations/0004_auto_20200915_1827.py
new file mode 100644
index 0000000..d94b486
--- /dev/null
+++ b/profiles/migrations/0004_auto_20200915_1827.py
@@ -0,0 +1,43 @@
+# 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"),
+        ),
+    ]
diff --git a/profiles/migrations/0004_auto_20200918_1320.py b/profiles/migrations/0004_auto_20200918_1320.py
new file mode 100644
index 0000000..0b1e26b
--- /dev/null
+++ b/profiles/migrations/0004_auto_20200918_1320.py
@@ -0,0 +1,43 @@
+# 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"),
+        ),
+    ]
diff --git a/profiles/migrations/0005_auto_20200918_1805.py b/profiles/migrations/0005_auto_20200918_1805.py
new file mode 100644
index 0000000..7e5756b
--- /dev/null
+++ b/profiles/migrations/0005_auto_20200918_1805.py
@@ -0,0 +1,18 @@
+# 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'),
+        ),
+    ]
diff --git a/profiles/migrations/0006_auto_20200918_1818.py b/profiles/migrations/0006_auto_20200918_1818.py
new file mode 100644
index 0000000..a1d92c8
--- /dev/null
+++ b/profiles/migrations/0006_auto_20200918_1818.py
@@ -0,0 +1,28 @@
+# 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'),
+        ),
+    ]
diff --git a/profiles/migrations/0007_merge_20200918_1824.py b/profiles/migrations/0007_merge_20200918_1824.py
new file mode 100644
index 0000000..ff45a34
--- /dev/null
+++ b/profiles/migrations/0007_merge_20200918_1824.py
@@ -0,0 +1,14 @@
+# 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 = [
+    ]
diff --git a/profiles/migrations/0008_auto_20200918_1913.py b/profiles/migrations/0008_auto_20200918_1913.py
new file mode 100644
index 0000000..cdf1810
--- /dev/null
+++ b/profiles/migrations/0008_auto_20200918_1913.py
@@ -0,0 +1,18 @@
+# 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'),
+        ),
+    ]
diff --git a/profiles/migrations/0009_auto_20200923_1329.py b/profiles/migrations/0009_auto_20200923_1329.py
new file mode 100644
index 0000000..5dc6c13
--- /dev/null
+++ b/profiles/migrations/0009_auto_20200923_1329.py
@@ -0,0 +1,43 @@
+# 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é'),
+        ),
+    ]
diff --git a/profiles/migrations/0010_auto_20200923_1444.py b/profiles/migrations/0010_auto_20200923_1444.py
new file mode 100644
index 0000000..fd5da54
--- /dev/null
+++ b/profiles/migrations/0010_auto_20200923_1444.py
@@ -0,0 +1,18 @@
+# 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',
+        ),
+    ]
diff --git a/profiles/migrations/0011_student_classtype.py b/profiles/migrations/0011_student_classtype.py
new file mode 100644
index 0000000..0482118
--- /dev/null
+++ b/profiles/migrations/0011_student_classtype.py
@@ -0,0 +1,18 @@
+# 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'),
+        ),
+    ]
diff --git a/profiles/migrations/0012_student_year.py b/profiles/migrations/0012_student_year.py
new file mode 100644
index 0000000..359b6cb
--- /dev/null
+++ b/profiles/migrations/0012_student_year.py
@@ -0,0 +1,18 @@
+# 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'),
+        ),
+    ]
diff --git a/profiles/models.py b/profiles/models.py
index 28061fa..675354d 100644
--- a/profiles/models.py
+++ b/profiles/models.py
@@ -4,7 +4,7 @@ from django.db import models
 from django.shortcuts import reverse
 from dry_rest_permissions.generics import authenticated_users
 from .utils import get_promotion_range
-
+from datetime import datetime
 
 class ProfileMixin:
     """Mixin with common functionnality for profiles."""
@@ -52,6 +52,144 @@ class Student(ProfileMixin, models.Model):
         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
         verbose_name = 'lycéen'
 
diff --git a/profiles/serializers.py b/profiles/serializers.py
index 31b2031..778dc21 100644
--- a/profiles/serializers.py
+++ b/profiles/serializers.py
@@ -31,11 +31,11 @@ class StudentSerializer(serializers.HyperlinkedModelSerializer):
         read_only=True)
     registration = StudentRegistrationSerializer()
 
+
     class Meta:  # noqa
         model = Student
         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 = {
             'url': {'view_name': 'api:student-detail'},
         }
diff --git a/profiles/views.py b/profiles/views.py
index 0c157ef..b81ec2c 100644
--- a/profiles/views.py
+++ b/profiles/views.py
@@ -5,6 +5,7 @@ from dry_rest_permissions.generics import DRYPermissions
 from rest_framework import viewsets
 from rest_framework.decorators import action
 from rest_framework.response import Response
+from django_filters.rest_framework import DjangoFilterBackend
 
 from visits.serializers import VisitSerializer
 
@@ -23,40 +24,21 @@ class TutorViewSet(viewsets.ReadOnlyModelViewSet):
     permission_classes = (DRYPermissions,)
 
 
-class StudentViewSet(viewsets.ReadOnlyModelViewSet):
-    """API endpoint that allows students to be viewed.
+class StudentViewSet(viewsets.ModelViewSet):
+    """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
     permission_classes = (DRYPermissions,)
 
diff --git a/projects/factory.py b/projects/factory.py
index f826ab8..8a53981 100644
--- a/projects/factory.py
+++ b/projects/factory.py
@@ -7,7 +7,7 @@ from users.factory import UserFactory
 from .models import Project, Edition, Participation, EditionForm
 
 
-class ProjectFactory(factory.DjangoModelFactory):
+class ProjectFactory(factory.django.DjangoModelFactory):
     """Project object factory."""
 
     class Meta:  # noqa
@@ -20,7 +20,7 @@ class ProjectFactory(factory.DjangoModelFactory):
     logo = factory.django.ImageField(color='green')
 
 
-class EditionFactory(factory.DjangoModelFactory):
+class EditionFactory(factory.django.DjangoModelFactory):
     """Edition object factory."""
 
     class Meta:  # noqa
@@ -37,7 +37,7 @@ class EditionFactory(factory.DjangoModelFactory):
         return project and project or ProjectFactory.create()
 
 
-class EditionFormFactory(factory.DjangoModelFactory):
+class EditionFormFactory(factory.django.DjangoModelFactory):
 
     class Meta:  # noqa
         model = EditionForm
@@ -45,7 +45,7 @@ class EditionFormFactory(factory.DjangoModelFactory):
     deadline = factory.Faker('future_date')
 
 
-class ParticipationFactory(factory.DjangoModelFactory):
+class ParticipationFactory(factory.django.DjangoModelFactory):
     """Participation object factory."""
 
     class Meta:  # noqa
diff --git a/register/factory.py b/register/factory.py
index 973d6e7..4fe9580 100644
--- a/register/factory.py
+++ b/register/factory.py
@@ -8,7 +8,7 @@ from utils import printable_only
 from . import models
 
 
-class RegistrationFactory(factory.DjangoModelFactory):
+class RegistrationFactory(factory.django.DjangoModelFactory):
     """Registration object factory."""
 
     class Meta:  # noqa
diff --git a/test b/test
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_profiles/test_student.py b/tests/test_profiles/test_student.py
index 0d9d65f..f5b6541 100644
--- a/tests/test_profiles/test_student.py
+++ b/tests/test_profiles/test_student.py
@@ -3,7 +3,7 @@
 from profiles.factory import StudentFactory
 from profiles.models import Student
 from tests.utils import ModelTestCase
-from users.factory import UserFactory
+from users.factory import UserFactory, StaffUserFactory
 
 
 class StudentTestCase(ModelTestCase):
@@ -32,7 +32,8 @@ class StudentTestCase(ModelTestCase):
         self.assertEqual(self.obj, self.obj.user.student)
 
     def test_get_absolute_url(self):
-        self.client.force_login(UserFactory.create())
+        staff_user = StaffUserFactory.create()
+        self.client.force_login(staff_user)
         url = self.obj.get_absolute_url()
         response = self.client.get(url)
         self.assertEqual(200, response.status_code)
diff --git a/tests/test_profiles/test_student_api.py b/tests/test_profiles/test_student_api.py
index 204649b..d9caacb 100644
--- a/tests/test_profiles/test_student_api.py
+++ b/tests/test_profiles/test_student_api.py
@@ -19,7 +19,7 @@ class StudentEndpointsTest(HyperlinkedAPITestCase):
     def perform_retrieve(self, obj=None):
         if obj is None:
             obj = self.factory.create()
-        response = self.client.get('/api/students/{obj.pk}/'.format(obj=obj))
+        response = self.client.get('/api/students/')
         return response
 
     def test_list(self):
diff --git a/users/factory.py b/users/factory.py
index b197e55..850d0c4 100644
--- a/users/factory.py
+++ b/users/factory.py
@@ -9,7 +9,7 @@ from utils import printable_only
 User = get_user_model()
 
 
-class UserFactory(factory.DjangoModelFactory):
+class UserFactory(factory.django.DjangoModelFactory):
     """User object factory."""
 
     class Meta:  # noqa
@@ -41,3 +41,7 @@ class UserFactory(factory.DjangoModelFactory):
         manager = cls._get_manager(model_class)
         # The default would use ``manager.create(*args, **kwargs)``
         return manager.create_user(*args, **kwargs)
+
+class StaffUserFactory(UserFactory):
+    """Staff user object factory."""
+    is_staff = True
\ No newline at end of file
diff --git a/visits/factory.py b/visits/factory.py
index 11817aa..05fce07 100644
--- a/visits/factory.py
+++ b/visits/factory.py
@@ -15,7 +15,7 @@ from . import models
 User = get_user_model()
 
 
-class PlaceFactory(factory.DjangoModelFactory):
+class PlaceFactory(factory.django.DjangoModelFactory):
     """Place object factory."""
 
     class Meta:  # noqa
@@ -28,7 +28,7 @@ class PlaceFactory(factory.DjangoModelFactory):
     description = factory.LazyAttribute(lambda o: '\n'.join(o._description))
 
 
-class VisitFactory(factory.DjangoModelFactory):
+class VisitFactory(factory.django.DjangoModelFactory):
     """Visit object factory."""
 
     deadline_random_range = (-5, -1)
@@ -81,7 +81,7 @@ class VisitWithClosedRegistrationsFactory(VisitFactory):
     deadline_random_range = (-10, -6)  # guaranteed to be before today
 
 
-class ParticipationFactory(factory.DjangoModelFactory):
+class ParticipationFactory(factory.django.DjangoModelFactory):
     """Visit participant object factory.
 
     Users and visit are picked from pre-existing objects,
-- 
GitLab