From 54c4c0e83d3051a5be3d5622cc98f3c989b19a4a Mon Sep 17 00:00:00 2001 From: Seon82 <46298009+Seon82@users.noreply.github.com> Date: Wed, 30 Sep 2020 17:11:59 +0200 Subject: [PATCH] Inscription sur le site (#21) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Password reset feature (#8) * Add Django Rest auth module * Try to make the send reset password email work * Modified template mail for reset * Add Django Rest auth module * Try to make the send reset password email work * Modified template mail for reset * test * Ajouté API student * API GET sans authentification * Added POST support * POST API fix * Fixed charfields length in wiki * loosen security * simplify API serialized data * Implemented partial_update * Added special teaching and nationality to serializer * dependantsNumber to int * Fixed factory imports * Added write auth and minor serializer fix * Added read permissions * Read permissions that work? * Actually added read permissions? * Updated tests to pass travis * Updated tests * Fixed all factory tests * Adapté les tests à la nouvelle structure de Student * Created StaffUserFactory and finished fixing tests * Update README.md * Added classType * Fixed migrations * Added last modified year & filters in admin interface Co-authored-by: chiahetcho <44137047+chiahetcho@users.noreply.github.com> Co-authored-by: florimondmanca <florimond.manca@gmail.com> Co-authored-by: Dylan Sechet <dylan.sechet@student-cs.fr> Co-authored-by: Arthur Guédon <arthur.guedon@student-cs.fr> --- README.md | 11 +- api/urls.py | 2 +- core/factory.py | 4 +- oser_backend/settings/common.py | 6 +- profiles/admin.py | 2 +- profiles/factory.py | 4 +- .../migrations/0003_auto_20200914_0057.py | 88 +++++++++++ .../migrations/0004_auto_20200915_1827.py | 43 ++++++ .../migrations/0004_auto_20200918_1320.py | 43 ++++++ .../migrations/0005_auto_20200918_1805.py | 18 +++ .../migrations/0006_auto_20200918_1818.py | 28 ++++ .../migrations/0007_merge_20200918_1824.py | 14 ++ .../migrations/0008_auto_20200918_1913.py | 18 +++ .../migrations/0009_auto_20200923_1329.py | 43 ++++++ .../migrations/0010_auto_20200923_1444.py | 18 +++ profiles/migrations/0011_student_classtype.py | 18 +++ profiles/migrations/0012_student_year.py | 18 +++ profiles/models.py | 140 +++++++++++++++++- profiles/serializers.py | 4 +- profiles/views.py | 42 ++---- projects/factory.py | 8 +- register/factory.py | 2 +- test | 0 tests/test_profiles/test_student.py | 5 +- tests/test_profiles/test_student_api.py | 2 +- users/factory.py | 6 +- visits/factory.py | 6 +- 27 files changed, 536 insertions(+), 57 deletions(-) create mode 100644 profiles/migrations/0003_auto_20200914_0057.py create mode 100644 profiles/migrations/0004_auto_20200915_1827.py create mode 100644 profiles/migrations/0004_auto_20200918_1320.py create mode 100644 profiles/migrations/0005_auto_20200918_1805.py create mode 100644 profiles/migrations/0006_auto_20200918_1818.py create mode 100644 profiles/migrations/0007_merge_20200918_1824.py create mode 100644 profiles/migrations/0008_auto_20200918_1913.py create mode 100644 profiles/migrations/0009_auto_20200923_1329.py create mode 100644 profiles/migrations/0010_auto_20200923_1444.py create mode 100644 profiles/migrations/0011_student_classtype.py create mode 100644 profiles/migrations/0012_student_year.py create mode 100644 test diff --git a/README.md b/README.md index f5660a8..1b64c6d 100644 --- a/README.md +++ b/README.md @@ -50,13 +50,17 @@ Le site utilise une base de données SQL. Plusieurs technologies existent mais o Après avoir installé PostgreSQL, démarrez le serveur en ouvrant pgAdmin, l'interface graphique qui sera installée en même temps que Postgres. +- Créez une nouvelle base de données : + +Dans le menu de gauche, dépliez `Servers (1)`, puis votre serveur (nommé `PostgreSQL XX` par défaut), puis faites un clic droit sur l'onglet `Databases` et créez une base de données nommée `oser_backend_db`. + ### Installation du projet -- (Recommandé) Créez un environnement virtuel (ici appelé `env`) puis activez-le : +- (Recommandé) Créez un environnement virtuel (appelé ici oser-back) à l'aide de conda et activez-le : ```bash -$ python -m venv env -$ source env/bin/activate +$ conda create -n oser-back +$ conda activate oser-back ``` - Installez les dépendances : @@ -70,6 +74,7 @@ $ pip install -r requirements.txt ```bash $ python manage.py migrate ``` +(En cas d'erreur, les logs du serveur PostgreSQL sont disponibles dans : %PROGRAMFILES%\PostgreSQL\POSTGRESQL_VERSION_NUM\data\log) Il ne vous reste plus qu'à lancer le serveur de développement : diff --git a/api/urls.py b/api/urls.py index dbd3f62..46497be 100644 --- a/api/urls.py +++ b/api/urls.py @@ -26,7 +26,7 @@ router.register('users', users_views.UserViewSet) # Profiles views router.register('tutors', profiles_views.TutorViewSet) -router.register('students', profiles_views.StudentViewSet) +router.register('students', profiles_views.StudentViewSet, base_name='student') # Register views router.register('registrations', register_views.RegistrationViewSet) diff --git a/core/factory.py b/core/factory.py index a812eed..7167327 100644 --- a/core/factory.py +++ b/core/factory.py @@ -5,7 +5,7 @@ import factory.django from . import models -class DocumentFactory(factory.DjangoModelFactory): +class DocumentFactory(factory.django.DjangoModelFactory): """Document object factory.""" class Meta: # noqa @@ -15,7 +15,7 @@ class DocumentFactory(factory.DjangoModelFactory): content = factory.Faker('text', max_nb_chars=1000, locale='fr') -class AddressFactory(factory.DjangoModelFactory): +class AddressFactory(factory.django.DjangoModelFactory): """Address object factory.""" class Meta: # noqa diff --git a/oser_backend/settings/common.py b/oser_backend/settings/common.py index eca12cc..9579af4 100644 --- a/oser_backend/settings/common.py +++ b/oser_backend/settings/common.py @@ -38,7 +38,7 @@ DJANGO_APPS = [ 'whitenoise.runserver_nostatic', 'django.contrib.staticfiles', 'django.forms', - 'django.contrib.sites', + 'django.contrib.sites' ] THIRD_PARTY_APPS = [ @@ -236,7 +236,7 @@ LOGGING = { # Connect custom PasswordResetSerializer to override default REST_AUTH_SERIALIZERS = { - 'PASSWORD_RESET_SERIALIZER': + 'PASSWORD_RESET_SERIALIZER': 'oser_backend.serializers.PasswordResetSerializer', } @@ -246,7 +246,7 @@ DEFAULT_FROM_EMAIL = "admin@oser-cs.fr" EMAIL_BACKEND = 'sendgrid_backend.SendgridBackend' SENDGRID_API_KEY = os.getenv('SENDGRID_API_KEY') -# Sendgrid configuration +# Sendgrid configuration EMAIL_HOST = 'smtp.sendgrid.net' EMAIL_HOST_USER = 'apikey' diff --git a/profiles/admin.py b/profiles/admin.py index 4f00aa1..63f6e66 100644 --- a/profiles/admin.py +++ b/profiles/admin.py @@ -43,7 +43,7 @@ class TutorAdmin(ProfileAdminMixin, admin.ModelAdmin,ExportCsvMixin): @admin.register(Student) class StudentAdmin(ProfileAdminMixin, admin.ModelAdmin,ExportCsvMixin): """Student admin panel.""" - + list_filter = ('school', 'year') class Meta: # noqa model = Student actions = ["export_as_csv"] \ No newline at end of file diff --git a/profiles/factory.py b/profiles/factory.py index 886b5dd..6b8b4ff 100644 --- a/profiles/factory.py +++ b/profiles/factory.py @@ -12,7 +12,7 @@ from users.factory import UserFactory from . import models -class StudentFactory(factory.DjangoModelFactory): +class StudentFactory(factory.django.DjangoModelFactory): """Student object factory.""" class Meta: # noqa @@ -24,7 +24,7 @@ class StudentFactory(factory.DjangoModelFactory): _this_year = datetime.today().year -class TutorFactory(factory.DjangoModelFactory): +class TutorFactory(factory.django.DjangoModelFactory): """Tutor object factory.""" class Meta: # noqa diff --git a/profiles/migrations/0003_auto_20200914_0057.py b/profiles/migrations/0003_auto_20200914_0057.py new file mode 100644 index 0000000..ebe33bd --- /dev/null +++ b/profiles/migrations/0003_auto_20200914_0057.py @@ -0,0 +1,88 @@ +# Generated by Django 2.2 on 2020-09-13 22:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('profiles', '0002_auto_20180911_1938'), + ] + + operations = [ + migrations.AddField( + model_name='student', + name='addressNumber', + field=models.IntegerField(blank=True, null=True, verbose_name='numéro de rue'), + ), + migrations.AddField( + model_name='student', + name='city', + field=models.CharField(blank=True, max_length=50, null=True, verbose_name='nom de ville'), + ), + migrations.AddField( + model_name='student', + name='dependantsNumber', + field=models.CharField(blank=True, max_length=12, null=True, verbose_name="numero d'urgence"), + ), + migrations.AddField( + model_name='student', + name='fatherActivity', + field=models.CharField(blank=True, max_length=70, null=True, verbose_name='metier du pere'), + ), + migrations.AddField( + model_name='student', + name='gender', + field=models.CharField(blank=True, max_length=20, null=True, verbose_name='genre'), + ), + migrations.AddField( + model_name='student', + name='grade', + field=models.CharField(blank=True, max_length=20, null=True, verbose_name='nom du niveau de la classe'), + ), + migrations.AddField( + model_name='student', + name='motherActivity', + field=models.CharField(blank=True, max_length=70, null=True, verbose_name='metier de la mere'), + ), + migrations.AddField( + model_name='student', + name='parentsEmail', + field=models.EmailField(blank=True, max_length=20, null=True, verbose_name='adresse mail parentale'), + ), + migrations.AddField( + model_name='student', + name='parentsPhone', + field=models.CharField(blank=True, max_length=12, null=True, verbose_name='numéro de téléphone parental'), + ), + migrations.AddField( + model_name='student', + name='parentsStatus', + field=models.CharField(blank=True, max_length=30, null=True, verbose_name='statut des parents'), + ), + migrations.AddField( + model_name='student', + name='personnalPhone', + field=models.CharField(blank=True, max_length=12, null=True, verbose_name='numéro de téléphone personnel'), + ), + migrations.AddField( + model_name='student', + name='scholarship', + field=models.BooleanField(blank=True, null=True, verbose_name='boursier'), + ), + migrations.AddField( + model_name='student', + name='school', + field=models.CharField(blank=True, max_length=70, null=True, verbose_name="nom de l'ecole"), + ), + migrations.AddField( + model_name='student', + name='street', + field=models.CharField(blank=True, max_length=70, null=True, verbose_name='nom de rue'), + ), + migrations.AlterField( + model_name='tutor', + name='promotion', + field=models.IntegerField(choices=[(2023, '2023'), (2022, '2022'), (2021, '2021'), (2020, '2020'), (2019, '2019')], default=2023), + ), + ] diff --git a/profiles/migrations/0004_auto_20200915_1827.py b/profiles/migrations/0004_auto_20200915_1827.py new file mode 100644 index 0000000..d94b486 --- /dev/null +++ b/profiles/migrations/0004_auto_20200915_1827.py @@ -0,0 +1,43 @@ +# Generated by Django 2.2 on 2020-09-15 16:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('profiles', '0003_auto_20200914_0057'), + ] + + operations = [ + migrations.AlterField( + model_name='student', + name='dependantsNumber', + field=models.CharField(blank=True, max_length=12, null=True, verbose_name="numéro d'urgence"), + ), + migrations.AlterField( + model_name='student', + name='fatherActivity', + field=models.CharField(blank=True, max_length=70, null=True, verbose_name='métier du père'), + ), + migrations.AlterField( + model_name='student', + name='grade', + field=models.CharField(blank=True, max_length=20, null=True, verbose_name='niveau de la classe'), + ), + migrations.AlterField( + model_name='student', + name='motherActivity', + field=models.CharField(blank=True, max_length=70, null=True, verbose_name='métier de la mère'), + ), + migrations.AlterField( + model_name='student', + name='scholarship', + field=models.NullBooleanField(verbose_name='boursier'), + ), + migrations.AlterField( + model_name='student', + name='school', + field=models.CharField(blank=True, max_length=70, null=True, verbose_name="nom de l'école"), + ), + ] diff --git a/profiles/migrations/0004_auto_20200918_1320.py b/profiles/migrations/0004_auto_20200918_1320.py new file mode 100644 index 0000000..0b1e26b --- /dev/null +++ b/profiles/migrations/0004_auto_20200918_1320.py @@ -0,0 +1,43 @@ +# Generated by Django 2.2 on 2020-09-18 11:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('profiles', '0003_auto_20200914_0057'), + ] + + operations = [ + migrations.AlterField( + model_name='student', + name='dependantsNumber', + field=models.CharField(blank=True, max_length=12, null=True, verbose_name="numéro d'urgence"), + ), + migrations.AlterField( + model_name='student', + name='fatherActivity', + field=models.CharField(blank=True, max_length=70, null=True, verbose_name='métier du père'), + ), + migrations.AlterField( + model_name='student', + name='grade', + field=models.CharField(blank=True, max_length=20, null=True, verbose_name='niveau de la classe'), + ), + migrations.AlterField( + model_name='student', + name='motherActivity', + field=models.CharField(blank=True, max_length=70, null=True, verbose_name='métier de la mère'), + ), + migrations.AlterField( + model_name='student', + name='scholarship', + field=models.NullBooleanField(verbose_name='boursier'), + ), + migrations.AlterField( + model_name='student', + name='school', + field=models.CharField(blank=True, max_length=70, null=True, verbose_name="nom de l'école"), + ), + ] diff --git a/profiles/migrations/0005_auto_20200918_1805.py b/profiles/migrations/0005_auto_20200918_1805.py new file mode 100644 index 0000000..7e5756b --- /dev/null +++ b/profiles/migrations/0005_auto_20200918_1805.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2 on 2020-09-18 16:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('profiles', '0004_auto_20200918_1320'), + ] + + operations = [ + migrations.AlterField( + model_name='student', + name='scholarship', + field=models.CharField(blank=True, max_length=10, null=True, verbose_name='boursier'), + ), + ] diff --git a/profiles/migrations/0006_auto_20200918_1818.py b/profiles/migrations/0006_auto_20200918_1818.py new file mode 100644 index 0000000..a1d92c8 --- /dev/null +++ b/profiles/migrations/0006_auto_20200918_1818.py @@ -0,0 +1,28 @@ +# Generated by Django 2.2 on 2020-09-18 16:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('profiles', '0005_auto_20200918_1805'), + ] + + operations = [ + migrations.AddField( + model_name='student', + name='nationality', + field=models.CharField(blank=True, max_length=30, null=True, verbose_name='nationalité'), + ), + migrations.AddField( + model_name='student', + name='specialTeaching', + field=models.CharField(blank=True, max_length=30, null=True, verbose_name='enseignement de spécialité'), + ), + migrations.AddField( + model_name='student', + name='zipCode', + field=models.CharField(blank=True, max_length=10, null=True, verbose_name='code postal'), + ), + ] diff --git a/profiles/migrations/0007_merge_20200918_1824.py b/profiles/migrations/0007_merge_20200918_1824.py new file mode 100644 index 0000000..ff45a34 --- /dev/null +++ b/profiles/migrations/0007_merge_20200918_1824.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2 on 2020-09-18 16:24 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('profiles', '0006_auto_20200918_1818'), + ('profiles', '0004_auto_20200915_1827'), + ] + + operations = [ + ] diff --git a/profiles/migrations/0008_auto_20200918_1913.py b/profiles/migrations/0008_auto_20200918_1913.py new file mode 100644 index 0000000..cdf1810 --- /dev/null +++ b/profiles/migrations/0008_auto_20200918_1913.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2 on 2020-09-18 17:13 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('profiles', '0007_merge_20200918_1824'), + ] + + operations = [ + migrations.AlterField( + model_name='student', + name='scholarship', + field=models.CharField(blank=True, max_length=10, null=True, verbose_name='boursier'), + ), + ] diff --git a/profiles/migrations/0009_auto_20200923_1329.py b/profiles/migrations/0009_auto_20200923_1329.py new file mode 100644 index 0000000..5dc6c13 --- /dev/null +++ b/profiles/migrations/0009_auto_20200923_1329.py @@ -0,0 +1,43 @@ +# Generated by Django 2.2 on 2020-09-23 11:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('profiles', '0008_auto_20200918_1913'), + ] + + operations = [ + migrations.AlterField( + model_name='student', + name='dependantsNumber', + field=models.IntegerField(blank=True, null=True, verbose_name='nombre de personnes à charge'), + ), + migrations.AlterField( + model_name='student', + name='nationality', + field=models.CharField(blank=True, max_length=50, null=True, verbose_name='nationalité'), + ), + migrations.AlterField( + model_name='student', + name='parentsEmail', + field=models.EmailField(blank=True, max_length=70, null=True, verbose_name='adresse mail parentale'), + ), + migrations.AlterField( + model_name='student', + name='parentsStatus', + field=models.CharField(blank=True, max_length=70, null=True, verbose_name='statut des parents'), + ), + migrations.AlterField( + model_name='student', + name='scholarship', + field=models.CharField(blank=True, max_length=50, null=True, verbose_name='boursier'), + ), + migrations.AlterField( + model_name='student', + name='specialTeaching', + field=models.CharField(blank=True, max_length=50, null=True, verbose_name='enseignement de spécialité'), + ), + ] diff --git a/profiles/migrations/0010_auto_20200923_1444.py b/profiles/migrations/0010_auto_20200923_1444.py new file mode 100644 index 0000000..fd5da54 --- /dev/null +++ b/profiles/migrations/0010_auto_20200923_1444.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2 on 2020-09-23 12:44 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('profiles', '0009_auto_20200923_1329'), + ] + + operations = [ + migrations.RenameField( + model_name='student', + old_name='personnalPhone', + new_name='personalPhone', + ), + ] diff --git a/profiles/migrations/0011_student_classtype.py b/profiles/migrations/0011_student_classtype.py new file mode 100644 index 0000000..0482118 --- /dev/null +++ b/profiles/migrations/0011_student_classtype.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2 on 2020-09-24 16:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('profiles', '0010_auto_20200923_1444'), + ] + + operations = [ + migrations.AddField( + model_name='student', + name='classType', + field=models.CharField(blank=True, max_length=50, null=True, verbose_name='général/techno'), + ), + ] diff --git a/profiles/migrations/0012_student_year.py b/profiles/migrations/0012_student_year.py new file mode 100644 index 0000000..359b6cb --- /dev/null +++ b/profiles/migrations/0012_student_year.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2 on 2020-09-25 12:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('profiles', '0011_student_classtype'), + ] + + operations = [ + migrations.AddField( + model_name='student', + name='year', + field=models.CharField(blank=True, max_length=10, null=True, verbose_name='année'), + ), + ] diff --git a/profiles/models.py b/profiles/models.py index 28061fa..675354d 100644 --- a/profiles/models.py +++ b/profiles/models.py @@ -4,7 +4,7 @@ from django.db import models from django.shortcuts import reverse from dry_rest_permissions.generics import authenticated_users from .utils import get_promotion_range - +from datetime import datetime class ProfileMixin: """Mixin with common functionnality for profiles.""" @@ -52,6 +52,144 @@ class Student(ProfileMixin, models.Model): related_name='student', ) + classType = models.CharField(max_length=50, + null=True, + blank=True, + verbose_name="général/techno", + ) + + nationality = models.CharField(max_length=50, + null=True, + blank=True, + verbose_name="nationalité", + ) + + specialTeaching = models.CharField(max_length=50, + null=True, + blank=True, + verbose_name="enseignement de spécialité", + ) + + zipCode = models.CharField(max_length=10, + null=True, + blank=True, + verbose_name="code postal", + ) + + + gender = models.CharField(max_length=20, + null=True, + blank=True, + verbose_name="genre", + ) + + addressNumber = models.IntegerField( + null=True, + blank=True, + verbose_name="numéro de rue" + ) + + street = models.CharField(max_length=70, + null=True, + blank=True, + verbose_name="nom de rue" + ) + + city = models.CharField(max_length=50, + null=True, + blank=True, + verbose_name="nom de ville" + ) + + personalPhone = models.CharField(max_length=12, + null=True, + blank=True, + verbose_name="numéro de téléphone personnel" + ) + + parentsPhone = models.CharField(max_length=12, + null=True, + blank=True, + verbose_name="numéro de téléphone parental" + ) + + parentsEmail = models.EmailField(max_length=70, + null=True, + blank=True, + verbose_name="adresse mail parentale" + ) + + + school = models.CharField(max_length=70, + null=True, + blank=True, + verbose_name="établissement" + ) + + + grade = models.CharField(max_length=20, + null=True, + blank=True, + verbose_name="niveau de la classe" + ) + + + scholarship = models.CharField(max_length=50, + null=True, + blank=True, + verbose_name="boursier" + ) + + + fatherActivity = models.CharField(max_length=70, + null=True, + blank=True, + verbose_name="métier du père" + ) + + + motherActivity = models.CharField(max_length=70, + null=True, + blank=True, + verbose_name="métier de la mère" + ) + + + parentsStatus = models.CharField(max_length=70, + null=True, + blank=True, + verbose_name="statut des parents" + ) + + + dependantsNumber = models.IntegerField( + null=True, + blank=True, + verbose_name="nombre de personnes à charge" + ) + + year = models.CharField(max_length=10, + null=True, + blank=True, + verbose_name="année" + ) + + @staticmethod + def has_write_permission(request): + return True + + def has_object_write_permission(self, request): + return request.user == self.user + + def save(self, *args, **kwargs): + """Updates the year field based on the last modified date""" + date_now = datetime.now() + if date_now.month>=9: + self.year = f"{date_now.year}/{date_now.year+1}" + else: + self.year = f"{date_now.year-1}/{date_now.year}" + return super(Student,self).save(*args, **kwargs) + class Meta: # noqa verbose_name = 'lycéen' diff --git a/profiles/serializers.py b/profiles/serializers.py index 31b2031..778dc21 100644 --- a/profiles/serializers.py +++ b/profiles/serializers.py @@ -31,11 +31,11 @@ class StudentSerializer(serializers.HyperlinkedModelSerializer): read_only=True) registration = StudentRegistrationSerializer() + class Meta: # noqa model = Student fields = ( - 'user_id', 'user', 'registration', 'visits', 'url', - ) + 'user_id', 'user', 'url', 'registration', 'visits', 'gender', 'addressNumber', 'street', 'city', 'personalPhone', 'parentsPhone', 'parentsEmail', 'school', 'grade', 'scholarship', 'fatherActivity', 'motherActivity', 'parentsStatus', 'dependantsNumber', 'specialTeaching', 'nationality', 'zipCode', 'classType') extra_kwargs = { 'url': {'view_name': 'api:student-detail'}, } diff --git a/profiles/views.py b/profiles/views.py index 0c157ef..b81ec2c 100644 --- a/profiles/views.py +++ b/profiles/views.py @@ -5,6 +5,7 @@ from dry_rest_permissions.generics import DRYPermissions from rest_framework import viewsets from rest_framework.decorators import action from rest_framework.response import Response +from django_filters.rest_framework import DjangoFilterBackend from visits.serializers import VisitSerializer @@ -23,40 +24,21 @@ class TutorViewSet(viewsets.ReadOnlyModelViewSet): permission_classes = (DRYPermissions,) -class StudentViewSet(viewsets.ReadOnlyModelViewSet): - """API endpoint that allows students to be viewed. +class StudentViewSet(viewsets.ModelViewSet): + """API endpoint that allows students to be viewed, and profiles to be updated.""" - list: + def get_serializer(self, *args, **kwargs): + kwargs['partial'] = True + return super(StudentViewSet, self).get_serializer(*args, **kwargs) - ### Example response + def get_queryset(self): + user = self.request.user + if user.is_staff: + return Student.objects.all() + else: + return Student.objects.filter(user_id = user.id) - List of results from `retrieve` (see the example response for `retrieve`). - retrieve: - - ### Example response - - { - "user_id": 4, - "user": { - "id": 4, - "email": "charles.dumont@example.net", - "profile_type": null, - "first_name": "", - "last_name": "", - "url": "http://localhost:8000/api/users/4/" - }, - "registration": { - "id": 3, - "submitted": "2018-05-05T14:15:10.998206+02:00", - "validated": false - }, - "visits": [], - "url": "http://localhost:8000/api/students/2/" - } - """ - - queryset = Student.objects.all() serializer_class = StudentSerializer permission_classes = (DRYPermissions,) diff --git a/projects/factory.py b/projects/factory.py index f826ab8..8a53981 100644 --- a/projects/factory.py +++ b/projects/factory.py @@ -7,7 +7,7 @@ from users.factory import UserFactory from .models import Project, Edition, Participation, EditionForm -class ProjectFactory(factory.DjangoModelFactory): +class ProjectFactory(factory.django.DjangoModelFactory): """Project object factory.""" class Meta: # noqa @@ -20,7 +20,7 @@ class ProjectFactory(factory.DjangoModelFactory): logo = factory.django.ImageField(color='green') -class EditionFactory(factory.DjangoModelFactory): +class EditionFactory(factory.django.DjangoModelFactory): """Edition object factory.""" class Meta: # noqa @@ -37,7 +37,7 @@ class EditionFactory(factory.DjangoModelFactory): return project and project or ProjectFactory.create() -class EditionFormFactory(factory.DjangoModelFactory): +class EditionFormFactory(factory.django.DjangoModelFactory): class Meta: # noqa model = EditionForm @@ -45,7 +45,7 @@ class EditionFormFactory(factory.DjangoModelFactory): deadline = factory.Faker('future_date') -class ParticipationFactory(factory.DjangoModelFactory): +class ParticipationFactory(factory.django.DjangoModelFactory): """Participation object factory.""" class Meta: # noqa diff --git a/register/factory.py b/register/factory.py index 973d6e7..4fe9580 100644 --- a/register/factory.py +++ b/register/factory.py @@ -8,7 +8,7 @@ from utils import printable_only from . import models -class RegistrationFactory(factory.DjangoModelFactory): +class RegistrationFactory(factory.django.DjangoModelFactory): """Registration object factory.""" class Meta: # noqa diff --git a/test b/test new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_profiles/test_student.py b/tests/test_profiles/test_student.py index 0d9d65f..f5b6541 100644 --- a/tests/test_profiles/test_student.py +++ b/tests/test_profiles/test_student.py @@ -3,7 +3,7 @@ from profiles.factory import StudentFactory from profiles.models import Student from tests.utils import ModelTestCase -from users.factory import UserFactory +from users.factory import UserFactory, StaffUserFactory class StudentTestCase(ModelTestCase): @@ -32,7 +32,8 @@ class StudentTestCase(ModelTestCase): self.assertEqual(self.obj, self.obj.user.student) def test_get_absolute_url(self): - self.client.force_login(UserFactory.create()) + staff_user = StaffUserFactory.create() + self.client.force_login(staff_user) url = self.obj.get_absolute_url() response = self.client.get(url) self.assertEqual(200, response.status_code) diff --git a/tests/test_profiles/test_student_api.py b/tests/test_profiles/test_student_api.py index 204649b..d9caacb 100644 --- a/tests/test_profiles/test_student_api.py +++ b/tests/test_profiles/test_student_api.py @@ -19,7 +19,7 @@ class StudentEndpointsTest(HyperlinkedAPITestCase): def perform_retrieve(self, obj=None): if obj is None: obj = self.factory.create() - response = self.client.get('/api/students/{obj.pk}/'.format(obj=obj)) + response = self.client.get('/api/students/') return response def test_list(self): diff --git a/users/factory.py b/users/factory.py index b197e55..850d0c4 100644 --- a/users/factory.py +++ b/users/factory.py @@ -9,7 +9,7 @@ from utils import printable_only User = get_user_model() -class UserFactory(factory.DjangoModelFactory): +class UserFactory(factory.django.DjangoModelFactory): """User object factory.""" class Meta: # noqa @@ -41,3 +41,7 @@ class UserFactory(factory.DjangoModelFactory): manager = cls._get_manager(model_class) # The default would use ``manager.create(*args, **kwargs)`` return manager.create_user(*args, **kwargs) + +class StaffUserFactory(UserFactory): + """Staff user object factory.""" + is_staff = True \ No newline at end of file diff --git a/visits/factory.py b/visits/factory.py index 11817aa..05fce07 100644 --- a/visits/factory.py +++ b/visits/factory.py @@ -15,7 +15,7 @@ from . import models User = get_user_model() -class PlaceFactory(factory.DjangoModelFactory): +class PlaceFactory(factory.django.DjangoModelFactory): """Place object factory.""" class Meta: # noqa @@ -28,7 +28,7 @@ class PlaceFactory(factory.DjangoModelFactory): description = factory.LazyAttribute(lambda o: '\n'.join(o._description)) -class VisitFactory(factory.DjangoModelFactory): +class VisitFactory(factory.django.DjangoModelFactory): """Visit object factory.""" deadline_random_range = (-5, -1) @@ -81,7 +81,7 @@ class VisitWithClosedRegistrationsFactory(VisitFactory): deadline_random_range = (-10, -6) # guaranteed to be before today -class ParticipationFactory(factory.DjangoModelFactory): +class ParticipationFactory(factory.django.DjangoModelFactory): """Visit participant object factory. Users and visit are picked from pre-existing objects, -- GitLab