diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml index b0c13a26..ce56c185 100644 --- a/.github/workflows/django.yml +++ b/.github/workflows/django.yml @@ -2,9 +2,9 @@ name: Django CI on: push: - branches: [ "develop" ] + branches: [ "develop", "tests" ] pull_request: - branches: [ "develop" ] + branches: [ "develop", "tests" ] jobs: build: @@ -17,7 +17,7 @@ jobs: env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres - POSTGRES_DB: github_actions + POSTGRES_DB: github_actions_2 ports: - 5433:5432 @@ -37,7 +37,9 @@ jobs: pip install -r requirements.txt - name: Linting API run: | - flake8 ./api + cd api + flake8 . + cd .. - name: Run Tests env: CLIENT_ID: ${{ secrets.CLIENT_ID }} diff --git a/.gitignore b/.gitignore index 0ed25f64..f29b818d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ venv migrations .env htmlcov -data \ No newline at end of file +data +uploads \ No newline at end of file diff --git a/README.md b/README.md index cd0b55c0..b13c6ec0 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ # UGent-4 +<<<<<<< HEAD +======= +>>>>>>> 43302259fa87109dcb81dc91d568e658a9aa7358 diff --git a/api/README.md b/api/README.md new file mode 100644 index 00000000..a822dcb4 --- /dev/null +++ b/api/README.md @@ -0,0 +1,36 @@ +# Backend +Deze folder bevat de code van de backend. + +## Hoe runnen? + +### Benodigdheden: +- .env bestand: In deze folder moet je lokaal een .env bestand zetten. Het .env bestand bevat geheime variabelen en codes die je niet in deze repo zal vinden. Vraag het aan iemand van het backend team als je de file wilt krijgen. + +- postgres databank: Je zal zelf een postgres databank moeten opzetten. De naam van de databank, user, paswoord, ... Kan je vinden in het .env bestand. + +- virtual environment: Je zal ook een virtual environment moeten maken. Doe dit op linux met het commando: `python -m venv /path/to/new/virtual/environment`. +Vervolgens activeer je de virtual environment als volgt: `source my_env/bin/activate`. +Tenslotte installeer je alle dependencies: `pip install -r requirements.txt`. + +### de api lokaal runnen: +Om de api lokaal te runnen activeer je eerst de virtual environment. Daarna voer je volgende commando's uit in de UGent-4 directory: +```sh +./manage.py makemigrations api +./manage.py migrate api +./manage.py runserver +``` +Je kan dan surfen naar http://127.0.0.1:8000/ om de api te zien. + +## Hoe testen? + +**OPMERKING**: Al de volgende commando's moeten uitgevoerd worden terwijl de virtual environment geactiveerd is. + +Je checkt zowel de linting als de tests. + +### Linting: +De volgende commando's run je van de ***api*** directory: +Run het commando `flake8 .`. De output vertelt je waar de codestijl fout is. Om snel deze stijlfouten op te lossen kan je `black .`runnen. + + +### Testing: +Run het commando `./manage.py test` in de ***UGENT-4*** directory. diff --git a/api/asgi.py b/api/asgi.py index 7f38f25f..4cb57e4b 100644 --- a/api/asgi.py +++ b/api/asgi.py @@ -11,6 +11,6 @@ from django.core.asgi import get_asgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "api.settings") application = get_asgi_application() diff --git a/api/middleware.py b/api/middleware.py index 47d922db..4b6872b6 100644 --- a/api/middleware.py +++ b/api/middleware.py @@ -1,6 +1,6 @@ from django.conf import settings from django.shortcuts import redirect -from django.urls import reverse + class RedirectAnonymousUserMiddleware: """ @@ -20,8 +20,11 @@ def __init__(self, get_response): def __call__(self, request): # Check if the user is anonymous and the current path is not the login page - if request.user.is_anonymous and request.path not in ['/oauth2/login', '/oauth2/callback']: + if request.user.is_anonymous and request.path not in [ + "/oauth2/login", + "/oauth2/callback", + ]: # Redirect to the login page return redirect(settings.LOGIN_URL) - return self.get_response(request) \ No newline at end of file + return self.get_response(request) diff --git a/api/migrations/0001_initial.py b/api/migrations/0001_initial.py deleted file mode 100644 index 5606a234..00000000 --- a/api/migrations/0001_initial.py +++ /dev/null @@ -1,102 +0,0 @@ -# Generated by Django 5.0.2 on 2024-03-05 14:15 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='Groep', - fields=[ - ('group_id', models.AutoField(primary_key=True, serialize=False)), - ], - ), - migrations.CreateModel( - name='Lesgever', - fields=[ - ('lesgever_id', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(max_length=100)), - ('email', models.EmailField(default='email@email.com', max_length=254)), - ('is_admin', models.BooleanField(default=False)), - ], - ), - migrations.CreateModel( - name='Project', - fields=[ - ('project_id', models.AutoField(primary_key=True, serialize=False)), - ('titel', models.CharField(max_length=100)), - ('description', models.TextField()), - ('opgavebestanden', models.FileField(upload_to='opgave/')), - ('deadline', models.DateTimeField(null=True)), - ], - ), - migrations.CreateModel( - name='Indiening', - fields=[ - ('indiening_id', models.AutoField(primary_key=True, serialize=False)), - ('indieningsbestanden', models.FileField(upload_to='uploads/')), - ('tijdstip', models.DateTimeField()), - ('indiener', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.groep')), - ], - ), - migrations.AddField( - model_name='groep', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.project'), - ), - migrations.CreateModel( - name='Score', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('score', models.SmallIntegerField()), - ('groep', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.groep')), - ('indiening', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.indiening')), - ], - ), - migrations.CreateModel( - name='Student', - fields=[ - ('student_id', models.AutoField(primary_key=True, serialize=False)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - ), - migrations.AddField( - model_name='groep', - name='students', - field=models.ManyToManyField(blank=True, related_name='groups_students', to='api.student'), - ), - migrations.CreateModel( - name='Vak', - fields=[ - ('vak_id', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(max_length=100)), - ('projects', models.ManyToManyField(blank=True, related_name='subjects_projects', to='api.project')), - ('students', models.ManyToManyField(blank=True, related_name='subjects_enrolled', to='api.student')), - ('teachers', models.ManyToManyField(related_name='subjects_teachers', to='api.lesgever')), - ], - ), - migrations.AddField( - model_name='student', - name='subjects', - field=models.ManyToManyField(related_name='students_enrolled', to='api.vak'), - ), - migrations.AddField( - model_name='project', - name='vak', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.vak'), - ), - migrations.AddField( - model_name='lesgever', - name='subjects', - field=models.ManyToManyField(blank=True, related_name='lesgevers_enrolled', to='api.vak'), - ), - ] diff --git a/api/migrations/__pycache__/0001_initial.cpython-311.pyc b/api/migrations/__pycache__/0001_initial.cpython-311.pyc deleted file mode 100644 index b973366d..00000000 Binary files a/api/migrations/__pycache__/0001_initial.cpython-311.pyc and /dev/null differ diff --git a/api/migrations/__pycache__/0002_remove_student_id_remove_student_name_and_more.cpython-311.pyc b/api/migrations/__pycache__/0002_remove_student_id_remove_student_name_and_more.cpython-311.pyc deleted file mode 100644 index 52dfba93..00000000 Binary files a/api/migrations/__pycache__/0002_remove_student_id_remove_student_name_and_more.cpython-311.pyc and /dev/null differ diff --git a/api/migrations/__pycache__/0003_alter_student_subjects.cpython-311.pyc b/api/migrations/__pycache__/0003_alter_student_subjects.cpython-311.pyc deleted file mode 100644 index ead4b8af..00000000 Binary files a/api/migrations/__pycache__/0003_alter_student_subjects.cpython-311.pyc and /dev/null differ diff --git a/api/migrations/__pycache__/0004_alter_student_subjects.cpython-311.pyc b/api/migrations/__pycache__/0004_alter_student_subjects.cpython-311.pyc deleted file mode 100644 index 0522acb5..00000000 Binary files a/api/migrations/__pycache__/0004_alter_student_subjects.cpython-311.pyc and /dev/null differ diff --git a/api/migrations/__pycache__/0005_alter_vak_teachers.cpython-311.pyc b/api/migrations/__pycache__/0005_alter_vak_teachers.cpython-311.pyc deleted file mode 100644 index 44e54ac0..00000000 Binary files a/api/migrations/__pycache__/0005_alter_vak_teachers.cpython-311.pyc and /dev/null differ diff --git a/api/migrations/__pycache__/0006_remove_lesgever_id_alter_lesgever_lesgever_id.cpython-311.pyc b/api/migrations/__pycache__/0006_remove_lesgever_id_alter_lesgever_lesgever_id.cpython-311.pyc deleted file mode 100644 index 24246888..00000000 Binary files a/api/migrations/__pycache__/0006_remove_lesgever_id_alter_lesgever_lesgever_id.cpython-311.pyc and /dev/null differ diff --git a/api/migrations/__pycache__/0007_alter_lesgever_subjects_alter_vak_teachers.cpython-311.pyc b/api/migrations/__pycache__/0007_alter_lesgever_subjects_alter_vak_teachers.cpython-311.pyc deleted file mode 100644 index 2f9ae735..00000000 Binary files a/api/migrations/__pycache__/0007_alter_lesgever_subjects_alter_vak_teachers.cpython-311.pyc and /dev/null differ diff --git a/api/migrations/__pycache__/__init__.cpython-311.pyc b/api/migrations/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index c48f5d71..00000000 Binary files a/api/migrations/__pycache__/__init__.cpython-311.pyc and /dev/null differ diff --git a/api/models/gebruiker.py b/api/models/gebruiker.py index 96766053..255ed036 100644 --- a/api/models/gebruiker.py +++ b/api/models/gebruiker.py @@ -17,4 +17,4 @@ class Gebruiker(models.Model): is_lesgever = models.BooleanField(default=False) def __str__(self): - return self.user.first_name + ' ' + self.user.last_name + return self.user.first_name + " " + self.user.last_name diff --git a/api/models/groep.py b/api/models/groep.py index 67bf6a00..7c85ec77 100644 --- a/api/models/groep.py +++ b/api/models/groep.py @@ -14,9 +14,10 @@ class Groep(models.Model): __str__(): Geeft een representatie van het model als een string terug, die de groeps-ID bevat. """ groep_id = models.AutoField(primary_key=True) - studenten = models.ManyToManyField('Gebruiker', related_name='groep_studenten', blank=True) - project = models.ForeignKey('Project', on_delete=models.CASCADE) + studenten = models.ManyToManyField( + "Gebruiker", related_name="groep_studenten", blank=True + ) + project = models.ForeignKey("Project", on_delete=models.CASCADE) def __str__(self): return f"Group {self.groep_id}" - diff --git a/api/models/indiening.py b/api/models/indiening.py index ecdc4efe..03d558da 100644 --- a/api/models/indiening.py +++ b/api/models/indiening.py @@ -27,12 +27,12 @@ class Indiening(models.Model): __str__(): Geeft een representatie van het model als een string terug, die de ID van de indiening bevat. """ indiening_id = models.AutoField(primary_key=True) - groep = models.ForeignKey('Groep', on_delete=models.CASCADE) + groep = models.ForeignKey("Groep", on_delete=models.CASCADE) tijdstip = models.DateTimeField(auto_now_add=True) def __str__(self): return str(self.indiening_id) - + class IndieningBestand(models.Model): """ @@ -47,8 +47,8 @@ class IndieningBestand(models.Model): __str__(): Geeft een representatie van het model als een string terug, die de bestandsnaam bevat. """ indiening_bestand_id = models.AutoField(primary_key=True) - indiening = models.ForeignKey('Indiening', on_delete=models.CASCADE) + indiening = models.ForeignKey("Indiening", on_delete=models.CASCADE) bestand = models.FileField(upload_to=upload_to) def __str__(self): - return str(self.bestand.name) \ No newline at end of file + return str(self.bestand.name) diff --git a/api/models/project.py b/api/models/project.py index a9b0e18b..bf8b2c12 100644 --- a/api/models/project.py +++ b/api/models/project.py @@ -1,6 +1,7 @@ from django.db import models from .vak import Vak + def upload_to(instance, filename): """ Functie om het pad te genereren waar het opgavebestand wordt opgeslagen. @@ -13,7 +14,7 @@ def upload_to(instance, filename): str: Het pad waar het opgavebestand moet worden opgeslagen. """ vak_id = instance.vak.vak_id - return f'data/opgaves/vak_{vak_id}/{filename}' + return f"data/opgaves/vak_{vak_id}/{filename}" class Project(models.Model): @@ -44,4 +45,3 @@ class Project(models.Model): def __str__(self): return self.titel - \ No newline at end of file diff --git a/api/models/score.py b/api/models/score.py index 74f96ffc..2d9187ae 100644 --- a/api/models/score.py +++ b/api/models/score.py @@ -1,6 +1,6 @@ from django.db import models - + class Score(models.Model): """ Model voor het bijhouden van scores voor indieningen. @@ -15,7 +15,7 @@ class Score(models.Model): """ score_id = models.AutoField(primary_key=True) score = models.SmallIntegerField() - indiening = models.ForeignKey('Indiening', on_delete=models.CASCADE) + indiening = models.ForeignKey("Indiening", on_delete=models.CASCADE) def __str__(self): - return self.score_id + return str(self.score_id) diff --git a/api/models/vak.py b/api/models/vak.py index 7be7b8d5..2fbaa286 100644 --- a/api/models/vak.py +++ b/api/models/vak.py @@ -16,10 +16,12 @@ class Vak(models.Model): """ vak_id = models.AutoField(primary_key=True) naam = models.CharField(max_length=100) - studenten = models.ManyToManyField('Gebruiker', related_name='vak_gebruikers', blank=True) - lesgevers = models.ManyToManyField('Gebruiker', related_name='vak_lesgevers', blank=True) + studenten = models.ManyToManyField( + "Gebruiker", related_name="vak_gebruikers", blank=True + ) + lesgevers = models.ManyToManyField( + "Gebruiker", related_name="vak_lesgevers", blank=True + ) def __str__(self): return self.naam - - diff --git a/api/serializers/gebruiker.py b/api/serializers/gebruiker.py index 768831a4..9da7d032 100644 --- a/api/serializers/gebruiker.py +++ b/api/serializers/gebruiker.py @@ -16,7 +16,7 @@ class GebruikerSerializer(serializers.ModelSerializer): """ class Meta: model = Gebruiker - fields = '__all__' + fields = "__all__" def create(self, validated_data): """ diff --git a/api/serializers/groep.py b/api/serializers/groep.py index 1ed9e978..d29791d4 100644 --- a/api/serializers/groep.py +++ b/api/serializers/groep.py @@ -1,5 +1,6 @@ from rest_framework import serializers from api.models.groep import Groep +from collections import Counter class GroepSerializer(serializers.ModelSerializer): @@ -16,9 +17,10 @@ class GroepSerializer(serializers.ModelSerializer): """ class Meta: model = Groep - fields = '__all__' + fields = "__all__" def create(self, validated_data): + """ Args: validated_data (dict): Gevalideerde gegevens over de groep. @@ -28,13 +30,13 @@ def create(self, validated_data): """ students_data = validated_data.pop('studenten') validate_students(students_data, validated_data['project']) - instance = Groep.objects.create(**validated_data) instance.studenten.set(students_data) return instance def update(self, instance, validated_data): + """ Args: instance (Groep): De groep die moet worden bijgewerkt. diff --git a/api/serializers/indiening.py b/api/serializers/indiening.py index c53dd8be..1d1007e7 100644 --- a/api/serializers/indiening.py +++ b/api/serializers/indiening.py @@ -13,7 +13,7 @@ class IndieningSerializer(serializers.ModelSerializer): """ class Meta: model = Indiening - fields = ('__all__') + fields = "__all__" class IndieningBestandSerializer(serializers.ModelSerializer): @@ -27,4 +27,4 @@ class IndieningBestandSerializer(serializers.ModelSerializer): """ class Meta: model = IndieningBestand - fields = ('__all__') \ No newline at end of file + fields = "__all__" diff --git a/api/serializers/project.py b/api/serializers/project.py index df8de252..93f6bd8d 100644 --- a/api/serializers/project.py +++ b/api/serializers/project.py @@ -17,7 +17,7 @@ class ProjectSerializer(serializers.ModelSerializer): """ class Meta: model = Project - fields = '__all__' + fields = "__all__" def create(self, validated_data): """ diff --git a/api/serializers/score.py b/api/serializers/score.py index 2d80e260..6173e303 100644 --- a/api/serializers/score.py +++ b/api/serializers/score.py @@ -16,7 +16,7 @@ class ScoreSerializer(serializers.ModelSerializer): """ class Meta: model = Score - fields = '__all__' + fields = "__all__" def create(self, validated_data): """ @@ -45,7 +45,7 @@ def update(self, instance, validated_data): super().update(instance=instance, validated_data=validated_data) instance.save() return instance - + def validate_score(data): """ Controleert of de opgegeven score niet hoger is dan de maximale score van het bijbehorende project. @@ -73,5 +73,4 @@ def validate_indiening(instance, data): """ if instance.indiening != data.get('indiening'): raise serializers.ValidationError('indiening_id kan niet aangepast worden') - diff --git a/api/serializers/vak.py b/api/serializers/vak.py index 98b7f13c..d28e6bca 100644 --- a/api/serializers/vak.py +++ b/api/serializers/vak.py @@ -58,6 +58,7 @@ def update(self, instance, validated_data): instance.save() return instance + def validate_students_teachers(students_data, teachers_data): """ Controleert of alle gebruikers in 'studenten' studenten zijn en alle gebruikers in 'lesgevers' lesgevers zijn. @@ -71,7 +72,9 @@ def validate_students_teachers(students_data, teachers_data): """ for student in students_data: if student.is_lesgever: - raise serializers.ValidationError("Alle gebruikers in 'studenten' moeten studenten zijn") + raise serializers.ValidationError( + "Alle gebruikers in 'studenten' moeten studenten zijn" + ) for teacher in teachers_data: if not teacher.is_lesgever: diff --git a/api/settings.py b/api/settings.py index bea4ecbe..f1f44c9b 100644 --- a/api/settings.py +++ b/api/settings.py @@ -24,71 +24,71 @@ # See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = os.environ.get('SECRET_KEY') +SECRET_KEY = os.environ.get("SECRET_KEY") # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = ['sel2-4.ugent.be', 'localhost', '127.0.0.1'] +ALLOWED_HOSTS = ["sel2-4.ugent.be", "localhost", "127.0.0.1"] # Application definition INSTALLED_APPS = [ - 'rest_framework', - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'django_auth_adfs', - 'api', + "rest_framework", + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "django_auth_adfs", + "api", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - #'api.middleware.RedirectAnonymousUserMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + # 'api.middleware.RedirectAnonymousUserMiddleware', ] -ROOT_URLCONF = 'api.urls' +ROOT_URLCONF = "api.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", ], }, }, ] -WSGI_APPLICATION = 'api.wsgi.application' +WSGI_APPLICATION = "api.wsgi.application" # Database # https://docs.djangoproject.com/en/5.0/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': os.environ.get('DB_ENGINE'), - 'NAME': os.environ.get('DB_NAME'), - 'USER': os.environ.get('DB_USER'), - 'PASSWORD': os.environ.get('DB_PASSWORD'), - 'HOST': os.environ.get('DB_HOST'), - 'PORT': os.environ.get('DB_PORT'), + "default": { + "ENGINE": os.environ.get("DB_ENGINE"), + "NAME": os.environ.get("DB_NAME"), + "USER": os.environ.get("DB_USER"), + "PASSWORD": os.environ.get("DB_PASSWORD"), + "HOST": os.environ.get("DB_HOST"), + "PORT": os.environ.get("DB_PORT"), } } @@ -98,16 +98,16 @@ AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] @@ -115,9 +115,9 @@ # Internationalization # https://docs.djangoproject.com/en/5.0/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -127,40 +127,42 @@ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/5.0/howto/static-files/ -STATIC_URL = 'static/' +STATIC_URL = "static/" # Default primary key field type # https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" -STATIC_ROOT = os.path.join(BASE_DIR, 'static/') +STATIC_ROOT = os.path.join(BASE_DIR, "static/") -CLIENT_ID = os.environ.get('CLIENT_ID') -CLIENT_SECRET = os.environ.get('CLIENT_SECRET') -TENANT_ID = os.environ.get('TENANT_ID') +CLIENT_ID = os.environ.get("CLIENT_ID") +CLIENT_SECRET = os.environ.get("CLIENT_SECRET") +TENANT_ID = os.environ.get("TENANT_ID") -AD_URL = os.environ.get('AD_URL') +AD_URL = os.environ.get("AD_URL") AUTH_ADFS = { - 'AUDIENCE': CLIENT_ID, - 'CLIENT_ID': CLIENT_ID, - 'CLIENT_SECRET': CLIENT_SECRET, - 'CLAIM_MAPPING': {'first_name': 'given_name', - 'last_name': 'family_name', - 'email': 'upn'}, - 'GROUPS_CLAIM': 'roles', - 'MIRROR_GROUPS': True, - 'USERNAME_CLAIM': 'upn', - 'TENANT_ID': TENANT_ID, - 'RELYING_PARTY_ID': CLIENT_ID, + "AUDIENCE": CLIENT_ID, + "CLIENT_ID": CLIENT_ID, + "CLIENT_SECRET": CLIENT_SECRET, + "CLAIM_MAPPING": { + "first_name": "given_name", + "last_name": "family_name", + "email": "upn", + }, + "GROUPS_CLAIM": "roles", + "MIRROR_GROUPS": True, + "USERNAME_CLAIM": "upn", + "TENANT_ID": TENANT_ID, + "RELYING_PARTY_ID": CLIENT_ID, } AUTHENTICATION_BACKENDS = [ - 'django_auth_adfs.backend.AdfsAuthCodeBackend', + "django_auth_adfs.backend.AdfsAuthCodeBackend", ] LOGIN_URL = "django_auth_adfs:login" -LOGIN_REDIRECT_URL = "/login_redirect" \ No newline at end of file +LOGIN_REDIRECT_URL = "/login_redirect" diff --git a/api/tests/factories/gebruiker.py b/api/tests/factories/gebruiker.py index 6267cf91..54bb2f13 100644 --- a/api/tests/factories/gebruiker.py +++ b/api/tests/factories/gebruiker.py @@ -1,17 +1,23 @@ from django.contrib.auth.models import User from api.models.gebruiker import Gebruiker -import factory +from factory.django import DjangoModelFactory +from factory import SubFactory, Faker -class UserFactory(factory.django.DjangoModelFactory): + +class UserFactory(DjangoModelFactory): class Meta: model = User - username = factory.Sequence(lambda n: f'user{n}') - password = factory.PostGenerationMethodCall('set_password', 'password') + username = Faker("user_name") + first_name = Faker("first_name") + last_name = Faker("last_name") + email = Faker("email") + is_superuser = Faker("boolean") + -class GebruikerFactory(factory.django.DjangoModelFactory): +class GebruikerFactory(DjangoModelFactory): class Meta: model = Gebruiker - user = factory.SubFactory(UserFactory) - is_lesgever = False + user = SubFactory(UserFactory) + is_lesgever = Faker("boolean") diff --git a/api/tests/factories/groep.py b/api/tests/factories/groep.py new file mode 100644 index 00000000..1602802c --- /dev/null +++ b/api/tests/factories/groep.py @@ -0,0 +1,24 @@ +import factory +from api.models.groep import Groep +from factory.django import DjangoModelFactory +from factory import SubFactory +from api.tests.factories.gebruiker import GebruikerFactory +from api.tests.factories.project import ProjectFactory + + +class GroepFactory(DjangoModelFactory): + class Meta: + model = Groep + + project = SubFactory(ProjectFactory) + + @factory.post_generation + def studenten(self, create, extracted, **kwargs): + if not create: + return + + if extracted: + for student in extracted: + self.studenten.add(student) + else: + self.studenten.add(GebruikerFactory(is_lesgever=False)) diff --git a/api/tests/factories/indiening.py b/api/tests/factories/indiening.py new file mode 100644 index 00000000..38d63ece --- /dev/null +++ b/api/tests/factories/indiening.py @@ -0,0 +1,33 @@ +import factory +from api.models.indiening import Indiening, IndieningBestand +from factory.django import DjangoModelFactory +from factory import SubFactory +from .groep import GroepFactory +from django.utils import timezone +from factory.django import FileField +from faker import Faker + + +fake = Faker() + + +class IndieningFactory(DjangoModelFactory): + class Meta: + model = Indiening + + indiening_id = factory.Sequence(lambda n: n) + groep = SubFactory(GroepFactory) + tijdstip = factory.LazyFunction( + lambda: timezone.make_aware( + fake.date_time_between(start_date="+1d", end_date="+30d") + ) + ) + + +class IndieningBestandFactory(DjangoModelFactory): + class Meta: + model = IndieningBestand + + indiening_bestand_id = factory.Sequence(lambda n: n) + indiening = SubFactory(IndieningFactory) + bestand = FileField(filename="test.txt", data=b"file content") diff --git a/api/tests/factories/project.py b/api/tests/factories/project.py new file mode 100644 index 00000000..216d1998 --- /dev/null +++ b/api/tests/factories/project.py @@ -0,0 +1,26 @@ +import factory +from api.models.project import Project +from factory.django import DjangoModelFactory +from factory import SubFactory +from django.utils import timezone +from .vak import VakFactory +from faker import Faker + +fake = Faker() + + +class ProjectFactory(DjangoModelFactory): + class Meta: + model = Project + + project_id = factory.Sequence(lambda n: n) + titel = factory.Faker("word") + beschrijving = factory.Faker("paragraph") + opgave_bestand = factory.django.FileField(data=b"file content") + vak = SubFactory(VakFactory) + deadline = factory.LazyFunction( + lambda: timezone.make_aware( + fake.date_time_between(start_date="+1d", end_date="+30d") + ) + ) + max_score = factory.Faker("random_int", min=10, max=100) diff --git a/api/tests/factories/score.py b/api/tests/factories/score.py new file mode 100644 index 00000000..2507da23 --- /dev/null +++ b/api/tests/factories/score.py @@ -0,0 +1,12 @@ +from api.models.score import Score +from factory.django import DjangoModelFactory +from factory import SubFactory, Faker +from api.tests.factories.indiening import IndieningFactory + + +class ScoreFactory(DjangoModelFactory): + class Meta: + model = Score + + score = Faker("random_int", min=0, max=100) + indiening = SubFactory(IndieningFactory) diff --git a/api/tests/factories/vak.py b/api/tests/factories/vak.py new file mode 100644 index 00000000..ed7806bc --- /dev/null +++ b/api/tests/factories/vak.py @@ -0,0 +1,34 @@ +import factory +from api.models.vak import Vak +from factory.django import DjangoModelFactory +from factory import Faker +from .gebruiker import GebruikerFactory + + +class VakFactory(DjangoModelFactory): + class Meta: + model = Vak + + naam = Faker("name") + + @factory.post_generation + def studenten(self, create, extracted, **kwargs): + if not create: + return + + if extracted: + for gebruiker in extracted: + self.studenten.add(gebruiker) + else: + self.studenten.add(GebruikerFactory()) + + @factory.post_generation + def lesgevers(self, create, extracted, **kwargs): + if not create: + return + + if extracted: + for lesgever in extracted: + self.lesgevers.add(lesgever) + else: + self.lesgevers.add(GebruikerFactory(is_lesgever=True)) diff --git a/api/tests/models/test_gebruiker.py b/api/tests/models/test_gebruiker.py index ca3cff74..f4bf162b 100644 --- a/api/tests/models/test_gebruiker.py +++ b/api/tests/models/test_gebruiker.py @@ -1,31 +1,20 @@ from django.test import TestCase -from api.models.gebruiker import Gebruiker -from django.contrib.auth.models import User +from api.tests.factories.gebruiker import UserFactory, GebruikerFactory -class GebruikerTestCase(TestCase): + +class GebruikerModelTest(TestCase): def setUp(self): - user1 = User.objects.create_user(username='user1') - user2 = User.objects.create_user(username='user2') - Gebruiker.objects.create(user=user1, is_lesgever=False) - Gebruiker.objects.create(user=user2, is_lesgever=True) + self.user1 = UserFactory.create(username="user1") + self.user2 = UserFactory.create(username="user2") + self.gebruiker1 = GebruikerFactory.create(user=self.user1, is_lesgever=False) + self.gebruiker2 = GebruikerFactory.create(user=self.user2, is_lesgever=True) def test_gebruiker_is_lesgever(self): - user1 = Gebruiker.objects.get(user__username='user1') - user2 = Gebruiker.objects.get(user__username='user2') - self.assertEqual(user1.is_lesgever, False) - self.assertEqual(user2.is_lesgever, True) - - def test_user_label(self): - user = Gebruiker.objects.get(user__username='user1') - field_label = user._meta.get_field('user').verbose_name - self.assertEqual(field_label, 'user') + self.assertEqual(self.gebruiker1.is_lesgever, False) + self.assertEqual(self.gebruiker2.is_lesgever, True) - def test_subjects_label(self): - user = Gebruiker.objects.get(user__username='user1') - field_label = user._meta.get_field('subjects').verbose_name - self.assertEqual(field_label, 'subjects') - def test_str_method(self): - gebruiker = Gebruiker.objects.get(user__username='user1') - expected_object_name = gebruiker.user.first_name - self.assertEqual(str(gebruiker), expected_object_name) \ No newline at end of file + expected_object_name = ( + self.gebruiker1.user.first_name + " " + self.gebruiker1.user.last_name + ) + self.assertEqual(str(self.gebruiker1), expected_object_name) diff --git a/api/tests/models/test_groep.py b/api/tests/models/test_groep.py new file mode 100644 index 00000000..e98da379 --- /dev/null +++ b/api/tests/models/test_groep.py @@ -0,0 +1,24 @@ +from django.test import TestCase +from api.tests.factories.groep import GroepFactory +from api.tests.factories.gebruiker import GebruikerFactory +from api.tests.factories.project import ProjectFactory +from api.tests.factories.vak import VakFactory + + +class GroepModelTest(TestCase): + def setUp(self): + self.gebruiker = GebruikerFactory.create() + self.vak = VakFactory.create() + self.project = ProjectFactory.create(vak=self.vak) + self.groep = GroepFactory.create(project=self.project) + + def test_str_method(self): + self.assertEqual(str(self.groep), f"Group {self.groep.groep_id}") + + def test_groep_studenten(self): + self.groep.studenten.add(self.gebruiker) + self.assertEqual(self.groep.studenten.count(), 2) + self.assertEqual(self.groep.studenten.first(), self.gebruiker) + + def test_groep_project(self): + self.assertEqual(self.groep.project, self.project) diff --git a/api/tests/models/test_indiening.py b/api/tests/models/test_indiening.py new file mode 100644 index 00000000..2ac3320c --- /dev/null +++ b/api/tests/models/test_indiening.py @@ -0,0 +1,43 @@ +# test_indiening.py +from django.test import TestCase +from django.core.files.uploadedfile import SimpleUploadedFile +from api.tests.factories.indiening import IndieningFactory, IndieningBestandFactory + + +class IndieningModelTest(TestCase): + def setUp(self): + self.indiening = IndieningFactory.create() + + def test_str_method(self): + self.assertEqual(str(self.indiening), str(self.indiening.indiening_id)) + + def test_groep(self): + self.assertIsNotNone(self.indiening.groep) + + def test_tijdstip(self): + self.assertIsNotNone(self.indiening.tijdstip) + + def test_indiening_bestand(self): + self.assertEqual(self.indiening.indieningbestand_set.count(), 0) + + def test_indiening_bestand_add(self): + IndieningBestandFactory.create(indiening=self.indiening) + self.assertEqual(self.indiening.indieningbestand_set.count(), 1) + + +class IndieningBestandModelTest(TestCase): + def setUp(self): + self.indiening_bestand = IndieningBestandFactory.create( + bestand=SimpleUploadedFile("file.txt", b"file_content") + ) + + def test_str_method(self): + self.assertEqual( + str(self.indiening_bestand), str(self.indiening_bestand.bestand.name) + ) + + def test_indiening(self): + self.assertIsNotNone(self.indiening_bestand.indiening) + + def test_bestand(self): + self.assertIsNotNone(self.indiening_bestand.bestand) diff --git a/api/tests/models/test_project.py b/api/tests/models/test_project.py new file mode 100644 index 00000000..971c0eec --- /dev/null +++ b/api/tests/models/test_project.py @@ -0,0 +1,16 @@ +from django.test import TestCase +from api.tests.factories.project import ProjectFactory + + +class ProjectModelTest(TestCase): + def setUp(self): + self.project = ProjectFactory.create() + + def test_str_method(self): + self.assertEqual(str(self.project), self.project.titel) + + def test_project_vak(self): + self.assertIsNotNone(self.project.vak) + + def test_project_max_score(self): + self.assertTrue(10 <= self.project.max_score <= 100) diff --git a/api/tests/models/test_score.py b/api/tests/models/test_score.py new file mode 100644 index 00000000..63b46f67 --- /dev/null +++ b/api/tests/models/test_score.py @@ -0,0 +1,18 @@ +from django.test import TestCase +from api.tests.factories.score import ScoreFactory +from api.tests.factories.indiening import IndieningFactory + + +class ScoreModelTest(TestCase): + def setUp(self): + self.indiening = IndieningFactory.create() + self.score = ScoreFactory.create(indiening=self.indiening) + + def test_str_method(self): + self.assertEqual(str(self.score), str(self.score.score_id)) + + def test_score_value(self): + self.assertTrue(0 <= self.score.score <= 100) + + def test_score_indiening(self): + self.assertEqual(self.score.indiening, self.indiening) diff --git a/api/tests/models/test_vak.py b/api/tests/models/test_vak.py new file mode 100644 index 00000000..e0f94427 --- /dev/null +++ b/api/tests/models/test_vak.py @@ -0,0 +1,16 @@ +from django.test import TestCase +from api.tests.factories.vak import VakFactory + + +class VakModelTest(TestCase): + def setUp(self): + self.vak = VakFactory.create() + + def test_str_method(self): + self.assertEqual(str(self.vak), self.vak.naam) + + def test_vak_studenten(self): + self.assertEqual(self.vak.studenten.count(), 1) + + def test_vak_lesgevers(self): + self.assertEqual(self.vak.lesgevers.count(), 1) diff --git a/api/tests/serializers/test_gebruiker.py b/api/tests/serializers/test_gebruiker.py index 209a8a14..a0279b4c 100644 --- a/api/tests/serializers/test_gebruiker.py +++ b/api/tests/serializers/test_gebruiker.py @@ -1,44 +1,46 @@ -from django.test import TestCase from rest_framework.test import APITestCase from rest_framework.exceptions import ValidationError -from api.models.gebruiker import Gebruiker from api.serializers.gebruiker import GebruikerSerializer -from django.contrib.auth.models import User -from api.models.vak import Vak +from api.tests.factories.gebruiker import UserFactory, GebruikerFactory -class GebruikerSerializerTest(APITestCase): +class GebruikerSerializerTest(APITestCase): def setUp(self): - # Create a User instance - self.user = User.objects.create_user(username='testuser') - - self.gebruiker_attributes = { - 'user': self.user, - } - - self.serializer_data = GebruikerSerializer().data - self.gebruiker = Gebruiker.objects.create(**self.gebruiker_attributes) - - subjects = [1, 2] - for subject in subjects: - vak = Vak.objects.create(name=subject) - self.gebruiker.subjects.add(vak) - + self.user = UserFactory.create() + self.gebruiker = GebruikerFactory.create(user=self.user, is_lesgever=False) self.serializer = GebruikerSerializer(instance=self.gebruiker) def test_contains_expected_fields(self): data = self.serializer.data - self.assertCountEqual(data.keys(), ['user', 'is_lesgever', 'subjects']) + self.assertCountEqual(data.keys(), ["user", "is_lesgever"]) def test_user_field_content(self): data = self.serializer.data - self.assertEqual(data['user'], self.user.id) + self.assertEqual(data["user"], self.user.id) + + def test_is_lesgever_field_content(self): + data = self.serializer.data + self.assertEqual(data["is_lesgever"], self.gebruiker.is_lesgever) + + def test_create(self): + data = {"user": UserFactory.create().id, "is_lesgever": False} + serializer = GebruikerSerializer(data=data) + self.assertTrue(serializer.is_valid()) + gebruiker = serializer.save() + self.assertEqual(gebruiker.user.id, data["user"]) + self.assertEqual(gebruiker.is_lesgever, data["is_lesgever"]) - def test_subjects_field_content(self): + def test_update(self): data = self.serializer.data - subjects = [subject.pk for subject in self.gebruiker.subjects.all()] - self.assertEqual(data['subjects'], subjects) + self.assertFalse(data["is_lesgever"]) + data["is_lesgever"] = True + serializer = GebruikerSerializer( + instance=self.gebruiker, data=data, partial=True + ) + self.assertTrue(serializer.is_valid()) + self.gebruiker = serializer.save() + self.assertTrue(self.gebruiker.is_lesgever) def test_validation_for_blank_items(self): - serializer = GebruikerSerializer(data={'name': '', 'subjects': []}) - self.assertRaises(ValidationError, serializer.is_valid, raise_exception=True) \ No newline at end of file + serializer = GebruikerSerializer(data={"user": "", "is_lesgever": []}) + self.assertRaises(ValidationError, serializer.is_valid, raise_exception=True) diff --git a/api/tests/serializers/test_groep.py b/api/tests/serializers/test_groep.py new file mode 100644 index 00000000..4b91dcfa --- /dev/null +++ b/api/tests/serializers/test_groep.py @@ -0,0 +1,55 @@ +from rest_framework.test import APITestCase +from rest_framework.exceptions import ValidationError +from api.serializers.groep import GroepSerializer +from api.tests.factories.groep import GroepFactory +from api.tests.factories.gebruiker import GebruikerFactory + + +class GroepSerializerTest(APITestCase): + def setUp(self): + self.groep = GroepFactory.create() + self.serializer = GroepSerializer(instance=self.groep) + + def test_contains_expected_fields(self): + data = self.serializer.data + self.assertCountEqual(data.keys(), ["groep_id", "project", "studenten"]) + + def test_project_field_content(self): + data = self.serializer.data + self.assertEqual(data["project"], self.groep.project.project_id) + + def test_studenten_field_content(self): + data = self.serializer.data + students = [student.user.id for student in self.groep.studenten.all()] + self.assertEqual(data["studenten"], students) + + def test_create(self): + data = { + "project": self.groep.project.project_id, + "studenten": [ + GebruikerFactory.create(is_lesgever=False).user.id for _ in range(3) + ], + } + serializer = GroepSerializer(data=data) + self.assertTrue(serializer.is_valid()) + groep = serializer.save() + self.assertEqual(groep.project.project_id, data["project"]) + self.assertEqual( + set([student.user.id for student in groep.studenten.all()]), + set(data["studenten"]), + ) + + def test_update(self): + data = self.serializer.data + self.assertEqual(len(data["studenten"]), 1) + data["studenten"].append(GebruikerFactory.create(is_lesgever=False).user.id) + serializer = GroepSerializer(instance=self.groep, data=data, partial=True) + self.assertTrue(serializer.is_valid()) + groep = serializer.save() + self.assertEqual( + [student.user.id for student in groep.studenten.all()], data["studenten"] + ) + + def test_validation_for_blank_items(self): + serializer = GroepSerializer(data={"project": "", "studenten": []}) + self.assertRaises(ValidationError, serializer.is_valid, raise_exception=True) diff --git a/api/tests/serializers/test_indiening.py b/api/tests/serializers/test_indiening.py new file mode 100644 index 00000000..70e90c92 --- /dev/null +++ b/api/tests/serializers/test_indiening.py @@ -0,0 +1,74 @@ +# test_indiening.py +from django.test import TestCase +from django.core.files.uploadedfile import SimpleUploadedFile +from api.serializers.indiening import IndieningSerializer, IndieningBestandSerializer +from api.tests.factories.indiening import IndieningFactory, IndieningBestandFactory +from api.tests.factories.groep import GroepFactory + + +class IndieningSerializerTest(TestCase): + def setUp(self): + self.indiening = IndieningFactory.create() + self.serializer = IndieningSerializer(instance=self.indiening) + + def test_indiening_serializer_fields(self): + data = self.serializer.data + self.assertEqual( + set(data.keys()), set(["indiening_id", "groep", "tijdstip"]) + ) # Add other fields + + def test_indiening_serializer_create(self): + # can't check tijdstip because it's auto_now_add + groep = GroepFactory.create() + data = {"groep": groep.groep_id} + serializer = IndieningSerializer(data=data) + self.assertTrue(serializer.is_valid()) + indiening = serializer.save() + self.assertEqual(indiening.groep, groep) + + def test_indiening_serializer_update(self): + # can't check tijdstip because it's auto_now_add + new_data = {"groep": self.indiening.groep.groep_id} + serializer = IndieningSerializer( + instance=self.indiening, data=new_data, partial=True + ) + self.assertTrue(serializer.is_valid()) + indiening = serializer.save() + self.assertEqual(indiening.groep, self.indiening.groep) + + +class IndieningBestandSerializerTest(TestCase): + def setUp(self): + self.indiening_bestand = IndieningBestandFactory.create( + bestand=SimpleUploadedFile("file.txt", b"file_content") + ) + self.serializer = IndieningBestandSerializer(instance=self.indiening_bestand) + + def test_indiening_bestand_serializer_fields(self): + data = self.serializer.data + self.assertEqual( + set(data.keys()), set(["indiening_bestand_id", "indiening", "bestand"]) + ) + + def test_indiening_bestand_serializer_create(self): + indiening = IndieningFactory.create() + data = { + "indiening": indiening.indiening_id, + "bestand": SimpleUploadedFile("file.txt", b"file_content"), + } # Add other fields + serializer = IndieningBestandSerializer(data=data) + self.assertTrue(serializer.is_valid()) + indiening_bestand = serializer.save() + self.assertEqual(indiening_bestand.indiening, indiening) + + def test_indiening_bestand_serializer_update(self): + new_data = { + "indiening": self.indiening_bestand.indiening.indiening_id, + "bestand": SimpleUploadedFile("file.txt", b"file_content"), + } # Add other fields + serializer = IndieningBestandSerializer( + instance=self.indiening_bestand, data=new_data, partial=True + ) + self.assertTrue(serializer.is_valid()) + indiening_bestand = serializer.save() + self.assertEqual(indiening_bestand.indiening, self.indiening_bestand.indiening) diff --git a/api/tests/serializers/test_project.py b/api/tests/serializers/test_project.py new file mode 100644 index 00000000..2f482167 --- /dev/null +++ b/api/tests/serializers/test_project.py @@ -0,0 +1,97 @@ +from rest_framework.test import APITestCase +from rest_framework.exceptions import ValidationError +from api.serializers.project import ProjectSerializer +from api.tests.factories.project import ProjectFactory +from dateutil.parser import parse +from api.tests.factories.vak import VakFactory +from django.core.files.uploadedfile import SimpleUploadedFile + + +class ProjectSerializerTest(APITestCase): + def setUp(self): + self.project = ProjectFactory.create() + self.serializer = ProjectSerializer(instance=self.project) + + def test_contains_expected_fields(self): + data = self.serializer.data + self.assertCountEqual( + data.keys(), + [ + "project_id", + "titel", + "beschrijving", + "opgave_bestand", + "vak", + "deadline", + "max_score", + ], + ) + + def test_titel_field_content(self): + data = self.serializer.data + self.assertEqual(data["titel"], self.project.titel) + + def test_beschrijving_field_content(self): + data = self.serializer.data + self.assertEqual(data["beschrijving"], self.project.beschrijving) + + def test_opgave_bestand_field_content(self): + data = self.serializer.data + self.assertEqual( + data["opgave_bestand"].lstrip("/"), str(self.project.opgave_bestand) + ) + + def test_vak_field_content(self): + data = self.serializer.data + self.assertEqual(data["vak"], self.project.vak.vak_id) + + def test_max_score_field_content(self): + data = self.serializer.data + self.assertGreaterEqual(data["max_score"], 10) + self.assertLessEqual(data["max_score"], 100) + + def test_deadline_field_content(self): + data = self.serializer.data + self.assertEqual(parse(data["deadline"]), self.project.deadline) + + def test_validation_for_blank_items(self): + serializer = ProjectSerializer( + data={ + "titel": "", + "beschrijving": "", + "opgave_bestand": "", + "vak": "", + "deadline": "", + "max_score": "", + } + ) + self.assertRaises(ValidationError, serializer.is_valid, raise_exception=True) + + def test_create(self): + vak = VakFactory.create().vak_id + data = { + "titel": "test project", + "beschrijving": "Dit is een test project.", + "opgave_bestand": SimpleUploadedFile("file.txt", b"file_content"), + "vak": vak, + "deadline": self.serializer.data["deadline"], + "max_score": 20, + } + serializer = ProjectSerializer(data=data) + self.assertTrue(serializer.is_valid()) + project = serializer.save() + self.assertEqual(project.deadline, parse(data["deadline"])) + + def test_update(self): + data = { + "titel": "test project", + "beschrijving": "Dit is een test project.", + "opgave_bestand": SimpleUploadedFile("file.txt", b"file_content"), + "vak": self.serializer.data["vak"], + "deadline": self.serializer.data["deadline"], + "max_score": 20, + } + serializer = ProjectSerializer(instance=self.project, data=data, partial=True) + self.assertTrue(serializer.is_valid()) + project = serializer.save() + self.assertEqual(project.deadline, parse(data["deadline"])) diff --git a/api/tests/serializers/test_score.py b/api/tests/serializers/test_score.py new file mode 100644 index 00000000..40b89b57 --- /dev/null +++ b/api/tests/serializers/test_score.py @@ -0,0 +1,51 @@ +# test_score.py +from django.test import TestCase +from api.serializers.score import ScoreSerializer +from api.tests.factories.score import ScoreFactory +from api.tests.factories.indiening import IndieningFactory + + +class ScoreSerializerTest(TestCase): + def setUp(self): + self.score = ScoreFactory.create() + self.score.score = self.score.indiening.groep.project.max_score + self.score.save() + self.serializer = ScoreSerializer(instance=self.score) + + def test_score_serializer_fields(self): + data = self.serializer.data + self.assertEqual(set(data.keys()), set(["score_id", "indiening", "score"])) + + def test_score_id_field_content(self): + data = self.serializer.data + self.assertEqual(data["score_id"], self.score.score_id) + + def test_indiening_field_content(self): + data = self.serializer.data + self.assertEqual(data["indiening"], self.score.indiening.indiening_id) + + def test_score_field_content(self): + data = self.serializer.data + self.assertEqual(data["score"], self.score.score) + + def test_score_serializer_create(self): + indiening = IndieningFactory.create() + max_score = indiening.groep.project.max_score + data = {"indiening": indiening.indiening_id, "score": max_score} + serializer = ScoreSerializer(data=data) + self.assertTrue(serializer.is_valid()) + score = serializer.save() + self.assertEqual(score.indiening, indiening) + self.assertEqual(score.score, data["score"]) + + def test_score_serializer_update(self): + score = self.score.score + new_data = { + "score": score - 1, + "indiening": self.score.indiening.indiening_id, + "score_id": self.score.score_id, + } + serializer = ScoreSerializer(instance=self.score, data=new_data, partial=True) + self.assertTrue(serializer.is_valid()) + score = serializer.save() + self.assertEqual(score.score, new_data["score"]) diff --git a/api/tests/serializers/test_vak.py b/api/tests/serializers/test_vak.py new file mode 100644 index 00000000..25f7601e --- /dev/null +++ b/api/tests/serializers/test_vak.py @@ -0,0 +1,98 @@ +from rest_framework.test import APITestCase +from rest_framework.exceptions import ValidationError +from api.serializers.vak import VakSerializer +from api.tests.factories.vak import VakFactory +from api.tests.factories.gebruiker import GebruikerFactory + + +class VakSerializerTest(APITestCase): + def setUp(self): + self.vak_data = VakFactory.create() + self.serializer = VakSerializer(instance=self.vak_data) + + def test_contains_expected_fields(self): + data = self.serializer.data + self.assertCountEqual(data.keys(), ["vak_id", "naam", "studenten", "lesgevers"]) + + def test_vak_id_field_content(self): + data = self.serializer.data + self.assertEqual(data["vak_id"], self.vak_data.vak_id) + + def test_naam_field_content(self): + data = self.serializer.data + self.assertEqual(data["naam"], self.vak_data.naam) + + def test_studenten_field_content(self): + data = self.serializer.data + self.assertEqual( + set(data["studenten"]), + set([student.user.id for student in self.vak_data.studenten.all()]), + ) + + def test_lesgevers_field_content(self): + data = self.serializer.data + self.assertEqual( + set(data["lesgevers"]), + set([teacher.user.id for teacher in self.vak_data.lesgevers.all()]), + ) + + def test_validation_for_blank_items(self): + serializer = VakSerializer( + data={ + "vak_id": "", + "naam": "", + "studenten": [], + "lesgevers": [], + } + ) + self.assertRaises(ValidationError, serializer.is_valid, raise_exception=True) + + def test_create(self): + students_data = [ + GebruikerFactory.create(is_lesgever=False).user.id for _ in range(3) + ] + teachers_data = [ + GebruikerFactory.create(is_lesgever=True).user.id for _ in range(3) + ] + data = { + "naam": "test vak", + "studenten": students_data, + "lesgevers": teachers_data, + } + serializer = VakSerializer(data=data) + self.assertTrue(serializer.is_valid()) + vak = serializer.save() + self.assertEqual( + set(students_data), + set([student.user.id for student in vak.studenten.all()]), + ) + self.assertEqual( + set(teachers_data), + set([teacher.user.id for teacher in vak.lesgevers.all()]), + ) + self.assertEqual(vak.naam, "test vak") + + def test_update(self): + students_data = [ + GebruikerFactory.create(is_lesgever=False).user.id for _ in range(3) + ] + teachers_data = [ + GebruikerFactory.create(is_lesgever=True).user.id for _ in range(3) + ] + data = { + "naam": "nieuw vak", + "studenten": students_data, + "lesgevers": teachers_data, + } + serializer = VakSerializer(instance=self.vak_data, data=data, partial=True) + self.assertTrue(serializer.is_valid()) + vak = serializer.save() + self.assertEqual( + set(students_data), + set([student.user.id for student in vak.studenten.all()]), + ) + self.assertEqual( + set(teachers_data), + set([teacher.user.id for teacher in vak.lesgevers.all()]), + ) + self.assertEqual(vak.naam, "nieuw vak") diff --git a/api/tests/test_views.py b/api/tests/test_views.py index 88a8195b..4f06e8a5 100644 --- a/api/tests/test_views.py +++ b/api/tests/test_views.py @@ -1,4 +1,4 @@ -''' from django.test import TestCase +""" from django.test import TestCase from django.urls import reverse class TestViews(TestCase): @@ -6,4 +6,4 @@ def test_should_show_register_page(self): #self.client.get(reverse('register')) #self.assertEqual(response.status_code, 200) #self.assertTemplateUsed(response, "authentication/register") - self.assertTrue(True) ''' \ No newline at end of file + self.assertTrue(True) """ diff --git a/api/tests/views/test_gebruiker.py b/api/tests/views/test_gebruiker.py index 0c2d8ff9..91102502 100644 --- a/api/tests/views/test_gebruiker.py +++ b/api/tests/views/test_gebruiker.py @@ -1,36 +1,40 @@ from rest_framework.test import APIClient, APITestCase -from api.tests.factories.gebruiker import GebruikerFactory, UserFactory +from api.tests.factories.gebruiker import GebruikerFactory from django.urls import reverse +from rest_framework import status class GebruikerListViewTest(APITestCase): def setUp(self): - self.client = APIClient() self.gebruiker = GebruikerFactory.create() + self.url = reverse("gebruiker_list") + self.client = APIClient() + self.client.force_authenticate(user=self.gebruiker.user) - def test_get_gebruiker_list(self): - response = self.client.get('/api/gebruikers/') - self.assertEqual(response.status_code, 200) - - def test_post_gebruiker_list(self): - data = {'user': UserFactory.create().id, 'is_lesgever': True} - response = self.client.post('/api/gebruikers/', data) - self.assertEqual(response.status_code, 201) + def test_gebruiker_list_get(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, status.HTTP_200_OK) class GebruikerDetailViewTest(APITestCase): def setUp(self): - self.client = APIClient() self.gebruiker = GebruikerFactory.create() - self.url = reverse('gebruiker_detail', kwargs={'id': self.gebruiker.user.id}) + self.gebruiker.user.is_superuser = True + self.gebruiker.user.save() + self.client = APIClient() + self.client.force_authenticate(user=self.gebruiker.user) + self.url = reverse("gebruiker_detail", kwargs={"id": self.gebruiker.user.id}) - def test_get_gebruiker_detail(self): + def test_gebruiker_detail_get(self): response = self.client.get(self.url) - self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['user'], self.gebruiker.user.id) - - def test_put_gebruiker_detail(self): - data = {'user': self.gebruiker.user.id, 'is_lesgever': True, 'subjects': []} - response = self.client.put(self.url, data) - self.assertEqual(response.status_code, 200) - #self.assertEqual(response.data['is_lesgever'], True) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_gebruiker_detail_put(self): + new_data = { + "user": self.gebruiker.user.id, + "is_lesgever": not self.gebruiker.is_lesgever, + } + response = self.client.put(self.url, new_data, format="json") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.gebruiker.refresh_from_db() + self.assertEqual(self.gebruiker.is_lesgever, new_data["is_lesgever"]) diff --git a/api/tests/views/test_groep.py b/api/tests/views/test_groep.py new file mode 100644 index 00000000..3d9ad45e --- /dev/null +++ b/api/tests/views/test_groep.py @@ -0,0 +1,60 @@ +from django.urls import reverse +from rest_framework import status +from rest_framework.test import APIClient, APITestCase +from api.tests.factories.groep import GroepFactory +from api.tests.factories.gebruiker import GebruikerFactory + + +class GroepListViewTest(APITestCase): + def setUp(self): + self.gebruiker = GebruikerFactory.create(is_lesgever=True) + self.url = reverse("groep_list") + self.client = APIClient() + self.client.force_authenticate(user=self.gebruiker.user) + + def test_groep_list_get(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_groep_list_post(self): + groep = GroepFactory.create() + data = { + "groep_id": groep.groep_id, + "project": groep.project.project_id, + "studenten": [], + } + response = self.client.post(self.url, data, format="json") + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + +class GroepDetailViewTest(APITestCase): + def setUp(self): + self.groep = GroepFactory.create() + self.gebruiker = GebruikerFactory.create(is_lesgever=True) + self.url = reverse("groep_detail", kwargs={"id": self.groep.groep_id}) + self.client = APIClient() + self.client.force_authenticate(user=self.gebruiker.user) + + def test_groep_detail_get(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_groep_detail_put(self): + new_data = { + "groep_id": self.groep.groep_id, + "project": self.groep.project.project_id, + "studenten": [], + } + response = self.client.put(self.url, new_data, format="json") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.groep.refresh_from_db() + self.assertEqual(set(self.groep.studenten.all()), set(new_data["studenten"])) + + def test_groep_detail_delete(self): + response = self.client.delete(self.url) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + + def test_get_invalid_groep(self): + url = reverse("groep_detail", kwargs={"id": 10101}) + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) diff --git a/api/tests/views/test_indiening.py b/api/tests/views/test_indiening.py new file mode 100644 index 00000000..cd2116ff --- /dev/null +++ b/api/tests/views/test_indiening.py @@ -0,0 +1,75 @@ +# test_indiening.py +from django.test import TestCase +from api.tests.factories.indiening import IndieningFactory, IndieningBestandFactory +from api.tests.factories.gebruiker import GebruikerFactory +from rest_framework.test import APIClient +from django.urls import reverse +from rest_framework import status +from django.core.files.uploadedfile import SimpleUploadedFile + + +class IndieningListViewTest(TestCase): + def setUp(self): + self.client = APIClient() + self.gebruiker = GebruikerFactory.create() + self.gebruiker.user.is_superuser = True + self.gebruiker.user.save() + self.client.force_authenticate(user=self.gebruiker.user) + self.url = reverse("indiening_list") + + def test_indiening_list_get(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_indiening_list_post(self): + indiening = IndieningFactory.create() + data = { + "indiening_id": indiening.indiening_id, + "groep": indiening.groep_id, + "tijdstip": indiening.tijdstip, + } + file1 = SimpleUploadedFile("file1.txt", b"file_content") + file2 = SimpleUploadedFile("file2.txt", b"file_content") + data["indiening_bestanden"] = [file1, file2] + response = self.client.post(self.url, data, format="multipart") + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + +class IndieningDetailViewTest(TestCase): + def setUp(self): + self.client = APIClient() + self.gebruiker = GebruikerFactory.create() + self.gebruiker.user.is_superuser = True + self.gebruiker.user.save() + self.indiening = IndieningFactory.create() + self.client.force_authenticate(user=self.gebruiker.user) + self.url = reverse( + "indiening_detail", kwargs={"id": self.indiening.indiening_id} + ) + + def test_indiening_detail_get(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_indiening_detail_delete(self): + response = self.client.delete(self.url) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + + +class IndieningBestandViewTest(TestCase): + def setUp(self): + self.client = APIClient() + self.gebruiker = GebruikerFactory.create() + self.gebruiker.user.is_superuser = True + self.gebruiker.user.save() + self.indiening_bestand = IndieningBestandFactory.create() + self.client.force_authenticate(user=self.gebruiker.user) + self.url = reverse("indiening_bestand_list") + + def test_indiening_bestand_list_get(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_indiening_bestand_detail_get(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, status.HTTP_200_OK) diff --git a/api/tests/views/test_project.py b/api/tests/views/test_project.py new file mode 100644 index 00000000..fef30bf6 --- /dev/null +++ b/api/tests/views/test_project.py @@ -0,0 +1,71 @@ +from django.urls import reverse +from rest_framework import status +from rest_framework.test import APITestCase +from api.tests.factories.project import ProjectFactory +from api.tests.factories.vak import VakFactory +from api.tests.factories.gebruiker import GebruikerFactory +from rest_framework.test import APIClient +from django.core.files.uploadedfile import SimpleUploadedFile + + +class ProjectListViewTest(APITestCase): + def setUp(self): + self.gebruiker = GebruikerFactory.create(is_lesgever=True) + self.url = reverse("project_list") + self.client = APIClient() + self.client.force_authenticate(user=self.gebruiker.user) + + def test_project_list_get(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_project_list_post(self): + vak = VakFactory.create().vak_id + data = { + "titel": "test project", + "beschrijving": "Dit is een test project.", + "opgave_bestand": SimpleUploadedFile("file.txt", b"file_content"), + "vak": vak, + "deadline": "2024-03-31T12:40:05.317980Z", + "max_score": 20, + } + response = self.client.post(self.url, data, format="multipart") + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + +class ProjectDetailViewTest(APITestCase): + def setUp(self): + self.project = ProjectFactory.create() + self.gebruiker = GebruikerFactory.create(is_lesgever=True) + self.url = reverse("project_detail", kwargs={"id": self.project.project_id}) + self.client = APIClient() + self.client.force_authenticate(user=self.gebruiker.user) + + def test_project_detail_get(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_invalid_project_detail_get(self): + url = reverse("project_detail", kwargs={"id": "9999999"}) + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_project_detail_put(self): + new_data = { + "project_id": self.project.project_id, + "titel": self.project.titel, + "beschrijving": "Aangepaste beschrijving", + "opgave_bestand": self.project.opgave_bestand, + "vak": self.project.vak.vak_id, + "deadline": self.project.deadline, + "max_score": self.project.max_score, + } + response = self.client.put(self.url, new_data, format="multipart") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.project.refresh_from_db() + self.assertEqual(self.project.titel, new_data["titel"]) + self.assertEqual(self.project.beschrijving, new_data["beschrijving"]) + + def test_project_detail_delete(self): + response = self.client.delete(self.url) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) diff --git a/api/tests/views/test_score.py b/api/tests/views/test_score.py new file mode 100644 index 00000000..ab4e20de --- /dev/null +++ b/api/tests/views/test_score.py @@ -0,0 +1,59 @@ +# test_score.py +from django.test import TestCase +from api.tests.factories.score import ScoreFactory +from api.tests.factories.indiening import IndieningFactory +from api.tests.factories.gebruiker import GebruikerFactory +from rest_framework.test import APIClient +from django.urls import reverse +from rest_framework import status + + +class ScoreListViewTest(TestCase): + def setUp(self): + self.client = APIClient() + self.gebruiker = GebruikerFactory.create(is_lesgever=True) + self.score = ScoreFactory.create() + self.score.score = self.score.indiening.groep.project.max_score + self.score.save() + self.client.force_authenticate(user=self.gebruiker.user) + self.url = reverse("score_list") + + def test_score_list_get(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_score_list_post(self): + indiening = IndieningFactory.create() + data = { + "score_id": self.score.score_id, + "score": indiening.groep.project.max_score, + "indiening": indiening.indiening_id, + } + response = self.client.post(self.url, data, format="json") + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + +class ScoreDetailViewTest(TestCase): + def setUp(self): + self.client = APIClient() + self.gebruiker = GebruikerFactory.create() + self.gebruiker.user.is_superuser = True + self.gebruiker.user.save() + self.score = ScoreFactory.create() + self.client.force_authenticate(user=self.gebruiker.user) + self.url = reverse("score_detail", kwargs={"id": self.score.score_id}) + + def test_score_detail_get(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_score_detail_put(self): + new_data = {"score": 10, "indiening": self.score.indiening.indiening_id} + response = self.client.put(self.url, new_data, format="json") + self.assertEqual(response.status_code, 200) + self.score.refresh_from_db() + self.assertEqual(self.score.score, new_data["score"]) # Add other assertions + + def test_score_detail_delete(self): + response = self.client.delete(self.url) + self.assertEqual(response.status_code, 204) diff --git a/api/tests/views/test_vak.py b/api/tests/views/test_vak.py new file mode 100644 index 00000000..19c07230 --- /dev/null +++ b/api/tests/views/test_vak.py @@ -0,0 +1,60 @@ +from django.urls import reverse +from rest_framework import status +from rest_framework.test import APITestCase +from api.tests.factories.vak import VakFactory +from api.tests.factories.gebruiker import GebruikerFactory +from rest_framework.test import APIClient + + +class VakListViewTest(APITestCase): + def setUp(self): + self.client = APIClient() + self.gebruiker = GebruikerFactory.create(is_lesgever=True) + self.studenten = GebruikerFactory.create_batch(3, is_lesgever=False) + self.lesgevers = GebruikerFactory.create_batch(2, is_lesgever=True) + self.client.force_authenticate(user=self.gebruiker.user) + self.url = reverse("vak_list") + + def test_vak_list_get(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_vak_list_post(self): + data = { + "naam": "test_name", + "studenten": [student.user.id for student in self.studenten], + "lesgevers": [lesgever.user.id for lesgever in self.lesgevers], + } + response = self.client.post(self.url, data) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + +class VakDetailViewTest(APITestCase): + def setUp(self): + self.vak = VakFactory.create() + self.client = APIClient() + self.gebruiker = GebruikerFactory.create(is_lesgever=True) + self.studenten = GebruikerFactory.create_batch(3, is_lesgever=False) + self.lesgevers = GebruikerFactory.create_batch(2, is_lesgever=True) + self.client.force_authenticate(user=self.gebruiker.user) + self.url = reverse("vak_detail", args=[self.vak.vak_id]) + + def test_vak_detail_get(self): + response = self.client.get(reverse("vak_detail", args=[self.vak.pk])) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["vak_id"], self.vak.vak_id) + + def test_vak_detail_put(self): + data = { + "vak_id": self.vak.vak_id, + "naam": "nieuwe_vak_naam", + "studenten": [student.user.id for student in self.studenten], + "teachers": [teacher.user.id for teacher in self.lesgevers], + } + response = self.client.put(reverse("vak_detail", args=[self.vak.pk]), data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["naam"], data["naam"]) + + def test_vak_detail_delete(self): + response = self.client.delete(self.url) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) diff --git a/api/urls.py b/api/urls.py index b25c92db..16d7321b 100644 --- a/api/urls.py +++ b/api/urls.py @@ -14,6 +14,7 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ + from django.contrib import admin from django.urls import path, include from rest_framework.urlpatterns import format_suffix_patterns @@ -22,30 +23,43 @@ from .views.gebruiker import gebruiker_list, gebruiker_detail from .views.vak import vak_list, vak_detail from .views.project import project_list, project_detail -from .views.indiening import indiening_list, indiening_detail, indiening_bestand_list, indiening_bestand_detail +from .views.indiening import ( + indiening_list, + indiening_detail, + indiening_bestand_list, + indiening_bestand_detail, +) from .views.score import score_list, score_detail from .views.groep import groep_list, groep_detail urlpatterns = [ - path('.well-known/microsoft-identity-association.json', microsoft_association), - path('admin/', admin.site.urls), - path('oauth2/', include('django_auth_adfs.urls')), - path('login_redirect/', login_redirect), - path('api/', home), - path('api/gebruikers/', gebruiker_list), - path('api/gebruikers//', gebruiker_detail, name='gebruiker_detail'), - path('api/vakken/', vak_list), - path('api/vakken//', vak_detail), - path('api/projecten/', project_list), - path('api/projecten//', project_detail), - path('api/indieningen/', indiening_list), - path('api/indieningen//', indiening_detail), - path('api/indiening_bestanden/', indiening_bestand_list), - path('api/indiening_bestanden//', indiening_bestand_detail), - path('api/scores/', score_list), - path('api/scores//', score_detail), - path('api/groepen/', groep_list), - path('api/groepen//', groep_detail) + path(".well-known/microsoft-identity-association.json", microsoft_association), + path("admin/", admin.site.urls), + path("oauth2/", include("django_auth_adfs.urls")), + path("login_redirect/", login_redirect), + path("api/", home), + path("api/gebruikers/", gebruiker_list, name="gebruiker_list"), + path("api/gebruikers//", gebruiker_detail, name="gebruiker_detail"), + path("api/vakken/", vak_list, name="vak_list"), + path("api/vakken//", vak_detail, name="vak_detail"), + path("api/projecten/", project_list, name="project_list"), + path("api/projecten//", project_detail, name="project_detail"), + path("api/indieningen/", indiening_list, name="indiening_list"), + path("api/indieningen//", indiening_detail, name="indiening_detail"), + path( + "api/indiening_bestanden/", + indiening_bestand_list, + name="indiening_bestand_list", + ), + path( + "api/indiening_bestanden//", + indiening_bestand_detail, + name="indiening_bestand_detail", + ), + path("api/scores/", score_list, name="score_list"), + path("api/scores//", score_detail, name="score_detail"), + path("api/groepen/", groep_list, name="groep_list"), + path("api/groepen//", groep_detail, name="groep_detail"), ] urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/api/utils.py b/api/utils.py index 7088d60b..6a25d2f6 100644 --- a/api/utils.py +++ b/api/utils.py @@ -4,37 +4,41 @@ API_URLS = { - 'gebruikers': '/api/gebruikers', - 'vakken': '/api/vakken', - 'groepen': '/api/groepen', - 'indieningen': '/api/indieningen', - 'indiening_bestanden': '/api/indiening_bestanden', - 'scores': 'api/scores', - 'projecten': 'api/projecten' + "gebruikers": "/api/gebruikers", + "vakken": "/api/vakken", + "groepen": "/api/groepen", + "indieningen": "/api/indieningen", + "indiening_bestanden": "/api/indiening_bestanden", + "scores": "api/scores", + "projecten": "api/projecten", } def get_graph_token(): """ - Get graph token from AD url. + Get graph token from AD url. """ try: url = settings.AD_URL - headers = {'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json'} + headers = { + "Content-Type": "application/x-www-form-urlencoded", + "Accept": "application/json", + } data = { - 'grant_type': 'client_credentials', - 'client_id': settings.CLIENT_ID, - 'client_secret': settings.CLIENT_SECRET, - 'scope': 'https://graph.microsoft.com/.default', + "grant_type": "client_credentials", + "client_id": settings.CLIENT_ID, + "client_secret": settings.CLIENT_SECRET, + "scope": "https://graph.microsoft.com/.default", } response = requests.post(url=url, headers=headers, data=data) return response.json() except Exception: return None - + + def is_lesgever(user): """ Controleert of de gebruiker een lesgever is. @@ -50,6 +54,7 @@ def is_lesgever(user): gebruiker = Gebruiker.objects.get(pk=user.id) return gebruiker.is_lesgever + def contains(lijst, user): """ Controleert of de gebruiker aanwezig is in de gegeven lijst. @@ -64,6 +69,7 @@ def contains(lijst, user): gebruiker = Gebruiker.objects.get(pk=user.id) return lijst.all().contains(gebruiker) + def get_gebruiker(user): """ Haalt de Gebruiker-instantie op voor de gegeven gebruiker. diff --git a/api/views/gebruiker.py b/api/views/gebruiker.py index d65ab422..fe90ebf3 100644 --- a/api/views/gebruiker.py +++ b/api/views/gebruiker.py @@ -8,9 +8,7 @@ from api.utils import is_lesgever - - -@api_view(['GET']) +@api_view(["GET"]) def gebruiker_list(request): """ Een view om alle gebruikers op te halen. @@ -28,17 +26,21 @@ def gebruiker_list(request): gebruikers = Gebruiker.objects.all() else: gebruikers = Gebruiker.objects.filter(user=request.user.id) - - if 'is_lesgever' in request.GET and request.GET.get('is_lesgever').lower() in ['true', 'false']: - gebruikers = gebruikers.filter(is_lesgever = (request.GET.get('is_lesgever').lower() == 'true')) + if "is_lesgever" in request.GET and request.GET.get("is_lesgever").lower() in [ + "true", + "false", + ]: + gebruikers = gebruikers.filter( + is_lesgever=(request.GET.get("is_lesgever").lower() == "true") + ) serializer = GebruikerSerializer(gebruikers, many=True) return Response(serializer.data) return Response(status=status.HTTP_403_FORBIDDEN) - -@api_view(['GET', 'PUT']) + +@api_view(["GET", "PUT"]) def gebruiker_detail(request, id): """ Een view om de gegevens van een specifieke gebruiker op te halen (GET) of bij te werken (PUT). @@ -54,12 +56,12 @@ def gebruiker_detail(request, id): except Gebruiker.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) - if request.method == 'GET': + if request.method == "GET": if is_lesgever(request.user) or id == request.user.id: serializer = GebruikerSerializer(gebruiker) return Response(serializer.data) return Response(status=status.HTTP_403_FORBIDDEN) - elif request.method == 'PUT': + elif request.method == "PUT": if request.user.is_superuser: serializer = GebruikerSerializer(gebruiker, data=request.data) if serializer.is_valid(): @@ -67,7 +69,3 @@ def gebruiker_detail(request, id): return Response(serializer.data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) return Response(status=status.HTTP_403_FORBIDDEN) - - - - diff --git a/api/views/groep.py b/api/views/groep.py index 3ff809d8..7de76cdc 100644 --- a/api/views/groep.py +++ b/api/views/groep.py @@ -7,7 +7,7 @@ from api.utils import is_lesgever, contains -@api_view(['GET', 'POST']) +@api_view(["GET", "POST"]) def groep_list(request, format=None): """ Een view om een lijst van groepen op te halen of een nieuwe groep toe te voegen. @@ -33,19 +33,18 @@ def groep_list(request, format=None): if "project" in request.GET: try: - project = eval(request.GET.get('project')) + project = eval(request.GET.get("project")) groepen = groepen.filter(project=project) except NameError: return Response(status=status.HTTP_400_BAD_REQUEST) if "student" in request.GET: try: - student = eval(request.GET.get('student')) + student = eval(request.GET.get("student")) groepen = groepen.filter(studenten=student) except NameError: return Response(status=status.HTTP_400_BAD_REQUEST) - serializer = GroepSerializer(groepen, many=True) return Response(serializer.data) @@ -74,21 +73,21 @@ def groep_detail(request, id, format=None): groep = Groep.objects.get(pk=id) except Groep.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) - if request.method == 'GET': + if request.method == "GET": if is_lesgever(request.user) or contains(groep.studenten, request.user): serializer = GroepSerializer(groep) return Response(serializer.data) return Response(status=status.HTTP_403_FORBIDDEN) - + if is_lesgever(request.user): - if request.method == 'PUT': + if request.method == "PUT": serializer = GroepSerializer(groep, data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - elif request.method == 'DELETE': + + elif request.method == "DELETE": groep.delete() return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_403_FORBIDDEN) diff --git a/api/views/indiening.py b/api/views/indiening.py index b5f0316a..681b5827 100644 --- a/api/views/indiening.py +++ b/api/views/indiening.py @@ -8,7 +8,7 @@ from api.utils import is_lesgever, contains -@api_view(['GET', 'POST']) +@api_view(["GET", "POST"]) def indiening_list(request, format=None): """ Een view om een lijst van indieningen op te halen of een nieuwe indiening toe te voegen. @@ -34,7 +34,7 @@ def indiening_list(request, format=None): if "groep" in request.GET: try: - groep = eval(request.GET.get('groep')) + groep = eval(request.GET.get("groep")) indieningen = indieningen.filter(groep=groep) except NameError: return Response(status=status.HTTP_400_BAD_REQUEST) @@ -53,8 +53,8 @@ def indiening_list(request, format=None): else: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - for file in request.FILES.getlist('indiening_bestanden'): - IndieningBestand.objects.create(indiening = indiening, bestand = file) + for file in request.FILES.getlist("indiening_bestanden"): + IndieningBestand.objects.create(indiening=indiening, bestand=file) return Response(serializer.data, status=status.HTTP_201_CREATED) @@ -75,24 +75,27 @@ def indiening_detail(request, id, format=None): except Indiening.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) - if request.method == 'GET': - if is_lesgever(request.user) or contains(indiening.groep.studenten, request.user): + if request.method == "GET": + if is_lesgever(request.user) or contains( + indiening.groep.studenten, request.user + ): serializer = IndieningSerializer(indiening) return Response(serializer.data) return Response(status=status.HTTP_403_FORBIDDEN) - - elif request.method == 'DELETE': - if is_lesgever(request.user) or contains(indiening.groep.studenten, request.user): + + elif request.method == "DELETE": + if is_lesgever(request.user) or contains( + indiening.groep.studenten, request.user + ): indiening.delete() return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_403_FORBIDDEN) -@api_view(['GET']) +@api_view(["GET"]) def indiening_bestand_list(request, format=None): """ Een view om een lijst van indieningbestanden op te halen (GET). - GET: Als de gebruiker een lesgever is, worden alle indieningbestanden opgehaald. Als de gebruiker geen lesgever is, worden alleen de indieningbestanden opgehaald van de ingelogde gebruiker. @@ -108,13 +111,16 @@ def indiening_bestand_list(request, format=None): else: groepen = Groep.objects.filter(studenten=request.user.id) indieningen = Indiening.objects.filter(groep__in=groepen) - indieningen_bestanden = IndieningBestand.objects.filter(indiening__in=indieningen) - + indieningen_bestanden = IndieningBestand.objects.filter( + indiening__in=indieningen + ) if "indiening" in request.GET: try: - indiening = eval(request.GET.get('indiening')) - indieningen_bestanden = indieningen_bestanden.filter(indiening=indiening) + indiening = eval(request.GET.get("indiening")) + indieningen_bestanden = indieningen_bestanden.filter( + indiening=indiening + ) except NameError: return Response(status=status.HTTP_400_BAD_REQUEST) @@ -122,7 +128,7 @@ def indiening_bestand_list(request, format=None): return Response(serializer.data) -@api_view(['GET']) +@api_view(["GET"]) def indiening_bestand_detail(request, id, format=None): """ Een view om de gegevens van een specifiek indieningbestand op te halen (GET). @@ -138,8 +144,10 @@ def indiening_bestand_detail(request, id, format=None): except IndieningBestand.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) - if request.method == 'GET': - if is_lesgever(request.user) or contains(indiening_bestand.indiening.groep.studenten, request.user): + if request.method == "GET": + if is_lesgever(request.user) or contains( + indiening_bestand.indiening.groep.studenten, request.user + ): serializer = IndieningBestandSerializer(indiening_bestand) return Response(serializer.data) return Response(status=status.HTTP_403_FORBIDDEN) diff --git a/api/views/project.py b/api/views/project.py index 343cdf87..63be642a 100644 --- a/api/views/project.py +++ b/api/views/project.py @@ -8,7 +8,7 @@ from api.utils import is_lesgever, contains -@api_view(['GET', 'POST']) +@api_view(["GET", "POST"]) def project_list(request, format=None): """ Een view om een lijst van projecten op te halen of een nieuw project toe te voegen. @@ -32,9 +32,9 @@ def project_list(request, format=None): vakken = Vak.objects.filter(studenten=request.user.id) projects = Project.objects.filter(vak__in=vakken) - if 'vak' in request.GET: + if "vak" in request.GET: try: - vak = eval(request.GET.get('vak')) + vak = eval(request.GET.get("vak")) projects = projects.filter(vak=vak) except NameError: return Response(status=status.HTTP_400_BAD_REQUEST) @@ -67,21 +67,21 @@ def project_detail(request, id, format=None): except Project.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) - if request.method == 'GET': + if request.method == "GET": if is_lesgever(request.user) or contains(project.vak.studenten, request.user): serializer = ProjectSerializer(project) return Response(serializer.data) return Response(status=status.HTTP_403_FORBIDDEN) - + if is_lesgever(request.user): - if request.method == 'PUT': + if request.method == "PUT": serializer = ProjectSerializer(project, data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - elif request.method == 'DELETE': + + elif request.method == "DELETE": project.delete() return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_403_FORBIDDEN) diff --git a/api/views/score.py b/api/views/score.py index d336fdf4..6101ddff 100644 --- a/api/views/score.py +++ b/api/views/score.py @@ -9,7 +9,7 @@ from api.utils import is_lesgever, contains -@api_view(['GET', 'POST']) +@api_view(["GET", "POST"]) def score_list(request, format=None): """ Een view om een lijst van scores op te halen of een nieuwe score toe te voegen. @@ -36,7 +36,7 @@ def score_list(request, format=None): if "indiening" in request.GET: try: - indiening = eval(request.GET.get('indiening')) + indiening = eval(request.GET.get("indiening")) scores = scores.filter(indiening=indiening) except NameError: return Response(status=status.HTTP_400_BAD_REQUEST) @@ -52,6 +52,7 @@ def score_list(request, format=None): return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) return Response(status=status.HTTP_403_FORBIDDEN) + @api_view(['GET', 'PUT', 'DELETE']) def score_detail(request, id, format=None): @@ -68,22 +69,24 @@ def score_detail(request, id, format=None): score = Score.objects.get(pk=id) except Score.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) - - if request.method == 'GET': - if is_lesgever(request.user) or contains(score.indiening.groep.studenten, request.user): + + if request.method == "GET": + if is_lesgever(request.user) or contains( + score.indiening.groep.studenten, request.user + ): serializer = ScoreSerializer(score) return Response(serializer.data) return Response(status=status.HTTP_403_FORBIDDEN) - if is_lesgever(request.user): - if request.method == 'PUT': + if is_lesgever(request.user): + if request.method == "PUT": serializer = ScoreSerializer(score, data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - elif request.method == 'DELETE': + + elif request.method == "DELETE": score.delete() return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_403_FORBIDDEN) diff --git a/api/views/vak.py b/api/views/vak.py index e2dc7d21..4da87459 100644 --- a/api/views/vak.py +++ b/api/views/vak.py @@ -9,7 +9,7 @@ from django.core.exceptions import ValidationError -@api_view(['GET', 'POST']) +@api_view(["GET", "POST"]) def vak_list(request, format=None): """ Een view om een lijst van vakken op te halen of een nieuw vak toe te voegen. @@ -58,13 +58,13 @@ def vak_detail(request, id, format=None): except Vak.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) - if request.method == 'GET': + if request.method == "GET": if is_lesgever(request.user) or contains(vak.studenten, request.user): serializer = VakSerializer(vak) return Response(serializer.data) return Response(status=status.HTTP_403_FORBIDDEN) if is_lesgever(request.user): - if request.method == 'PUT': + if request.method == "PUT": try: serializer = VakSerializer(vak, data=request.data) if serializer.is_valid(): @@ -72,9 +72,9 @@ def vak_detail(request, id, format=None): return Response(serializer.data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) except ValidationError as e: - return Response({'error': e}, status=status.HTTP_400_BAD_REQUEST) - - elif request.method == 'DELETE': + return Response({"error": e}, status=status.HTTP_400_BAD_REQUEST) + + elif request.method == "DELETE": vak.delete() return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_403_FORBIDDEN) diff --git a/api/views/views.py b/api/views/views.py index 24565f06..07c38651 100644 --- a/api/views/views.py +++ b/api/views/views.py @@ -3,7 +3,7 @@ from rest_framework.response import Response from rest_framework.decorators import api_view from api.serializers.gebruiker import GebruikerSerializer -from api.utils import API_URLS, get_graph_token +from api.utils import API_URLS def login_redirect(request): @@ -18,12 +18,10 @@ def login_redirect(request): HttpResponseRedirect: Een HTTP-verzoek naar de startpagina na het verwerken van de login-redirect. """ - print(get_graph_token()) - gebruiker_post_data = { - 'user': request.user.id, - 'subjects': [], - 'is_lesgever': False + "user": request.user.id, + "subjects": [], + "is_lesgever": False, } serializer = GebruikerSerializer(data=gebruiker_post_data) if serializer.is_valid(): @@ -31,7 +29,8 @@ def login_redirect(request): return redirect(home) -@api_view(['GET']) + +@api_view(["GET"]) def home(request): """ Een view die de startpagina van de API retourneert. @@ -44,6 +43,7 @@ def home(request): """ return Response(data=API_URLS) + def microsoft_association(request): """ Een view die wordt gebruikt om de associatie met Microsoft-applicaties te bevestigen. diff --git a/api/wsgi.py b/api/wsgi.py index e494f617..db45992e 100644 --- a/api/wsgi.py +++ b/api/wsgi.py @@ -11,6 +11,6 @@ from django.core.wsgi import get_wsgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "api.settings") application = get_wsgi_application() diff --git a/requirements.txt b/requirements.txt index 545a50cf..92d10c3d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,5 @@ django-auth-adfs python-dotenv coverage flake8 +black factory_boy