diff --git a/backend/api/serializers/group_serializer.py b/backend/api/serializers/group_serializer.py
index 3496bcbc..3865f7a2 100644
--- a/backend/api/serializers/group_serializer.py
+++ b/backend/api/serializers/group_serializer.py
@@ -1,6 +1,8 @@
from api.models.group import Group
from api.models.student import Student
-from api.permissions.role_permissions import is_student
+from api.models.assistant import Assistant
+from api.models.teacher import Teacher
+from api.permissions.role_permissions import is_student, is_assistant, is_teacher
from api.serializers.project_serializer import ProjectSerializer
from api.serializers.student_serializer import StudentIDSerializer
from django.utils.translation import gettext
@@ -30,12 +32,17 @@ class Meta:
def to_representation(self, instance):
data = super().to_representation(instance)
- # If you are not a student, you can always see the score
- if is_student(self.context["request"].user):
- # Student can not see the score if they are not part of the group, or it is not visible yet
- if not instance.students.filter(id=self.context["request"].user.student.id).exists() or\
- not instance.project.score_visible:
+ user = self.context["request"].user
+ course_id = instance.project.course.id
+ # If you are not a student, you can always see the score
+ if is_student(user):
+ student_in_course = user.student.courses.filter(id=course_id).exists()
+ # Student can not see the score if they are not part of the course associated with group and
+ # neither an assistant or a teacher,
+ # or it is not visible yet when they are part of the course associated with the group
+ if not student_in_course and not is_assistant(user) and not is_teacher(user) or \
+ not instance.project.score_visible and student_in_course:
data.pop("score")
return data
diff --git a/frontend/src/assets/lang/app/en.json b/frontend/src/assets/lang/app/en.json
index d2f951a7..6478779f 100644
--- a/frontend/src/assets/lang/app/en.json
+++ b/frontend/src/assets/lang/app/en.json
@@ -167,7 +167,8 @@
"searchCourse": "Search a course",
"createCourse": "Create a new course",
"public": "Public",
- "protected": "Protected"
+ "protected": "Protected",
+ "csv": "Download grades as a .csv file"
},
"card": {
"open": "Details",
diff --git a/frontend/src/assets/lang/app/nl.json b/frontend/src/assets/lang/app/nl.json
index 92456aa0..2cca7874 100644
--- a/frontend/src/assets/lang/app/nl.json
+++ b/frontend/src/assets/lang/app/nl.json
@@ -164,7 +164,8 @@
"searchCourse": "Zoek een vak",
"createCourse": "Maak een vak",
"public": "Publiek",
- "protected": "Besloten"
+ "protected": "Besloten",
+ "csv": "Download punten als een .csv bestand"
},
"card": {
"open": "Details",
diff --git a/frontend/src/components/projects/DownloadCSVButton.vue b/frontend/src/components/projects/DownloadCSVButton.vue
new file mode 100644
index 00000000..4f6915c3
--- /dev/null
+++ b/frontend/src/components/projects/DownloadCSVButton.vue
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
diff --git a/frontend/src/composables/services/student.service.ts b/frontend/src/composables/services/student.service.ts
index 8c1e62b2..c70c67bd 100644
--- a/frontend/src/composables/services/student.service.ts
+++ b/frontend/src/composables/services/student.service.ts
@@ -73,7 +73,7 @@ export function useStudents(): StudentsState {
endpoint,
{
user: studentData.id,
- student_id: studentData.studentId,
+ student_id: studentData.student_id,
},
student,
Student.fromJSON,
diff --git a/frontend/src/test/unit/services/setup/data.ts b/frontend/src/test/unit/services/setup/data.ts
index 1ad12327..f25084c9 100644
--- a/frontend/src/test/unit/services/setup/data.ts
+++ b/frontend/src/test/unit/services/setup/data.ts
@@ -158,7 +158,7 @@ export const students = [
last_name: 'Doe',
last_enrolled: 2023,
create_time: new Date('July 21, 2024 01:15:00'),
- studentId: null,
+ student_id: null,
roles: ['student'],
courses: ['1', '2', '3'],
groups: ['0'],
@@ -174,7 +174,7 @@ export const students = [
last_name: 'Verhaege',
last_enrolled: 2023,
create_time: new Date('July 21, 2024 01:15:00'),
- studentId: null,
+ student_id: null,
roles: ['student'],
courses: [],
groups: [],
@@ -190,7 +190,7 @@ export const students = [
last_name: 'Verslype',
last_enrolled: 2023,
create_time: new Date('July 21, 2024 01:15:00'),
- studentId: '02012470',
+ student_id: '02012470',
roles: ['student'],
courses: [],
groups: [],
@@ -206,7 +206,7 @@ export const students = [
last_name: 'somtin',
last_enrolled: 2023,
create_time: new Date('July 21, 2024 01:15:00'),
- studentId: null,
+ student_id: null,
roles: ['student'],
courses: [],
groups: [],
diff --git a/frontend/src/test/unit/services/setup/post_handlers.ts b/frontend/src/test/unit/services/setup/post_handlers.ts
index 6ae01af8..5409c5df 100644
--- a/frontend/src/test/unit/services/setup/post_handlers.ts
+++ b/frontend/src/test/unit/services/setup/post_handlers.ts
@@ -37,7 +37,7 @@ export const postHandlers = [
const buffer = await request.arrayBuffer();
const requestBody = new TextDecoder().decode(buffer);
const newStudent = JSON.parse(requestBody);
- students.push({ id: newStudent.user, studentId: newStudent.student_id, ...newStudent });
+ students.push({ id: newStudent.user, student_id: newStudent.student_id, ...newStudent });
return HttpResponse.json(students);
}),
http.post(baseUrl + endpoints.courses.index, async ({ request }) => {
diff --git a/frontend/src/test/unit/services/student_service.test.ts b/frontend/src/test/unit/services/student_service.test.ts
index d8c18467..6b8ddc12 100644
--- a/frontend/src/test/unit/services/student_service.test.ts
+++ b/frontend/src/test/unit/services/student_service.test.ts
@@ -38,7 +38,7 @@ describe('students', (): void => {
expect(student.value?.first_name).toBe('John');
expect(student.value?.last_name).toBe('Doe');
expect(student.value?.last_enrolled).toBe(2023);
- expect(student.value?.studentId).toBeNull();
+ expect(student.value?.student_id).toBeNull();
expect(student.value?.last_login).toBeNull();
expect(student.value?.create_time.toISOString()).toEqual('2024-07-20T23:15:00.000Z');
expect(student.value?.courses).toEqual([]);
@@ -60,7 +60,7 @@ describe('students', (): void => {
expect(students.value?.[0]?.first_name).toBe('John');
expect(students.value?.[0]?.last_name).toBe('Doe');
expect(students.value?.[0]?.last_enrolled).toBe(2023);
- expect(students.value?.[0]?.studentId).toBeNull();
+ expect(students.value?.[0]?.student_id).toBeNull();
expect(students.value?.[0]?.last_login).toBeNull();
expect(students.value?.[0]?.create_time).toEqual(new Date('July 21, 2024 01:15:00'));
expect(students.value?.[0]?.courses).toEqual([]);
@@ -73,7 +73,7 @@ describe('students', (): void => {
expect(students.value?.[1]?.first_name).toBe('Bartje');
expect(students.value?.[1]?.last_name).toBe('Verhaege');
expect(students.value?.[1]?.last_enrolled).toBe(2023);
- expect(students.value?.[1]?.studentId).toBeNull();
+ expect(students.value?.[1]?.student_id).toBeNull();
expect(students.value?.[1]?.last_login).toBeNull();
expect(students.value?.[1]?.create_time).toEqual(new Date('July 21, 2024 01:15:00'));
expect(students.value?.[1]?.courses).toEqual([]);
@@ -86,7 +86,7 @@ describe('students', (): void => {
expect(students.value?.[2]?.first_name).toBe('Tybo');
expect(students.value?.[2]?.last_name).toBe('Verslype');
expect(students.value?.[2]?.last_enrolled).toBe(2023);
- expect(students.value?.[2]?.studentId).toBe('02012470');
+ expect(students.value?.[2]?.student_id).toBe('02012470');
expect(students.value?.[2]?.last_login).toEqual(new Date('July 30, 2024 01:15:00'));
expect(students.value?.[2]?.create_time).toEqual(new Date('July 21, 2024 01:15:00'));
expect(students.value?.[2]?.courses).toEqual([]);
@@ -99,7 +99,7 @@ describe('students', (): void => {
expect(students.value?.[3]?.first_name).toBe('somtin');
expect(students.value?.[3]?.last_name).toBe('somtin');
expect(students.value?.[3]?.last_enrolled).toBe(2023);
- expect(students.value?.[3]?.studentId).toBeNull();
+ expect(students.value?.[3]?.student_id).toBeNull();
expect(students.value?.[3]?.last_login).toBeNull();
expect(students.value?.[3]?.create_time).toEqual(new Date('July 21, 2024 01:15:00'));
expect(students.value?.[3]?.courses).toEqual([]);
@@ -121,7 +121,7 @@ describe('students', (): void => {
expect(students.value?.[0]?.first_name).toBe('John');
expect(students.value?.[0]?.last_name).toBe('Doe');
expect(students.value?.[0]?.last_enrolled).toBe(2023);
- expect(students.value?.[0]?.studentId).toBeNull();
+ expect(students.value?.[0]?.student_id).toBeNull();
expect(students.value?.[0]?.last_login).toBeNull();
expect(students.value?.[0]?.create_time).toEqual(new Date('July 21, 2024 01:15:00'));
expect(students.value?.[0]?.courses).toEqual([]);
@@ -134,7 +134,7 @@ describe('students', (): void => {
expect(students.value?.[1]?.first_name).toBe('Bartje');
expect(students.value?.[1]?.last_name).toBe('Verhaege');
expect(students.value?.[1]?.last_enrolled).toBe(2023);
- expect(students.value?.[1]?.studentId).toBeNull();
+ expect(students.value?.[1]?.student_id).toBeNull();
expect(students.value?.[1]?.last_login).toBeNull();
expect(students.value?.[1]?.create_time).toEqual(new Date('July 21, 2024 01:15:00'));
expect(students.value?.[1]?.courses).toEqual([]);
@@ -147,7 +147,7 @@ describe('students', (): void => {
expect(students.value?.[2]?.first_name).toBe('Tybo');
expect(students.value?.[2]?.last_name).toBe('Verslype');
expect(students.value?.[2]?.last_enrolled).toBe(2023);
- expect(students.value?.[2]?.studentId).toBe('02012470');
+ expect(students.value?.[2]?.student_id).toBe('02012470');
expect(students.value?.[2]?.last_login).toEqual(new Date('July 30, 2024 01:15:00'));
expect(students.value?.[2]?.create_time).toEqual(new Date('July 21, 2024 01:15:00'));
expect(students.value?.[2]?.courses).toEqual([]);
@@ -160,7 +160,7 @@ describe('students', (): void => {
expect(students.value?.[3]?.first_name).toBe('somtin');
expect(students.value?.[3]?.last_name).toBe('somtin');
expect(students.value?.[3]?.last_enrolled).toBe(2023);
- expect(students.value?.[3]?.studentId).toBeNull();
+ expect(students.value?.[3]?.student_id).toBeNull();
expect(students.value?.[3]?.last_login).toBeNull();
expect(students.value?.[3]?.create_time).toEqual(new Date('July 21, 2024 01:15:00'));
expect(students.value?.[3]?.courses).toEqual([]);
@@ -181,7 +181,7 @@ describe('students', (): void => {
2024, // last_enrolled
new Date(), // create_time
null, // last_login
- 'studentId', // studentId
+ 'student_id', // student_id
[],
[],
[],
@@ -202,6 +202,6 @@ describe('students', (): void => {
// Only check for fields that are sent to the backend
expect(students.value?.[prevLength]?.id).toBe('id');
- expect(students.value?.[prevLength]?.studentId).toBe('studentId');
+ expect(students.value?.[prevLength]?.student_id).toBe('student_id');
});
});
diff --git a/frontend/src/test/unit/types/data.ts b/frontend/src/test/unit/types/data.ts
index 307e73bf..9c004c27 100644
--- a/frontend/src/test/unit/types/data.ts
+++ b/frontend/src/test/unit/types/data.ts
@@ -26,7 +26,7 @@ export const studentData = {
courses: [],
create_time: new Date(),
last_login: null,
- studentId: '1',
+ student_id: '1',
groups: [],
};
diff --git a/frontend/src/test/unit/types/helper.ts b/frontend/src/test/unit/types/helper.ts
index 97604c47..60c32d6b 100644
--- a/frontend/src/test/unit/types/helper.ts
+++ b/frontend/src/test/unit/types/helper.ts
@@ -25,7 +25,7 @@ export function createStudent(studentData: any): Student {
studentData.last_enrolled,
studentData.create_time,
studentData.last_login,
- studentData.studentId,
+ studentData.student_id,
studentData.roles.slice(),
studentData.courses.slice(),
studentData.groups.slice(),
diff --git a/frontend/src/test/unit/types/student.test.ts b/frontend/src/test/unit/types/student.test.ts
index 07f7856c..a1a1978e 100644
--- a/frontend/src/test/unit/types/student.test.ts
+++ b/frontend/src/test/unit/types/student.test.ts
@@ -18,7 +18,7 @@ describe('student type', () => {
expect(student.last_enrolled).toBe(studentData.last_enrolled);
expect(student.create_time).toStrictEqual(studentData.create_time);
expect(student.last_login).toStrictEqual(studentData.last_login);
- expect(student.studentId).toBe(studentData.studentId);
+ expect(student.student_id).toBe(studentData.student_id);
expect(student.roles).toStrictEqual(studentData.roles);
expect(student.courses).toStrictEqual(studentData.courses);
expect(student.groups).toStrictEqual(studentData.groups);
@@ -39,7 +39,7 @@ describe('student type', () => {
expect(student.last_enrolled).toBe(studentData.last_enrolled);
expect(student.create_time).toStrictEqual(studentData.create_time);
expect(student.last_login).toStrictEqual(studentData.last_login);
- expect(student.studentId).toBe(studentData.studentId);
+ expect(student.student_id).toBe(studentData.student_id);
expect(student.roles).toStrictEqual(studentData.roles);
expect(student.courses).toStrictEqual(studentData.courses);
expect(student.groups).toStrictEqual(studentData.groups);
diff --git a/frontend/src/types/users/Student.ts b/frontend/src/types/users/Student.ts
index 8ba17f8c..34285a98 100644
--- a/frontend/src/types/users/Student.ts
+++ b/frontend/src/types/users/Student.ts
@@ -14,7 +14,7 @@ export class Student extends User {
public last_enrolled: number,
public create_time: Date,
public last_login: Date | null,
- public studentId: string,
+ public student_id: string,
public roles: Role[] = [],
public courses: Course[] = [],
public groups: Group[] = [],
@@ -51,7 +51,7 @@ export class Student extends User {
student.last_enrolled,
new Date(student.create_time),
student.last_login !== null ? new Date(student.last_login) : null,
- student.studentId,
+ student.student_id,
);
}
diff --git a/frontend/src/views/admin/UsersView.vue b/frontend/src/views/admin/UsersView.vue
index 42c329ac..c073e34b 100644
--- a/frontend/src/views/admin/UsersView.vue
+++ b/frontend/src/views/admin/UsersView.vue
@@ -104,7 +104,7 @@ const saveItem = async (): Promise => {
if (role === 'student') {
const data: Record = {
...editItem.value,
- studentId: editItem.value.id,
+ student_id: editItem.value.id,
};
await func(data);
} else {
diff --git a/frontend/src/views/projects/roles/TeacherProjectView.vue b/frontend/src/views/projects/roles/TeacherProjectView.vue
index b83406b8..328b22d6 100644
--- a/frontend/src/views/projects/roles/TeacherProjectView.vue
+++ b/frontend/src/views/projects/roles/TeacherProjectView.vue
@@ -5,6 +5,7 @@ import { type Teacher } from '@/types/users/Teacher.ts';
import { type Project } from '@/types/Project.ts';
import ProjectInfo from '@/components/projects/ProjectInfo.vue';
import ProjectMeter from '@/components/projects/ProjectMeter.vue';
+import DownloadCSVButton from '@/components/projects/DownloadCSVButton.vue';
/* Props */
defineProps<{
@@ -36,6 +37,11 @@ defineProps<{
+
+
+
+
+