Skip to content

Commit

Permalink
Merge pull request #200 from SELab-2/development
Browse files Browse the repository at this point in the history
Deploy first version of the frontend
  • Loading branch information
BramMeir authored Mar 29, 2024
2 parents 37cbb36 + f9c2070 commit 90e9d07
Show file tree
Hide file tree
Showing 226 changed files with 20,941 additions and 1,314 deletions.
2 changes: 1 addition & 1 deletion .dev.env
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ DJANGO_CAS_URL_PREFIX="" # URL prefix for the CAS server. Should be empty for de
DJANGO_CAS_PORT=8080 # Port for the CAS server. Should be 8080 if DJANGO_DOMAIN_NAME is localhost
DJANGO_DB_ENGINE=django.db.backends.sqlite3 # Database engine
DJANGO_REDIS_HOST=${REDIS_IP} # Redis configuration
DJANGO_REDIS_PORT=${REDIS_PORT}
DJANGO_REDIS_PORT=${REDIS_PORT}
2 changes: 1 addition & 1 deletion .github/workflows/deployement.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ jobs:
script: |
cd UGent-7
docker-compose -f production.yml down
${{ secrets.PULL_SCRIPT }}
git pull
docker-compose -f production.yml build --no-cache
docker-compose -f production.yml up -d
2 changes: 1 addition & 1 deletion .prod.env
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ DJANGO_DB_PASSWORD=${POSTGRES_PASSWORD}
DJANGO_DB_HOST=${POSTGRES_IP}
DJANGO_DB_PORT=${POSTGRES_PORT}
DJANGO_REDIS_HOST=${REDIS_IP} # Redis configuration
DJANGO_REDIS_PORT=${REDIS_PORT}
DJANGO_REDIS_PORT=${REDIS_PORT}
Binary file modified backend/.coverage
Binary file not shown.
5 changes: 4 additions & 1 deletion backend/api/fixtures/groups.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@
fields:
project: 1
score: 8
students: []
students:
- '1'
- '2'
- '3'
9 changes: 8 additions & 1 deletion backend/api/fixtures/students.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,11 @@
pk: '2'
fields:
student_id: null
courses: []
courses:
- 1
- model: api.student
pk: '3'
fields:
student_id: null
courses:
- 1
6 changes: 4 additions & 2 deletions backend/api/models/course.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,17 @@ def is_past(self) -> bool:

def clone(self, clone_assistants=True) -> Self:
"""Clone the course to the next academic start year"""
course = Course(
course = Course.objects.create(
name=self.name,
description=self.description,
academic_startyear=self.academic_startyear + 1,
parent_course=self
)

if clone_assistants:
course.assistants.add(self.assistants)
# Add all the assistants of the current course to the follow-up course
for assistant in self.assistants.all():
course.assistants.add(assistant)

return course

Expand Down
2 changes: 1 addition & 1 deletion backend/api/permissions/assistant_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def has_permission(self, request: Request, view: ViewSet) -> bool:
"""Check if user has permission to view a general assistant endpoint."""
user = request.user

if view.action == "list":
if view.action in ['list', 'create', 'destroy']:
# Only teachers can query the assistant list.
return user.is_authenticated and is_teacher(user)

Expand Down
16 changes: 0 additions & 16 deletions backend/api/permissions/course_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,3 @@ def has_object_permission(self, request: Request, view: ViewSet, course: Course)

# Teachers and assistants can add and remove any student.
return super().has_object_permission(request, view, course)


class CourseProjectPermission(CoursePermission):
"""Permission class for project related endpoints."""
def has_permission(self, request: Request, view: ViewSet) -> bool:
return request.user and request.user.is_authenticated

def has_object_permission(self, request: Request, view: ViewSet, course: Course):
user: User = request.user

# Logged-in users can fetch course projects.
if request.method in SAFE_METHODS:
return user.is_authenticated

# Teachers and assistants can modify projects.
return super().has_object_permission(request, view, course)
15 changes: 15 additions & 0 deletions backend/api/permissions/student_permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from rest_framework.permissions import IsAuthenticated, SAFE_METHODS
from api.permissions.role_permissions import is_teacher
from authentication.models import User


class StudentPermission(IsAuthenticated):

def has_permission(self, request, view):
"""Check if user has permission to view a general student endpoint."""
return view.action == 'retrieve'

def has_object_permission(self, request, view, obj):
"""Check if user has permission to view a detailed group endpoint"""
user: User = request.user
return request.method in SAFE_METHODS and (user.id == request.user.id or is_teacher(user))
15 changes: 15 additions & 0 deletions backend/api/permissions/teacher_permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from rest_framework.permissions import IsAuthenticated, SAFE_METHODS
from authentication.models import User


# (Almost) same as StudentPermission
class TeacherPermission(IsAuthenticated):

def has_permission(self, request, view):
"""Check if user has permission to view a general Teacher endpoint."""
return view.action == 'retrieve'

def has_object_permission(self, request, view, obj):
"""Check if user has permission to view a detailed group endpoint"""
user: User = request.user
return request.method in SAFE_METHODS and user.id == request.user.id
11 changes: 1 addition & 10 deletions backend/api/serializers/assistant_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,7 @@ class AssistantSerializer(serializers.ModelSerializer):

class Meta:
model = Assistant
fields = [
"id",
"first_name",
"last_name",
"email",
"faculties",
"last_enrolled",
"create_time",
"courses",
]
fields = "__all__"


class AssistantIDSerializer(serializers.Serializer):
Expand Down
12 changes: 3 additions & 9 deletions backend/api/serializers/checks_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,13 @@ class StructureCheckSerializer(serializers.ModelSerializer):
read_only=True
)

obligated_extensions = FileExtensionSerializer(many=True, required=False, default=[], read_only=True)
obligated_extensions = FileExtensionSerializer(many=True, required=False, default=[])

blocked_extensions = FileExtensionSerializer(many=True, required=False, default=[], read_only=True)
blocked_extensions = FileExtensionSerializer(many=True, required=False, default=[])

class Meta:
model = StructureCheck
fields = [
"id",
"name",
"project",
"obligated_extensions",
"blocked_extensions"
]
fields = "__all__"


class ExtraCheckSerializer(serializers.ModelSerializer):
Expand Down
16 changes: 5 additions & 11 deletions backend/api/serializers/course_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,7 @@ class CourseSerializer(serializers.ModelSerializer):

class Meta:
model = Course
fields = [
"id",
"name",
"academic_startyear",
"description",
"parent_course",
"teachers",
"assistants",
"students",
"projects",
]
fields = "__all__"


class CourseIDSerializer(serializers.Serializer):
Expand All @@ -51,6 +41,10 @@ class CourseIDSerializer(serializers.Serializer):
)


class CourseCloneSerializer(serializers.Serializer):
clone_assistants = serializers.BooleanField()


class StudentJoinSerializer(StudentIDSerializer):
def validate(self, data):
# The validator needs the course context.
Expand Down
2 changes: 1 addition & 1 deletion backend/api/serializers/faculty_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
class facultySerializer(serializers.ModelSerializer):
class Meta:
model = Faculty
fields = ["name"]
fields = "__all__"
2 changes: 1 addition & 1 deletion backend/api/serializers/group_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class GroupSerializer(serializers.ModelSerializer):

class Meta:
model = Group
fields = ["id", "project", "students", "score", "submissions"]
fields = "__all__"

def to_representation(self, instance):
data = super().to_representation(instance)
Expand Down
56 changes: 34 additions & 22 deletions backend/api/serializers/project_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@
from api.models.group import Group
from rest_framework.exceptions import ValidationError
from django.utils import timezone
from api.models.submission import Submission, SubmissionFile
from api.models.checks import FileExtension, StructureCheck
from api.models.checks import FileExtension
from api.serializers.submission_serializer import SubmissionSerializer
from api.serializers.checks_serializer import StructureCheckSerializer
from rest_framework.request import Request


class ProjectSerializer(serializers.ModelSerializer):
Expand All @@ -33,24 +31,14 @@ class ProjectSerializer(serializers.ModelSerializer):
read_only=True
)

submissions = serializers.HyperlinkedIdentityField(
view_name="project-submissions",
read_only=True
)

class Meta:
model = Project
fields = [
"id",
"name",
"description",
"visible",
"archived",
"start_date",
"deadline",
"max_score",
"score_visible",
"group_size",
"structure_checks",
"extra_checks",
"course",
"groups"
]
fields = "__all__"

def validate(self, data):
if "course" in self.context:
Expand All @@ -69,12 +57,36 @@ def validate(self, data):
return data


class CreateProjectSerializer(ProjectSerializer):
number_groups = serializers.IntegerField(min_value=1, required=False)

def create(self, validated_data):
# Pop the 'number_groups' field from validated_data
number_groups = validated_data.pop('number_groups', None)

# Create the project object without passing 'number_groups' field
project = super().create(validated_data)

# Create groups for the project, if specified
if number_groups is not None:

for _ in range(number_groups):
Group.objects.create(project=project)

elif project.group_size == 1:
# If the group_size is set to one, create a group for each student
students = project.course.students.all()

for student in students:
group = Group.objects.create(project=project)
group.students.add(student)

return project


class TeacherCreateGroupSerializer(serializers.Serializer):
number_groups = serializers.IntegerField(min_value=1)

def validate(self, data):
return data


class SubmissionStatusSerializer(serializers.Serializer):
non_empty_groups = serializers.IntegerField(read_only=True)
Expand Down
10 changes: 1 addition & 9 deletions backend/api/serializers/submission_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,7 @@ class SubmissionSerializer(serializers.ModelSerializer):

class Meta:
model = Submission
fields = [
"id",
"group",
"submission_number",
"submission_time",
"files",
"structure_checks_passed",
"extra_checks_results"
]
fields = "__all__"
extra_kwargs = {
"submission_number": {
"required": False,
Expand Down
11 changes: 1 addition & 10 deletions backend/api/serializers/teacher_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,4 @@ class TeacherSerializer(serializers.ModelSerializer):

class Meta:
model = Teacher
fields = [
"id",
"first_name",
"last_name",
"email",
"faculties",
"last_enrolled",
"create_time",
"courses",
]
fields = "__all__"
42 changes: 42 additions & 0 deletions backend/api/tests/test_assistant.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from django.urls import reverse
from rest_framework.test import APITestCase
from api.models.assistant import Assistant
from api.models.teacher import Teacher
from api.models.course import Course
from authentication.models import Faculty, User

Expand Down Expand Up @@ -287,3 +288,44 @@ def test_assistant_courses(self):
self.assertEqual(content["name"], course2.name)
self.assertEqual(int(content["academic_startyear"]), course2.academic_startyear)
self.assertEqual(content["description"], course2.description)


class AssitantModelAsTeacherTests(APITestCase):
def setUp(self) -> None:
self.user = Teacher.objects.create(
id=1,
first_name="John",
last_name="Doe",
username="john_doe",
email="[email protected]"
)

self.client.force_authenticate(self.user)

def test_retrieve_assistant_list(self):
"""
Able to retrieve assistant list as a teacher.
"""
# Create an assistant for testing with the name "Bob Peeters"
create_assistant(
id=5, first_name="Bob", last_name="Peeters", email="[email protected]"
)

create_assistant(
id=6, first_name="Jane", last_name="Doe", email="[email protected]"
)

# Make a GET request to retrieve the assistant details
response = self.client.get(reverse("assistant-list"), follow=True)

# Check if the response was successful
self.assertEqual(response.status_code, 200)

# Assert that the response is JSON
self.assertEqual(response.accepted_media_type, "application/json")

# Parse the JSON content from the response
content_json = json.loads(response.content.decode("utf-8"))

# Assert that the parsed JSON is a list with multiple assistant
self.assertEqual(len(content_json), 2)
Loading

0 comments on commit 90e9d07

Please sign in to comment.