diff --git a/.travis.yml b/.travis.yml
index 0bf10ab4c59f91118d19a1a49e0e9e77142fdc67..529ca87f75c4295c8f51c73a0bbc250222482aed 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,7 +8,10 @@ python:
 
 services:
   - postgresql
-  - redis-server
+
+addons:
+  # Django 2.1+ requires PostgreSQL 9.4+
+  postgresql: '9.4'
 
 install:
   - pip install -r requirements.txt
@@ -17,9 +20,6 @@ install:
   - pip install git+https://github.com/Supervisor/supervisor.git
 
 before_script:
-  # Start Celery using the supervisor config
-  - supervisord
-
   # Create local PostgreSQL database
   # NOTE: the database name (here 'oser_backend_db') must match the name
   # in one of these DATABASE_URL setting:
diff --git a/Procfile b/Procfile
index 2a56f02829ca9006e52a3fd82f2a88d90090a069..672688e1e2398c6deb51abf484f41afcc37b0374 100644
--- a/Procfile
+++ b/Procfile
@@ -1,4 +1 @@
 web: gunicorn oser_backend.wsgi:application --bind 0.0.0.0:$PORT
-
-# Toggle next line to start Celery in a worker process
-# worker: celery -A oser_backend worker --beat -l info
diff --git a/README.md b/README.md
index 3630ff14bc73de803311573598745969c7d29bae..ef37427cb693380eefb3ff81971a962997b8f0a3 100644
--- a/README.md
+++ b/README.md
@@ -50,18 +50,6 @@ 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.
 
-#### Optionnel : Redis, supervisord
-
-Le backend Django est relié à [Celery](http://www.celeryproject.org), une librairie Python permet d'effectuer des traitements ou opérations en tâche de fond.
-
-> NOTE : Pour l'instant, Celery n'est utilisé que pour effectuer un nettoyage périodique des fichiers de médias inutilisés, opération qui peut de toute façon être déclenchée par `$ python manage.py cleanmedia`. Il n'est donc **pas obligatoire d'installer ce qui suit en développement.**
-
-Celery a besoin d'un système de *messaging* pour fonctionner, on utilise donc ici [Redis](https://redis.io).
-
-Enfin, [supervisord](http://supervisord.org) est un gestionnaire de processus qui nous permet de lancer Redis et Celery en une seule commande.
-
-Le plus simple est de se référer aux sites de chaque logiciel/librairie pour leur installation. :wink:
-
 ### Installation du projet
 
 - (Recommandé) Créez un environnement virtuel (ici appelé `env`) puis activez-le :
@@ -162,29 +150,6 @@ $ curl -X GET "localhost:8000/api/articles/" -H "Authorization: Token b6302cebe7
 [{"id": 39, "content": ...}, ...]
 ```
 
-### Tâches de fond
-
-Le daemon Celery gère le calendrier des tâches de fond (nettoyage des fichiers de médias non-utilisés ou autres tâches définies dans le `settings.py`). Pour fonctionner, Celery nécessite un serveur de messages, on utilise ici Redis.
-
-Les opérations nécessaires pour lancer Celery ainsi que la configuration avec Redis sont rassemblées dans le fichier `supervisord.conf`. Assurez-vous donc d'avoir installé Redis et Supervisor puis démarrez Supervisor au même niveau que le fichier `supervisord.conf` :
-
-```
-# Supervisor ne supporte toujours pas officiellement Python 3,
-# mais la dernière version de développement oui.
-$ pip install git+https://github.com/Supervisor/supervisor.git
-$ supervisord
-```
-
-Pour accéder aux derniers logs de Celery ou de Redis, utilisez `supervisorctl tail (celery|redis)`:
-
-```
-$ supervisorctl tail celery
-[2018-04-29 10:59:31,550: INFO/MainProcess] Connected to redis://localhost:6379//
-[2018-04-29 10:59:31,566: INFO/MainProcess] mingle: searching for neighbors
-[2018-04-29 10:59:32,601: INFO/MainProcess] mingle: all alone
-[2018-04-29 10:59:32,657: INFO/MainProcess] celery@MacBook-Pro-de-Florimond-2.local ready.
-```
-
 ### Envoi de mails
 
 Le backend utilise le plan gratuit de [SendGrid](https://sendgrid.com) (jusqu'à 100 emails par jour) pour envoyer des emails et notifications aux utilisateurs.
diff --git a/api/urls.py b/api/urls.py
index d5641dcaa93f2727f881396f5ac399cd5eebea55..dbd3f62fe5ce8ba2ed67d64dca500e2b15060aee 100644
--- a/api/urls.py
+++ b/api/urls.py
@@ -6,7 +6,6 @@ from api.auth import obtain_auth_token
 from core import views as core_views
 from profiles import views as profiles_views
 from register import views as register_views
-from tutoring import views as tutoring_views
 from users import views as users_views
 from visits import views as visits_views
 import projects.views
@@ -29,13 +28,6 @@ router.register('users', users_views.UserViewSet)
 router.register('tutors', profiles_views.TutorViewSet)
 router.register('students', profiles_views.StudentViewSet)
 
-# Tutoring views
-router.register('schools', tutoring_views.SchoolViewSet)
-router.register('groups', tutoring_views.TutoringGroupViewSet,
-                base_name='tutoring_group')
-router.register('sessions', tutoring_views.TutoringSessionViewSet,
-                base_name='tutoring_session')
-
 # Register views
 router.register('registrations', register_views.RegistrationViewSet)
 
diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py
index c1ad72650b1b74e6026357c8e0935128a3361e46..e7c95e755f75d922bab1f69531383614981d5471 100644
--- a/core/migrations/0001_initial.py
+++ b/core/migrations/0001_initial.py
@@ -1,6 +1,8 @@
-# Generated by Django 2.0.3 on 2018-03-17 15:20
+# Generated by Django 2.0.7 on 2018-09-11 17:38
 
 from django.db import migrations, models
+import django_countries.fields
+import markdownx.models
 
 
 class Migration(migrations.Migration):
@@ -12,14 +14,29 @@ class Migration(migrations.Migration):
 
     operations = [
         migrations.CreateModel(
-            name='Link',
+            name='Address',
             fields=[
-                ('slug', models.SlugField(help_text="Un identifiant unique pour ce lien. Privilégiez 'ce-format-de-slug'.", primary_key=True, serialize=False, unique=True)),
-                ('url', models.URLField(verbose_name='URL')),
-                ('description', models.TextField(help_text='Précisez ce que contient ce lien et comment il est utilisé.')),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('line1', models.CharField(help_text='Numéro, voie, rue…', max_length=300, verbose_name='ligne 1')),
+                ('line2', models.CharField(blank=True, default='', help_text='Résidence, appartement, lieu-dit…', max_length=300, verbose_name='ligne 2')),
+                ('post_code', models.CharField(help_text="Code postal. Note : le format n'est pas vérifié.", max_length=20, verbose_name='code postal')),
+                ('city', models.CharField(help_text='Ville', max_length=100, verbose_name='ville')),
+                ('country', django_countries.fields.CountryField(default='FR', help_text='Pays (FR par défaut).', max_length=2, verbose_name='pays')),
             ],
             options={
-                'verbose_name': 'lien',
+                'verbose_name': 'adresse',
+            },
+        ),
+        migrations.CreateModel(
+            name='Document',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('title', models.CharField(help_text='Titre du document', max_length=300, verbose_name='titre')),
+                ('slug', models.SlugField(help_text='Un court identifiant généré après la création du document.', max_length=100, unique=True)),
+                ('content', markdownx.models.MarkdownxField(help_text='Contenu du document (Markdown est supporté).', verbose_name='contenu')),
+            ],
+            options={
+                'ordering': ('title',),
             },
         ),
     ]
diff --git a/core/migrations/0002_delete_link.py b/core/migrations/0002_delete_link.py
deleted file mode 100644
index 8aaa68754c8c7eb647a5d08c1d2b93a66106745f..0000000000000000000000000000000000000000
--- a/core/migrations/0002_delete_link.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Generated by Django 2.0.3 on 2018-03-19 21:54
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('core', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.DeleteModel(
-            name='Link',
-        ),
-    ]
diff --git a/core/migrations/0003_document.py b/core/migrations/0003_document.py
deleted file mode 100644
index 0a61c566173f279a5cf469b7024f108ac3af7a16..0000000000000000000000000000000000000000
--- a/core/migrations/0003_document.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Generated by Django 2.0.3 on 2018-03-24 18:37
-
-from django.db import migrations, models
-import markdownx.models
-
-
-class Migration(migrations.Migration):
-
-    initial = True
-
-    dependencies = [
-        ('core', '0002_delete_link'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='Document',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('title', models.CharField(help_text='Titre du document', max_length=300, verbose_name='titre')),
-                ('slug', models.SlugField(help_text='Un court identifiant généré après la création du document.', max_length=100, unique=True)),
-                ('content', markdownx.models.MarkdownxField(help_text='Contenu du document (Markdown est supporté).', verbose_name='contenu')),
-            ],
-            options={
-                'ordering': ('title',),
-            },
-        ),
-    ]
diff --git a/core/migrations/0004_address.py b/core/migrations/0004_address.py
deleted file mode 100644
index 8d37478b3e1b132746353728e4168bbca652df15..0000000000000000000000000000000000000000
--- a/core/migrations/0004_address.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 2.0.3 on 2018-04-08 13:18
-
-from django.db import migrations, models
-import django_countries.fields
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('core', '0003_document'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='Address',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('line1', models.CharField(help_text='Numéro, voie, rue…', max_length=300, verbose_name='ligne 1')),
-                ('line2', models.CharField(blank=True, default='', help_text='Résidence, appartement, lieu-dit…', max_length=300, verbose_name='ligne 2')),
-                ('post_code', models.CharField(help_text="Code postal. Note : le format n'est pas vérifié.", max_length=20, verbose_name='code postal')),
-                ('city', models.CharField(help_text='Ville', max_length=100, verbose_name='ville')),
-                ('country', django_countries.fields.CountryField(default='FR', help_text='Pays (FR par défaut).', max_length=2)),
-            ],
-        ),
-    ]
diff --git a/core/migrations/0005_auto_20180408_1525.py b/core/migrations/0005_auto_20180408_1525.py
deleted file mode 100644
index 4666f56cd97a8f1650cd0ab314fa37be8ed8f96b..0000000000000000000000000000000000000000
--- a/core/migrations/0005_auto_20180408_1525.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.0.3 on 2018-04-08 13:25
-
-from django.db import migrations
-import django_countries.fields
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('core', '0004_address'),
-    ]
-
-    operations = [
-        migrations.AlterModelOptions(
-            name='address',
-            options={'verbose_name': 'adresse'},
-        ),
-        migrations.AlterField(
-            model_name='address',
-            name='country',
-            field=django_countries.fields.CountryField(default='FR', help_text='Pays (FR par défaut).', max_length=2, verbose_name='pays'),
-        ),
-    ]
diff --git a/core/tasks.py b/core/tasks.py
deleted file mode 100644
index 621e04608f6d76d355f10fb0a32887f71a4d3ecd..0000000000000000000000000000000000000000
--- a/core/tasks.py
+++ /dev/null
@@ -1,10 +0,0 @@
-"""Core Celery tasks."""
-
-from oser_backend.celery import app
-from django.core.management import call_command
-
-
-@app.task
-def cleanmedia():
-    """Clean unused media files."""
-    call_command('cleanmedia')
diff --git a/dynamicforms/migrations/0001_initial.py b/dynamicforms/migrations/0001_initial.py
index 55d2002216d179944a22b2fb9a7e414f71471bfb..b8fb902153da059f631b594d680032b9c5604d54 100644
--- a/dynamicforms/migrations/0001_initial.py
+++ b/dynamicforms/migrations/0001_initial.py
@@ -1,7 +1,8 @@
-# Generated by Django 2.0.6 on 2018-06-15 21:24
+# Generated by Django 2.0.7 on 2018-09-11 17:38
 
 from django.db import migrations, models
 import django.db.models.deletion
+import dynamicforms.utils
 
 
 class Migration(migrations.Migration):
@@ -18,15 +19,32 @@ class Migration(migrations.Migration):
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('answer', models.TextField(blank=True, null=True, verbose_name='réponse')),
             ],
+            options={
+                'verbose_name': 'réponse',
+            },
+        ),
+        migrations.CreateModel(
+            name='File',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=300, verbose_name='nom')),
+                ('file', models.FileField(upload_to=dynamicforms.utils.file_upload_to, verbose_name='fichier')),
+            ],
+            options={
+                'verbose_name': 'fichier',
+                'verbose_name_plural': 'fichiers',
+            },
         ),
         migrations.CreateModel(
             name='Form',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('title', models.CharField(max_length=300, verbose_name='titre')),
+                ('slug', models.SlugField(blank=True, default='', max_length=100)),
                 ('created', models.DateTimeField(auto_now_add=True, verbose_name='créé le')),
             ],
             options={
+                'verbose_name': 'formulaire',
                 'ordering': ('-created',),
             },
         ),
@@ -35,9 +53,11 @@ class Migration(migrations.Migration):
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('submitted', models.DateTimeField(auto_now_add=True, help_text="Date et heure de soumission de l'entrée.", verbose_name='soumis le')),
-                ('form', models.ForeignKey(help_text="Formulaire associé à l'entrée.", on_delete=django.db.models.deletion.CASCADE, related_name='entries', to='dynamicforms.Form')),
+                ('form', models.ForeignKey(help_text="Formulaire associé à l'entrée.", on_delete=django.db.models.deletion.CASCADE, related_name='entries', to='dynamicforms.Form', verbose_name='formulaire')),
             ],
             options={
+                'verbose_name': 'entrée de formulaire',
+                'verbose_name_plural': 'entrées de formulaire',
                 'ordering': ('-submitted',),
             },
         ),
@@ -45,16 +65,42 @@ class Migration(migrations.Migration):
             name='Question',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('question_type', models.CharField(max_length=100, verbose_name='type de question')),
-                ('text', models.TextField(help_text='intitulé de la question', verbose_name='intitulé')),
-                ('required', models.BooleanField(verbose_name='requis')),
-                ('form', models.ForeignKey(help_text='Formulaire associé à la question.', on_delete=django.db.models.deletion.CASCADE, related_name='questions', to='dynamicforms.Form')),
+                ('text', models.CharField(help_text='intitulé de la question', max_length=300, verbose_name='intitulé')),
+                ('type', models.CharField(choices=[('text-small', 'Texte court'), ('text-long', 'Texte long'), ('yes-no', 'Oui/Non'), ('date', 'Date'), ('sex', 'Sexe')], max_length=100, verbose_name='type de question')),
+                ('help_text', models.CharField(blank=True, default='', help_text='Apporte des précisions sur la question', max_length=300, verbose_name='aide')),
+                ('required', models.BooleanField(default=True, verbose_name='requis')),
+                ('order', models.PositiveIntegerField(default=0, verbose_name='position')),
+            ],
+            options={
+                'ordering': ('order',),
+            },
+        ),
+        migrations.CreateModel(
+            name='Section',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('title', models.CharField(max_length=100, verbose_name='titre')),
+                ('order', models.PositiveIntegerField(default=0, verbose_name='position')),
+                ('form', models.ForeignKey(help_text='Formulaire associé à la section.', on_delete=django.db.models.deletion.CASCADE, related_name='sections', to='dynamicforms.Form', verbose_name='formulaire')),
             ],
+            options={
+                'ordering': ('order',),
+            },
+        ),
+        migrations.AddField(
+            model_name='question',
+            name='section',
+            field=models.ForeignKey(help_text='Section de formulaire associée à la question.', on_delete=django.db.models.deletion.CASCADE, related_name='questions', to='dynamicforms.Section', verbose_name='section'),
+        ),
+        migrations.AddField(
+            model_name='file',
+            name='form',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='files', to='dynamicforms.Form', verbose_name='formulaire'),
         ),
         migrations.AddField(
             model_name='answer',
             name='entry',
-            field=models.ForeignKey(help_text='Entrée associée à la réponse.', on_delete=django.db.models.deletion.CASCADE, related_name='answers', to='dynamicforms.FormEntry'),
+            field=models.ForeignKey(help_text='Entrée associée à la réponse.', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='answers', to='dynamicforms.FormEntry', verbose_name='entrée'),
         ),
         migrations.AddField(
             model_name='answer',
diff --git a/dynamicforms/migrations/0002_auto_20180615_2325.py b/dynamicforms/migrations/0002_auto_20180615_2325.py
deleted file mode 100644
index 0e69174a842362e44149c28905a0353e843fb328..0000000000000000000000000000000000000000
--- a/dynamicforms/migrations/0002_auto_20180615_2325.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-15 21:25
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('dynamicforms', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.AlterModelOptions(
-            name='answer',
-            options={'verbose_name': 'réponse'},
-        ),
-        migrations.AlterModelOptions(
-            name='form',
-            options={'ordering': ('-created',), 'verbose_name': 'formulaire'},
-        ),
-        migrations.AlterModelOptions(
-            name='formentry',
-            options={'ordering': ('-submitted',), 'verbose_name': 'entrée de formulaire', 'verbose_name_plural': 'entrées de formulaire'},
-        ),
-    ]
diff --git a/dynamicforms/migrations/0003_auto_20180615_2338.py b/dynamicforms/migrations/0003_auto_20180615_2338.py
deleted file mode 100644
index 4113138308aa84f3dbc0ed714bc5ff813151d6f2..0000000000000000000000000000000000000000
--- a/dynamicforms/migrations/0003_auto_20180615_2338.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-15 21:38
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('dynamicforms', '0002_auto_20180615_2325'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='answer',
-            name='entry',
-            field=models.ForeignKey(help_text='Entrée associée à la réponse.', on_delete=django.db.models.deletion.CASCADE, related_name='answers', to='dynamicforms.FormEntry', verbose_name='entrée'),
-        ),
-        migrations.AlterField(
-            model_name='formentry',
-            name='form',
-            field=models.ForeignKey(help_text="Formulaire associé à l'entrée.", on_delete=django.db.models.deletion.CASCADE, related_name='entries', to='dynamicforms.Form', verbose_name='formulaire'),
-        ),
-        migrations.AlterField(
-            model_name='question',
-            name='form',
-            field=models.ForeignKey(help_text='Formulaire associé à la question.', on_delete=django.db.models.deletion.CASCADE, related_name='questions', to='dynamicforms.Form', verbose_name='formulaire'),
-        ),
-        migrations.AlterField(
-            model_name='question',
-            name='required',
-            field=models.BooleanField(default=True, verbose_name='requis'),
-        ),
-    ]
diff --git a/dynamicforms/migrations/0004_auto_20180615_2339.py b/dynamicforms/migrations/0004_auto_20180615_2339.py
deleted file mode 100644
index 0e3d50a345a09256a0f3061a9ab47086b64474dc..0000000000000000000000000000000000000000
--- a/dynamicforms/migrations/0004_auto_20180615_2339.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-15 21:39
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('dynamicforms', '0003_auto_20180615_2338'),
-    ]
-
-    operations = [
-        migrations.RenameField(
-            model_name='question',
-            old_name='question_type',
-            new_name='type',
-        ),
-    ]
diff --git a/dynamicforms/migrations/0005_question_help_text.py b/dynamicforms/migrations/0005_question_help_text.py
deleted file mode 100644
index 410c852a22e7a7b327505b1e4ad5ecfd709b8647..0000000000000000000000000000000000000000
--- a/dynamicforms/migrations/0005_question_help_text.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-15 21:56
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('dynamicforms', '0004_auto_20180615_2339'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='question',
-            name='help_text',
-            field=models.CharField(blank=True, default='', help_text='Apporte des précisions sur la question', max_length=500, verbose_name='aide'),
-        ),
-    ]
diff --git a/dynamicforms/migrations/0006_auto_20180615_2357.py b/dynamicforms/migrations/0006_auto_20180615_2357.py
deleted file mode 100644
index 513ff1801133777976bf3843a0e5a530e0a223ab..0000000000000000000000000000000000000000
--- a/dynamicforms/migrations/0006_auto_20180615_2357.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-15 21:57
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('dynamicforms', '0005_question_help_text'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='question',
-            name='help_text',
-            field=models.CharField(blank=True, default='', help_text='Apporte des précisions sur la question', max_length=300, verbose_name='aide'),
-        ),
-        migrations.AlterField(
-            model_name='question',
-            name='text',
-            field=models.CharField(help_text='intitulé de la question', max_length=300, verbose_name='intitulé'),
-        ),
-    ]
diff --git a/dynamicforms/migrations/0007_auto_20180616_0000.py b/dynamicforms/migrations/0007_auto_20180616_0000.py
deleted file mode 100644
index 99086f7e38ae01cfbcdbdabdc07203de112aec97..0000000000000000000000000000000000000000
--- a/dynamicforms/migrations/0007_auto_20180616_0000.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-15 22:00
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('dynamicforms', '0006_auto_20180615_2357'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='question',
-            name='type',
-            field=models.CharField(choices=[('text-small', 'Texte court'), ('text-small', 'Texte long'), ('date', 'Date'), ('sex', 'Sexe')], max_length=100, verbose_name='type de question'),
-        ),
-    ]
diff --git a/dynamicforms/migrations/0008_auto_20180616_0007.py b/dynamicforms/migrations/0008_auto_20180616_0007.py
deleted file mode 100644
index 9a903f9cc7e05aaa0d1a587cfde743fa28dbf69e..0000000000000000000000000000000000000000
--- a/dynamicforms/migrations/0008_auto_20180616_0007.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-15 22:07
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('dynamicforms', '0007_auto_20180616_0000'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='Section',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('title', models.CharField(max_length=100, verbose_name='titre')),
-                ('form', models.ForeignKey(help_text='Formulaire associé à la section.', on_delete=django.db.models.deletion.CASCADE, related_name='sections', to='dynamicforms.Form', verbose_name='formulaire')),
-            ],
-        ),
-        migrations.RemoveField(
-            model_name='question',
-            name='form',
-        ),
-        migrations.AddField(
-            model_name='question',
-            name='section',
-            field=models.ForeignKey(help_text='Section de formulaire associée à la question.', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='questions', to='dynamicforms.Section', verbose_name='section'),
-        ),
-    ]
diff --git a/dynamicforms/migrations/0009_auto_20180616_0105.py b/dynamicforms/migrations/0009_auto_20180616_0105.py
deleted file mode 100644
index 3cc1f5c97b0b296274583865e17764afb6d2a350..0000000000000000000000000000000000000000
--- a/dynamicforms/migrations/0009_auto_20180616_0105.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-15 23:05
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('dynamicforms', '0008_auto_20180616_0007'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='File',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('name', models.CharField(max_length=300, verbose_name='nom')),
-                ('file', models.FileField(upload_to='forms/attachments/', verbose_name='fichier')),
-                ('form', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='files', to='dynamicforms.Form', verbose_name='formulaire')),
-            ],
-            options={
-                'verbose_name': 'fichier',
-                'verbose_name_plural': 'fichiers',
-            },
-        ),
-        migrations.AlterField(
-            model_name='question',
-            name='type',
-            field=models.CharField(choices=[('text-small', 'Texte court'), ('text-small', 'Texte long'), ('yes-no', 'Oui/Non'), ('date', 'Date'), ('sex', 'Sexe')], max_length=100, verbose_name='type de question'),
-        ),
-    ]
diff --git a/dynamicforms/migrations/0010_auto_20180616_0121.py b/dynamicforms/migrations/0010_auto_20180616_0121.py
deleted file mode 100644
index cd7165f75bd3cff952b01abe4af9f978a52b8361..0000000000000000000000000000000000000000
--- a/dynamicforms/migrations/0010_auto_20180616_0121.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-15 23:21
-
-from django.db import migrations, models
-import dynamicforms.utils
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('dynamicforms', '0009_auto_20180616_0105'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='form',
-            name='slug',
-            field=models.SlugField(default='noname', max_length=100),
-        ),
-        migrations.AlterField(
-            model_name='file',
-            name='file',
-            field=models.FileField(upload_to=dynamicforms.utils.file_upload_to, verbose_name='fichier'),
-        ),
-    ]
diff --git a/dynamicforms/migrations/0011_auto_20180616_0141.py b/dynamicforms/migrations/0011_auto_20180616_0141.py
deleted file mode 100644
index b372744a89693ee399d3ef85ab0270f077445f5f..0000000000000000000000000000000000000000
--- a/dynamicforms/migrations/0011_auto_20180616_0141.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-15 23:41
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('dynamicforms', '0010_auto_20180616_0121'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='form',
-            name='slug',
-            field=models.SlugField(blank=True, default='', max_length=100),
-        ),
-    ]
diff --git a/dynamicforms/migrations/0012_auto_20180616_0145.py b/dynamicforms/migrations/0012_auto_20180616_0145.py
deleted file mode 100644
index a34858e1cb5ed7b7e6be6bb0adafef7466a65e87..0000000000000000000000000000000000000000
--- a/dynamicforms/migrations/0012_auto_20180616_0145.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-15 23:45
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-def remove_questions_with_null_sections(apps, schema_editor):
-    Question = apps.get_model('dynamicforms', 'Question')
-    Question.objects.filter(section__isnull=True).delete()
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('dynamicforms', '0011_auto_20180616_0141'),
-    ]
-
-    operations = [
-        migrations.RunPython(remove_questions_with_null_sections),
-    ]
diff --git a/dynamicforms/migrations/0013_auto_20180616_0149.py b/dynamicforms/migrations/0013_auto_20180616_0149.py
deleted file mode 100644
index c964bcc273366592ffa3438bf56ec3609ebcfe1a..0000000000000000000000000000000000000000
--- a/dynamicforms/migrations/0013_auto_20180616_0149.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-15 23:49
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('dynamicforms', '0012_auto_20180616_0145'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='question',
-            name='section',
-            field=models.ForeignKey(help_text='Section de formulaire associée à la question.', on_delete=django.db.models.deletion.CASCADE, related_name='questions', to='dynamicforms.Section', verbose_name='section'),
-        ),
-    ]
diff --git a/dynamicforms/migrations/0014_auto_20180616_1812.py b/dynamicforms/migrations/0014_auto_20180616_1812.py
deleted file mode 100644
index 5933a33697e2811600c1ac8e51b7c4038a5efb20..0000000000000000000000000000000000000000
--- a/dynamicforms/migrations/0014_auto_20180616_1812.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-16 16:12
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('dynamicforms', '0013_auto_20180616_0149'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='answer',
-            name='entry',
-            field=models.ForeignKey(help_text='Entrée associée à la réponse.', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='answers', to='dynamicforms.FormEntry', verbose_name='entrée'),
-        ),
-        migrations.AlterField(
-            model_name='question',
-            name='type',
-            field=models.CharField(choices=[('text-small', 'Texte court'), ('text-long', 'Texte long'), ('yes-no', 'Oui/Non'), ('date', 'Date'), ('sex', 'Sexe')], max_length=100, verbose_name='type de question'),
-        ),
-    ]
diff --git a/dynamicforms/migrations/0015_auto_20180616_2136.py b/dynamicforms/migrations/0015_auto_20180616_2136.py
deleted file mode 100644
index 59bc79cb1a42d7b31a59d3b7ca2a11674ed53e13..0000000000000000000000000000000000000000
--- a/dynamicforms/migrations/0015_auto_20180616_2136.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-16 19:36
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('dynamicforms', '0014_auto_20180616_1812'),
-    ]
-
-    operations = [
-        migrations.AlterModelOptions(
-            name='question',
-            options={'ordering': ('order',)},
-        ),
-        migrations.AlterModelOptions(
-            name='section',
-            options={'ordering': ('order',)},
-        ),
-        migrations.AddField(
-            model_name='question',
-            name='order',
-            field=models.PositiveIntegerField(default=0, verbose_name='position'),
-        ),
-        migrations.AddField(
-            model_name='section',
-            name='order',
-            field=models.PositiveIntegerField(default=0, verbose_name='position'),
-        ),
-    ]
diff --git a/oser_backend/__init__.py b/oser_backend/__init__.py
index f272f82a626bb76362bfc3770c4686f8a967ca6e..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644
--- a/oser_backend/__init__.py
+++ b/oser_backend/__init__.py
@@ -1,5 +0,0 @@
-# Celery: make sure the app is always imported when
-# Django starts so that shared_task will use this app.
-from .celery import app as celery_app
-
-__all__ = ('celery_app',)
diff --git a/oser_backend/celery.py b/oser_backend/celery.py
deleted file mode 100644
index b17f8cb4d2f85144ccc21cd0e8318723ddd55d43..0000000000000000000000000000000000000000
--- a/oser_backend/celery.py
+++ /dev/null
@@ -1,32 +0,0 @@
-"""
-Celery config for oser_backend project.
-
-It exposes the Celery application that other apps can use to
-schedule and execute tasks in the background.
-
-See:
-http://docs.celeryproject.org/en/latest/django/first-steps-with-django.html#using-celery-with-django
-"""
-import os
-from celery import Celery
-
-# set the default Django settings module for Celery
-os.environ.setdefault(
-    'DJANGO_SETTINGS_MODULE', 'oser_backend.settings.production')
-
-app = Celery('oser_backend')
-
-# Using a string here means the worker doesn't have to serialize
-# the configuration object to child processes.
-# - namespace='CELERY' means all celery-related configuration keys
-#   should have a `CELERY_` prefix.
-app.config_from_object('django.conf:settings', namespace='CELERY')
-
-# Load tasks from 'tasks.py' modules from all registered Django apps.
-app.autodiscover_tasks()
-
-
-@app.task(bind=True)
-def debug_task(self):
-    """Log Celery requests information."""
-    print('Request: {0!r}'.format(self.request))
diff --git a/oser_backend/settings/common.py b/oser_backend/settings/common.py
index 85aadb61e3d8b6251379cbdf43d5223f4cb7b788..d8da47f708501d33d814ef4f28d522daed5b8d77 100644
--- a/oser_backend/settings/common.py
+++ b/oser_backend/settings/common.py
@@ -35,6 +35,7 @@ DJANGO_APPS = [
     'whitenoise.runserver_nostatic',
     'django.contrib.staticfiles',
     'django.forms',
+    'django.contrib.sites',
 ]
 
 THIRD_PARTY_APPS = [
@@ -60,11 +61,11 @@ THIRD_PARTY_APPS = [
     # Easy filtering on the API
     'django_filters',
 ]
+
 PROJECT_APPS = [
     'core.apps.CoreConfig',
     'users.apps.UsersConfig',
     'profiles.apps.ProfilesConfig',
-    'tutoring.apps.TutoringConfig',
     'visits.apps.VisitsConfig',
     'register.apps.RegisterConfig',
     'api.apps.ApiConfig',
@@ -72,8 +73,14 @@ PROJECT_APPS = [
     'dynamicforms.apps.DynamicformsConfig',
     'projects.apps.ProjectsConfig',
 ]
+
 INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + PROJECT_APPS
 
+# Activate the sites framework
+# It is used to define the domain of the frontend website in
+# the admin (via the 'Sites' section)
+SITE_ID = 1
+
 MIDDLEWARE = [
     'django.middleware.security.SecurityMiddleware',
     'corsheaders.middleware.CorsMiddleware',
@@ -256,11 +263,3 @@ STATICFILES_DIRS = [
 
 MEDIA_URL = '/media/'
 MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
-
-# Celery settings
-
-CELERY_BROKER_URL = os.environ.get('REDIS_URL', 'redis://localhost:6379')
-CELERY_RESULT_BACKEND = CELERY_BROKER_URL
-CELERY_ACCEPT_CONTENT = ('application/json',)
-CELERY_TASK_SERIALIZER = 'json'
-CELERY_RESULT_SERIALIZER = 'json'
diff --git a/oser_backend/settings/production.py b/oser_backend/settings/production.py
index 29cd58b244fd1c4c19ad4a7e84167e14186d3908..f7a85fa5ef8d19b8838d9dd530ea1b29b36db2a9 100644
--- a/oser_backend/settings/production.py
+++ b/oser_backend/settings/production.py
@@ -2,8 +2,6 @@
 
 import os
 
-from celery.schedules import crontab
-
 from aws.conf import *
 
 from .common import *
@@ -29,13 +27,3 @@ MAILS_RAISE_EXCEPTIONS = os.environ.get('MAILS_RAISE_EXCEPTIONS', False)
 # SendGrid
 # Allow Sandbox if DEBUG is True (we're in prod anyway)
 SENDGRID_SANDBOX_MODE_IN_DEBUG = False
-
-# Celery settings
-
-CELERY_BEAT_SCHEDULE = {
-    # Clean media files every day at 22:00
-    'clean-media-every-hour': {
-        'task': 'core.tasks.cleanmedia',
-        'schedule': crontab(minute='0', hour='22'),
-    },
-}
diff --git a/profiles/admin.py b/profiles/admin.py
index dfd5fbf041a08d4214d13177046fec50ed0c1a62..82207b0dd026276567ef92a1583a8bcd5b74d246 100644
--- a/profiles/admin.py
+++ b/profiles/admin.py
@@ -2,7 +2,6 @@
 
 from django.contrib import admin
 from .models import Student, Tutor
-from tutoring.models import TutoringGroup
 
 
 class ProfileAdminMixin:
@@ -11,21 +10,10 @@ class ProfileAdminMixin:
     search_fields = ('user__email', 'user__first_name', 'user__last_name',)
 
 
-class TutorTutoringGroupsInline(admin.TabularInline):
-    """Inline for tutor tutoring groups."""
-
-    model = TutoringGroup.tutors.through
-    extra = 0
-    max_num = 0
-    readonly_fields = ('tutoring_group', 'is_leader')
-    can_delete = False
-
-
 @admin.register(Tutor)
 class TutorAdmin(ProfileAdminMixin, admin.ModelAdmin):
     """Tutor admin panel."""
 
-    inlines = (TutorTutoringGroupsInline,)
     autocomplete_fields = ('address',)
 
     class Meta:  # noqa
diff --git a/profiles/factory.py b/profiles/factory.py
index ea03ddd9b733673ce25ad06b25aea18143a7d93c..886b5dde680d2945e4fbc1b30455990a390fa491 100644
--- a/profiles/factory.py
+++ b/profiles/factory.py
@@ -1,21 +1,19 @@
 """Profile factories."""
 
-import random
 from datetime import datetime
 
 import factory
 import factory.django
 from django.contrib.auth.models import Group
 
-from tutoring.factory import TutoringGroupFactory
-from tutoring.models import TutoringGroup
+from core.factory import AddressFactory
 from users.factory import UserFactory
 
 from . import models
 
 
 class StudentFactory(factory.DjangoModelFactory):
-    """Student object factory. Not assigned to a tutoring group."""
+    """Student object factory."""
 
     class Meta:  # noqa
         model = models.Student
@@ -23,21 +21,6 @@ class StudentFactory(factory.DjangoModelFactory):
     user = factory.SubFactory(UserFactory)
 
 
-class StudentInTutoringGroupFactory(StudentFactory):
-    """Student object factory, member of a tutoring group."""
-
-    @factory.lazy_attribute
-    def tutoring_group(self):
-        """Return an existing tutoring group in 70% of cases."""
-        groups = TutoringGroup.objects.all()
-        if groups and random.random() > .3:
-            return random.choice(groups)
-        return TutoringGroupFactory.create()
-
-    # student's school is the same as the student's tutoring group's
-    school = factory.SelfAttribute('tutoring_group.school')
-
-
 _this_year = datetime.today().year
 
 
@@ -49,6 +32,7 @@ class TutorFactory(factory.DjangoModelFactory):
 
     user = factory.SubFactory(UserFactory)
     promotion = factory.Iterator([_this_year, _this_year + 1, _this_year + 2])
+    address = factory.SubFactory(AddressFactory)
 
 
 class TutorInGroupFactory(TutorFactory):
diff --git a/profiles/migrations/0001_initial.py b/profiles/migrations/0001_initial.py
index 25365006d866860cc12b004e97711c9dc22138e8..8720e63e4f24ec7112ca916034e9eb52252f0654 100644
--- a/profiles/migrations/0001_initial.py
+++ b/profiles/migrations/0001_initial.py
@@ -1,6 +1,5 @@
-# Generated by Django 2.0.4 on 2018-05-05 15:34
+# Generated by Django 2.0.7 on 2018-09-11 17:38
 
-from django.conf import settings
 from django.db import migrations, models
 import django.db.models.deletion
 import profiles.models
@@ -11,8 +10,7 @@ class Migration(migrations.Migration):
     initial = True
 
     dependencies = [
-        ('register', '0015_auto_20180505_1403'),
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('core', '0001_initial'),
     ]
 
     operations = [
@@ -20,10 +18,6 @@ class Migration(migrations.Migration):
             name='Student',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('registration', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='student', to='register.Registration', verbose_name="dossier d'inscription")),
-                ('school', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='students', to='tutoring.School', verbose_name='lycée')),
-                ('tutoring_group', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='students', to='tutoring.TutoringGroup', verbose_name='groupe de tutorat')),
-                ('user', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='student', to=settings.AUTH_USER_MODEL, verbose_name='utilisateur')),
             ],
             options={
                 'verbose_name': 'lycéen',
@@ -34,8 +28,8 @@ class Migration(migrations.Migration):
             name='Tutor',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('promotion', models.IntegerField(choices=[(2020, '2020'), (2019, '2019'), (2018, '2018'), (2017, '2017'), (2016, '2016')], default=2020)),
-                ('user', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='tutor', to=settings.AUTH_USER_MODEL, verbose_name='utilisateur')),
+                ('promotion', models.IntegerField(choices=[(2021, '2021'), (2020, '2020'), (2019, '2019'), (2018, '2018'), (2017, '2017')], default=2021)),
+                ('address', models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tutor', to='core.Address', verbose_name='adresse')),
             ],
             options={
                 'verbose_name': 'tuteur',
diff --git a/profiles/migrations/0002_auto_20180512_1344.py b/profiles/migrations/0002_auto_20180512_1344.py
deleted file mode 100644
index 27a7fa5d858a7cdc5630c747972d6008dd794719..0000000000000000000000000000000000000000
--- a/profiles/migrations/0002_auto_20180512_1344.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-12 11:44
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('profiles', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='student',
-            name='school',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='students', to='tutoring.School', verbose_name='lycée'),
-        ),
-        migrations.AlterField(
-            model_name='student',
-            name='tutoring_group',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='students', to='tutoring.TutoringGroup', verbose_name='groupe de tutorat'),
-        ),
-    ]
diff --git a/profiles/migrations/0002_auto_20180911_1938.py b/profiles/migrations/0002_auto_20180911_1938.py
new file mode 100644
index 0000000000000000000000000000000000000000..aefb82e9b5a2a7ff8f48c27230c6f674fc0a2299
--- /dev/null
+++ b/profiles/migrations/0002_auto_20180911_1938.py
@@ -0,0 +1,34 @@
+# Generated by Django 2.0.7 on 2018-09-11 17:38
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('profiles', '0001_initial'),
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('register', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='tutor',
+            name='user',
+            field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='tutor', to=settings.AUTH_USER_MODEL, verbose_name='utilisateur'),
+        ),
+        migrations.AddField(
+            model_name='student',
+            name='registration',
+            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='student', to='register.Registration', verbose_name="dossier d'inscription"),
+        ),
+        migrations.AddField(
+            model_name='student',
+            name='user',
+            field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='student', to=settings.AUTH_USER_MODEL, verbose_name='utilisateur'),
+        ),
+    ]
diff --git a/profiles/migrations/0003_tutor_address.py b/profiles/migrations/0003_tutor_address.py
deleted file mode 100644
index b5bc917146171d6bc1a1e122d43846984733aacb..0000000000000000000000000000000000000000
--- a/profiles/migrations/0003_tutor_address.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-15 23:41
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('core', '0005_auto_20180408_1525'),
-        ('profiles', '0002_auto_20180512_1344'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='tutor',
-            name='address',
-            field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tutor', to='core.Address', verbose_name='adresse'),
-        ),
-    ]
diff --git a/profiles/models.py b/profiles/models.py
index 25cb1ebe2d297e13a51eac85e78dacf657eb295a..28061fa97817f7fd8dd4c0a58ad90d3e8da141bd 100644
--- a/profiles/models.py
+++ b/profiles/models.py
@@ -43,22 +43,6 @@ class Student(ProfileMixin, models.Model):
         verbose_name='utilisateur',
         related_name='student')
 
-    tutoring_group = models.ForeignKey(
-        'tutoring.TutoringGroup',
-        on_delete=models.SET_NULL,
-        null=True,
-        blank=True,
-        related_name='students',
-        verbose_name='groupe de tutorat')
-
-    school = models.ForeignKey(
-        'tutoring.School',
-        on_delete=models.SET_NULL,
-        null=True,
-        blank=True,
-        related_name='students',
-        verbose_name='lycée')
-
     registration = models.OneToOneField(
         'register.Registration',
         on_delete=models.SET_NULL,
@@ -68,16 +52,6 @@ class Student(ProfileMixin, models.Model):
         related_name='student',
     )
 
-    @property
-    def address(self):
-        """Address of the student defined in their registration."""
-        return getattr(self.registration, 'address', None)
-
-    @property
-    def emergency_contact(self):
-        """Emergency contact of the student defined in their registration."""
-        return getattr(self.registration, 'emergency_contact', None)
-
     class Meta:  # noqa
         verbose_name = 'lycéen'
 
diff --git a/profiles/serializers.py b/profiles/serializers.py
index e15ff43f1360638580ce48be87c15dd4ff7a3577..31b20310d293647aacde450dca91509f8af66088 100644
--- a/profiles/serializers.py
+++ b/profiles/serializers.py
@@ -2,10 +2,7 @@
 
 from rest_framework import serializers
 
-from core.serializers import AddressSerializer
-from register.serializers import (EmergencyContactSerializer,
-                                  StudentRegistrationSerializer)
-from tutoring.models import School, TutoringGroup
+from register.serializers import StudentRegistrationSerializer
 from users.serializers import UserSerializer
 
 from .models import Student, Tutor
@@ -15,13 +12,10 @@ class TutorSerializer(serializers.HyperlinkedModelSerializer):
     """Hyperlinked serializer for Tutor."""
 
     user = UserSerializer()
-    tutoring_groups = serializers.PrimaryKeyRelatedField(
-        many=True, read_only=True)
-    address = AddressSerializer()
 
     class Meta:  # noqa
         model = Tutor
-        fields = ('user', 'address', 'promotion', 'tutoring_groups', 'url',)
+        fields = ('user', 'promotion', 'url',)
         extra_kwargs = {
             'url': {'view_name': 'api:tutor-detail'},
         }
@@ -31,25 +25,17 @@ class StudentSerializer(serializers.HyperlinkedModelSerializer):
     """Hyperlinked serializer for Student."""
 
     user = UserSerializer()
-    tutoring_group = serializers.PrimaryKeyRelatedField(
-        queryset=TutoringGroup.objects.all(),
-    )
-    school = serializers.PrimaryKeyRelatedField(
-        queryset=School.objects.all(),
-    )
     visits = serializers.PrimaryKeyRelatedField(
         source='user.visit_set',
         many=True,
         read_only=True)
-    address = AddressSerializer()
-    emergency_contact = EmergencyContactSerializer()
     registration = StudentRegistrationSerializer()
 
     class Meta:  # noqa
         model = Student
-        fields = ('user_id', 'user', 'address', 'tutoring_group',
-                  'school', 'emergency_contact', 'registration',
-                  'visits', 'url',)
+        fields = (
+            'user_id', 'user', 'registration', 'visits', 'url',
+        )
         extra_kwargs = {
             'url': {'view_name': 'api:student-detail'},
         }
diff --git a/profiles/views.py b/profiles/views.py
index f27b3f76a79b84d421fb9c3d7b8ca00dc67b7dc3..0c157ef80761cfde6b830d6820eae66d48ab365b 100644
--- a/profiles/views.py
+++ b/profiles/views.py
@@ -6,7 +6,6 @@ from rest_framework import viewsets
 from rest_framework.decorators import action
 from rest_framework.response import Response
 
-from tutoring.serializers import TutoringGroupSerializer
 from visits.serializers import VisitSerializer
 
 from .models import Student, Tutor
@@ -23,15 +22,6 @@ class TutorViewSet(viewsets.ReadOnlyModelViewSet):
     serializer_class = TutorSerializer
     permission_classes = (DRYPermissions,)
 
-    @action(detail=True)
-    def tutoringgroups(self, request, pk=None):
-        """Retrieve the tutoring groups of a tutor."""
-        tutor = self.get_object()
-        tutoring_groups = tutor.tutoring_groups.all()
-        serializer = TutoringGroupSerializer(tutoring_groups, many=True,
-                                             context={'request': request})
-        return Response(serializer.data)
-
 
 class StudentViewSet(viewsets.ReadOnlyModelViewSet):
     """API endpoint that allows students to be viewed.
@@ -54,30 +44,8 @@ class StudentViewSet(viewsets.ReadOnlyModelViewSet):
                 "profile_type": null,
                 "first_name": "",
                 "last_name": "",
-                "gender": null,
-                "phone_number": null,
-                "date_of_birth": null,
                 "url": "http://localhost:8000/api/users/4/"
             },
-            "address": {
-                "line1": "88 bis rue Jules Guesde",
-                "line2": "",
-                "post_code": "93100",
-                "city": "Montreuil",
-                "country": {
-                    "code": "FR",
-                    "name": "France"
-                }
-            },
-            "tutoring_group": 1,
-            "school": "0930965U",
-            "emergency_contact": {
-                "first_name": "Marie-Claude",
-                "last_name": "Perret",
-                "email": null,
-                "home_phone": "+33312344556",
-                "mobile_phone": null
-            },
             "registration": {
                 "id": 3,
                 "submitted": "2018-05-05T14:15:10.998206+02:00",
@@ -92,15 +60,6 @@ class StudentViewSet(viewsets.ReadOnlyModelViewSet):
     serializer_class = StudentSerializer
     permission_classes = (DRYPermissions,)
 
-    @action(detail=True)
-    def tutoringgroup(self, request, pk=None):
-        """Retrieve the tutoring group of a student."""
-        student = self.get_object()
-        tutoring_group = student.tutoring_group
-        serializer = TutoringGroupSerializer(tutoring_group,
-                                             context={'request': request})
-        return Response(serializer.data)
-
     @action(detail=True)
     def visits(self, request, pk=None):
         """List detailed info about the visits a student participates in."""
diff --git a/projects/migrations/0001_initial.py b/projects/migrations/0001_initial.py
index 93d192e7521b47365d53778184ad523d9b7a000c..2a575ed903e4db5e1f85c83b443b8e0f1682ed3f 100644
--- a/projects/migrations/0001_initial.py
+++ b/projects/migrations/0001_initial.py
@@ -1,10 +1,9 @@
-# Generated by Django 2.0.4 on 2018-06-06 21:11
+# Generated by Django 2.0.7 on 2018-09-11 17:38
 
-from django.conf import settings
 from django.db import migrations, models
 import django.db.models.deletion
 import markdownx.models
-import projects.models
+import projects.utils
 
 
 class Migration(migrations.Migration):
@@ -12,7 +11,7 @@ class Migration(migrations.Migration):
     initial = True
 
     dependencies = [
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('dynamicforms', '0001_initial'),
     ]
 
     operations = [
@@ -20,21 +19,33 @@ class Migration(migrations.Migration):
             name='Edition',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('year', models.IntegerField(default=projects.utils.this_year, help_text="L'année où se déroule cette édition.", verbose_name='année')),
                 ('name', models.CharField(blank=True, default='', help_text='Un nom optionnel pour cette édition (exemple : "Berlin").', max_length=200, verbose_name='nom')),
-                ('year', models.IntegerField(default=projects.models.this_year, help_text="L'année où se déroule cette édition.", verbose_name='année')),
-                ('description', markdownx.models.MarkdownxField(help_text='Une description spécifique pour cette édition.')),
+                ('description', markdownx.models.MarkdownxField(blank=True, default='', help_text='Une description spécifique pour cette édition.')),
             ],
             options={
                 'verbose_name': 'édition',
                 'ordering': ('-year',),
+                'get_latest_by': 'year',
+            },
+        ),
+        migrations.CreateModel(
+            name='EditionForm',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('deadline', models.DateField(help_text="Les lycéens ne pourront plus s'inscrire après cette date.", verbose_name='date butoir')),
+            ],
+            options={
+                'verbose_name': 'formulaire projet',
+                'verbose_name_plural': 'formulaires projet',
+                'ordering': ('deadline',),
             },
         ),
         migrations.CreateModel(
             name='EditionOrganizer',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('edition', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='projects.Edition', verbose_name='édition')),
-                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='utilisateur')),
+                ('role', models.CharField(blank=True, default='', help_text='Exemple : responsable projet, responsable inscriptions…', max_length=100, verbose_name='rôle')),
             ],
             options={
                 'verbose_name': 'organisateur',
@@ -45,9 +56,9 @@ class Migration(migrations.Migration):
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('submitted', models.DateTimeField(auto_now_add=True, help_text='Date de soumission de la participation', verbose_name='soumis le')),
-                ('status', models.CharField(choices=[('pending', 'En attente'), ('valid', 'Validé'), ('accepted', 'Accepté'), ('rejected', 'Refusé'), ('cancelled', 'Annulé')], help_text="L'état de la participation. En attente = en cours de validation par les organisateurs. Validé = toutes les pièces ont été reçues et sont conformes. Accepté = le lycéen a été sélectionné pour participer. Refusé = le lycéen n'a pas été sélectionné pour participer. Annulé = le lycéen a annulé sa participation.", max_length=10, verbose_name='statut')),
-                ('edition', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='participations', to='projects.Edition', verbose_name='sortie')),
-                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_participations', to=settings.AUTH_USER_MODEL, verbose_name='utilisateur')),
+                ('state', models.CharField(choices=[('pending', 'En attente'), ('valid', 'Validé'), ('accepted', 'Accepté'), ('rejected', 'Refusé'), ('cancelled', 'Annulé')], default='pending', help_text="État de la participation. En attente = en cours de validation par les organisateurs. Validé = toutes les pièces ont été reçues et sont conformes. Accepté = le lycéen a été sélectionné pour participer. Refusé = le lycéen n'a pas été sélectionné pour participer. Annulé = le lycéen a annulé sa participation.", max_length=10, verbose_name='état')),
+                ('edition', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='participations', to='projects.Edition', verbose_name='édition')),
+                ('entry', models.OneToOneField(help_text="Réponses au formulaire d'inscription", null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='project_participation', to='dynamicforms.FormEntry', verbose_name='entrée')),
             ],
             options={
                 'ordering': ('-submitted',),
@@ -58,7 +69,7 @@ class Migration(migrations.Migration):
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('name', models.CharField(help_text='Le nom du projet.', max_length=200, verbose_name='nom')),
-                ('description', markdownx.models.MarkdownxField(help_text='Une description générale du projet')),
+                ('description', markdownx.models.MarkdownxField(blank=True, default='', help_text='Une description générale du projet')),
                 ('logo', models.ImageField(blank=True, help_text='Le logo du projet ou une image représentative.', null=True, upload_to='projects/logos/')),
             ],
             options={
@@ -66,14 +77,4 @@ class Migration(migrations.Migration):
                 'ordering': ('name',),
             },
         ),
-        migrations.AddField(
-            model_name='edition',
-            name='organizers',
-            field=models.ManyToManyField(through='projects.EditionOrganizer', to=settings.AUTH_USER_MODEL),
-        ),
-        migrations.AddField(
-            model_name='edition',
-            name='project',
-            field=models.ForeignKey(help_text='Le projet dont ceci est une édition.', on_delete=django.db.models.deletion.CASCADE, related_name='editions', to='projects.Project', verbose_name='projet'),
-        ),
     ]
diff --git a/projects/migrations/0002_auto_20180606_2329.py b/projects/migrations/0002_auto_20180606_2329.py
deleted file mode 100644
index 065dfdfcf3bd1d710ffbc4d50630c239bc30f19a..0000000000000000000000000000000000000000
--- a/projects/migrations/0002_auto_20180606_2329.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Generated by Django 2.0.4 on 2018-06-06 21:29
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('projects', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.RemoveField(
-            model_name='participation',
-            name='status',
-        ),
-        migrations.AddField(
-            model_name='participation',
-            name='state',
-            field=models.CharField(choices=[('pending', 'En attente'), ('valid', 'Validé'), ('accepted', 'Accepté'), ('rejected', 'Refusé'), ('cancelled', 'Annulé')], default='pending', help_text="État de la participation. En attente = en cours de validation par les organisateurs. Validé = toutes les pièces ont été reçues et sont conformes. Accepté = le lycéen a été sélectionné pour participer. Refusé = le lycéen n'a pas été sélectionné pour participer. Annulé = le lycéen a annulé sa participation.", max_length=10, verbose_name='état'),
-        ),
-    ]
diff --git a/projects/migrations/0002_auto_20180911_1938.py b/projects/migrations/0002_auto_20180911_1938.py
new file mode 100644
index 0000000000000000000000000000000000000000..d170e209dc1690e2437ec57177369cf97c3f05b0
--- /dev/null
+++ b/projects/migrations/0002_auto_20180911_1938.py
@@ -0,0 +1,61 @@
+# Generated by Django 2.0.7 on 2018-09-11 17:38
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import projects.models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('dynamicforms', '0001_initial'),
+        ('profiles', '0002_auto_20180911_1938'),
+        ('projects', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='participation',
+            name='user',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_participations', to=settings.AUTH_USER_MODEL, verbose_name='utilisateur'),
+        ),
+        migrations.AddField(
+            model_name='editionorganizer',
+            name='edition',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='projects.Edition', verbose_name='édition'),
+        ),
+        migrations.AddField(
+            model_name='editionorganizer',
+            name='user',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='utilisateur'),
+        ),
+        migrations.AddField(
+            model_name='editionform',
+            name='edition',
+            field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='edition_form', to='projects.Edition', verbose_name='édition'),
+        ),
+        migrations.AddField(
+            model_name='editionform',
+            name='form',
+            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dynamicforms.Form', verbose_name="formulaire d'inscription"),
+        ),
+        migrations.AddField(
+            model_name='editionform',
+            name='recipient',
+            field=models.ForeignKey(blank=True, help_text='Tuteur/tutrice à qui envoyer les pièces justificatives. Son adresse doit être renseignée dans son profil.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='profiles.Tutor', validators=[projects.models._validate_address_is_set], verbose_name='destinataire'),
+        ),
+        migrations.AddField(
+            model_name='edition',
+            name='organizers',
+            field=models.ManyToManyField(through='projects.EditionOrganizer', to=settings.AUTH_USER_MODEL),
+        ),
+        migrations.AddField(
+            model_name='edition',
+            name='project',
+            field=models.ForeignKey(help_text='Le projet dont ceci est une édition.', on_delete=django.db.models.deletion.CASCADE, related_name='editions', to='projects.Project', verbose_name='projet'),
+        ),
+    ]
diff --git a/projects/migrations/0003_auto_20180615_2323.py b/projects/migrations/0003_auto_20180615_2323.py
deleted file mode 100644
index 64a96009accc9a01266c2a9307c9795d964c5cd9..0000000000000000000000000000000000000000
--- a/projects/migrations/0003_auto_20180615_2323.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-15 21:23
-
-from django.db import migrations, models
-import django.db.models.deletion
-import markdownx.models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('projects', '0002_auto_20180606_2329'),
-    ]
-
-    operations = [
-        migrations.AlterModelOptions(
-            name='edition',
-            options={'get_latest_by': 'year', 'ordering': ('-year',), 'verbose_name': 'édition'},
-        ),
-        migrations.AlterField(
-            model_name='edition',
-            name='description',
-            field=markdownx.models.MarkdownxField(blank=True, default='', help_text='Une description spécifique pour cette édition.'),
-        ),
-        migrations.AlterField(
-            model_name='participation',
-            name='edition',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='participations', to='projects.Edition', verbose_name='édition'),
-        ),
-        migrations.AlterField(
-            model_name='project',
-            name='description',
-            field=markdownx.models.MarkdownxField(blank=True, default='', help_text='Une description générale du projet'),
-        ),
-    ]
diff --git a/projects/migrations/0004_editionform.py b/projects/migrations/0004_editionform.py
deleted file mode 100644
index 4dc610a999b84e9d988c9e9630fa3c6e9303c0a1..0000000000000000000000000000000000000000
--- a/projects/migrations/0004_editionform.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-15 23:41
-
-from django.db import migrations, models
-import django.db.models.deletion
-import projects.models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('profiles', '0003_tutor_address'),
-        ('dynamicforms', '0011_auto_20180616_0141'),
-        ('projects', '0003_auto_20180615_2323'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='EditionForm',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('deadline', models.DateField(help_text="Les lycéens ne pourront plus s'inscrire après cette date.", verbose_name='date butoir')),
-                ('form', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dynamicforms.Form', verbose_name='formulaire')),
-                ('recipient', models.ForeignKey(blank=True, help_text='Tuteur/tutrice à qui envoyer les pièces justificatives. Son adresse doit être renseignée dans son profil.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='profiles.Tutor', validators=[projects.models.validate_address_is_set], verbose_name='destinataire')),
-            ],
-            options={
-                'verbose_name': 'formulaire projet',
-                'verbose_name_plural': 'formulaires projet',
-                'ordering': ('deadline',),
-            },
-        ),
-    ]
diff --git a/projects/migrations/0005_auto_20180616_0144.py b/projects/migrations/0005_auto_20180616_0144.py
deleted file mode 100644
index 73439c42a1b5de4e2c28b48b23facdc6a1a18dc2..0000000000000000000000000000000000000000
--- a/projects/migrations/0005_auto_20180616_0144.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-15 23:44
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('projects', '0004_editionform'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='editionform',
-            name='edition',
-            field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='projects.Edition', verbose_name='édition'),
-        ),
-        migrations.AlterField(
-            model_name='editionform',
-            name='form',
-            field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='dynamicforms.Form', verbose_name='formulaire'),
-        ),
-    ]
diff --git a/projects/migrations/0006_auto_20180616_0145.py b/projects/migrations/0006_auto_20180616_0145.py
deleted file mode 100644
index ba4934abeabbad5380fafc30589738035a479e2d..0000000000000000000000000000000000000000
--- a/projects/migrations/0006_auto_20180616_0145.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-15 23:45
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('projects', '0005_auto_20180616_0144'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='editionform',
-            name='edition',
-            field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='projects.Edition', verbose_name='édition'),
-        ),
-    ]
diff --git a/projects/migrations/0007_auto_20180616_1812.py b/projects/migrations/0007_auto_20180616_1812.py
deleted file mode 100644
index 7835d9d7f1539e21b13ba82aecd56e7a0d1be6fc..0000000000000000000000000000000000000000
--- a/projects/migrations/0007_auto_20180616_1812.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-16 16:12
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('dynamicforms', '0014_auto_20180616_1812'),
-        ('projects', '0006_auto_20180616_0145'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='participation',
-            name='entry',
-            field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='project_participation', to='dynamicforms.FormEntry'),
-        ),
-        migrations.AlterField(
-            model_name='editionform',
-            name='edition',
-            field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='edition_form', to='projects.Edition', verbose_name='édition'),
-        ),
-        migrations.AlterField(
-            model_name='editionform',
-            name='form',
-            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dynamicforms.Form', verbose_name="formulaire d'inscription"),
-        ),
-    ]
diff --git a/projects/migrations/0008_auto_20180616_1919.py b/projects/migrations/0008_auto_20180616_1919.py
deleted file mode 100644
index 0a3740d95f0795c16037c58061be8fc61c505913..0000000000000000000000000000000000000000
--- a/projects/migrations/0008_auto_20180616_1919.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-16 17:19
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('projects', '0007_auto_20180616_1812'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='participation',
-            name='entry',
-            field=models.OneToOneField(help_text="Réponses au formulaire d'inscription", null=True, on_delete=django.db.models.deletion.CASCADE, related_name='project_participation', to='dynamicforms.FormEntry', verbose_name='entrée'),
-        ),
-    ]
diff --git a/projects/migrations/0009_auto_20180630_1457.py b/projects/migrations/0009_auto_20180630_1457.py
deleted file mode 100644
index 672dfbb8e2c5cc30d82a0d405c94241f23cbd51d..0000000000000000000000000000000000000000
--- a/projects/migrations/0009_auto_20180630_1457.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Django 2.0.6 on 2018-06-30 12:57
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('projects', '0008_auto_20180616_1919'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='editionorganizer',
-            name='role',
-            field=models.CharField(blank=True, default='', help_text='Exemple : responsable projet, responsable inscriptions…', max_length=100, verbose_name='rôle'),
-        ),
-        migrations.AlterField(
-            model_name='participation',
-            name='entry',
-            field=models.OneToOneField(help_text="Réponses au formulaire d'inscription", null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='project_participation', to='dynamicforms.FormEntry', verbose_name='entrée'),
-        ),
-    ]
diff --git a/projects/models.py b/projects/models.py
index c31a7249738670d31d91f1f6050511da9b29a882..cd5783fe39a32c73f0004f45ced25afc72531d34 100644
--- a/projects/models.py
+++ b/projects/models.py
@@ -1,6 +1,7 @@
 """Projects models."""
 
 from django.db import models
+from django.contrib.sites.models import Site
 from django.core.validators import ValidationError
 
 from markdownx.models import MarkdownxField
@@ -53,10 +54,6 @@ class Project(models.Model):
 class Edition(models.Model):
     """Represents an instance of a project for a given year."""
 
-    name = models.CharField(
-        'nom', max_length=200, default='', blank=True,
-        help_text='Un nom optionnel pour cette édition (exemple : "Berlin").')
-
     year = models.IntegerField(
         'année', default=this_year,
         help_text="L'année où se déroule cette édition.")
@@ -66,6 +63,10 @@ class Edition(models.Model):
         verbose_name='projet', related_name='editions',
         help_text='Le projet dont ceci est une édition.')
 
+    name = models.CharField(
+        'nom', max_length=200, default='', blank=True,
+        help_text='Un nom optionnel pour cette édition (exemple : "Berlin").')
+
     description = MarkdownxField(
         blank=True, default='',
         help_text=(
@@ -82,6 +83,14 @@ class Edition(models.Model):
         verbose_name = 'édition'
         get_latest_by = 'year'
 
+    def get_projects_site_url(self) -> str:
+        site = Site.objects.get_current()
+        return f'https://{site.domain}/projets/'
+
+    def get_registration_url(self) -> str:
+        site = Site.objects.get_current()
+        return f'https://{site.domain}/projets/mes-inscriptions'
+
     def __str__(self) -> str:
         """Represent using the project name, the year and the edition name."""
         s = f'{self.project} édition {self.year}'
@@ -219,6 +228,16 @@ class Participation(models.Model):
     class Meta:  # noqa
         ordering = ('-submitted',)
 
+    def __init__(self, *args, **kwargs):
+        """Store the initial value of `state` to detect changes."""
+        super().__init__(*args, **kwargs)
+        self.initial_state = self.state
+
+    @property
+    def state_changed(self) -> bool:
+        """Return whether the `state` field has changed."""
+        return self.initial_state != self.state
+
     def __str__(self):
         """Represent by its user."""
         return str(self.user)
diff --git a/projects/notifications.py b/projects/notifications.py
new file mode 100644
index 0000000000000000000000000000000000000000..7ee9008841f675eed76cf5ad53a507656df69f8b
--- /dev/null
+++ b/projects/notifications.py
@@ -0,0 +1,123 @@
+"""Projects notifications."""
+
+from django.shortcuts import reverse
+from django.utils.timezone import now
+
+from mails import Notification
+from users.models import User
+
+from .models import Edition, Project
+
+
+class _BaseParticipationNotification(Notification):
+    """Base notification for project participations."""
+
+    args = ('user', 'edition',)
+
+    @classmethod
+    def example(cls):
+        """Example notification."""
+        user = User(email='john.doe@example.com', first_name='John')
+        project = Project(title='Focus Europe')
+        edition = Edition(project=project, year=now().year)
+        return cls(user=user, edition=edition)
+
+
+class _NotifyOrgnizers(_BaseParticipationNotification):
+    """Notify the edition organizers a participation has changed."""
+
+    title: str
+
+    def get_subject(self):
+        return f'{self.title}: {self.edition}'
+
+    def _get_editionform_admin_url(self) -> str:
+        base = 'https://oser-backend.herokuapp.com'
+        view = reverse('admin:projects_editionform_changelist')
+        return base + view
+
+    def get_context(self) -> dict:
+        context = super().get_context()
+        context['editionform_admin_url'] = self._get_editionform_admin_url()
+        return context
+
+    def get_recipients(self):
+        """Return the email of each organizer."""
+        edition = self.kwargs['edition']
+        # TODO add the project team's email
+        return list(edition.organizers.values_list('email', flat=True))
+
+
+class _NotifyUser(_BaseParticipationNotification):
+    """Notify a user their participation state has changed."""
+
+    verb: str
+
+    def get_subject(self):
+        return f'Participation {self.verb}: {self.edition}'
+
+    def get_recipients(self):
+        return [self.kwargs['user'].email]
+
+
+class OrganizersReceived(_NotifyOrgnizers):
+    """Notify the edition organizers that a new participation was created."""
+
+    title = 'Nouvelle participation'
+    template_name = 'projects/organizers_participation_received.md'
+
+
+class UserReceived(_NotifyUser):
+    """Notify a user their participation was well received."""
+
+    verb = 'en attente'
+    template_name = 'projects/participation_received.md'
+
+
+class UserValid(_NotifyUser):
+    """Notify a user their participation was marked as valid."""
+
+    verb = 'vérifiée'
+    template_name = 'projects/participation_valid.md'
+
+
+class UserAccepted(_NotifyUser):
+    """Notify a user their participation was marked as accepted."""
+
+    verb = 'acceptée'
+    template_name = 'projects/participation_accepted.md'
+
+
+class UserRejected(_NotifyUser):
+    """Notify a user their participation was marked as rejected."""
+
+    verb = 'rejetée'
+    template_name = 'projects/participation_rejected.md'
+
+
+class UserCancelled(_NotifyUser):
+    """Notify a user their participation was correctly cancelled."""
+
+    verb = 'annulée'
+    template_name = 'projects/participation_cancelled.md'
+
+
+class UserDeleted(_NotifyUser):
+    """Notify a user their participation was correctly deleted."""
+
+    verb = 'supprimée'
+    template_name = 'projects/participation_deleted.md'
+
+
+class OrganizersCancelled(_NotifyOrgnizers):
+    """Notify organizers that a user has cancelled their participation."""
+
+    title = 'Participation annulée'
+    template_name = 'projects/organizers_participation_cancelled.md'
+
+
+class OrganizersDeleted(_NotifyOrgnizers):
+    """Notify organizers a user has deleted their participation."""
+
+    title = 'Participation supprimée'
+    template_name = 'projects/organizers_participation_deleted.md'
diff --git a/projects/signals.py b/projects/signals.py
index 4b75cea9a475f587c0874e5a2b8582cb55b24124..25f4fc9dddc41931febcee1683108a1dc00984aa 100644
--- a/projects/signals.py
+++ b/projects/signals.py
@@ -1,20 +1,92 @@
 """Projects app signals."""
 
 import logging
-from django.db.models.signals import pre_delete
-from django.dispatch import receiver
 
-from .models import Participation
+from django.db.models.signals import post_save, pre_delete
+from django.dispatch import receiver, Signal
 
+from . import notifications
+from .models import Participation
 
 logger = logging.getLogger('web.projects.signals')
 
 
+pending = Signal(providing_args=('instance',))
+valid = Signal(providing_args=('instance',))
+accepted = Signal(providing_args=('instance',))
+rejected = Signal(providing_args=('instance',))
+cancelled = Signal(providing_args=('instance',))
+deleted = Signal(providing_args=('instance',))
+deleted_organizers = Signal(providing_args=('instance',))
+
+
+def _send(cls, instance: Participation):
+    cls(user=instance.user, edition=instance.edition).send()
+
+
 @receiver(pre_delete, sender=Participation)
-def delete_associated_form_entry(sender, instance: Participation,
-                                 *args, **kwargs):
+def delete_associated_form_entry(sender, instance: Participation, **kwargs):
     """Delete the form entry associated to a participation being deleted."""
     entry = instance.entry
     if entry:
         entry.delete()
         logger.info('entry %s deleted', entry.id)
+
+
+@receiver(post_save, sender=Participation)
+def send_state_notifications(sender, instance: Participation,
+                             created, **kwargs):
+    """Send notifications when the state of a participation has changed."""
+    if not created and not instance.state_changed:
+        return
+    signals = {
+        Participation.STATE_PENDING: pending,
+        Participation.STATE_VALIDATED: valid,
+        Participation.STATE_ACCEPTED: accepted,
+        Participation.STATE_REJECTED: rejected,
+        Participation.STATE_CANCELLED: cancelled,
+    }
+    if instance.state in signals.keys():
+        signals[instance.state].send(Participation, instance=instance)
+
+
+@receiver(pre_delete, sender=Participation)
+def send_participation_deleted_notifications(sender, instance: Participation,
+                                             **kwargs):
+    """Send notifications when a participation is deleted."""
+    deleted.send(Participation, instance=instance)
+
+
+# Notiication senders
+
+@receiver(pending)
+def notify_pending(sender, instance, **kwargs):
+    _send(notifications.UserReceived, instance)
+    _send(notifications.OrganizersReceived, instance)
+
+
+@receiver(valid)
+def notify_valid(sender, instance, **kwargs):
+    _send(notifications.UserValid, instance)
+
+
+@receiver(accepted)
+def notify_accepted(sender, instance, **kwargs):
+    _send(notifications.UserAccepted, instance)
+
+
+@receiver(rejected)
+def notify_rejected(sender, instance, **kwargs):
+    _send(notifications.UserRejected, instance)
+
+
+@receiver(cancelled)
+def notify_cancelled(sender, instance, **kwargs):
+    _send(notifications.UserCancelled, instance)
+    _send(notifications.OrganizersCancelled, instance)
+
+
+@receiver(deleted)
+def notify_deleted(sender, instance, **kwargs):
+    _send(notifications.UserDeleted, instance)
+    _send(notifications.OrganizersDeleted, instance)
diff --git a/projects/templates/projects/organizers_participation_cancelled.md b/projects/templates/projects/organizers_participation_cancelled.md
new file mode 100644
index 0000000000000000000000000000000000000000..6cb2ca7fd8da492a3a9387ed9c20d3f43db229df
--- /dev/null
+++ b/projects/templates/projects/organizers_participation_cancelled.md
@@ -0,0 +1,9 @@
+{% extends 'projects/to_user.md' %}
+
+{% block body %}
+L'utilisateur {{ user }} a supprimé sa participation à {{ edition }}.
+
+Si besoin, vous pouvez contacter {{ user }} via son adresse email : {{ user.email }}.
+
+Vous pouvez télécharger la feuille des inscrits mise à jour sur [le site d'administration]({{ editionform_admin_url }}).
+{% endblock %}
diff --git a/projects/templates/projects/organizers_participation_deleted.md b/projects/templates/projects/organizers_participation_deleted.md
new file mode 100644
index 0000000000000000000000000000000000000000..edd36920c1658e8a618743e69cf106b74e8c3fa0
--- /dev/null
+++ b/projects/templates/projects/organizers_participation_deleted.md
@@ -0,0 +1,9 @@
+{% extends 'projects/to_user.md' %}
+
+{% block body %}
+L'utilisateur {{ user }} a annulé sa participation à {{ edition }}.
+
+Si besoin, vous pouvez contacter {{ user }} via son adresse email : {{ user.email }}.
+
+Vous pouvez télécharger la feuille des inscrits mise à jour sur [le site d'administration]({{ editionform_admin_url }}).
+{% endblock %}
diff --git a/projects/templates/projects/organizers_participation_received.md b/projects/templates/projects/organizers_participation_received.md
new file mode 100644
index 0000000000000000000000000000000000000000..98f48d8199e7fc844e374be452a1185d17a09cea
--- /dev/null
+++ b/projects/templates/projects/organizers_participation_received.md
@@ -0,0 +1,7 @@
+{% extends 'projects/to_user.md' %}
+
+{% block body %}
+{{ user }} s'est inscrit à {{ edition }}.
+
+Vous pouvez télécharger la feuille des inscrits mise à jour sur [le site d'administration]({{ editionform_admin_url }}).
+{% endblock %}
diff --git a/projects/templates/projects/participation_accepted.md b/projects/templates/projects/participation_accepted.md
new file mode 100644
index 0000000000000000000000000000000000000000..2774311572aaee752fa24ee787c4da28197ae3b2
--- /dev/null
+++ b/projects/templates/projects/participation_accepted.md
@@ -0,0 +1,8 @@
+{% extends 'projects/to_user.md' %}
+
+{% block body %}
+
+Nous avons le plaisir de te confirmer ta participation à {{ edition }} ! 🎉
+
+Nous te recontacterons très prochainement pour te communiquer les derniers détails pratiques.
+{% endblock %}
diff --git a/projects/templates/projects/participation_cancelled.md b/projects/templates/projects/participation_cancelled.md
new file mode 100644
index 0000000000000000000000000000000000000000..58eef49ca2a93650c11608d5fb16a4019f019d42
--- /dev/null
+++ b/projects/templates/projects/participation_cancelled.md
@@ -0,0 +1,9 @@
+{% extends 'projects/to_user.md' %}
+
+{% block body %}
+Ta participation à {{ edition }} a bien été annulée.
+
+Si tu souhaites réactiver ta demande de participation, tu peux le faire
+avant le {{ edition.edition_form.deadline | date }} dans la section
+[Mes inscriptions]({{ edition.get_registration_url }}).
+{% endblock %}
diff --git a/projects/templates/projects/participation_deleted.md b/projects/templates/projects/participation_deleted.md
new file mode 100644
index 0000000000000000000000000000000000000000..def88a1caee4dcc3c06f462eaf2cf1743fd7229f
--- /dev/null
+++ b/projects/templates/projects/participation_deleted.md
@@ -0,0 +1,7 @@
+{% extends 'projects/to_user.md' %}
+
+{% block body %}
+Ta participation à {{ edition }} a bien été supprimée.
+
+Si tu souhaites finalement participer à ce projet, tu devras te réinscrire en te rendant sur [l'espace projets]({{ edition.get_projects_site_url }}).
+{% endblock %}
diff --git a/projects/templates/projects/participation_received.md b/projects/templates/projects/participation_received.md
new file mode 100644
index 0000000000000000000000000000000000000000..37609f270e1b49109c0b86e764ba7090c78f1ee5
--- /dev/null
+++ b/projects/templates/projects/participation_received.md
@@ -0,0 +1,20 @@
+{% extends 'projects/to_user.md' %}
+
+{% block body %}
+Tu as demandé t'inscrire à {{ edition }} via l'espace projets.
+
+Nous avons bien reçu ta demande et allons vérifier ton dossier dès que possible.
+
+{% if edition.edition_form.form.files.count %}
+📖 **Rappel** : l'inscription à ce projet nécessite de fournir des documents complémentaires.
+
+🔗 Tu peux télécharger ces documents à tout moment en te rendant dans la section [Mes inscriptions]({{ edition.get_registration_url }}).
+
+Fais-nous parvenir ces documents **impérativement avant le {{ edition.edition_form.deadline | date }}** à l'adresse suivante :
+
+{{ edition.edition_form.recipient.user.get_full_name }}  
+{{ edition.edition_form.recipient.address }}
+
+⚠️ Nous ne pourrons valider ton dossier qu'une fois ces documents reçus.
+{% endif %}
+{% endblock %}
diff --git a/projects/templates/projects/participation_rejected.md b/projects/templates/projects/participation_rejected.md
new file mode 100644
index 0000000000000000000000000000000000000000..e55a80cc09322d4754e880273f0cc7ec77bc8d66
--- /dev/null
+++ b/projects/templates/projects/participation_rejected.md
@@ -0,0 +1,9 @@
+{% extends 'projects/to_user.md' %}
+
+{% block body %}
+Nous avons le regret de t'annoncer qu'en raison du nombre de places limité,
+nous n'avons pas pu retenir ta demande de participation à {{ edition }}. 😔
+
+Ne t'en fais pas ! Tu as été placé sur la liste d'attente et
+nous te recontacterons si des places se libèrent suite à des désistements.
+{% endblock %}
diff --git a/projects/templates/projects/participation_valid.md b/projects/templates/projects/participation_valid.md
new file mode 100644
index 0000000000000000000000000000000000000000..f31c3b649cac2fa9ce734ec1ce2e47c832c9d3f8
--- /dev/null
+++ b/projects/templates/projects/participation_valid.md
@@ -0,0 +1,10 @@
+{% extends 'projects/to_user.md' %}
+
+{% block body %}
+Nous venons de valider ton dossier pour {{ edition }} : celui-ci est bien complet ! ✅
+
+Une fois la période des inscriptions terminée, nous étudierons chaque demande
+avec soin pour établir la liste des inscrits. Cela se fera à partir du {{ edition.edition_form.deadline | date }}.
+
+Nous te recontacterons alors pour te confirmer ta participation.
+{% endblock %}
diff --git a/projects/templates/projects/to_user.md b/projects/templates/projects/to_user.md
new file mode 100644
index 0000000000000000000000000000000000000000..00fc3dffc3de9c18ca092ddeaa54f288df36f031
--- /dev/null
+++ b/projects/templates/projects/to_user.md
@@ -0,0 +1,11 @@
+{% extends 'mails/notification.md' %}
+
+{% block greeting %}
+Bonjour{% if participation.user.first_name %} {{ participation.user.first_name }}{% endif %},
+{% endblock %}
+
+{% block signature %}
+À très bientôt,
+
+Les organisateurs
+{% endblock %}
diff --git a/projects/views.py b/projects/views.py
index 92e1e5a499e02d9d47043a9c1e87bc96dcfc21a2..95227ceff466924ce6c41de627afbb31235c9f23 100644
--- a/projects/views.py
+++ b/projects/views.py
@@ -197,25 +197,9 @@ class EditionViewSet(viewsets.ReadOnlyModelViewSet):
                         "profile_type": null,
                         "first_name": "John",
                         "last_name": "Doe",
-                        "gender": null,
-                        "phone_number": "+33 6 12 34 56 78",
-                        "date_of_birth": null,
                         "url": "http://localhost:8000/api/users/3/"
                     },
-                    "address": {
-                        "line1": "Rue de Rivoli",
-                        "line2": "",
-                        "post_code": "75001",
-                        "city": "Paris",
-                        "country": {
-                            "code": "FR",
-                            "name": "France"
-                        }
-                    },
                     "promotion": 2020,
-                    "tutoring_groups": [
-                        1
-                    ],
                     "url": "http://localhost:8000/api/tutors/1/"
                 }
             }
@@ -305,23 +289,9 @@ class EditionViewSet(viewsets.ReadOnlyModelViewSet):
                         "profile_type": null,
                         "first_name": "John",
                         "last_name": "Doe",
-                        "gender": null,
-                        "phone_number": "+33 6 12 34 56 78",
-                        "date_of_birth": null,
                         "url": "http://localhost:8000/api/users/3/"
                     },
-                    "address": {
-                        "line1": "Rue de Rivoli",
-                        "line2": "",
-                        "post_code": "75001",
-                        "city": "Paris",
-                        "country": {
-                            "code": "FR",
-                            "name": "France"
-                        }
-                    },
                     "promotion": 2020,
-                    "tutoring_groups": [1],
                     "url": "http://localhost:8000/api/tutors/1/"
                 },
                 "deadline": "2018-07-29",
diff --git a/register/admin.py b/register/admin.py
index a33c425f9f23a69c7f17bf666e0fd7e95955c1b0..fa128f3a0bc4369d2d89b303696a4ab1a3b155d1 100644
--- a/register/admin.py
+++ b/register/admin.py
@@ -1,36 +1,15 @@
 """Register admin panels."""
 
 from django.contrib import admin
-from django.utils.html import format_html
-from core.admin import AutocompleteAddressMixin
-from .models import Registration, EmergencyContact
+from .models import Registration
 
 # Register your models here.
 
 
 @admin.register(Registration)
-class RegistrationAdmin(AutocompleteAddressMixin, admin.ModelAdmin):
+class RegistrationAdmin(admin.ModelAdmin):
     """Admin panel for registrations."""
 
-    list_display = ('last_name', 'first_name', 'school', 'grade', 'submitted')
+    list_display = ('last_name', 'first_name', 'submitted')
     readonly_fields = ('submitted',)
-    list_filter = ('submitted', 'school', 'grade',)
-    autocomplete_fields = ('emergency_contact', 'school',)
-
-
-@admin.register(EmergencyContact)
-class EmergencyContactAdmin(admin.ModelAdmin):
-    """Admin panel for emergency contacts."""
-
-    list_display = ('last_name', 'first_name',
-                    'email', 'home_phone', 'mobile_phone', 'related_student',)
-    search_fields = ('last_name', 'first_name',)
-
-    def related_student(self, obj):
-        """Link to the contact's registration object."""
-        url = '/admin/register/registration/{}'.format(obj.registration.pk)
-        return format_html(
-            '<a href="{}">{}</a>',
-            url, str(obj.registration)
-        )
-    related_student.short_description = "Inscription administrative associée"
+    list_filter = ('submitted',)
diff --git a/register/factory.py b/register/factory.py
index 7d2b7145a713ccb72cf0391461b1455a0724258f..973d6e793e7b4abeef18708936f0b0eeef3b44fd 100644
--- a/register/factory.py
+++ b/register/factory.py
@@ -1,35 +1,13 @@
 """Register factories."""
 
-import random
-
 import factory
 import factory.django
 
-from core.factory import AddressFactory
 from utils import printable_only
 
 from . import models
 
 
-class EmergencyContactFactory(factory.DjangoModelFactory):
-    """Emergency contact object factory."""
-
-    class Meta:  # noqa
-        model = models.EmergencyContact
-
-    first_name = factory.Faker('first_name', locale='fr')
-    last_name = factory.Faker('last_name', locale='fr')
-
-    @factory.lazy_attribute
-    def email(self):
-        return '{}.{}@fake.net'.format(
-            printable_only(self.first_name.lower()),
-            printable_only(self.last_name.lower()))
-
-    home_phone = factory.Faker('phone_number', locale='fr')
-    mobile_phone = factory.Faker('phone_number', locale='fr')
-
-
 class RegistrationFactory(factory.DjangoModelFactory):
     """Registration object factory."""
 
@@ -46,13 +24,4 @@ class RegistrationFactory(factory.DjangoModelFactory):
             printable_only(self.first_name.lower()),
             printable_only(self.last_name.lower()))
 
-    phone = factory.Faker('phone_number', locale='fr')
-    date_of_birth = factory.Faker('past_date', start_date='-20y')
-    address = factory.SubFactory(AddressFactory)
-    emergency_contact = factory.SubFactory(EmergencyContactFactory)
-
-    @factory.lazy_attribute
-    def grade(self):
-        level = random.choice(['Seconde', 'Première', 'Terminale'])
-        section = random.choice(['S', 'L', 'ES', 'Pro'])
-        return f'{level} {section}'
+    phone_number = factory.Faker('phone_number', locale='fr')
diff --git a/register/migrations/0001_initial.py b/register/migrations/0001_initial.py
index 5f884ead0b5826979939dc2fc596bf6904c58c84..f8ff25f7dcf676246bdf3b324632b826f3254899 100644
--- a/register/migrations/0001_initial.py
+++ b/register/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.0.3 on 2018-04-07 21:04
+# Generated by Django 2.0.7 on 2018-09-11 17:38
 
 from django.db import migrations, models
 
@@ -15,14 +15,15 @@ class Migration(migrations.Migration):
             name='Registration',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('first_name', models.CharField(max_length=50, verbose_name='prénom')),
-                ('last_name', models.CharField(max_length=50, verbose_name='nom')),
-                ('email', models.EmailField(max_length=254, verbose_name='adresse email')),
-                ('phone', models.CharField(blank=True, max_length=15, null=True, verbose_name='téléphone')),
-                ('date_of_birth', models.DateField(null=True, verbose_name='date de naissance')),
-                ('submitted', models.DateTimeField(auto_now_add=True, verbose_name='envoyé le')),
+                ('first_name', models.CharField(help_text='Prénom du lycéen (50 caractères max)', max_length=50, verbose_name='prénom')),
+                ('last_name', models.CharField(help_text='Nom du lycéen (50 caracèteres max)', max_length=50, verbose_name='nom')),
+                ('email', models.EmailField(help_text='Adresse email personnelle du lycéen. Note : doit être une adresse mail valide.', max_length=254, verbose_name='adresse email')),
+                ('submitted', models.DateTimeField(auto_now_add=True, help_text="Date d'envoi du dossier d'inscription", verbose_name='envoyé le')),
+                ('validated', models.BooleanField(default=False, help_text="Cocher pour valider le dossier d'inscription. Le lycéen pourra alors avoir accès à toutes les fonctionnalités associées à son profil.", verbose_name='validé')),
             ],
             options={
+                'verbose_name': "dossier d'inscription",
+                'verbose_name_plural': "dossiers d'inscription",
                 'ordering': ('-submitted',),
             },
         ),
diff --git a/register/migrations/0002_auto_20180407_2314.py b/register/migrations/0002_auto_20180407_2314.py
deleted file mode 100644
index 4b0647839441dd30a4c3fda89bbccbd8612b94bc..0000000000000000000000000000000000000000
--- a/register/migrations/0002_auto_20180407_2314.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 2.0.3 on 2018-04-07 21:14
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('register', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.AlterModelOptions(
-            name='registration',
-            options={'ordering': ('-submitted',), 'verbose_name': 'inscription administrative', 'verbose_name_plural': 'inscriptions administratives'},
-        ),
-    ]
diff --git a/register/migrations/0002_registration_phone_number.py b/register/migrations/0002_registration_phone_number.py
new file mode 100644
index 0000000000000000000000000000000000000000..14f20a952682c759e377f468d83a8627d1172ab4
--- /dev/null
+++ b/register/migrations/0002_registration_phone_number.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.1.1 on 2018-09-20 08:11
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('register', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='registration',
+            name='phone_number',
+            field=models.CharField(default='', help_text='Numéro de téléphone du lycéen (20 caractères max)', max_length=20, verbose_name='téléphone'),
+        ),
+    ]
diff --git a/register/migrations/0003_auto_20180407_2342.py b/register/migrations/0003_auto_20180407_2342.py
deleted file mode 100644
index b156411d82df9d3db857559b34be250e839121fc..0000000000000000000000000000000000000000
--- a/register/migrations/0003_auto_20180407_2342.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.0.3 on 2018-04-07 21:42
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('register', '0002_auto_20180407_2314'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='registration',
-            name='phone',
-            field=models.CharField(blank=True, max_length=30, null=True, verbose_name='téléphone'),
-        ),
-    ]
diff --git a/register/migrations/0004_auto_20180408_1412.py b/register/migrations/0004_auto_20180408_1412.py
deleted file mode 100644
index 9075386599a8d1c7b9a45977006930b744fd3e1f..0000000000000000000000000000000000000000
--- a/register/migrations/0004_auto_20180408_1412.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# Generated by Django 2.0.3 on 2018-04-08 12:12
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('register', '0003_auto_20180407_2342'),
-    ]
-
-    operations = [
-        migrations.AlterModelOptions(
-            name='registration',
-            options={'ordering': ('-submitted',), 'verbose_name': "dossier d'inscription", 'verbose_name_plural': "dossiers d'inscription"},
-        ),
-        migrations.AlterField(
-            model_name='registration',
-            name='date_of_birth',
-            field=models.DateField(help_text='Date de naissance du lycéen', null=True, verbose_name='date de naissance'),
-        ),
-        migrations.AlterField(
-            model_name='registration',
-            name='email',
-            field=models.EmailField(help_text='Adresse email personnelle du lycéen (doit être une adresse mail valide)', max_length=254, verbose_name='adresse email'),
-        ),
-        migrations.AlterField(
-            model_name='registration',
-            name='first_name',
-            field=models.CharField(help_text='Prénom du lycéen (50 caractères max)', max_length=50, verbose_name='prénom'),
-        ),
-        migrations.AlterField(
-            model_name='registration',
-            name='last_name',
-            field=models.CharField(help_text='Nom du lycéen (50 caracèteres max)', max_length=50, verbose_name='nom'),
-        ),
-        migrations.AlterField(
-            model_name='registration',
-            name='phone',
-            field=models.CharField(blank=True, help_text="Numéro de téléphone personnel du lycéen (30 caracètres max). Note : le format n'est pas vérifié.", max_length=30, null=True, verbose_name='téléphone'),
-        ),
-        migrations.AlterField(
-            model_name='registration',
-            name='submitted',
-            field=models.DateTimeField(auto_now_add=True, help_text="Date d'envoi du dossier d'inscription", verbose_name='envoyé le'),
-        ),
-    ]
diff --git a/register/migrations/0005_auto_20180408_1416.py b/register/migrations/0005_auto_20180408_1416.py
deleted file mode 100644
index 21a2af050bf990bdc15284899ae1b98bd12e3894..0000000000000000000000000000000000000000
--- a/register/migrations/0005_auto_20180408_1416.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Generated by Django 2.0.3 on 2018-04-08 12:16
-
-import datetime
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('register', '0004_auto_20180408_1412'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='registration',
-            name='date_of_birth',
-            field=models.DateField(default=datetime.date(2018, 4, 8), help_text='Date de naissance du lycéen', verbose_name='date de naissance'),
-            preserve_default=False,
-        ),
-    ]
diff --git a/register/migrations/0006_address.py b/register/migrations/0006_address.py
deleted file mode 100644
index 5c8d5eaa7c31b0d0af653b96c775815cd2f73460..0000000000000000000000000000000000000000
--- a/register/migrations/0006_address.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 2.0.3 on 2018-04-08 12:53
-
-from django.db import migrations, models
-import django_countries.fields
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('register', '0005_auto_20180408_1416'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='Address',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('line1', models.CharField(help_text='Numéro, voie, rue…', max_length=300, verbose_name='ligne 1')),
-                ('line2', models.CharField(blank=True, default='', help_text='Résidence, appartement, lieu-dit…', max_length=300, verbose_name='ligne 2')),
-                ('post_code', models.CharField(help_text="Code postal. Note : le format n'est pas vérifié.", max_length=20, verbose_name='code postal')),
-                ('city', models.CharField(help_text='Ville', max_length=100, verbose_name='ville')),
-                ('country', django_countries.fields.CountryField(default='FR', help_text='Pays (FR par défaut).', max_length=2)),
-            ],
-        ),
-    ]
diff --git a/register/migrations/0007_auto_20180408_1457.py b/register/migrations/0007_auto_20180408_1457.py
deleted file mode 100644
index 075ad46ce1bca6738ebdc695e318836624524147..0000000000000000000000000000000000000000
--- a/register/migrations/0007_auto_20180408_1457.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Django 2.0.3 on 2018-04-08 12:57
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('register', '0006_address'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='registration',
-            name='address',
-            field=models.ForeignKey(blank=True, help_text='Adresse du lycéen', null=True, on_delete=django.db.models.deletion.CASCADE, to='register.Address'),
-        ),
-        migrations.AddField(
-            model_name='registration',
-            name='emergency_contact',
-            field=models.CharField(blank=True, help_text="Contact en cas d'urgence Exemple : adresse mail ou numéro de téléphone d'un parent.", max_length=100, null=True, verbose_name="contact d'urgence"),
-        ),
-    ]
diff --git a/register/migrations/0008_auto_20180408_1518.py b/register/migrations/0008_auto_20180408_1518.py
deleted file mode 100644
index e5b81ba9d2c7d2e7bd51eff2c22546d5e9f93b96..0000000000000000000000000000000000000000
--- a/register/migrations/0008_auto_20180408_1518.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Generated by Django 2.0.3 on 2018-04-08 13:18
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('register', '0007_auto_20180408_1457'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='registration',
-            name='address',
-            field=models.ForeignKey(blank=True, help_text='Adresse du lycéen', null=True, on_delete=django.db.models.deletion.CASCADE, to='core.Address'),
-        ),
-        migrations.DeleteModel(
-            name='Address',
-        ),
-    ]
diff --git a/register/migrations/0009_auto_20180408_1525.py b/register/migrations/0009_auto_20180408_1525.py
deleted file mode 100644
index 9931621ead48edc0c52106f7b313cb8b2f07d430..0000000000000000000000000000000000000000
--- a/register/migrations/0009_auto_20180408_1525.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Generated by Django 2.0.3 on 2018-04-08 13:25
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('register', '0008_auto_20180408_1518'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='registration',
-            name='address',
-            field=models.ForeignKey(blank=True, help_text='Adresse du lycéen', null=True, on_delete=django.db.models.deletion.CASCADE, to='core.Address', verbose_name='adresse'),
-        ),
-        migrations.AlterField(
-            model_name='registration',
-            name='email',
-            field=models.EmailField(help_text='Adresse email personnelle du lycéen. Note : doit être une adresse mail valide.', max_length=254, verbose_name='adresse email'),
-        ),
-        migrations.AlterField(
-            model_name='registration',
-            name='emergency_contact',
-            field=models.CharField(blank=True, help_text="Contact en cas d'urgence (100 caractères max). Exemple : adresse mail ou numéro de téléphone d'un parent.", max_length=100, null=True, verbose_name="contact d'urgence"),
-        ),
-    ]
diff --git a/register/migrations/0010_auto_20180414_0000.py b/register/migrations/0010_auto_20180414_0000.py
deleted file mode 100644
index 3eb0211bc527310367cd8cc7ea455a66fd8c75fc..0000000000000000000000000000000000000000
--- a/register/migrations/0010_auto_20180414_0000.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Generated by Django 2.0.3 on 2018-04-13 22:00
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('register', '0009_auto_20180408_1525'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='EmergencyContact',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('first_name', models.CharField(help_text='Prénom du contact (50 caractères max).', max_length=50, verbose_name='prénom')),
-                ('last_name', models.CharField(help_text='Nom du contact (50 caractères max).', max_length=50, verbose_name='nom')),
-                ('contact', models.CharField(help_text='Téléphone, adresse email…', max_length=100)),
-            ],
-            options={
-                'verbose_name': "contact d'urgence",
-                'verbose_name_plural': "contacts d'urgence",
-                'ordering': ('last_name', 'first_name'),
-            },
-        ),
-        migrations.AlterField(
-            model_name='registration',
-            name='emergency_contact',
-            field=models.ForeignKey(blank=True, help_text="Contact en cas d'urgence.", null=True, on_delete=django.db.models.deletion.CASCADE, to='register.EmergencyContact', verbose_name="contact d'urgence"),
-        ),
-    ]
diff --git a/register/migrations/0011_auto_20180414_0010.py b/register/migrations/0011_auto_20180414_0010.py
deleted file mode 100644
index 73bbea6416af40926d7f684dc611f4c9904b977b..0000000000000000000000000000000000000000
--- a/register/migrations/0011_auto_20180414_0010.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.0.3 on 2018-04-13 22:10
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('register', '0010_auto_20180414_0000'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='emergencycontact',
-            name='first_name',
-            field=models.CharField(blank=True, default='', help_text='Prénom du contact (50 caractères max).', max_length=50, verbose_name='prénom'),
-        ),
-        migrations.AlterField(
-            model_name='emergencycontact',
-            name='last_name',
-            field=models.CharField(blank=True, default='', help_text='Nom du contact (50 caractères max).', max_length=50, verbose_name='nom'),
-        ),
-    ]
diff --git a/register/migrations/0012_auto_20180414_0016.py b/register/migrations/0012_auto_20180414_0016.py
deleted file mode 100644
index d889829098515e0272d8738d9c5fbf07d56dec92..0000000000000000000000000000000000000000
--- a/register/migrations/0012_auto_20180414_0016.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.0.3 on 2018-04-13 22:16
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('register', '0011_auto_20180414_0010'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='registration',
-            name='emergency_contact',
-            field=models.OneToOneField(blank=True, help_text="Contact en cas d'urgence.", null=True, on_delete=django.db.models.deletion.CASCADE, to='register.EmergencyContact', verbose_name="contact d'urgence"),
-        ),
-    ]
diff --git a/register/migrations/0013_auto_20180505_1046.py b/register/migrations/0013_auto_20180505_1046.py
deleted file mode 100644
index a079b8ebf39b55c6285d3add9782050d4d08ec72..0000000000000000000000000000000000000000
--- a/register/migrations/0013_auto_20180505_1046.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-05 08:46
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('register', '0012_auto_20180414_0016'),
-    ]
-
-    operations = [
-        migrations.RemoveField(
-            model_name='emergencycontact',
-            name='contact',
-        ),
-        migrations.AddField(
-            model_name='emergencycontact',
-            name='email',
-            field=models.EmailField(blank=True, max_length=254, null=True, verbose_name='adresse email'),
-        ),
-        migrations.AddField(
-            model_name='emergencycontact',
-            name='home_phone',
-            field=models.CharField(blank=True, max_length=50, null=True, verbose_name='téléphone fixe'),
-        ),
-        migrations.AddField(
-            model_name='emergencycontact',
-            name='mobile_phone',
-            field=models.CharField(blank=True, max_length=50, null=True, verbose_name='téléphone portable'),
-        ),
-        migrations.AlterField(
-            model_name='emergencycontact',
-            name='first_name',
-            field=models.CharField(help_text='Prénom du contact (50 caractères max).', max_length=50, verbose_name='prénom'),
-        ),
-        migrations.AlterField(
-            model_name='emergencycontact',
-            name='last_name',
-            field=models.CharField(help_text='Nom du contact (50 caractères max).', max_length=50, verbose_name='nom'),
-        ),
-    ]
diff --git a/register/migrations/0014_registration_validated.py b/register/migrations/0014_registration_validated.py
deleted file mode 100644
index df488838ff6ef139016a375cccceb08c5b69ee70..0000000000000000000000000000000000000000
--- a/register/migrations/0014_registration_validated.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-05 12:01
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('register', '0013_auto_20180505_1046'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='registration',
-            name='validated',
-            field=models.BooleanField(default=False, help_text="Cocher pour valider le dossier d'inscription. Le lycéen pourra alors avoir accès à toutes les fonctionnalités associées à son profil.", verbose_name='validé'),
-        ),
-    ]
diff --git a/register/migrations/0015_auto_20180505_1403.py b/register/migrations/0015_auto_20180505_1403.py
deleted file mode 100644
index 7df3903f547fef9da4f70b09f993091a0a6d9e50..0000000000000000000000000000000000000000
--- a/register/migrations/0015_auto_20180505_1403.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-05 12:03
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('register', '0014_registration_validated'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='registration',
-            name='date_of_birth',
-            field=models.DateField(blank=True, help_text='Date de naissance du lycéen', null=True, verbose_name='date de naissance'),
-        ),
-    ]
diff --git a/register/migrations/0016_auto_20180512_1047.py b/register/migrations/0016_auto_20180512_1047.py
deleted file mode 100644
index 1653bbadc81706aa6734d122b505aee3f7c1b977..0000000000000000000000000000000000000000
--- a/register/migrations/0016_auto_20180512_1047.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-12 08:47
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('tutoring', '0004_auto_20180429_1159'),
-        ('register', '0015_auto_20180505_1403'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='registration',
-            name='level',
-            field=models.CharField(blank=True, help_text='Classe du lycée (texte libre)', max_length=200, null=True),
-        ),
-        migrations.AddField(
-            model_name='registration',
-            name='school',
-            field=models.ForeignKey(blank=True, help_text='Lycée du lycéen', null=True, on_delete=django.db.models.deletion.SET_NULL, to='tutoring.School', verbose_name='lycée'),
-        ),
-    ]
diff --git a/register/migrations/0017_auto_20180512_1050.py b/register/migrations/0017_auto_20180512_1050.py
deleted file mode 100644
index e603ee30d3b36a5827cb9c36c76e25e18a8976d9..0000000000000000000000000000000000000000
--- a/register/migrations/0017_auto_20180512_1050.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-12 08:50
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('register', '0016_auto_20180512_1047'),
-    ]
-
-    operations = [
-        migrations.RemoveField(
-            model_name='registration',
-            name='level',
-        ),
-        migrations.AddField(
-            model_name='registration',
-            name='grade',
-            field=models.CharField(blank=True, help_text='Classe/filière du lycéen (texte libre)', max_length=200, null=True),
-        ),
-    ]
diff --git a/register/migrations/0018_auto_20180512_1112.py b/register/migrations/0018_auto_20180512_1112.py
deleted file mode 100644
index 07bcbcde3e33abec686d511159716956bd5f0445..0000000000000000000000000000000000000000
--- a/register/migrations/0018_auto_20180512_1112.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-12 09:12
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('register', '0017_auto_20180512_1050'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='registration',
-            name='grade',
-            field=models.CharField(blank=True, help_text='Classe/filière du lycéen (texte libre)', max_length=200, null=True, verbose_name='classe'),
-        ),
-    ]
diff --git a/register/models.py b/register/models.py
index 2d465803d846b27e872bffbadaf07d4d9d45d663..d4966c41200ee3a37b9b324b75b5519205b6dbbb 100644
--- a/register/models.py
+++ b/register/models.py
@@ -20,38 +20,11 @@ class Registration(models.Model):
             'Adresse email personnelle du lycéen. '
             'Note : doit être une adresse mail valide.'
         ))
-    phone = models.CharField(
-        max_length=30, blank=True, null=True, verbose_name='téléphone',
-        help_text=(
-            "Numéro de téléphone personnel du lycéen (30 caracètres max). "
-            "Note : le format n'est pas vérifié."
-        ))
-    date_of_birth = models.DateField(
-        verbose_name='date de naissance',
-        help_text="Date de naissance du lycéen",
-        blank=True, null=True
-    )
-    address = models.ForeignKey(
-        'core.Address', on_delete=models.CASCADE, blank=True, null=True,
-        verbose_name='adresse',
-        help_text="Adresse du lycéen")
-    school = models.ForeignKey(
-        'tutoring.School', on_delete=models.SET_NULL, null=True, blank=True,
-        verbose_name='lycée',
-        help_text='Lycée du lycéen',
+    phone_number = models.CharField(
+        max_length=20, verbose_name='téléphone',
+        help_text='Numéro de téléphone du lycéen (20 caractères max)',
+        blank=False, default='',
     )
-    grade = models.CharField(
-        max_length=200,
-        verbose_name='classe',
-        help_text='Classe/filière du lycéen (texte libre)',
-        blank=True,
-        null=True,
-    )
-    emergency_contact = models.OneToOneField(
-        'EmergencyContact',
-        on_delete=models.CASCADE, blank=True, null=True,
-        verbose_name="contact d'urgence",
-        help_text="Contact en cas d'urgence.")
     submitted = models.DateTimeField(
         auto_now_add=True, verbose_name='envoyé le',
         help_text="Date d'envoi du dossier d'inscription")
@@ -86,37 +59,3 @@ class Registration(models.Model):
 
     def __str__(self):
         return '{o.full_name} ({o.submitted})'.format(o=self)
-
-
-class EmergencyContact(models.Model):
-    """Represents an emergency contact for a student."""
-
-    first_name = models.CharField(
-        'prénom', max_length=50,
-        help_text='Prénom du contact (50 caractères max).'
-    )
-    last_name = models.CharField(
-        'nom', max_length=50,
-        help_text='Nom du contact (50 caractères max).'
-    )
-    email = models.EmailField(
-        verbose_name='adresse email',
-        blank=True, null=True,
-    )
-    home_phone = models.CharField(
-        'téléphone fixe', max_length=50,
-        blank=True, null=True,
-    )
-    mobile_phone = models.CharField(
-        'téléphone portable', max_length=50,
-        blank=True, null=True,
-    )
-
-    def __str__(self):
-        """Represent the emergency contact by its full name."""
-        return '{o.first_name} {o.last_name}'.format(o=self)
-
-    class Meta:  # noqa
-        verbose_name = "contact d'urgence"
-        verbose_name_plural = "contacts d'urgence"
-        ordering = ('last_name', 'first_name',)
diff --git a/register/serializers.py b/register/serializers.py
index 3e8c1b4bec14d1d425fb280480fb79d0baef0b65..01cea366ada0994d6e2b0d6ef2bd5ea185d32e11 100644
--- a/register/serializers.py
+++ b/register/serializers.py
@@ -1,30 +1,15 @@
 """Register serializers."""
 
 from django.contrib.auth import get_user_model
-from django.db import transaction
 from rest_framework import serializers
 
-from core.models import Address
-from core.serializers import AddressSerializer
-from tutoring.models import School
-from profiles.models import Student
-
-from .models import EmergencyContact, Registration
+from .models import Registration
 from .signals import registration_created
 
 
 User = get_user_model()
 
 
-class EmergencyContactSerializer(serializers.ModelSerializer):
-    """Serializer for emergency contacts."""
-
-    class Meta:  # noqa
-        model = EmergencyContact
-        fields = ('first_name', 'last_name',
-                  'email', 'home_phone', 'mobile_phone')
-
-
 class RegistrationSerializer(serializers.ModelSerializer):
     """Serializer for documents."""
 
@@ -33,28 +18,13 @@ class RegistrationSerializer(serializers.ModelSerializer):
         write_only=True,
         style={'input_type': 'password'},
     )
-    school = serializers.PrimaryKeyRelatedField(
-        label='Lycée',
-        help_text='Lycée du lycéen',
-        queryset=School.objects.all(),
-        required=False,
-        allow_null=True,
-    )
-    address = AddressSerializer(
-        required=False,
-        help_text="Adresse du lycéen")
-    emergency_contact = EmergencyContactSerializer(
-        required=False,
-        help_text="Contact en cas d'urgence")
     validated = serializers.HiddenField(default=False)
 
     class Meta:  # noqa
         model = Registration
         fields = ('id', 'email', 'password',
-                  'first_name', 'last_name', 'date_of_birth', 'phone',
-                  'school', 'grade',
-                  'submitted', 'validated',
-                  'address', 'emergency_contact',)
+                  'first_name', 'last_name', 'phone_number',
+                  'submitted', 'validated',)
 
         extra_kwargs = {
             'submitted': {'read_only': True},
@@ -74,43 +44,15 @@ class RegistrationSerializer(serializers.ModelSerializer):
         - Build/save a user and a student profile
         """
         password = validated_data.pop('password')
-        address_data = validated_data.pop('address', None)
-        emergency_contact_data = validated_data.pop('emergency_contact', None)
-
-        # The following block will create a bunch of objects and save them
-        # in the database. We don't want them to be saved separately.
-        # => Use an atomic transaction to not save anything in case an
-        # exception is raised.
-        # (Hint: it disables the autocommit mode and commit all queries at
-        # the end of the "with" block.)
-        # See the Django docs on atomic transactions for more info.
-        with transaction.atomic():
-
-            # Create the address if given
-            if address_data:
-                address = Address.objects.create(**address_data)
-            else:
-                address = None
-
-            # Create the emergency contact if given
-            if emergency_contact_data:
-                emergency_contact = EmergencyContact.objects.create(
-                    **emergency_contact_data)
-            else:
-                emergency_contact = None
-
-            registration = Registration.objects.create(
-                **validated_data,
-                address=address,
-                emergency_contact=emergency_contact,
-            )
-
-            # Fire a registration_created signal
-            registration_created.send(
-                sender=Registration,
-                instance=registration,
-                password=password,
-            )
+
+        registration = Registration.objects.create(**validated_data)
+
+        # Fire a registration_created signal
+        registration_created.send(
+            sender=Registration,
+            instance=registration,
+            password=password,
+        )
 
         return registration
 
diff --git a/register/signals.py b/register/signals.py
index 81e77a2a4dcc3c2ddc0889dd88423aa279acb9fa..05723916545d82fbd1d52383913b6884eefc2e92 100644
--- a/register/signals.py
+++ b/register/signals.py
@@ -22,12 +22,10 @@ def create_user_and_student(sender, instance: Registration,
         password=password,
         first_name=instance.first_name,
         last_name=instance.last_name,
-        date_of_birth=instance.date_of_birth,
-        phone_number=instance.phone,
+        phone_number=instance.phone_number,
     )
 
     Student.objects.create(
         user=user,
-        school=instance.school,
         registration=instance,
     )
diff --git a/register/views.py b/register/views.py
index 1ff4028b4185130e0795e7ee31af2f32aad0f48f..f7efb2810e2ea485f7a25bde791f1adaf4cacf79 100644
--- a/register/views.py
+++ b/register/views.py
@@ -26,28 +26,7 @@ class RegistrationViewSet(
                 "email": "charles.dumont@example.net",
                 "first_name": "Charles",
                 "last_name": "Dumont",
-                "date_of_birth": null,
-                "phone": null,
-                "school": "0930965U",
-                "grade": "Première S",
                 "submitted": "2018-05-05T14:15:10.998206+02:00",
-                "address": {
-                    "line1": "88 bis rue Jules Guesde",
-                    "line2": "",
-                    "post_code": "93100",
-                    "city": "Montreuil",
-                    "country": {
-                        "code": "FR",
-                        "name": "France"
-                    }
-                },
-                "emergency_contact": {
-                    "first_name": "Marie-Claude",
-                    "last_name": "Perret",
-                    "email": null,
-                    "home_phone": "+33312344556",
-                    "mobile_phone": null
-                }
             }
         ]
 
@@ -60,49 +39,6 @@ class RegistrationViewSet(
     1. Create a user for the student
     2. Create the registration
     3. Create a student profile and link it to the user and the registration.
-
-    ### Date of birth
-
-    Date of birth must be sent in a ISO-compliant format (in Javascript,
-    `Date.toISOString()` can be used for this).
-
-    ### Address
-
-    Address format is the following :
-
-        {
-            "line1": "...",
-            "line2": "...",
-            "post_code": "...",
-            "city": "...",
-            "country": "..."
-        }
-
-    `line2` : optional, default is `""`.
-
-    `country` : optional, default is `"FR"`. Must be given as a
-    [country code](https://en.wikipedia.org/wiki/Country_code).
-
-    ### Emergency contact
-
-    Emergency contact format is the following:
-
-        {
-            "first_name": "...",
-            "last_name": "...",
-            "email": "...",
-            "home_phone": "...",
-            "mobile_phone": "..."
-        }
-
-    `email` must be a valid email address. Phone format for `home_phone`
-    and `mobile_phone` is not verified.
-
-    ### School
-
-    The value given must be the school's `uai_code`.
-    You can retrieve the list of available schools thanks to the
-    [schools-choices](#schools-choices) endpoint.
     """
 
     queryset = Registration.objects.all()
diff --git a/requirements.txt b/requirements.txt
index 0db00a41c89108fbf81dbe23c667dceb1341cdb6..276fb91beef51d4861124dc26ce65bebd897a9ef 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -16,7 +16,6 @@ gunicorn
 django-storages
 boto3
 whitenoise
-celery[redis]
 django-countries
 django-sendgrid-v5
 django-filter
diff --git a/supervisord.conf b/supervisord.conf
deleted file mode 100644
index f01181a18e936e6f5d42311e2fb045c16d82d276..0000000000000000000000000000000000000000
--- a/supervisord.conf
+++ /dev/null
@@ -1,18 +0,0 @@
-[supervisord]
-
-[supervisorctl]
-
-[inet_http_server]
-port = 127.0.0.1:9001
-
-[rpcinterface:supervisor]
-supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
-
-[program:redis]
-command = redis-server
-
-[program:celery]
-command = celery -A oser_backend worker --beat -l info
-stdout_logfile=celery.log
-stderr_logfile=celery.log
-autorestart=true
diff --git a/tests/test_core/test_tasks.py b/tests/test_core/test_tasks.py
deleted file mode 100644
index 1b36bdfc87e4f2ae3c874e3cc726595a70b5b97c..0000000000000000000000000000000000000000
--- a/tests/test_core/test_tasks.py
+++ /dev/null
@@ -1,17 +0,0 @@
-"""Test Celery tasks."""
-
-from django.test import TestCase
-from celery.exceptions import TimeoutError
-
-from core.tasks import cleanmedia
-
-
-class CleanMediaTaskTest(TestCase):
-    """Test the cleanmedia Celery task."""
-
-    def test_run_task(self):
-        try:
-            cleanmedia.delay().get(timeout=2)
-        except TimeoutError as e:
-            message = str(e) + ' Is the Celery worker running?'
-            raise TimeoutError(message)
diff --git a/tests/test_profiles/test_student.py b/tests/test_profiles/test_student.py
index 7ca9935d57c7c7017f225f0ecc61362827c6e2f9..0d9d65fc47e1619475c78325b8eb82f4af3641b2 100644
--- a/tests/test_profiles/test_student.py
+++ b/tests/test_profiles/test_student.py
@@ -1,9 +1,8 @@
 """Student model tests."""
 
-from profiles.factory import StudentInTutoringGroupFactory
+from profiles.factory import StudentFactory
 from profiles.models import Student
 from tests.utils import ModelTestCase
-from tutoring.models import School, TutoringGroup
 from users.factory import UserFactory
 
 
@@ -15,16 +14,6 @@ class StudentTestCase(ModelTestCase):
         'user': {
             'verbose_name': 'utilisateur',
         },
-        'tutoring_group': {
-            'verbose_name': 'groupe de tutorat',
-            'null': True,
-            'blank': True,
-        },
-        'school': {
-            'verbose_name': 'lycée',
-            'null': True,
-            'blank': True,
-        },
         'registration': {
             'verbose_name': "dossier d'inscription",
             'null': True,
@@ -37,19 +26,11 @@ class StudentTestCase(ModelTestCase):
 
     @classmethod
     def setUpTestData(self):
-        self.obj = StudentInTutoringGroupFactory.create()
+        self.obj = StudentFactory.create()
 
     def test_user_relationship(self):
         self.assertEqual(self.obj, self.obj.user.student)
 
-    def test_school_relationship(self):
-        self.assertEqual(School.objects.get(), self.obj.school)
-        self.assertIn(self.obj, School.objects.get().students.all())
-
-    def test_tutoring_group_relationship(self):
-        self.assertEqual(TutoringGroup.objects.get(), self.obj.tutoring_group)
-        self.assertIn(self.obj, TutoringGroup.objects.get().students.all())
-
     def test_get_absolute_url(self):
         self.client.force_login(UserFactory.create())
         url = self.obj.get_absolute_url()
diff --git a/tests/test_profiles/test_student_api.py b/tests/test_profiles/test_student_api.py
index 47237616dec3accdb997655ed8055521f543a782..204649b47eadba66ce7be8a9e17c09568244d664 100644
--- a/tests/test_profiles/test_student_api.py
+++ b/tests/test_profiles/test_student_api.py
@@ -1,7 +1,7 @@
 """Student API tests."""
 from rest_framework import status
 
-from profiles.factory import StudentInTutoringGroupFactory
+from profiles.factory import StudentFactory
 from profiles.serializers import StudentSerializer
 from tests.utils.api import HyperlinkedAPITestCase
 
@@ -9,7 +9,7 @@ from tests.utils.api import HyperlinkedAPITestCase
 class StudentEndpointsTest(HyperlinkedAPITestCase):
     """Test access to the students endpoints."""
 
-    factory = StudentInTutoringGroupFactory
+    factory = StudentFactory
     serializer_class = StudentSerializer
 
     def perform_list(self):
@@ -31,14 +31,3 @@ class StudentEndpointsTest(HyperlinkedAPITestCase):
         self.assertRequiresAuth(
             self.perform_retrieve,
             expected_status_code=status.HTTP_200_OK)
-
-    def test_retrieve_tutoring_group(self):
-        def perform_retrieve_tutoring_group():
-            obj = self.factory.create()
-            response = self.client.get(
-                '/api/students/{}/tutoringgroup/'.format(obj.pk))
-            return response
-
-        self.assertRequiresAuth(
-            perform_retrieve_tutoring_group,
-            expected_status_code=status.HTTP_200_OK)
diff --git a/tests/test_profiles/test_tutor_api.py b/tests/test_profiles/test_tutor_api.py
index 154181776463b8e06ec6ddc9c3b32d26396b3f51..901c57a9daa76956f48c4e610d3647bfc3614c96 100644
--- a/tests/test_profiles/test_tutor_api.py
+++ b/tests/test_profiles/test_tutor_api.py
@@ -4,7 +4,6 @@ from rest_framework import status
 from profiles.factory import TutorFactory
 from profiles.serializers import TutorSerializer
 from tests.utils.api import HyperlinkedAPITestCase
-from tutoring.factory import TutorTutoringGroupFactory
 
 
 class TutorEndpointsTest(HyperlinkedAPITestCase):
@@ -32,16 +31,3 @@ class TutorEndpointsTest(HyperlinkedAPITestCase):
         self.assertRequiresAuth(
             self.perform_retrieve,
             expected_status_code=status.HTTP_200_OK)
-
-    def test_list_tutoring_groups(self):
-        def perform_list_tutoring_groups():
-            obj = self.factory.create()
-            # add tutor to several tutoring groups
-            TutorTutoringGroupFactory.create_batch(3, tutor=obj)
-            url = '/api/tutors/{}/tutoringgroups/'.format(obj.pk)
-            response = self.client.get(url)
-            return response
-
-        self.assertRequiresAuth(
-            perform_list_tutoring_groups,
-            expected_status_code=status.HTTP_200_OK)
diff --git a/tests/test_projects/test_signals.py b/tests/test_projects/test_signals.py
new file mode 100644
index 0000000000000000000000000000000000000000..52649c99a6fd000c75b5240a26935a4ccbfdd3e1
--- /dev/null
+++ b/tests/test_projects/test_signals.py
@@ -0,0 +1,69 @@
+"""Test the projects app signals."""
+
+from django.test import TestCase
+from django.utils.timezone import now
+from tests.utils.mixins import SignalTestMixin
+
+from dynamicforms.models import Form
+from profiles.factory import TutorFactory
+from projects.factory import (EditionFactory, ParticipationFactory,
+                              ProjectFactory)
+from projects.models import EditionForm, Participation, EditionOrganizer
+from projects.signals import (accepted, cancelled, deleted, pending, rejected,
+                              valid)
+
+
+class NotifyParticipationTest(SignalTestMixin, TestCase):
+    """Test project participation signal handlers."""
+
+    def setUp(self):
+        # Create all objects that need to exist for rendering emails
+        project = ProjectFactory.create(name='Focus Europe')
+        recipient = TutorFactory.create()
+        self.edition = EditionFactory.create(project=project, year=2018)
+        EditionOrganizer.objects.create(
+            user=recipient.user,
+            edition=self.edition)
+        form = Form.objects.create(title=f'Inscriptions à {self.edition}')
+        EditionForm.objects.create(
+            edition=self.edition,
+            form=form,
+            recipient=recipient,
+            deadline=now(),
+        )
+        self.obj = self.create_obj()
+
+    def create_obj(self):
+        return ParticipationFactory.create(edition=self.edition)
+
+    def change(self, state):
+        self.obj.state = state
+        self.obj.save()
+
+    def test_create_participation_pending_called(self):
+        with self.assertCalled(pending):
+            self.create_obj()
+
+    def test_save_without_changing_state_not_called(self):
+        with self.assertNotCalled(pending):
+            self.obj.save()
+
+    def test_valid_called(self):
+        with self.assertCalled(valid):
+            self.change(Participation.STATE_VALIDATED)
+
+    def test_accepted_called(self):
+        with self.assertCalled(accepted):
+            self.change(Participation.STATE_ACCEPTED)
+
+    def test_rejected_called(self):
+        with self.assertCalled(rejected):
+            self.change(Participation.STATE_REJECTED)
+
+    def test_cancelled_called(self):
+        with self.assertCalled(cancelled):
+            self.change(Participation.STATE_CANCELLED)
+
+    def test_deleted_called(self):
+        with self.assertCalled(deleted):
+            self.obj.delete()
diff --git a/tests/test_register/test_emergency_contact.py b/tests/test_register/test_emergency_contact.py
deleted file mode 100644
index 80a9748c83f45cfcb554d67ebc5a2f0086dd5639..0000000000000000000000000000000000000000
--- a/tests/test_register/test_emergency_contact.py
+++ /dev/null
@@ -1,49 +0,0 @@
-"""EmergencyContact model tests."""
-
-from register.models import EmergencyContact
-from register.factory import EmergencyContactFactory
-from tests.utils import ModelTestCase
-
-
-class EmergencyContactTest(ModelTestCase):
-    """Test the EmergencyContact model."""
-
-    model = EmergencyContact
-    field_tests = {
-        'first_name': {
-            'max_length': 50,
-            'verbose_name': 'prénom',
-        },
-        'last_name': {
-            'max_length': 50,
-            'verbose_name': 'nom',
-        },
-        'email': {
-            'verbose_name': 'adresse email',
-            'blank': True,
-            'null': True,
-        },
-        'home_phone': {
-            'verbose_name': 'téléphone fixe',
-            'blank': True,
-            'null': True,
-        },
-        'mobile_phone': {
-            'verbose_name': 'téléphone portable',
-            'blank': True,
-            'null': True,
-        },
-    }
-    model_tests = {
-        'ordering': ('last_name', 'first_name',),
-        'verbose_name': "contact d'urgence",
-        'verbose_name_plural': "contacts d'urgence",
-    }
-
-    @classmethod
-    def setUpTestData(cls):
-        cls.obj = EmergencyContactFactory.create()
-
-    def test_str(self):
-        expected = '{o.first_name} {o.last_name}'.format(o=self.obj)
-        self.assertEqual(expected, str(self.obj))
diff --git a/tests/test_register/test_registration.py b/tests/test_register/test_registration.py
index b1dd44e2c5ad57a652fe41d0fe1c9d8a56f88c90..2691697bace11bb8c897d7ed1cafab2512f1c4c5 100644
--- a/tests/test_register/test_registration.py
+++ b/tests/test_register/test_registration.py
@@ -18,17 +18,6 @@ class RegistrationTest(ModelTestCase):
             'max_length': 50,
             'verbose_name': 'nom',
         },
-        'date_of_birth': {
-            'blank': True,
-            'null': True,
-            'verbose_name': 'date de naissance',
-        },
-        'phone': {
-            'max_length': 30,
-            'blank': True,
-            'null': True,
-            'verbose_name': 'téléphone',
-        },
         'email': {
             'verbose_name': 'adresse email',
         },
@@ -40,16 +29,6 @@ class RegistrationTest(ModelTestCase):
             'verbose_name': 'validé',
             'default': False,
         },
-        'address': {
-            'blank': True,
-            'null': True,
-            'verbose_name': 'adresse',
-        },
-        'emergency_contact': {
-            'blank': True,
-            'null': True,
-            'verbose_name': "contact d'urgence",
-        },
     }
     model_tests = {
         'ordering': ('-submitted',),
diff --git a/tests/test_register/test_registration_api.py b/tests/test_register/test_registration_api.py
index aaab7080792b267a47ba3024a46446c63ef92597..84389b07a62ddc8e5a6f557827bf1798fdfba1ba 100644
--- a/tests/test_register/test_registration_api.py
+++ b/tests/test_register/test_registration_api.py
@@ -4,14 +4,10 @@ from django.contrib.auth import get_user_model
 from rest_framework import status
 from tests.utils import SimpleAPITestCase
 
-from core.factory import AddressFactory
-from core.serializers import AddressSerializer
 from profiles.models import Student
-from register.factory import EmergencyContactFactory, RegistrationFactory
+from register.factory import RegistrationFactory
 from register.models import Registration
-from register.serializers import (EmergencyContactSerializer,
-                                  RegistrationSerializer)
-from tutoring.factory import SchoolFactory, TutoringGroupFactory
+from register.serializers import RegistrationSerializer
 from users.factory import UserFactory
 
 User = get_user_model()
@@ -42,6 +38,7 @@ class RegistrationCreateTest(SimpleAPITestCase):
 
     There is some complex logic which requires a few dedicated tests.
     """
+
     factory = RegistrationFactory
     serializer_class = RegistrationSerializer
 
@@ -80,40 +77,6 @@ class RegistrationCreateTest(SimpleAPITestCase):
         user = User.objects.get(email=email)
         self.assertEqual(obj.student, Student.objects.get(user=user))
 
-    def test_create_with_address_and_emergency_contact(self):
-        data = self.get_create_data()
-        address = AddressFactory.build()
-        emergency_contact = EmergencyContactFactory.build()
-        data['address'] = AddressSerializer(address).data
-        data['emergency_contact'] = EmergencyContactSerializer(
-            emergency_contact).data
-
-        response = self._create(data)
-        self.assertEqual(response.status_code, status.HTTP_201_CREATED,
-                         response.data)
-
-        # Verify that address and emergency contact were set on registration
-        pk = response.data['id']
-        obj = Registration.objects.get(pk=pk)
-        self.assertEqual(obj.address.line1, address.line1)
-        self.assertEqual(obj.emergency_contact.first_name,
-                         emergency_contact.first_name)
-
-    def test_create_with_school(self):
-        school = SchoolFactory.create()
-        data = self.get_create_data()
-        data['school'] = school.pk
-
-        response = self._create(data)
-        self.assertEqual(response.status_code, status.HTTP_201_CREATED,
-                         response.data)
-
-        # Verify that school was set on registration and student
-        pk = response.data['id']
-        obj = Registration.objects.get(pk=pk)
-        self.assertEqual(obj.school.pk, school.pk)
-        self.assertEqual(obj.student.school.pk, school.pk)
-
     def test_if_email_of_existing_user_returns_bad_request(self):
         user = UserFactory.create()
         data = self.get_create_data()
diff --git a/tests/test_tutoring/__init__.py b/tests/test_tutoring/__init__.py
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/tests/test_tutoring/test_school.py b/tests/test_tutoring/test_school.py
deleted file mode 100644
index b6c2b2408d67b42ccfb6c632b79c042c17f586ca..0000000000000000000000000000000000000000
--- a/tests/test_tutoring/test_school.py
+++ /dev/null
@@ -1,59 +0,0 @@
-"""School model tests."""
-
-from django.contrib.auth import get_user_model
-from tutoring.factory import SchoolFactory
-from users.factory import UserFactory
-from tests.utils import ModelTestCase
-
-import tutoring.models
-
-User = get_user_model()
-
-
-class SchoolTest(ModelTestCase):
-    """Test the School model."""
-
-    model = tutoring.models.School
-    field_tests = {
-        'uai_code': {
-            'unique': True,
-            'primary_key': True,
-            'max_length': 8,
-            'verbose_name': 'code UAI',
-        },
-        'name': {
-            'verbose_name': 'nom',
-        },
-        'address': {
-            'verbose_name': 'adresse',
-            'null': True,
-        }
-    }
-    model_tests = {
-        'verbose_name': 'lycée',
-        'ordering': ('name',),
-    }
-
-    @classmethod
-    def setUpTestData(cls):
-        cls.obj = SchoolFactory.create(name='Lycée Michelin')
-
-    def test_uai_code_help_text_indicates_format(self):
-        help_text = self.model._meta.get_field('uai_code').help_text
-        self.assertIsNotNone(help_text)
-        self.assertIn('UAI', help_text)
-        self.assertIn('ex-RNE', help_text)
-        self.assertIn('7 chiffres', help_text)
-        self.assertIn('une lettre', help_text)
-
-    def test_uai_code_help_text_indicates_where_to_find_it(self):
-        help_text = self.model._meta.get_field('uai_code').help_text
-        self.assertIn("site du ministère de l'Éducation Nationale", help_text)
-
-    def test_get_absolute_url(self):
-        self.client.force_login(UserFactory.create())
-        url = self.obj.get_absolute_url()
-        expected = '/api/schools/{}/'.format(self.obj.uai_code)
-        self.assertEqual(url, expected)
-        response = self.client.get(url)
-        self.assertEqual(200, response.status_code)
diff --git a/tests/test_tutoring/test_school_api.py b/tests/test_tutoring/test_school_api.py
deleted file mode 100644
index 81269f5636d759c69236577328d039a6b947abd6..0000000000000000000000000000000000000000
--- a/tests/test_tutoring/test_school_api.py
+++ /dev/null
@@ -1,44 +0,0 @@
-"""School API tests."""
-
-from rest_framework import status
-from tutoring.factory import SchoolFactory
-from tests.utils.api import HyperlinkedAPITestCase
-
-from tutoring.serializers import SchoolSerializer
-
-
-class SchoolEndpointsTest(HyperlinkedAPITestCase):
-    """Test access to the school endpoints."""
-
-    factory = SchoolFactory
-    serializer_class = SchoolSerializer
-
-    def perform_list(self):
-        url = '/api/schools/'
-        response = self.client.get(url)
-        return response
-
-    def test_list(self):
-        self.assertRequestResponse(
-            self.perform_list,
-            user=None,
-            expected_status_code=status.HTTP_200_OK)
-
-    def perform_retrieve(self):
-        obj = self.factory.create()
-        url = '/api/schools/{obj.pk}/'.format(obj=obj)
-        response = self.client.get(url)
-        return response
-
-    def test_retrieve(self):
-        self.assertRequiresAuth(
-            self.perform_retrieve,
-            expected_status_code=status.HTTP_200_OK)
-
-    def test_choices_returns_list_of_uai_codes_and_names(self):
-        self.factory.create_batch(5)
-        url = '/api/schools/choices/'
-        response = self.client.get(url)
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        for item in response.data:
-            self.assertSetEqual(set(item), {'uai_code', 'name'})
diff --git a/tests/test_tutoring/test_tutoring_group.py b/tests/test_tutoring/test_tutoring_group.py
deleted file mode 100644
index 4f4c33fa0be333c8fd4febc77a9c7f92f87c7433..0000000000000000000000000000000000000000
--- a/tests/test_tutoring/test_tutoring_group.py
+++ /dev/null
@@ -1,47 +0,0 @@
-"""Tutoring group model tests."""
-
-from tests.utils import ModelTestCase
-from tutoring.factory import TutoringGroupFactory, TutorTutoringGroupFactory
-from tutoring.models import TutoringGroup
-from users.factory import UserFactory
-from profiles.factory import TutorFactory
-
-
-class TutoringGroupTest(ModelTestCase):
-    """Test the TutoringGroup model."""
-
-    model = TutoringGroup
-    field_tests = {
-        'name': {
-            'verbose_name': 'nom',
-            'max_length': 200,
-        },
-        'tutors': {
-            'verbose_name': 'tuteurs',
-            'blank': True,
-        },
-    }
-    model_tests = {
-        'verbose_name': 'groupe de tutorat',
-        'verbose_name_plural': 'groupes de tutorat',
-        'ordering': ('name',),
-    }
-
-    @classmethod
-    def setUpTestData(cls):
-        cls.obj = TutoringGroupFactory.create()
-
-    def test_get_absolute_url(self):
-        self.client.force_login(UserFactory.create())
-        response = self.client.get(
-            '/api/groups/{}/'.format(self.obj.pk))
-        self.assertEqual(200, response.status_code)
-
-    def test_tutors_many_to_many_relationship(self):
-        tutor = TutorFactory.create()
-        membership = TutorTutoringGroupFactory.create(
-            tutor=tutor,
-            tutoring_group=self.obj)
-        tutor = membership.tutor
-        self.assertIn(tutor, self.obj.tutors.all())
-        self.assertIn(self.obj, tutor.tutoring_groups.all())
diff --git a/tests/test_tutoring/test_tutoring_group_api.py b/tests/test_tutoring/test_tutoring_group_api.py
deleted file mode 100644
index e7d9905c924c44a09134ab1501d400e509d6c139..0000000000000000000000000000000000000000
--- a/tests/test_tutoring/test_tutoring_group_api.py
+++ /dev/null
@@ -1,66 +0,0 @@
-"""Tutoring group API tests."""
-
-from rest_framework import status
-from tutoring.factory import TutoringGroupFactory
-from tests.utils.api import HyperlinkedAPITestCase
-
-from tutoring.serializers import TutoringGroupSerializer
-
-
-class TutoringGroupEndpointsTest(HyperlinkedAPITestCase):
-    """Test access to the tutoring group endpoints."""
-
-    factory = TutoringGroupFactory
-    serializer_class = TutoringGroupSerializer
-
-    def perform_list(self):
-        url = '/api/groups/'
-        response = self.client.get(url)
-        return response
-
-    def test_list_requires_authentication(self):
-        self.assertRequiresAuth(self.perform_list,
-                                expected_status_code=status.HTTP_200_OK)
-
-    def perform_retrieve(self):
-        obj = self.factory.create()
-        url = '/api/groups/{obj.pk}/'.format(obj=obj)
-        response = self.client.get(url)
-        return response
-
-    def test_retrieve_requires_authentication(self):
-        self.assertRequiresAuth(self.perform_retrieve,
-                                expected_status_code=status.HTTP_200_OK)
-
-    def test_list_students(self):
-        pass  # TODO
-
-    def test_add_student(self):
-        pass  # TODO
-
-    def test_remove_student(self):
-        pass  # TODO
-
-    def test_list_tutors(self):
-        pass  # TODO
-
-    def test_add_tutor(self):
-        pass  # TODO
-
-    def test_remove_tutor(self):
-        pass  # TODO
-
-    def test_list_meetings(self):
-        pass  # TODO
-
-    def test_list_past_meetings(self):
-        pass  # TODO
-
-    def test_list_next_meetings(self):
-        pass  # TODO
-
-    def test_add_meeting(self):
-        pass  # TODO
-
-    def test_remove_meeting(self):
-        pass  # TODO
diff --git a/tests/test_tutoring/test_tutoring_session.py b/tests/test_tutoring/test_tutoring_session.py
deleted file mode 100644
index 972daff3075dd44755d599947ce45564584e01f3..0000000000000000000000000000000000000000
--- a/tests/test_tutoring/test_tutoring_session.py
+++ /dev/null
@@ -1,46 +0,0 @@
-"""Tutoring session model tests."""
-
-import tutoring.models
-from tests.utils import ModelTestCase
-from tutoring.factory import TutoringSessionFactory
-
-
-class TutoringSessionTest(ModelTestCase):
-    """Test the TutoringSession model."""
-
-    model = tutoring.models.TutoringSession
-    field_tests = {
-        'date': {
-            'verbose_name': 'date',
-        },
-        'start_time': {
-            'verbose_name': 'heure de début',
-        },
-        'end_time': {
-            'verbose_name': 'heure de fin',
-        },
-        'tutoring_group': {
-            'verbose_name': 'groupe de tutorat',
-        },
-    }
-    model_tests = {
-        'verbose_name': 'séance de tutorat',
-        'verbose_name_plural': 'séances de tutorat',
-        'ordering': ('date', 'start_time'),
-    }
-
-    @classmethod
-    def setUpTestData(self):
-        self.obj = TutoringSessionFactory.create()
-
-    def test_get_absolute_url(self):
-        url = '/api/sessions/{}/'.format(self.obj.pk)
-        response = self.client.get(url)
-        self.assertEqual(200, response.status_code)
-
-    def test_tutoring_group_one_to_many_relationship(self):
-        self.assertEqual(tutoring.models.TutoringGroup.objects.get(),
-                         self.obj.tutoring_group)
-        self.assertIn(self.obj,
-                      tutoring.models.TutoringGroup.objects.get()
-                      .sessions.all())
diff --git a/tests/test_users/test_user.py b/tests/test_users/test_user.py
index d7ff66b53d4b84f2adce276effd620b35de90565..79a56fcd242c8118cbb129f933a0fa51e17d6ef5 100644
--- a/tests/test_users/test_user.py
+++ b/tests/test_users/test_user.py
@@ -56,22 +56,6 @@ class UserModelTest(ModelTestCase):
             'blank': False,
             'null': False,
         },
-        'date_of_birth': {
-            'verbose_name': 'date de naissance',
-            'blank': True,
-            'null': True,
-        },
-        'gender': {
-            'verbose_name': 'sexe',
-            'max_length': 1,
-            'choices': (('M', 'Homme'), ('F', 'Femme')),
-            'blank': True,
-        },
-        'phone_number': {
-            'verbose_name': 'téléphone',
-            'blank': True,
-            'null': True,
-        },
         'profile_type': {
             'verbose_name': 'type de profil',
             'blank': False,
@@ -80,6 +64,10 @@ class UserModelTest(ModelTestCase):
                 (User.PROFILE_STUDENT, 'Lycéen'),
                 (User.PROFILE_TUTOR, 'Tuteur'),
             )
+        },
+        'phone_number': {
+            'blank': True,
+            'null': True,
         }
     }
     model_tests = {
diff --git a/tests/test_visits/test_signals.py b/tests/test_visits/test_signals.py
index 01027bc744db579ed305fc79f5bb90a585fb8133..188bd95bf69c6669e32b1999182c07aeae732141 100644
--- a/tests/test_visits/test_signals.py
+++ b/tests/test_visits/test_signals.py
@@ -4,7 +4,6 @@ from django.test import TestCase
 from tests.utils.mixins import SignalTestMixin
 
 from mails.signals import notification_sent
-from users.factory import UserFactory
 from visits.factory import ParticipationFactory, VisitFactory
 from visits.notifications import ConfirmParticipation
 from visits.signals import accepted_changed
diff --git a/tests/utils/mixins.py b/tests/utils/mixins.py
index b7cd15e056ef14ec1f40db339caf9ba3b551c101..37de2965a851af517cbfce6ff3420726b565c1e5 100644
--- a/tests/utils/mixins.py
+++ b/tests/utils/mixins.py
@@ -52,7 +52,7 @@ class SignalTestMixin:
 
         Pass `sender` to check the signal's sender too.
         """
-        called = {'value': None}
+        called = {'value': False}
 
         def listen(sender, **kwargs):
             called['value'] = True
@@ -65,3 +65,17 @@ class SignalTestMixin:
         self.assertTrue(called['value'])
         if 'sender' in kwargs:
             self.assertEqual(called['sender'], kwargs['sender'])
+
+    @contextmanager
+    def assertNotCalled(self, signal):
+        """Verify that a signal is NOT called."""
+        called = {'value': False}
+
+        def listen(sender, **kwargs):
+            called['value'] = True
+
+        signal.connect(listen)
+        yield
+        signal.disconnect(listen)
+
+        self.assertFalse(called['value'])
diff --git a/tutoring/__init__.py b/tutoring/__init__.py
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/tutoring/admin.py b/tutoring/admin.py
deleted file mode 100644
index e3a0d3ce9b8867cd9f727578a5c52e0396425d03..0000000000000000000000000000000000000000
--- a/tutoring/admin.py
+++ /dev/null
@@ -1,98 +0,0 @@
-"""Tutoring admin panel configuration."""
-
-from django.contrib import admin
-from django.shortcuts import reverse
-from django.utils.html import format_html
-from core.admin import AutocompleteAddressMixin
-from profiles.models import Student
-from .models import TutoringGroup, School, TutoringSession
-
-# Register your models here.
-
-
-class TutoringGroupMembershipInline(admin.TabularInline):
-    """Inline for tutoring group membership."""
-
-    model = TutoringGroup.tutors.through
-    extra = 0
-
-
-class TutoringGroupStudentsInline(admin.TabularInline):
-    """Inline to show students in a tutoring group."""
-
-    model = Student
-    extra = 0
-    max_num = 0
-    readonly_fields = ('user', 'school',)
-    can_delete = False
-
-
-@admin.register(TutoringGroup)
-class TutoringGroupAdmin(admin.ModelAdmin):
-    """Tutoring group admin panel."""
-
-    inlines = [
-        TutoringGroupStudentsInline,
-        TutoringGroupMembershipInline,
-    ]
-    search_fields = ('name',)
-
-    class Meta:  # noqa
-        model = TutoringGroup
-
-
-@admin.register(School)
-class SchoolAdmin(AutocompleteAddressMixin, admin.ModelAdmin):
-    """School admin panel."""
-
-    list_display = ('__str__', 'uai_code',
-                    'get_student_count', 'get_groups_count')
-    search_fields = ('name',)
-
-    def get_student_count(self, obj):
-        """Display number of students."""
-        return obj.students.count()
-    get_student_count.short_description = 'Nombre de lycéens'
-
-    def get_groups_count(self, obj):
-        """Display number of tutoring groups."""
-        return obj.tutoring_groups.count()
-    get_groups_count.short_description = 'Nombre de groupes de tutorat'
-
-    def get_readonly_fields(self, request, obj=None):
-        """Make the UAI code (school's ID) read-only when editing."""
-        if obj is not None:
-            return ['uai_code']
-        return []
-
-    class Meta:  # noqa
-        model = School
-
-
-@admin.register(TutoringSession)
-class TutoringSessionAdmin(admin.ModelAdmin):
-    """Tutoring session admin panel."""
-
-    list_display = ('__str__', 'link_tutoring_group', 'link_school', 'date',)
-    autocomplete_fields = ('tutoring_group',)
-
-    def link_tutoring_group(self, obj):
-        link = reverse('admin:tutoring_tutoringgroup_change',
-                       args=[obj.tutoring_group.pk])
-        s = str(obj.tutoring_group)
-        return format_html("<a href='{link}'>{s}</a>", link=link, s=s)
-    link_tutoring_group.admin_order_field = 'groupe de tutorat'
-    link_tutoring_group.short_description = 'groupe de tutorat'
-
-    def link_school(self, obj):
-        if not obj.school:
-            return None
-        link = reverse('admin:tutoring_school_change',
-                       args=[obj.school.pk])
-        s = str(obj.school)
-        return format_html("<a href='{link}'>{s}</a>", link=link, s=s)
-    link_school.admin_order_field = 'lycée'
-    link_school.short_description = 'lycée'
-
-    class Meta:  # noqa
-        model = TutoringSession
diff --git a/tutoring/apps.py b/tutoring/apps.py
deleted file mode 100644
index 7b1885f543e7f66fe6d2be7f788500cfcdbf03ae..0000000000000000000000000000000000000000
--- a/tutoring/apps.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from django.apps import AppConfig
-
-
-class TutoringConfig(AppConfig):
-    name = 'tutoring'
-    verbose_name = 'Tutorat'
diff --git a/tutoring/conf.py b/tutoring/conf.py
deleted file mode 100644
index 0533d33a8e88a4100b757aff4474a8b0fc197d9d..0000000000000000000000000000000000000000
--- a/tutoring/conf.py
+++ /dev/null
@@ -1,8 +0,0 @@
-"""Tutoring settings."""
-
-from django.conf import settings
-from utils import setdefault
-
-
-setdefault(settings, 'DEFAULT_SESSION_START_TIME', (17, 0))  # (h, m)
-setdefault(settings, 'DEFAULT_SESSION_END_TIME', (19, 0))  # (h, m)
diff --git a/tutoring/factory.py b/tutoring/factory.py
deleted file mode 100644
index c00b6897088378d1b237c18389cb91838fd82329..0000000000000000000000000000000000000000
--- a/tutoring/factory.py
+++ /dev/null
@@ -1,70 +0,0 @@
-"""Tutoring factories."""
-
-from datetime import timedelta
-
-import factory
-import factory.django
-import pytz
-from django.contrib.auth import get_user_model
-from django.utils import timezone
-
-from core.factory import AddressFactory
-from tutoring.utils import random_uai_code
-
-from . import models
-
-User = get_user_model()
-utc = pytz.UTC
-
-
-# Create test objects factories here
-
-class SchoolFactory(factory.DjangoModelFactory):
-    """School object factory."""
-
-    class Meta:  # noqa
-        model = models.School
-        exclude = ('school_name',)
-
-    uai_code = factory.LazyFunction(random_uai_code)
-    school_name = factory.Faker('name', locale='fr')
-    name = factory.LazyAttribute(lambda o: 'Lycée {o.school_name}'.format(o=o))
-    address = factory.SubFactory(AddressFactory)
-
-
-class TutoringGroupFactory(factory.DjangoModelFactory):
-    """TutoringGroup object factory."""
-
-    class Meta:  # noqa
-        model = models.TutoringGroup
-        exclude = ('level',)
-
-    level = factory.Iterator(['Seconde', 'Première', 'Terminale'])
-    name = factory.LazyAttribute(
-        lambda o: '{o.school} ({o.level})'.format(o=o))
-    school = factory.SubFactory(SchoolFactory)
-
-
-class TutorTutoringGroupFactory(factory.DjangoModelFactory):
-    """Intermediate tutor-tutoring group object factory."""
-
-    class Meta:  # noqa
-        model = models.TutorTutoringGroup
-
-    # tutor can be passed on creation
-    tutoring_group = factory.SubFactory(TutoringGroupFactory)
-    is_leader = False
-
-
-class TutoringSessionFactory(factory.DjangoModelFactory):
-    """Tutoring session object factory."""
-
-    class Meta:  # noqa
-        model = models.TutoringSession
-
-    # random date 30 days ahead in time
-    date = factory.Faker('future_date', end_date='+30d')
-    start_time = factory.LazyFunction(timezone.now)
-    end_time = factory.LazyAttribute(
-        lambda o: o.start_time + timedelta(hours=2))
-    tutoring_group = factory.SubFactory(TutoringGroupFactory)
diff --git a/tutoring/migrations/0001_initial.py b/tutoring/migrations/0001_initial.py
deleted file mode 100644
index d169b2883f77e2d443b9bb632ac058607d72eedf..0000000000000000000000000000000000000000
--- a/tutoring/migrations/0001_initial.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# Generated by Django 2.0.4 on 2018-04-21 12:25
-
-import datetime
-import django.core.validators
-from django.db import migrations, models
-import tutoring.models
-
-
-class Migration(migrations.Migration):
-
-    initial = True
-
-    dependencies = [
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='School',
-            fields=[
-                ('name', models.CharField(help_text='Nom du lycée', max_length=200, verbose_name='nom')),
-                ('uai_code', models.CharField(help_text="Code UAI (ex-RNE) de l'établissement qui sert à l'identifier. Celui-ci est composé de 7 chiffres et une lettre. Il est répertorié dans l'annuaire des établissements sur le site du ministère de l'Éducation Nationale.", max_length=8, primary_key=True, serialize=False, validators=[django.core.validators.RegexValidator(message="Un code UAI doit être composé de 7 chiffres suivis d'une lettre.", regex='^\\d{7}[a-zA-Z]$')], verbose_name='code UAI')),
-            ],
-            options={
-                'verbose_name': 'lycée',
-                'ordering': ('name',),
-            },
-        ),
-        migrations.CreateModel(
-            name='TutoringGroup',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('name', models.CharField(max_length=200, verbose_name='nom')),
-            ],
-            options={
-                'verbose_name': 'groupe de tutorat',
-                'verbose_name_plural': 'groupes de tutorat',
-                'ordering': ('name',),
-            },
-        ),
-        migrations.CreateModel(
-            name='TutoringSession',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('date', models.DateField(default=datetime.datetime.now)),
-                ('start_time', models.TimeField(default=tutoring.models.default_start_time, verbose_name='heure de début')),
-                ('end_time', models.TimeField(default=tutoring.models.default_end_time, verbose_name='heure de fin')),
-            ],
-            options={
-                'verbose_name': 'séance de tutorat',
-                'verbose_name_plural': 'séances de tutorat',
-                'ordering': ('date', 'start_time'),
-            },
-        ),
-        migrations.CreateModel(
-            name='TutorTutoringGroup',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('is_leader', models.BooleanField(default=False, verbose_name='Responsable')),
-            ],
-            options={
-                'verbose_name': 'membre du groupe de tutorat',
-                'verbose_name_plural': 'membres du groupe de tutorat',
-            },
-        ),
-    ]
diff --git a/tutoring/migrations/0002_auto_20180421_1425.py b/tutoring/migrations/0002_auto_20180421_1425.py
deleted file mode 100644
index 98994b0f652b791e656d6b63526c19f9d13e3e89..0000000000000000000000000000000000000000
--- a/tutoring/migrations/0002_auto_20180421_1425.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Generated by Django 2.0.4 on 2018-04-21 12:25
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    initial = True
-
-    dependencies = [
-        ('tutoring', '0001_initial'),
-        ('core', '0005_auto_20180408_1525'),
-        ('users', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='tutortutoringgroup',
-            name='tutoring_group',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tutoring.TutoringGroup', verbose_name='groupe de tutorat'),
-        ),
-        migrations.AddField(
-            model_name='tutoringsession',
-            name='tutoring_group',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tutoring_sessions', to='tutoring.TutoringGroup', verbose_name='groupe de tutorat'),
-        ),
-        migrations.AddField(
-            model_name='tutoringgroup',
-            name='school',
-            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tutoring_groups', to='tutoring.School', verbose_name='lycée'),
-        ),
-        migrations.AddField(
-            model_name='school',
-            name='address',
-            field=models.ForeignKey(help_text='Adresse complète du lycée', null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.Address', verbose_name='adresse'),
-        ),
-    ]
diff --git a/tutoring/migrations/0003_auto_20180429_1053.py b/tutoring/migrations/0003_auto_20180429_1053.py
deleted file mode 100644
index 3c8ee0634c099682206f19b0452a9277b6e2877b..0000000000000000000000000000000000000000
--- a/tutoring/migrations/0003_auto_20180429_1053.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.0.4 on 2018-04-29 08:53
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('tutoring', '0002_auto_20180421_1425'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='school',
-            name='address',
-            field=models.OneToOneField(help_text='Adresse complète du lycée', null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.Address', verbose_name='adresse'),
-        ),
-    ]
diff --git a/tutoring/migrations/0004_auto_20180429_1159.py b/tutoring/migrations/0004_auto_20180429_1159.py
deleted file mode 100644
index 3f0dc70f3fb9c164aca8f30f8470111717c04683..0000000000000000000000000000000000000000
--- a/tutoring/migrations/0004_auto_20180429_1159.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.0.4 on 2018-04-29 09:59
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('tutoring', '0003_auto_20180429_1053'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='tutoringsession',
-            name='tutoring_group',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sessions', to='tutoring.TutoringGroup', verbose_name='groupe de tutorat'),
-        ),
-    ]
diff --git a/tutoring/migrations/0005_auto_20180512_1325.py b/tutoring/migrations/0005_auto_20180512_1325.py
deleted file mode 100644
index aaa0cfd392dc2c500b105b1f09c5d6f21ffc2615..0000000000000000000000000000000000000000
--- a/tutoring/migrations/0005_auto_20180512_1325.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-12 11:25
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('profiles', '0001_initial'),
-        ('tutoring', '0004_auto_20180429_1159'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='tutoringgroup',
-            name='tutors',
-            field=models.ManyToManyField(blank=True, null=True, related_name='tutoring_groups', through='tutoring.TutorTutoringGroup', to='profiles.Tutor', verbose_name='tuteurs'),
-        ),
-        migrations.AddField(
-            model_name='tutortutoringgroup',
-            name='tutor',
-            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='profiles.Tutor', verbose_name='Tuteur'),
-        ),
-    ]
diff --git a/tutoring/migrations/0006_auto_20180512_1344.py b/tutoring/migrations/0006_auto_20180512_1344.py
deleted file mode 100644
index c409bb05e7b3759985206630f178c902070cc84c..0000000000000000000000000000000000000000
--- a/tutoring/migrations/0006_auto_20180512_1344.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-12 11:44
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('tutoring', '0005_auto_20180512_1325'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='tutoringgroup',
-            name='tutors',
-            field=models.ManyToManyField(blank=True, related_name='tutoring_groups', through='tutoring.TutorTutoringGroup', to='profiles.Tutor', verbose_name='tuteurs'),
-        ),
-        migrations.AlterField(
-            model_name='tutortutoringgroup',
-            name='tutor',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='profiles.Tutor', verbose_name='Tuteur'),
-        ),
-    ]
diff --git a/tutoring/migrations/__init__.py b/tutoring/migrations/__init__.py
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/tutoring/models.py b/tutoring/models.py
deleted file mode 100644
index 8fcc10f051e1190344907b6692a61cbec43fe404..0000000000000000000000000000000000000000
--- a/tutoring/models.py
+++ /dev/null
@@ -1,212 +0,0 @@
-"""Tutoring models."""
-
-from datetime import datetime, timedelta
-
-from django.db import models
-from django.shortcuts import reverse
-from django.template.defaulttags import date as date_tag
-from dry_rest_permissions.generics import (allow_staff_or_superuser,
-                                           authenticated_users)
-
-from .conf import settings
-from .validators import uai_code_validator
-
-
-# Create your models here.
-
-
-class TutorTutoringGroup(models.Model):
-    """Intermediate model for tutoring group and tutors n-n relationship."""
-
-    tutoring_group = models.ForeignKey(
-        'TutoringGroup', on_delete=models.CASCADE,
-        verbose_name='groupe de tutorat')
-    tutor = models.ForeignKey(
-        'profiles.Tutor', on_delete=models.CASCADE,
-        verbose_name='Tuteur')
-    is_leader = models.BooleanField(default=False, verbose_name='Responsable')
-
-    class Meta:  # noqa
-        verbose_name = 'membre du groupe de tutorat'
-        verbose_name_plural = 'membres du groupe de tutorat'
-
-
-class TutoringGroup(models.Model):
-    """Represents a tutoring group to which tutors and students participate.
-
-    Fields
-    ------
-    name : char
-    tutors : n-n with profiles.Tutor
-    school : 1-n with tutoring.School
-        Deletion rule: SET_NULL
-
-    Relationships
-    -------------
-    students : n-1 with profiles.Student
-    """
-
-    name = models.CharField('nom', max_length=200)
-    tutors = models.ManyToManyField('profiles.Tutor',
-                                    related_name='tutoring_groups',
-                                    verbose_name='tuteurs',
-                                    blank=True,
-                                    through='TutorTutoringGroup')
-    school = models.ForeignKey('School', on_delete=models.SET_NULL,
-                               null=True,
-                               related_name='tutoring_groups',
-                               verbose_name='lycée')
-
-    class Meta:  # noqa
-        ordering = ('name',)
-        verbose_name = 'groupe de tutorat'
-        verbose_name_plural = 'groupes de tutorat'
-
-    def get_absolute_url(self):
-        return reverse('api:tutoring_group-detail', args=[str(self.id)])
-
-    @staticmethod
-    @authenticated_users
-    def has_read_permission(request):
-        return True
-
-    @authenticated_users
-    def has_object_read_permission(self, request):
-        return True
-
-    @staticmethod
-    @allow_staff_or_superuser
-    def has_write_permission(request):
-        return True
-
-    @allow_staff_or_superuser
-    def has_object_write_permission(self, request):
-        """Can only be written by admin, leader tutor."""
-        is_leader = (self.tutors
-                     .filter(user_id=request.user.id,
-                             tutortutoringgroup__is_leader=True)
-                     .exists())
-        return is_leader
-
-    def __str__(self):
-        return str(self.name)
-
-
-class School(models.Model):
-    """Represents a (high) school.
-
-    Fields
-    ------
-    uai_code : char, primary key
-        UAI code of the school.
-    name : char
-    address : char
-
-    Relationships
-    -------------
-    students : n-1 with profiles.Student
-
-    Meta
-    ----
-    ordering : by name
-    """
-
-    name = models.CharField('nom', max_length=200, help_text='Nom du lycée')
-
-    # TODO add UAI code validation
-    uai_code = models.CharField(
-        'code UAI',
-        max_length=8,
-        primary_key=True,
-        validators=[uai_code_validator],
-        help_text=(
-            "Code UAI (ex-RNE) de l'établissement qui sert à l'identifier. "
-            "Celui-ci est composé de 7 chiffres et une lettre. "
-            "Il est répertorié dans "
-            "l'annuaire des établissements sur le site du "
-            "ministère de l'Éducation Nationale."))
-
-    address = models.OneToOneField(
-        'core.Address', on_delete=models.SET_NULL, verbose_name='adresse',
-        null=True, help_text='Adresse complète du lycée')
-
-    class Meta:  # noqa
-        ordering = ('name',)
-        verbose_name = 'lycée'
-
-    def save(self, *args, **kwargs):
-        if self.pk is None:
-            # ensure the letter in UAI code is always uppercase.
-            # do only at object creation to prevent PK from changing while
-            # object is alive.
-            self.uai_code = self.uai_code.upper()
-        super().save(*args, **kwargs)
-
-    def get_absolute_url(self):
-        return reverse('api:school-detail', args=[str(self.uai_code)])
-
-    @staticmethod
-    def has_read_permission(request):
-        return True
-
-    @authenticated_users
-    def has_object_read_permission(self, request):
-        return True
-
-    @staticmethod
-    @authenticated_users
-    @allow_staff_or_superuser
-    def has_write_permission(request):
-        return True
-
-    @authenticated_users
-    @allow_staff_or_superuser
-    def has_object_write_permission(self, request):
-        return True
-
-    def __str__(self):
-        return str(self.name)
-
-
-def default_start_time():
-    """Return the default tutoring session start time."""
-    h, m = settings.DEFAULT_SESSION_START_TIME
-    now = datetime.now()
-    start = now.replace(hour=h, minute=m, second=0, microsecond=0)
-    return (start if start > now else start + timedelta(days=1)).time()
-
-
-def default_end_time():
-    """Return the default tutoring session end time."""
-    h, m = settings.DEFAULT_SESSION_END_TIME
-    now = datetime.now()
-    end = now.replace(hour=h, minute=m, second=0, microsecond=0)
-    return (end if end > now else end + timedelta(days=1)).time()
-
-
-class TutoringSession(models.Model):
-    """Represents a tutoring session event."""
-
-    date = models.DateField(default=datetime.now)
-    start_time = models.TimeField('heure de début',
-                                  default=default_start_time)
-    end_time = models.TimeField('heure de fin',
-                                default=default_end_time)
-    tutoring_group = models.ForeignKey('TutoringGroup',
-                                       on_delete=models.CASCADE,
-                                       verbose_name='groupe de tutorat',
-                                       related_name='sessions')
-
-    class Meta:  # noqa
-        verbose_name = 'séance de tutorat'
-        verbose_name_plural = 'séances de tutorat'
-        ordering = ('date', 'start_time',)
-
-    @property
-    def school(self):
-        return self.tutoring_group.school
-    school.fget.short_description = 'lycée'
-
-    def __str__(self):
-        date = date_tag(self.date, 'SHORT_DATE_FORMAT')
-        return '{} ({})'.format(self.tutoring_group, date)
diff --git a/tutoring/serializers.py b/tutoring/serializers.py
deleted file mode 100644
index 9376948b7e7792f8971c489f589b17335e1c21be..0000000000000000000000000000000000000000
--- a/tutoring/serializers.py
+++ /dev/null
@@ -1,105 +0,0 @@
-"""Tutoring API serializers."""
-
-from rest_framework import serializers
-
-from profiles.models import Student
-from core.serializers import AddressSerializer
-
-from .models import School, TutoringGroup, TutoringSession
-
-
-class SchoolSerializer(serializers.HyperlinkedModelSerializer):
-    """Serializer for School.
-
-    Suited for: list, retrieve, update, partial_update, delete
-    """
-
-    address = AddressSerializer()
-    students = serializers.PrimaryKeyRelatedField(
-        many=True,
-        queryset=Student.objects.all(),
-        help_text='Lycéens inscrits à ce lycée',
-    )
-    students_count = serializers.IntegerField(source='students.count',
-                                              read_only=True)
-
-    class Meta:  # noqa
-        model = School
-        fields = ('uai_code', 'name', 'address', 'students',
-                  'students_count', 'url',)
-        extra_kwargs = {
-            'url': {'view_name': 'api:school-detail'},
-            'uai_code': {'read_only': True},
-        }
-
-    @staticmethod
-    def setup_eager_loading(queryset):
-        """Setup eager loading in advance.
-
-        Prevents the N+1 query problem by pre-fetching the students.
-
-        Source: http://ses4j.github.io/2015/11/23/
-                optimizing-slow-django-rest-framework-performance
-        """
-        queryset = queryset.prefetch_related('students')
-        return queryset
-
-
-class SchoolChoicesSerializer(serializers.ModelSerializer):
-    """Serializer for available schools."""
-
-    class Meta:  # noqa
-        model = School
-        fields = ('uai_code', 'name')
-
-
-class TutoringGroupSerializer(serializers.HyperlinkedModelSerializer):
-    """Serializer for TutoringGroup."""
-
-    tutors = serializers.PrimaryKeyRelatedField(
-        many=True,
-        read_only=True,
-    )
-    students = serializers.PrimaryKeyRelatedField(
-        many=True,
-        read_only=True,
-    )
-    school = serializers.PrimaryKeyRelatedField(
-        read_only=True,
-    )
-    students_count = serializers.IntegerField(source='students.count',
-                                              read_only=True)
-    tutors_count = serializers.IntegerField(source='tutors.count',
-                                            read_only=True)
-
-    class Meta:  # noqa
-        model = TutoringGroup
-        fields = ('id', 'name', 'tutors', 'students', 'school',
-                  'students_count', 'tutors_count', 'url',)
-        extra_kwargs = {
-            'url': {'view_name': 'api:tutoring_group-detail'},
-        }
-
-    @staticmethod
-    def setup_eager_loading(queryset):
-        queryset = queryset.select_related('school')
-        queryset = queryset.prefetch_related('tutors')
-        queryset = queryset.prefetch_related('students')
-        return queryset
-
-
-class TutoringSessionSerializer(serializers.HyperlinkedModelSerializer):
-    """Serializer for TutoringSession."""
-
-    tutoring_group = serializers.HyperlinkedRelatedField(
-        queryset=TutoringGroup.objects.all(),
-        view_name='api:tutoring_group-detail',
-    )
-
-    class Meta:  # noqa
-        model = TutoringSession
-        fields = ('id', 'date', 'start_time', 'end_time',
-                  'tutoring_group', 'url',)
-        extra_kwargs = {
-            'url': {'view_name': 'api:tutoring_session-detail'},
-        }
diff --git a/tutoring/utils.py b/tutoring/utils.py
deleted file mode 100644
index 68cd978b02ac2b3a64573eac18b8760d6a28f67c..0000000000000000000000000000000000000000
--- a/tutoring/utils.py
+++ /dev/null
@@ -1,11 +0,0 @@
-"""Tutoring utilities."""
-
-import random
-from string import ascii_lowercase, digits
-
-
-def random_uai_code():
-    """Return a random UAI code (French school identifier)."""
-    seven_digits = ''.join(random.choices(digits, k=7))
-    one_letter = random.choice(ascii_lowercase)
-    return seven_digits + one_letter
diff --git a/tutoring/validators.py b/tutoring/validators.py
deleted file mode 100644
index 6979d73b51508b3ab789945cc3dc0f137a43fbb7..0000000000000000000000000000000000000000
--- a/tutoring/validators.py
+++ /dev/null
@@ -1,10 +0,0 @@
-"""Tutoring validators."""
-
-from django.core.validators import RegexValidator
-
-
-uai_code_validator = RegexValidator(
-    regex=r'^\d{7}[a-zA-Z]$',
-    message=("Un code UAI doit être composé de 7 chiffres "
-             "suivis d'une lettre.")
-)
diff --git a/tutoring/views.py b/tutoring/views.py
deleted file mode 100644
index 95f607d68ff5067a89556bacd7bd689c6db603f0..0000000000000000000000000000000000000000
--- a/tutoring/views.py
+++ /dev/null
@@ -1,126 +0,0 @@
-"""Tutoring API views."""
-
-from rest_framework.response import Response
-from rest_framework.viewsets import ReadOnlyModelViewSet
-from rest_framework.decorators import action
-from dry_rest_permissions.generics import DRYPermissions
-
-from .models import TutoringGroup, School, TutoringSession
-from .serializers import (
-    TutoringGroupSerializer,
-    SchoolSerializer,
-    SchoolChoicesSerializer,
-    TutoringSessionSerializer,
-)
-
-# Create your views here.
-
-
-class TutoringGroupViewSet(ReadOnlyModelViewSet):
-    """API endpoint that allows tutoring groups to be viewed or edited.
-
-    Actions: list, retrieve, create, update, partial_update, destroy
-    """
-
-    queryset = TutoringGroup.objects.all()
-    serializer_class = TutoringGroupSerializer
-    permission_classes = (DRYPermissions,)
-
-    def get_queryset(self):
-        queryset = self.queryset
-        queryset = self.get_serializer_class().setup_eager_loading(queryset)
-        return queryset
-
-
-class SchoolViewSet(ReadOnlyModelViewSet):
-    """API endpoint that allows schools to be viewed.
-
-    list:
-
-    List all schools.
-
-    ### Example response
-
-        [
-            {
-                "uai_code": "0930965U",
-                "url": "http://localhost:8000/api/schools/0930965U/",
-                "name": "Henri Matisse",
-                "address": {
-                    "line1": "88 Bis rue Rules Guesde",
-                    "line2": "",
-                    "post_code": "93100",
-                    "city": "Montreuil",
-                    "country": {
-                        "code": "FR",
-                        "name": "France"
-                    }
-                },
-                "students": [ ],
-                "students_count": 0
-            }
-        ]
-
-    retrieve:
-
-    Retrieve information about a specific school.
-
-    ### Example response
-
-        {
-            "uai_code": "0930965U",
-            "url": "http://localhost:8000/api/schools/0930965U/",
-            "name": "Henri Matisse",
-            "address": {
-                "line1": "88 Bis rue Rules Guesde",
-                "line2": "",
-                "post_code": "93100",
-                "city": "Montreuil",
-                "country": {
-                "code": "FR",
-                "name": "France"
-                }
-            },
-            "students": [ ],
-            "students_count": 0
-        }
-    """
-
-    queryset = School.objects.all()
-    permission_classes = (DRYPermissions,)
-
-    def get_queryset(self):
-        queryset = self.queryset
-        queryset = self.get_serializer_class().setup_eager_loading(queryset)
-        return queryset
-
-    def get_serializer_class(self):
-        if self.action == 'choices':
-            return SchoolChoicesSerializer
-        return SchoolSerializer
-
-    @action(methods=['get'], detail=False)
-    def choices(self, request):
-        """Return list of available schools.
-
-        ### Example response
-
-            [
-                {
-                    "uai_code": "0930965U",
-                    "name": "Lycée Henri Matisse"
-                }
-            ]
-        """
-        serializer = self.get_serializer(School.objects.all(), many=True)
-        return Response(serializer.data)
-
-
-class TutoringSessionViewSet(ReadOnlyModelViewSet):
-    """API endpoint that allows tutoring sessions to be viewed or edited.
-
-    Actions: list, retrieve, create, update, partial_update, destroy
-    """
-
-    queryset = TutoringSession.objects.all()
-    serializer_class = TutoringSessionSerializer
diff --git a/users/admin.py b/users/admin.py
index 1e452282829b122d2f4c56ce184e7a4afa70669c..5131546c64907e3e684fb5d537becff1efd161f9 100644
--- a/users/admin.py
+++ b/users/admin.py
@@ -40,8 +40,7 @@ class CustomUserAdmin(UserAdmin):
     fieldsets = (
         (None, {'fields': ('email', 'password')}),
         (_('Personal info'), {'fields': (
-            'first_name', 'last_name', 'date_of_birth', 'gender',
-            'phone_number',
+            'first_name', 'last_name', 'phone_number',
         )}),
         (_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser',
                                        'groups', 'user_permissions')}),
diff --git a/users/factory.py b/users/factory.py
index 3901957bd09c9970116cb598069b19c6114dff0b..b197e55f088bb9b1901794aaad9f16272576836c 100644
--- a/users/factory.py
+++ b/users/factory.py
@@ -34,11 +34,6 @@ class UserFactory(factory.DjangoModelFactory):
 
     # this is a default, override by passing `profile_type='...'` in create()
     profile_type = None
-    date_of_birth = factory.Faker('date_this_century',
-                                  before_today=True, after_today=False,
-                                  locale='fr')
-    phone_number = factory.Faker('phone_number', locale='fr')
-    gender = factory.Iterator([User.MALE, User.FEMALE])
 
     @classmethod
     def _create(cls, model_class, *args, **kwargs):
diff --git a/users/migrations/0001_initial.py b/users/migrations/0001_initial.py
index 2e98f8e7380e32f8d0591018f2615149189ed67b..b0daf0c49a47e1fda356d3d402abb275bad6493c 100644
--- a/users/migrations/0001_initial.py
+++ b/users/migrations/0001_initial.py
@@ -1,9 +1,7 @@
-# Generated by Django 2.0.4 on 2018-04-21 12:25
+# Generated by Django 2.0.7 on 2018-09-11 17:38
 
-from django.conf import settings
 import django.contrib.auth.validators
 from django.db import migrations, models
-import django.db.models.deletion
 import django.utils.timezone
 import users.models
 
@@ -13,8 +11,6 @@ class Migration(migrations.Migration):
     initial = True
 
     dependencies = [
-        ('tutoring', '0001_initial'),
-        ('core', '0005_auto_20180408_1525'),
         ('auth', '0009_alter_user_last_name_max_length'),
     ]
 
@@ -49,28 +45,4 @@ class Migration(migrations.Migration):
                 ('objects', users.models.UserManager()),
             ],
         ),
-        migrations.CreateModel(
-            name='Student',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('address', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.Address', verbose_name='adresse')),
-                ('school', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='students', to='tutoring.School', verbose_name='lycée')),
-                ('tutoring_group', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='students', to='tutoring.TutoringGroup', verbose_name='groupe de tutorat')),
-                ('user', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='student', to=settings.AUTH_USER_MODEL, verbose_name='utilisateur')),
-            ],
-            options={
-                'verbose_name': 'lycéen',
-            },
-        ),
-        migrations.CreateModel(
-            name='Tutor',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('promotion', models.IntegerField(choices=[(2020, '2020'), (2019, '2019'), (2018, '2018'), (2017, '2017'), (2016, '2016')], default=2020)),
-                ('user', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='tutor', to=settings.AUTH_USER_MODEL, verbose_name='utilisateur')),
-            ],
-            options={
-                'verbose_name': 'tuteur',
-            },
-        ),
     ]
diff --git a/users/migrations/0002_auto_20180911_2223.py b/users/migrations/0002_auto_20180911_2223.py
new file mode 100644
index 0000000000000000000000000000000000000000..5d9ec40a8222ef98ab5782f8315997d3598f4a4d
--- /dev/null
+++ b/users/migrations/0002_auto_20180911_2223.py
@@ -0,0 +1,25 @@
+# Generated by Django 2.1.1 on 2018-09-11 20:23
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('users', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='user',
+            name='date_of_birth',
+        ),
+        migrations.RemoveField(
+            model_name='user',
+            name='gender',
+        ),
+        migrations.RemoveField(
+            model_name='user',
+            name='phone_number',
+        ),
+    ]
diff --git a/users/migrations/0002_student_registration.py b/users/migrations/0002_student_registration.py
deleted file mode 100644
index 18dd63068ab0a33ef777fe08c3902309a8d16e1b..0000000000000000000000000000000000000000
--- a/users/migrations/0002_student_registration.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-05 12:14
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('register', '0015_auto_20180505_1403'),
-        ('users', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='student',
-            name='registration',
-            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='student', to='register.Registration', verbose_name="dossier d'inscription"),
-        ),
-    ]
diff --git a/users/migrations/0003_auto_20180505_1734.py b/users/migrations/0003_auto_20180505_1734.py
deleted file mode 100644
index c32c2368fe94f6455aa1ea8b6b3c743b418eef1b..0000000000000000000000000000000000000000
--- a/users/migrations/0003_auto_20180505_1734.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-05 15:34
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('users', '0002_student_registration'),
-    ]
-
-    operations = [
-        migrations.RemoveField(
-            model_name='student',
-            name='address',
-        ),
-        migrations.RemoveField(
-            model_name='student',
-            name='registration',
-        ),
-        migrations.RemoveField(
-            model_name='student',
-            name='school',
-        ),
-        migrations.RemoveField(
-            model_name='student',
-            name='tutoring_group',
-        ),
-        migrations.RemoveField(
-            model_name='student',
-            name='user',
-        ),
-        migrations.RemoveField(
-            model_name='tutor',
-            name='user',
-        ),
-        migrations.DeleteModel(
-            name='Student',
-        ),
-        migrations.DeleteModel(
-            name='Tutor',
-        ),
-    ]
diff --git a/users/migrations/0003_user_phone_number.py b/users/migrations/0003_user_phone_number.py
new file mode 100644
index 0000000000000000000000000000000000000000..e21f92f36549728df4075addd578036a9cdf6fb9
--- /dev/null
+++ b/users/migrations/0003_user_phone_number.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.1.1 on 2018-09-20 07:50
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('users', '0002_auto_20180911_2223'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='user',
+            name='phone_number',
+            field=models.CharField(blank=True, max_length=20, null=True, verbose_name='téléphone'),
+        ),
+    ]
diff --git a/users/models.py b/users/models.py
index 5125af71812b220785bc8312c929f44112734188..264547d35d316da3c3bb6d4c5301f2a23ec5cd61 100644
--- a/users/models.py
+++ b/users/models.py
@@ -65,22 +65,6 @@ class User(AbstractUser):
 
     objects = UserManager()
 
-    date_of_birth = models.DateField(blank=True, null=True,
-                                     verbose_name='date de naissance')
-
-    MALE = 'M'
-    FEMALE = 'F'
-    GENDER_CHOICES = (
-        (MALE, 'Homme'),
-        (FEMALE, 'Femme'),
-    )
-    gender = models.CharField('sexe', blank=True, null=True,
-                              max_length=1, choices=GENDER_CHOICES)
-
-    # TODO add a proper phone number validator
-    phone_number = models.CharField('téléphone',
-                                    max_length=20, null=True, blank=True)
-
     # type of profile of the user
     PROFILE_STUDENT = 0
     PROFILE_TUTOR = 1
@@ -93,6 +77,9 @@ class User(AbstractUser):
                                     choices=PROFILE_CHOICES,
                                     verbose_name='type de profil')
 
+    phone_number = models.CharField('téléphone',
+                                    max_length=20, null=True, blank=True)
+
     @property
     def student(self):
         return getattr(self, 'student', None)
diff --git a/users/serializers.py b/users/serializers.py
index 2a294e195df3aadbf5a889a934349dd3f763b99d..fa0d945045b66544a26a25ab4fe243c098ca2425 100644
--- a/users/serializers.py
+++ b/users/serializers.py
@@ -12,9 +12,7 @@ class UserSerializer(serializers.HyperlinkedModelSerializer):
     class Meta:  # noqa
         model = User
         fields = ('id', 'email', 'profile_type',
-                  'first_name', 'last_name',
-                  'gender',
-                  'phone_number', 'date_of_birth', 'url',)
+                  'first_name', 'last_name', 'phone_number', 'url',)
         extra_kwargs = {
             'email': {'read_only': True},
             'url': {'view_name': 'api:user-detail'},
diff --git a/visits/migrations/0001_initial.py b/visits/migrations/0001_initial.py
index 3435609c2d7510dca67a1f296e20e051dab02c39..590b42b244f9793387eb2828640e3d881ac76070 100644
--- a/visits/migrations/0001_initial.py
+++ b/visits/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.0.2 on 2018-03-01 22:32
+# Generated by Django 2.0.7 on 2018-09-11 17:38
 
 from django.conf import settings
 from django.db import migrations, models
@@ -12,17 +12,32 @@ class Migration(migrations.Migration):
 
     dependencies = [
         migrations.swappable_dependency(settings.AUTH_USER_MODEL),
-        ('auth', '0009_alter_user_last_name_max_length'),
+        ('profiles', '0001_initial'),
+        ('core', '0001_initial'),
     ]
 
     operations = [
+        migrations.CreateModel(
+            name='Participation',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('submitted', models.DateTimeField(auto_now_add=True, help_text='Date de soumission de la participation', null=True, verbose_name='soumis le')),
+                ('accepted', models.NullBooleanField(help_text='Cocher pour confirmer au tutoré sa participation à la sortie.', verbose_name='accepté')),
+                ('present', models.NullBooleanField(help_text='Une fois la sortie passée, indiquer si le lycéen était présent.', verbose_name='présent')),
+                ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='participations', to=settings.AUTH_USER_MODEL, verbose_name='utilisateur')),
+            ],
+            options={
+                'verbose_name': 'participation',
+                'ordering': ('-submitted',),
+            },
+        ),
         migrations.CreateModel(
             name='Place',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('name', models.CharField(max_length=200, verbose_name='nom')),
-                ('address', models.CharField(help_text="L'adresse complète de ce lieu : numéro, rue ou voie, code postal, ville, pays si pertinent.", max_length=200, verbose_name='adresse')),
                 ('description', markdownx.models.MarkdownxField(blank=True, default='', help_text="Une description de ce lieu : de quoi s'agit-il ? Ce champ supporte Markdown.")),
+                ('address', models.ForeignKey(help_text='Adresse complète de ce lieu', null=True, on_delete=django.db.models.deletion.CASCADE, to='core.Address', verbose_name='adresse')),
             ],
             options={
                 'verbose_name': 'lieu',
@@ -37,43 +52,53 @@ class Migration(migrations.Migration):
                 ('title', models.CharField(help_text='Préciser si besoin le type de sortie (exposition, concert…) ', max_length=100, verbose_name='titre')),
                 ('summary', models.CharField(blank=True, default='', help_text='Une ou deux phrases décrivant la sortie de manière attrayante.', max_length=300, verbose_name='résumé')),
                 ('description', markdownx.models.MarkdownxField(blank=True, default='', help_text='Une description plus complète des activités proposées durant la sortie. Ce champ supporte Markdown.')),
-                ('date', models.DateTimeField(help_text="Heure de début de la sortie. Format de l'heure : hh:mm.")),
-                ('deadline', models.DateTimeField(help_text="Note : les lycéens ne pourront plus s'inscrire passé cette date. Format de l'heure : hh:mm.", verbose_name="date limite d'inscription")),
-                ('image', models.ImageField(blank=True, help_text='Une illustration représentative de la sortie. Dimensions : ???x???', null=True, upload_to='', verbose_name='illustration')),
-                ('fact_sheet', models.FileField(blank=True, help_text='Formats supportés : PDF', null=True, upload_to='', verbose_name='fiche sortie')),
-                ('organizers_group', models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to='auth.Group', verbose_name='organisateurs')),
+                ('date', models.DateField(help_text='Date de la sortie.')),
+                ('start_time', models.TimeField(help_text='Heure de début de la sortie. Format : hh:mm.', verbose_name='heure de début')),
+                ('end_time', models.TimeField(help_text='Heure de fin de la sortie. Format : hh:mm.', verbose_name='heure de fin')),
+                ('meeting', models.CharField(blank=True, default='', help_text='Indiquez aux tutorés où ils devront vous retrouver. Exemple : "devant le musée".', max_length=100, verbose_name='lieu de rendez-vous')),
+                ('deadline', models.DateTimeField(help_text="Note : les lycéens ne pourront plus s'inscrire passée cette date. Format de l'heure : hh:mm.", verbose_name="date limite d'inscription")),
+                ('image', models.ImageField(blank=True, help_text='Une illustration représentative de la sortie.', null=True, upload_to='visits/images/', verbose_name='illustration')),
+                ('fact_sheet', models.FileField(blank=True, help_text='Informe le lycéen de détails sur la sortie. Tous formats supportés, PDF recommandé.', null=True, upload_to='visits/fact_sheets/', verbose_name='fiche sortie')),
+                ('permission', models.FileField(blank=True, help_text='À mettre à disposition pour que le lycéen la remplisse. Tout format supporté, PDF recommandé.', null=True, upload_to='visits/visit_permissions/', verbose_name='autorisation de sortie')),
             ],
             options={
                 'verbose_name': 'sortie',
                 'ordering': ('date',),
-                'permissions': (('manage_visit', 'Can manage visit'),),
             },
         ),
         migrations.CreateModel(
-            name='VisitParticipant',
+            name='VisitOrganizer',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('present', models.NullBooleanField(verbose_name='présent')),
-                ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='utilisateur')),
+                ('tutor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='profiles.Tutor', verbose_name='tuteur')),
                 ('visit', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='visits.Visit', verbose_name='sortie')),
             ],
             options={
-                'verbose_name': 'participant à la sortie',
-                'verbose_name_plural': 'participants à la sortie',
+                'verbose_name': 'organisateur',
             },
         ),
+        migrations.AddField(
+            model_name='visit',
+            name='organizers',
+            field=models.ManyToManyField(related_name='organized_visits', through='visits.VisitOrganizer', to='profiles.Tutor'),
+        ),
         migrations.AddField(
             model_name='visit',
             name='participants',
-            field=models.ManyToManyField(through='visits.VisitParticipant', to=settings.AUTH_USER_MODEL),
+            field=models.ManyToManyField(through='visits.Participation', to=settings.AUTH_USER_MODEL),
         ),
         migrations.AddField(
             model_name='visit',
             name='place',
             field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='visits.Place', verbose_name='lieu'),
         ),
+        migrations.AddField(
+            model_name='participation',
+            name='visit',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='participations', to='visits.Visit', verbose_name='sortie'),
+        ),
         migrations.AlterUniqueTogether(
-            name='visitparticipant',
+            name='participation',
             unique_together={('user', 'visit')},
         ),
     ]
diff --git a/visits/migrations/0002_visitparticipant_accepted.py b/visits/migrations/0002_visitparticipant_accepted.py
deleted file mode 100644
index 13082c273f34d9625ff04c93f792becb4ef0f0a7..0000000000000000000000000000000000000000
--- a/visits/migrations/0002_visitparticipant_accepted.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.0.2 on 2018-03-02 09:24
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('visits', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='visitparticipant',
-            name='accepted',
-            field=models.BooleanField(default=False, verbose_name='accepté'),
-        ),
-    ]
diff --git a/visits/migrations/0003_auto_20180302_1104.py b/visits/migrations/0003_auto_20180302_1104.py
deleted file mode 100644
index cd9ffff5479f837834c0711cd0c5548c3694c3bc..0000000000000000000000000000000000000000
--- a/visits/migrations/0003_auto_20180302_1104.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.0.2 on 2018-03-02 10:04
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('visits', '0002_visitparticipant_accepted'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='visitparticipant',
-            name='accepted',
-            field=models.NullBooleanField(help_text='Cocher si les pièces jointes envoyées sont valides.', verbose_name='dossier validé'),
-        ),
-        migrations.AlterField(
-            model_name='visitparticipant',
-            name='present',
-            field=models.NullBooleanField(help_text='Une fois la sortie passée, indiquer si le lycéen était présent.', verbose_name='présent'),
-        ),
-    ]
diff --git a/visits/migrations/0004_visitattachedfile.py b/visits/migrations/0004_visitattachedfile.py
deleted file mode 100644
index 5b487815709cf11c469d184d1baea97bd793c15e..0000000000000000000000000000000000000000
--- a/visits/migrations/0004_visitattachedfile.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Generated by Django 2.0.2 on 2018-03-02 10:19
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('visits', '0003_auto_20180302_1104'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='VisitAttachedFile',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('name', models.CharField(max_length=200, verbose_name='nom')),
-                ('file', models.FileField(upload_to='', verbose_name='fichier')),
-                ('visit', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attached_files', to='visits.Visit')),
-            ],
-            options={
-                'verbose_name': 'pièce jointe',
-                'verbose_name_plural': 'pièces jointes',
-                'ordering': ('visit',),
-            },
-        ),
-    ]
diff --git a/visits/migrations/0005_remove_visitattachedfile_file.py b/visits/migrations/0005_remove_visitattachedfile_file.py
deleted file mode 100644
index 3c893f90549b5a22f089be05948f204dfd7f9a22..0000000000000000000000000000000000000000
--- a/visits/migrations/0005_remove_visitattachedfile_file.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 2.0.2 on 2018-03-02 10:22
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('visits', '0004_visitattachedfile'),
-    ]
-
-    operations = [
-        migrations.RemoveField(
-            model_name='visitattachedfile',
-            name='file',
-        ),
-    ]
diff --git a/visits/migrations/0006_visitattachedfile_required.py b/visits/migrations/0006_visitattachedfile_required.py
deleted file mode 100644
index 00123fde9488339d5c8916d0244eb16522265f06..0000000000000000000000000000000000000000
--- a/visits/migrations/0006_visitattachedfile_required.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.0.2 on 2018-03-02 10:23
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('visits', '0005_remove_visitattachedfile_file'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='visitattachedfile',
-            name='required',
-            field=models.BooleanField(default=True, verbose_name='requis'),
-        ),
-    ]
diff --git a/visits/migrations/0007_auto_20180304_1413.py b/visits/migrations/0007_auto_20180304_1413.py
deleted file mode 100644
index 76a225a85467cab2b772cfa89c5c95e16bcbcc2d..0000000000000000000000000000000000000000
--- a/visits/migrations/0007_auto_20180304_1413.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.0.2 on 2018-03-04 13:13
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('visits', '0006_visitattachedfile_required'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='visitattachedfile',
-            name='visit',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attached_files', to='visits.Visit', verbose_name='sortie'),
-        ),
-    ]
diff --git a/visits/migrations/0008_auto_20180319_2127.py b/visits/migrations/0008_auto_20180319_2127.py
deleted file mode 100644
index 22d1f91d2eaceb82d3fec55ca3cea299ef93eecd..0000000000000000000000000000000000000000
--- a/visits/migrations/0008_auto_20180319_2127.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.0.3 on 2018-03-19 20:27
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('visits', '0007_auto_20180304_1413'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='visit',
-            name='fact_sheet',
-            field=models.FileField(blank=True, help_text='Formats supportés : PDF', null=True, upload_to='visits/fact_sheets/', verbose_name='fiche sortie'),
-        ),
-        migrations.AlterField(
-            model_name='visit',
-            name='image',
-            field=models.ImageField(blank=True, help_text='Une illustration représentative de la sortie. Dimensions : ???x???', null=True, upload_to='visits/images/', verbose_name='illustration'),
-        ),
-    ]
diff --git a/visits/migrations/0009_auto_20180520_1230.py b/visits/migrations/0009_auto_20180520_1230.py
deleted file mode 100644
index cf10471aa22729423fe72e5f66bc018d7fbcb002..0000000000000000000000000000000000000000
--- a/visits/migrations/0009_auto_20180520_1230.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-20 10:30
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('visits', '0008_auto_20180319_2127'),
-    ]
-
-    operations = [
-        migrations.RenameModel(
-            old_name='VisitAttachedFile',
-            new_name='AttachedFile',
-        ),
-    ]
diff --git a/visits/migrations/0010_auto_20180520_1248.py b/visits/migrations/0010_auto_20180520_1248.py
deleted file mode 100644
index 75bb2c91e0886477d99e6d30c5d1323fd809e912..0000000000000000000000000000000000000000
--- a/visits/migrations/0010_auto_20180520_1248.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-20 10:48
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('profiles', '0002_auto_20180512_1344'),
-        ('visits', '0009_auto_20180520_1230'),
-    ]
-
-    operations = [
-        migrations.RemoveField(
-            model_name='visit',
-            name='organizers_group',
-        ),
-        migrations.AddField(
-            model_name='visit',
-            name='organizers',
-            field=models.ManyToManyField(related_name='organized_visits', to='profiles.Tutor'),
-        ),
-    ]
diff --git a/visits/migrations/0011_auto_20180520_1253.py b/visits/migrations/0011_auto_20180520_1253.py
deleted file mode 100644
index 428e03139ba844969fa561836404b789e036959c..0000000000000000000000000000000000000000
--- a/visits/migrations/0011_auto_20180520_1253.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-20 10:53
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('visits', '0010_auto_20180520_1248'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='place',
-            name='address',
-            field=models.ForeignKey(help_text='Adresse complète de ce lieu', null=True, on_delete=django.db.models.deletion.CASCADE, to='core.Address'),
-        ),
-    ]
diff --git a/visits/migrations/0012_auto_20180520_1305.py b/visits/migrations/0012_auto_20180520_1305.py
deleted file mode 100644
index 9a09da5ea898af0828ab05255d816a97b81afaa5..0000000000000000000000000000000000000000
--- a/visits/migrations/0012_auto_20180520_1305.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-20 11:05
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('profiles', '0002_auto_20180512_1344'),
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
-        ('visits', '0011_auto_20180520_1253'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='VisitOrganizer',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('tutor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='profiles.Tutor', verbose_name='tuteur')),
-            ],
-        ),
-        migrations.RenameModel(
-            old_name='VisitParticipant',
-            new_name='Participation',
-        ),
-        migrations.AlterModelOptions(
-            name='participation',
-            options={'verbose_name': 'participation'},
-        ),
-        migrations.AlterModelOptions(
-            name='visit',
-            options={'ordering': ('date',), 'verbose_name': 'sortie'},
-        ),
-        migrations.RemoveField(
-            model_name='visit',
-            name='organizers',
-        ),
-        migrations.AlterField(
-            model_name='place',
-            name='address',
-            field=models.ForeignKey(help_text='Adresse complète de ce lieu', null=True, on_delete=django.db.models.deletion.CASCADE, to='core.Address', verbose_name='adresse'),
-        ),
-        migrations.AddField(
-            model_name='visitorganizer',
-            name='visit',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='visits.Visit', verbose_name='sortie'),
-        ),
-    ]
diff --git a/visits/migrations/0013_visit_organizers.py b/visits/migrations/0013_visit_organizers.py
deleted file mode 100644
index 785d8b4186b1aee9f06c6e74bd586ee305ddaaa0..0000000000000000000000000000000000000000
--- a/visits/migrations/0013_visit_organizers.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-20 11:05
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('profiles', '0002_auto_20180512_1344'),
-        ('visits', '0012_auto_20180520_1305'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='visit',
-            name='organizers',
-            field=models.ManyToManyField(related_name='organized_visits', through='visits.VisitOrganizer', to='profiles.Tutor'),
-        ),
-    ]
diff --git a/visits/migrations/0014_auto_20180520_1317.py b/visits/migrations/0014_auto_20180520_1317.py
deleted file mode 100644
index c73c7301c0d9e3358bc2002150b4c2b14ee12cef..0000000000000000000000000000000000000000
--- a/visits/migrations/0014_auto_20180520_1317.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-20 11:17
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('visits', '0013_visit_organizers'),
-    ]
-
-    operations = [
-        migrations.AlterModelOptions(
-            name='visitorganizer',
-            options={'verbose_name': 'organisateur'},
-        ),
-    ]
diff --git a/visits/migrations/0015_auto_20180520_1403.py b/visits/migrations/0015_auto_20180520_1403.py
deleted file mode 100644
index bf7694c0676758a49b2683e24dabed9c1561911c..0000000000000000000000000000000000000000
--- a/visits/migrations/0015_auto_20180520_1403.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-20 12:03
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('visits', '0014_auto_20180520_1317'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='participation',
-            name='user',
-            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='participations', to=settings.AUTH_USER_MODEL, verbose_name='utilisateur'),
-        ),
-        migrations.AlterField(
-            model_name='participation',
-            name='visit',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='participations', to='visits.Visit', verbose_name='sortie'),
-        ),
-    ]
diff --git a/visits/migrations/0016_auto_20180522_2139.py b/visits/migrations/0016_auto_20180522_2139.py
deleted file mode 100644
index 642d4b7881ac5919767db35e76d8c31443ffae00..0000000000000000000000000000000000000000
--- a/visits/migrations/0016_auto_20180522_2139.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-22 19:39
-
-import datetime
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('visits', '0015_auto_20180520_1403'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='visit',
-            name='end_time',
-            field=models.TimeField(default=datetime.time(21, 39, 23, 229059), help_text='Heure de fin de la sortie. Format : hh:mm.', verbose_name='heure de fin'),
-            preserve_default=False,
-        ),
-        migrations.AddField(
-            model_name='visit',
-            name='meeting',
-            field=models.CharField(blank=True, default='', help_text='Indiquez aux tutorés où ils devront vous retrouver. Exemple : "devant le musée".', max_length=100, verbose_name='lieu de rendez-vous'),
-        ),
-        migrations.AddField(
-            model_name='visit',
-            name='permission',
-            field=models.FileField(blank=True, help_text='À mettre à disposition pour que le lycéen la remplisse. Tout format supporté, PDF recommandé.', null=True, upload_to='visits/visit_permissions/', verbose_name='autorisation de sortie'),
-        ),
-        migrations.AddField(
-            model_name='visit',
-            name='start_time',
-            field=models.TimeField(default=datetime.time(21, 39, 32, 604487), help_text='Heure de début de la sortie. Format : hh:mm.', verbose_name='heure de début'),
-            preserve_default=False,
-        ),
-        migrations.AlterField(
-            model_name='visit',
-            name='date',
-            field=models.DateField(help_text='Date de la sortie.'),
-        ),
-        migrations.AlterField(
-            model_name='visit',
-            name='deadline',
-            field=models.DateTimeField(help_text="Note : les lycéens ne pourront plus s'inscrire passée cette date. Format de l'heure : hh:mm.", verbose_name="date limite d'inscription"),
-        ),
-        migrations.AlterField(
-            model_name='visit',
-            name='fact_sheet',
-            field=models.FileField(blank=True, help_text='Informe le lycéen de détails sur la sortie. Tous formats supportés, PDF recommandé.', null=True, upload_to='visits/fact_sheets/', verbose_name='fiche sortie'),
-        ),
-        migrations.AlterField(
-            model_name='visit',
-            name='image',
-            field=models.ImageField(blank=True, help_text='Une illustration représentative de la sortie.', null=True, upload_to='visits/images/', verbose_name='illustration'),
-        ),
-    ]
diff --git a/visits/migrations/0017_auto_20180522_2233.py b/visits/migrations/0017_auto_20180522_2233.py
deleted file mode 100644
index c8e6e052e579b1fd0d4b5892e7e2eeb85e82ca09..0000000000000000000000000000000000000000
--- a/visits/migrations/0017_auto_20180522_2233.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-22 20:33
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('visits', '0016_auto_20180522_2139'),
-    ]
-
-    operations = [
-        migrations.RemoveField(
-            model_name='attachedfile',
-            name='visit',
-        ),
-        migrations.DeleteModel(
-            name='AttachedFile',
-        ),
-    ]
diff --git a/visits/migrations/0018_auto_20180523_2156.py b/visits/migrations/0018_auto_20180523_2156.py
deleted file mode 100644
index 1d41d58ce1609a57d954696a241c1ec80ffdf32a..0000000000000000000000000000000000000000
--- a/visits/migrations/0018_auto_20180523_2156.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-23 19:56
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('visits', '0017_auto_20180522_2233'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='participation',
-            name='accepted',
-            field=models.NullBooleanField(help_text='Cocher pour confirmer au tutoré sa participation à la sortie.', verbose_name='accepté'),
-        ),
-    ]
diff --git a/visits/migrations/0019_auto_20180526_2034.py b/visits/migrations/0019_auto_20180526_2034.py
deleted file mode 100644
index c5a12a0982f35cf87fa4391a66d1f2ba3d9e8e05..0000000000000000000000000000000000000000
--- a/visits/migrations/0019_auto_20180526_2034.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Generated by Django 2.0.4 on 2018-05-26 18:34
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('visits', '0018_auto_20180523_2156'),
-    ]
-
-    operations = [
-        migrations.AlterModelOptions(
-            name='participation',
-            options={'ordering': ('-submitted',), 'verbose_name': 'participation'},
-        ),
-        migrations.AddField(
-            model_name='participation',
-            name='submitted',
-            field=models.DateTimeField(auto_now_add=True, help_text='Date de soumission de la participation', null=True, verbose_name='soumis le'),
-        ),
-    ]
diff --git a/visits/models.py b/visits/models.py
index 62aef12da83f59786fb49b24ebd251169244e56b..63fadd8684e1024d63dc718407bcc9f1d2df22e1 100644
--- a/visits/models.py
+++ b/visits/models.py
@@ -1,5 +1,6 @@
 """Visits models."""
 from django.db import models
+from django.contrib.sites.models import Site
 from django.shortcuts import reverse
 from django.utils.timezone import now
 from dry_rest_permissions.generics import authenticated_users
@@ -214,7 +215,8 @@ class Visit(models.Model):
         return reverse('api:visit-detail', args=[str(self.pk)])
 
     def get_site_url(self):
-        return f'http://oser-cs.fr/visits/{self.pk}'
+        site = Site.objects.get_current()
+        return f'https://{site.domain}/visits/{self.pk}'
 
     def __str__(self):
         return str(self.title)