Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

Commit 0d505ea

Browse files
authored
Merge pull request #56 from SELab-2/45-backend-fixes-refactors
Backend changes
2 parents da5e6d4 + eb54a78 commit 0d505ea

40 files changed

+691
-299
lines changed

README.md

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
# UGent-1
22

33
## Samenstelling
4-
- Alexander Van Oyen
54
- Axel Lorreyne: Projectleider
6-
- Gilles Arnout
75
- Pieter-Jan De Smijter: Technical lead
86
- Reinhard De Paepe: Backend lead
9-
- Robin Paret: Systeembeheerder
10-
- Rune Dyselinck
117
- Thibaud Collyn : Customer relations officer, Frontend lead
8+
- Gilles Arnout: Frontend dev
9+
- Alexander Van Oyen: Testing
10+
- Robin Paret: Systeembeheerder
11+
- Rune Dyselinck: Secretary
12+
13+
## Bekijk de [wiki](https://github.com/SELab-2/UGent-1/wiki) om alles over de applicatie te weten te komen.

backend/pigeonhole/apps/courses/migrations/0001_initial.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Generated by Django 5.0.3 on 2024-03-12 22:52
1+
# Generated by Django 4.2.11 on 2024-04-01 15:13
22

33
from django.db import migrations, models
44

@@ -17,6 +17,8 @@ class Migration(migrations.Migration):
1717
('course_id', models.BigAutoField(primary_key=True, serialize=False)),
1818
('name', models.CharField(max_length=256)),
1919
('description', models.TextField()),
20+
('open_course', models.BooleanField(default=False)),
21+
('invite_token', models.CharField(blank=True, max_length=20, null=True)),
2022
],
2123
),
2224
]
+28-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from django.db import models
2+
from django.utils.crypto import get_random_string
23
from rest_framework import serializers
34

45

@@ -7,11 +8,37 @@ class Course(models.Model):
78
course_id = models.BigAutoField(primary_key=True)
89
name = models.CharField(max_length=256)
910
description = models.TextField()
11+
open_course = models.BooleanField(default=False)
12+
invite_token = models.CharField(max_length=20, blank=True, null=True)
1013

1114
objects = models.Manager()
1215

16+
def generate_new_token(self):
17+
# generate a new token (could be useful if you want to use a different invite token)
18+
token = get_random_string(length=20)
19+
self.invitation_token = token
20+
self.save()
21+
return token
22+
23+
def save(self, *args, **kwargs):
24+
# generate new code on initialization of a new course
25+
if not self.invite_token:
26+
self.invite_token = get_random_string(length=20)
27+
return super().save(*args, **kwargs)
28+
1329

1430
class CourseSerializer(serializers.ModelSerializer):
31+
invite_token = serializers.ReadOnlyField()
32+
1533
class Meta:
1634
model = Course
17-
fields = ['course_id', 'name', 'description']
35+
fields = ['course_id', 'name', 'open_course', 'description', 'invite_token']
36+
37+
def to_representation(self, instance):
38+
data = super().to_representation(instance)
39+
request = self.context.get('request')
40+
41+
if request and request.user.is_student:
42+
if 'invite_token' in data:
43+
del data['invite_token']
44+
return data

backend/pigeonhole/apps/courses/permissions.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def has_permission(self, request, view):
99
if request.user.is_admin or request.user.is_superuser:
1010
return True
1111

12-
if view.action in ['join_course', 'get_selected_courses']:
12+
if view.action in ['join_course', 'get_selected_courses', 'join_course_with_token', 'leave_course']:
1313
return True
1414

1515
if request.user.is_teacher:
@@ -20,7 +20,7 @@ def has_permission(self, request, view):
2020
course=view.kwargs[
2121
'pk']).exists():
2222
return True
23-
return
23+
return False
2424

2525
if request.user.is_student:
2626
if view.action == 'get_projects':

backend/pigeonhole/apps/courses/views.py

+39-9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from rest_framework.response import Response
55

66
from backend.pigeonhole.apps.courses.models import CourseSerializer
7+
from backend.pigeonhole.apps.groups.models import Group
78
from backend.pigeonhole.apps.projects.models import Project
89
from backend.pigeonhole.apps.projects.models import ProjectSerializer
910
from .models import Course
@@ -25,16 +26,45 @@ def join_course(self, request, *args, **kwargs):
2526
course = self.get_object()
2627
user = request.user
2728

28-
user.course.add(course)
29-
return Response(status=status.HTTP_200_OK)
29+
if request.user.is_student:
30+
if course.open_course:
31+
user.course.add(course)
32+
return Response(status=status.HTTP_200_OK)
33+
else:
34+
return Response({'message': 'Invite token required.'}, status=status.HTTP_403_FORBIDDEN)
35+
else:
36+
user.course.add(course)
37+
return Response(status=status.HTTP_200_OK)
38+
39+
@action(detail=True, methods=['post'], url_path='join_course_with_token/(?P<invite_token>[^/.]+)')
40+
def join_course_with_token(self, request, *args, **kwargs):
41+
course = self.get_object()
42+
user = request.user
43+
invite_token = kwargs.get('invite_token')
3044

31-
# def leave_course(self, request, *args, **kwargs):
32-
# course = self.get_object()
33-
# user = request.user
34-
#
35-
# user.course.remove(course)
36-
# return Response(status=status.HTTP_200_OK)
37-
# TODO implement leave_course (dont forget to leave all groups as well)
45+
if invite_token == course.invite_token:
46+
user.course.add(course)
47+
return Response({'message': 'Successfully joined the course with invite token.'},
48+
status=status.HTTP_200_OK)
49+
else:
50+
return Response({'error': 'Invalid invite token.'}, status=status.HTTP_400_BAD_REQUEST)
51+
52+
@action(detail=True, methods=['post'])
53+
def leave_course(self, request, *args, **kwargs):
54+
course = self.get_object()
55+
user = request.user
56+
if course in user.course.all():
57+
projects = Project.objects.filter(course_id=course.course_id)
58+
for project in projects:
59+
groups = Group.objects.filter(project_id=project.project_id)
60+
for group in groups:
61+
if user in group.user.all():
62+
group.user.remove(user)
63+
group.save()
64+
user.course.remove(course)
65+
else:
66+
return Response({'message': 'User is not in course'}, status=status.HTTP_400_BAD_REQUEST)
67+
return Response(status=status.HTTP_200_OK)
3868

3969
@action(detail=False, methods=['GET'])
4070
def get_selected_courses(self, request, *args, **kwargs):

backend/pigeonhole/apps/groups/migrations/0001_initial.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
# Generated by Django 5.0.3 on 2024-03-12 22:52
1+
# Generated by Django 4.2.11 on 2024-04-01 15:13
22

3-
import django.db.models.deletion
43
from django.db import migrations, models
4+
import django.db.models.deletion
55

66

77
class Migration(migrations.Migration):
@@ -20,7 +20,7 @@ class Migration(migrations.Migration):
2020
('group_nr', models.IntegerField(blank=True, null=True)),
2121
('feedback', models.TextField(null=True)),
2222
('final_score', models.IntegerField(blank=True, null=True)),
23-
('visible', models.BooleanField(default=True)),
23+
('visible', models.BooleanField(default=False)),
2424
('project_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='projects.project')),
2525
],
2626
),
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Generated by Django 5.0.3 on 2024-03-12 22:52
1+
# Generated by Django 4.2.11 on 2024-04-01 15:13
22

33
from django.conf import settings
44
from django.db import migrations, models
@@ -9,14 +9,14 @@ class Migration(migrations.Migration):
99
initial = True
1010

1111
dependencies = [
12-
('groups', '0001_initial'),
1312
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
13+
('groups', '0001_initial'),
1414
]
1515

1616
operations = [
1717
migrations.AddField(
1818
model_name='group',
1919
name='user',
20-
field=models.ManyToManyField(to=settings.AUTH_USER_MODEL),
20+
field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL),
2121
),
2222
]

backend/pigeonhole/apps/groups/migrations/0003_alter_group_visible.py

-18
This file was deleted.

backend/pigeonhole/apps/groups/migrations/0004_alter_group_user.py

-20
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
# Generated by Django 5.0.3 on 2024-03-12 22:52
1+
# Generated by Django 4.2.11 on 2024-04-01 15:13
22

3-
import django.db.models.deletion
43
from django.db import migrations, models
4+
import django.db.models.deletion
55

66

77
class Migration(migrations.Migration):
8+
89
initial = True
910

1011
dependencies = [
@@ -15,29 +16,35 @@ class Migration(migrations.Migration):
1516
migrations.CreateModel(
1617
name='Project',
1718
fields=[
18-
('project_id', models.BigAutoField(primary_key=True, serialize=False)),
19+
('project_id', models.BigAutoField(primary_key=True,
20+
serialize=False)),
1921
('name', models.CharField(max_length=256)),
2022
('description', models.TextField()),
21-
('deadline', models.DateTimeField(blank=True, null=True)),
23+
('deadline', models.DateTimeField()),
2224
('visible', models.BooleanField(default=False)),
25+
('max_score', models.IntegerField(default=10)),
2326
('number_of_groups', models.IntegerField(default=5)),
2427
('group_size', models.IntegerField(default=1)),
2528
('file_structure', models.CharField(max_length=1024, null=True)),
26-
('max_score', models.IntegerField(blank=True, null=True)),
27-
('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.course')),
29+
('course_id', models.ForeignKey(
30+
on_delete=django.db.models.deletion.CASCADE,
31+
to='courses.course')),
2832
],
2933
),
3034
migrations.CreateModel(
3135
name='Test',
3236
fields=[
33-
('project_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, primary_key=True,
34-
serialize=False, to='projects.project')),
37+
('project_id',
38+
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,
39+
primary_key=True, serialize=False,
40+
to='projects.project')),
3541
('test_nr', models.IntegerField()),
36-
('test_file_type', models.FileField(max_length=255, null=True, upload_to='uploads/projects/<django.db.'
37-
'models.fields.related.'
38-
'ForeignKey>/<django.db.'
39-
'models.fields.'
40-
'IntegerField>')),
42+
('test_file_type',
43+
models.FileField(max_length=255,
44+
null=True,
45+
upload_to='uploads/projects/<django.db.'
46+
'models.fields.related.ForeignKey>/'
47+
'<django.db.models.fields.IntegerField>')),
4148
],
4249
),
4350
]

backend/pigeonhole/apps/projects/models.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,12 @@ class Project(models.Model):
1010
course_id = models.ForeignKey(Course, on_delete=models.CASCADE)
1111
name = models.CharField(max_length=256)
1212
description = models.TextField()
13-
deadline = models.DateTimeField(null=True, blank=True)
13+
deadline = models.DateTimeField()
1414
visible = models.BooleanField(default=False)
15-
max_score = models.IntegerField(null=True, blank=True)
15+
max_score = models.IntegerField(default=10)
1616
number_of_groups = models.IntegerField(default=5)
1717
group_size = models.IntegerField(default=1)
1818
file_structure = models.CharField(max_length=1024, null=True)
19-
max_score = models.IntegerField(null=True, blank=True)
2019

2120

2221
class ProjectSerializer(serializers.ModelSerializer):
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,56 @@
1-
# Generated by Django 5.0.3 on 2024-03-12 22:52
1+
# Generated by Django 4.2.11 on 2024-04-01 15:13
22

33
import backend.pigeonhole.apps.submissions.models
4-
import django.db.models.deletion
54
from django.db import migrations, models
5+
import django.db.models.deletion
66

77

88
class Migration(migrations.Migration):
99
initial = True
1010

1111
dependencies = [
12-
('groups', '0001_initial'),
12+
("groups", "0001_initial"),
1313
]
1414

1515
operations = [
1616
migrations.CreateModel(
17-
name='Submissions',
17+
name="Submissions",
1818
fields=[
19-
('submission_id',
20-
models.BigAutoField(primary_key=True, serialize=False)),
21-
('submission_nr', models.IntegerField(blank=True)),
22-
('file',
23-
models.FileField(max_length=255, null=True,
24-
upload_to=backend.pigeonhole.apps.
25-
submissions.models.get_upload_to)),
26-
('timestamp', models.DateTimeField(auto_now_add=True)),
27-
('output_test',
28-
models.FileField(blank=True,
29-
max_length=255,
30-
null=True,
31-
upload_to='uploads/submissions/outputs/'
32-
'<django.db.models.fields.related.Foreign'
33-
'Key>/<django.db.models.fields.In'
34-
'tegerField>/output_test/')),
35-
('group_id',
36-
models.ForeignKey(blank=True,
37-
on_delete=django.db.models.deletion.CASCADE,
38-
to='groups.group')),
19+
(
20+
"submission_id",
21+
models.BigAutoField(primary_key=True, serialize=False),
22+
),
23+
("submission_nr", models.IntegerField(blank=True)),
24+
(
25+
"file",
26+
models.FileField(
27+
max_length=255,
28+
null=True,
29+
upload_to=backend.
30+
pigeonhole.apps.submissions.models.get_upload_to,
31+
),
32+
),
33+
("timestamp", models.DateTimeField(auto_now_add=True)),
34+
(
35+
"output_test",
36+
models.FileField(
37+
blank=True,
38+
max_length=255,
39+
null=True,
40+
upload_to="uploads/submissions/outputs/"
41+
"<django.db.models.fields.related.Foreign"
42+
"Key>/<django.db.models.fields.Integer"
43+
"Field>/output_test/",
44+
),
45+
),
46+
(
47+
"group_id",
48+
models.ForeignKey(
49+
blank=True,
50+
on_delete=django.db.models.deletion.CASCADE,
51+
to="groups.group",
52+
),
53+
),
3954
],
4055
),
4156
]

0 commit comments

Comments
 (0)