Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: past deadline message, eager loading submission status #391

Merged
merged 18 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions backend/api/serializers/group_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
from api.permissions.role_permissions import is_student
from api.models.group import Group
from api.models.student import Student
from api.serializers.project_serializer import ProjectSerializer
from api.serializers.student_serializer import StudentIDSerializer


class GroupSerializer(serializers.ModelSerializer):
project = serializers.HyperlinkedRelatedField(
many=False, read_only=True, view_name="project-detail"
project = ProjectSerializer(
read_only=True,
)

students = serializers.HyperlinkedIdentityField(
Expand Down
45 changes: 38 additions & 7 deletions backend/api/serializers/project_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from api.models.course import Course
from api.models.group import Group
from api.models.project import Project
from api.models.submission import Submission
from api.serializers.checks_serializer import StructureCheckSerializer
from api.serializers.course_serializer import CourseSerializer
from api.serializers.submission_serializer import SubmissionSerializer
Expand All @@ -15,9 +16,45 @@
from rest_framework.exceptions import ValidationError


class SubmissionStatusSerializer(serializers.Serializer):
non_empty_groups = serializers.IntegerField(read_only=True)
groups_submitted = serializers.IntegerField(read_only=True)
submissions_passed = serializers.IntegerField(read_only=True)

def to_representation(self, instance: Project):
"""Return the submission status of the project"""
if not isinstance(instance, Project):
raise ValidationError(gettext("project.errors.invalid_instance"))

non_empty_groups = instance.groups.filter(students__isnull=False).count()
groups_submitted = Submission.objects.filter(group__project=instance).count()
submissions_passed = Submission.objects.filter(group__project=instance, is_valid=True).count()

return {
"non_empty_groups": non_empty_groups,
"groups_submitted": groups_submitted,
"submissions_passed": submissions_passed,
}

class Meta:
fields = [
"non_empty_groups",
"groups_submitted",
"submissions_passed",
]


class ProjectSerializer(serializers.ModelSerializer):
# We want the course to be eager loaded
course = CourseSerializer(read_only=True)
course = CourseSerializer(
read_only=True
)

# We want the status to be eager loaded
status = SubmissionStatusSerializer(
source="*",
read_only=True
)

structure_checks = serializers.HyperlinkedIdentityField(
view_name="project-structure-checks",
Expand Down Expand Up @@ -131,12 +168,6 @@ class TeacherCreateGroupSerializer(serializers.Serializer):
number_groups = serializers.IntegerField(min_value=1)


class SubmissionStatusSerializer(serializers.Serializer):
non_empty_groups = serializers.IntegerField(read_only=True)
groups_submitted = serializers.IntegerField(read_only=True)
submissions_passed = serializers.IntegerField(read_only=True)


class SubmissionAddSerializer(SubmissionSerializer):
def validate(self, data):

Expand Down
17 changes: 3 additions & 14 deletions backend/api/tests/test_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def test_group_detail_view(self):
project = create_project(
name="Project 1", description="Description 1", days=7, course=course
)

student = create_student(
id=5, first_name="John", last_name="Doe", email="john.doe@example.com"
)
Expand All @@ -36,12 +37,8 @@ def test_group_detail_view(self):

content_json = json.loads(response.content.decode("utf-8"))

expected_project_url = settings.TESTING_BASE_LINK + reverse(
"project-detail", args=[str(project.id)]
)

self.assertEqual(int(content_json["id"]), group.id)
self.assertEqual(content_json["project"], expected_project_url)
self.assertEqual(content_json["project"]["id"], group.project.id)
self.assertEqual(content_json["score"], group.score)

def test_group_project(self):
Expand Down Expand Up @@ -69,16 +66,8 @@ def test_group_project(self):
self.assertEqual(int(content_json["id"]), group.id)
self.assertEqual(content_json["score"], group.score)

response = self.client.get(content_json["project"], 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"))
content_json = content_json["project"]

self.assertEqual(content_json["name"], project.name)
self.assertEqual(content_json["description"], project.description)
Expand Down
35 changes: 35 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@vuelidate/validators": "^2.0.4",
"@vueuse/core": "^10.9.0",
"axios": "^1.6.8",
"axios-cache-interceptor": "^1.5.2",
"js-cookie": "^3.0.5",
"moment": "^2.30.1",
"msw": "^2.2.13",
Expand Down
Binary file not shown.
7 changes: 5 additions & 2 deletions frontend/src/assets/lang/app/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"coming": "Near deadlines",
"deadline": "Deadline",
"days": "Today at {hour} | Tomorrow at {hour} | In {count} days",
"ago": "{count} days ago",
"groupName": "Group name",
"groupPopulation": "Size",
"groupStatus": "Status",
Expand All @@ -70,7 +71,9 @@
"scoreVisibility": "Make score, when uploaded, automatically visible to students",
"dockerUpload": "Upload a Dockerfile",
"submissionStructure": "Structure of how a submission should be made",
"noStudents": "No students in this group"
"noStudents": "No students in this group",
"locked": "Closed",
"unlocked": "Open"
},
"submissions": {
"title": "Submissions",
Expand All @@ -97,6 +100,7 @@
"clone": "Clone course",
"cloneAssistants": "Clone assistants:",
"cloneTeachers": "Clone teachers:",
"cloneCourse": "Clone teachers:",
"name": "Course name",
"description": "Description",
"excerpt": "Short description",
Expand Down Expand Up @@ -137,7 +141,6 @@
"duration": "Validity period link (in days):",
"link": "Invitation link:"
}

}
},
"composables": {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/assets/lang/app/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"coming": "Aankomende deadlines",
"deadline": "Deadline",
"days": "Vandaag om {hour} | Morgen om {hour} | Over {count} dagen",
"ago": "{count} dagen geleden",
"groupName": "Groepsnaam",
"groupPopulation": "Grootte",
"groupStatus": "Status",
Expand Down
13 changes: 6 additions & 7 deletions frontend/src/components/courses/CourseGeneralList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,12 @@ watch(user, loadCourses, { immediate: true });
<template v-else>
<div class="w-30rem text-center mx-auto">
<span class="pi pi-exclamation-circle text-6xl text-primary" />
<p>{{ t('components.list.noCourses') }}</p>
<RouterLink :to="{ name: 'courses' }" v-if="user?.isStudent()">
<Button :label="t('components.button.searchCourse')" icon="pi pi-search" />
</RouterLink>
<RouterLink :to="{ name: 'course-create' }" v-else>
<Button :label="t('components.button.createCourse')" icon="pi pi-plus" />
</RouterLink>
<slot name="empty">
<p>{{ t('components.list.noCourses') }}</p>
<RouterLink :to="{ name: 'courses' }">
<Button :label="t('components.button.searchCourse')" icon="pi pi-search" />
</RouterLink>
</slot>
</div>
</template>
</template>
Expand Down
Loading