Skip to content

Commit

Permalink
Merge pull request #216 from SELab-2/teacher-view
Browse files Browse the repository at this point in the history
Dashboard based on role + course creation
  • Loading branch information
francisvaut authored Apr 1, 2024
2 parents e1619ff + 39ce425 commit 2d01f62
Show file tree
Hide file tree
Showing 15 changed files with 344 additions and 51 deletions.
4 changes: 4 additions & 0 deletions backend/api/tests/test_course.py
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,10 @@ def test_create_course(self):
self.assertEqual(response.status_code, 201)
self.assertTrue(Course.objects.filter(name="Introduction to Computer Science").exists())

# Make sure the teacher is added to the course
course = Course.objects.get(name="Introduction to Computer Science")
self.assertTrue(course.teachers.filter(id=self.user.id).exists())

def test_create_project(self):
"""
Able to create a project for a course.
Expand Down
19 changes: 18 additions & 1 deletion backend/api/views/course_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
from rest_framework.response import Response
from rest_framework.request import Request
from drf_yasg.utils import swagger_auto_schema
from rest_framework import status
from api.models.course import Course
from api.permissions.course_permissions import (
CoursePermission,
CourseAssistantPermission,
CourseStudentPermission,
CourseTeacherPermission
)
from api.permissions.role_permissions import IsTeacher
from api.permissions.role_permissions import IsTeacher, is_teacher
from api.serializers.course_serializer import (
CourseSerializer, StudentJoinSerializer, StudentLeaveSerializer, CourseCloneSerializer,
TeacherJoinSerializer, TeacherLeaveSerializer
Expand All @@ -29,6 +30,22 @@ class CourseViewSet(viewsets.ModelViewSet):
serializer_class = CourseSerializer
permission_classes = [IsAdminUser | CoursePermission]

def create(self, request: Request, *_):
"""Override the create method to add the teacher to the course"""
serializer = CourseSerializer(data=request.data, context={"request": request})

if serializer.is_valid(raise_exception=True):
course = serializer.save()

# If it was a teacher who created the course, add them as a teacher
if is_teacher(request.user):
course.teachers.add(request.user.id)

return Response(
{"message": gettext("courses.success.create")},
status=status.HTTP_201_CREATED
)

@action(detail=True, permission_classes=[IsAdminUser | CourseAssistantPermission])
def assistants(self, request: Request, **_):
"""Returns a list of assistants for the given course"""
Expand Down
18 changes: 17 additions & 1 deletion frontend/src/assets/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"header": {
"logo": "Ghent University logo",
"login": "login",
"view": "View as {0}",
"navigation": {
"dashboard": "Dashboard",
"calendar": "Calendar",
Expand All @@ -19,7 +20,9 @@
"views": {
"dashboard": {
"courses": "My courses",
"projects": "Current projects"
"projects": "Current projects",
"no_projects": "No projects available for this academic year.",
"no_courses": "No courses available for this academic year."
},
"login": {
"title": "Login",
Expand All @@ -38,6 +41,12 @@
"deadline": "Deadline",
"submissionStatus": "Indienstatus",
"groupMembers": "Group members"
},
"courses": {
"create": "Create course",
"name": "Course name",
"description": "Description",
"year": "Academic year"
}
},
"composables": {
Expand All @@ -64,6 +73,13 @@
"open": "details"
}
},
"types": {
"roles": {
"student": "Student",
"assistant": "Assistant",
"teacher": "Teacher"
}
},
"toasts": {
"messages": {
"unknown": "An unknown error has occurred."
Expand Down
12 changes: 10 additions & 2 deletions frontend/src/assets/lang/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
"views": {
"dashboard": {
"courses": "Mijn vakken",
"projects": "Lopende projecten"
"projects": "Lopende projecten",
"no_projects": "Geen projecten beschikbaar voor dit academiejaar.",
"no_courses": "Geen vakken beschikbaar voor dit academiejaar."
},
"login": {
"title": "Inloggen",
Expand All @@ -39,7 +41,13 @@
"deadline": "Deadline",
"submissionStatus": "Indienstatus",
"groupMembers": "Groepsleden"
}
},
"courses": {
"create": "Creëer vak",
"name": "Vaknaam",
"description": "Beschrijving",
"year": "Academiejaar"
}
},
"composables": {
"helpers": {
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/composables/services/courses.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ export function useCourses() {
await getList<Course>(endpoint, courses, Course.fromJSON);
}

async function getCoursesByTeacher(teacher_id: string) {
const endpoint = endpoints.courses.byTeacher.replace('{teacher_id}', teacher_id);
await getList<Course>(endpoint, courses, Course.fromJSON);
}

async function getCourseByAssistant(assistant_id: string) {
const endpoint = endpoints.courses.byAssistant.replace('{assistant_id}', assistant_id);
await getList<Course>(endpoint, courses, Course.fromJSON);
}

async function createCourse(course_data: Course) {
const endpoint = endpoints.courses.index;
await create<Course>(endpoint,
Expand Down Expand Up @@ -50,6 +60,8 @@ export function useCourses() {
getCourseByID,
getCourses,
getCoursesByStudent,
getCoursesByTeacher,
getCourseByAssistant,

createCourse,
cloneCourse,
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/config/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export const endpoints = {
index: '/api/courses/',
retrieve: '/api/courses/{id}/',
byStudent: '/api/students/{student_id}/courses/',
byTeacher: '/api/teachers/{teacher_id}/courses/',
byAssistant: '/api/assistants/{assistant_id}/courses/',
clone: '/api/courses/{course_id}/clone/'
},
students: {
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/router/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import DashboardView from '@/views/dashboard/DashboardView.vue';
import CourseView from '@/views/courses/CourseView.vue';
import CreateCourseView from '@/views/courses/CreateCourseView.vue';
import Dummy from '@/components/Dummy.vue';
import LoginView from '@/views/authentication/LoginView.vue';
import CalendarView from '@/views/calendar/CalendarView.vue';
Expand All @@ -27,7 +28,7 @@ const routes: RouteRecordRaw[] = [
// Courses
{ path: '/courses', children: [
{ path: '', component: Dummy, name: 'courses' },
{ path: 'create', component: Dummy, name: 'course-create' },
{ path: 'create', component: CreateCourseView, name: 'course-create' },
// Single course
{ path: ':courseId', children: [
{ path: '', component: CourseView, name: 'course' },
Expand Down
74 changes: 64 additions & 10 deletions frontend/src/store/authentication.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,74 @@ import {Role, User} from '@/types/User.ts';
import {endpoints} from '@/config/endpoints.ts';
import {useMessagesStore} from '@/store/messages.store.ts';
import {client} from '@/composables/axios.ts';
import {Teacher} from '@/types/Teacher.ts';
import {Student} from '@/types/Student.ts';
import {Assistant} from '@/types/Assistant.ts';
import {useLocalStorage} from '@vueuse/core';
import {computed, ref} from 'vue';
import { useCourses } from '@/composables/services/courses.service';
import {computed, ref, watch} from 'vue';
import { useAssistant } from '@/composables/services/assistant.service';
import { useStudents } from '@/composables/services/students.service';
import { useTeacher } from '@/composables/services/teachers.service';

export const useAuthStore = defineStore('auth', () => {
/* Stores */
const user = ref<User|null>(null);
const teacher = ref<Teacher|null>(null);
const student = ref<Student|null>(null);
const assistant = ref<Assistant|null>(null);
const view = useLocalStorage<Role|null>('view', null);
const intent = useLocalStorage<string>('intent', '/');

/* Services */
const { courses, getCoursesByTeacher, getCoursesByStudent, getCourseByAssistant } = useCourses();
const { assistant, getAssistantByID } = useAssistant();
const { student, getStudentByID } = useStudents();
const { teacher, getTeacherByID } = useTeacher();

/* Update the user object when the view changes. */
watch(view, async () => {
initUser();
});

const initUser = async () => {
if (user.value !== null) {
if (view.value === 'teacher') {
// Get the teacher information.
await getTeacherByID(user.value.id);

// Get the courses for the teacher.
await getCoursesByTeacher(user.value.id);

// Set the user object with the teacher information.
teacher.value ? teacher.value.courses = courses.value ?? [] : null;
teacher.value ? teacher.value.roles = user.value.roles : null;

user.value = teacher.value;

}else if (view.value === 'student') {
// Get the student information.
await getStudentByID(user.value.id);

// Get the courses for the student.
await getCoursesByStudent(user.value.id);

// Set the user object with the student information.
student.value ? student.value.courses = courses.value ?? [] : null;
student.value ? student.value.roles = user.value.roles : null;

user.value = student.value;
}else {
// Get the assistant information.
await getAssistantByID(user.value.id);

// Get the courses for the assistant.
await getCourseByAssistant(user.value.id);

// Set the user object with the assistant information.
assistant.value ? assistant.value.courses = courses.value ?? [] : null;
assistant.value ? assistant.value.roles = user.value.roles : null;

user.value = assistant.value;
}
}
};


/**
* Attempt to log in the user using a CAS ticket.
*
Expand Down Expand Up @@ -60,8 +113,9 @@ export const useAuthStore = defineStore('auth', () => {
if (view.value === null) {
view.value = user.value.roles[0];
}

// TODO: Get the teacher, student, and assistant information.

// Init the user depending on the role selected.
initUser();

}).catch((error) => {
add({
Expand All @@ -86,7 +140,7 @@ export const useAuthStore = defineStore('auth', () => {
});

return {
user, teacher, student, assistant, view, intent,
user, view, intent,
login, refresh, logout,
isAuthenticated
}
Expand Down
13 changes: 11 additions & 2 deletions frontend/src/types/Assistant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ export class Assistant extends User {
public faculties: Faculty[] = [],
public courses: Course[] = [],
public create_time: Date,
public last_login: Date |null,
public last_login: Date |null
) {
super(id, username, email, first_name, last_name, last_enrolled, is_staff, roles, faculties, create_time, last_login);
super(id, username, email, first_name, last_name, last_enrolled, is_staff, roles, faculties, create_time, last_login, courses);
}

/**
Expand All @@ -41,4 +41,13 @@ export class Assistant extends User {
assistant.last_login ? new Date(assistant.last_login) : null
);
}

/**
* Check if the user is a assistant.
*
* @returns boolean
*/
public isAssistant(): boolean {
return true;
}
}
11 changes: 10 additions & 1 deletion frontend/src/types/Student.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class Student extends User {
public groups: Group[] = [],
public faculties: Faculty[] = [],
) {
super(id, username, email, first_name, last_name, last_enrolled, is_staff, roles, faculties, create_time, last_login);
super(id, username, email, first_name, last_name, last_enrolled, is_staff, roles, faculties, create_time, last_login, courses);
}

/**
Expand All @@ -42,4 +42,13 @@ export class Student extends User {
student.student_id
);
}

/**
* Check if the user is a student.
*
* @returns boolean
*/
public isStudent(): boolean {
return true;
}
}
11 changes: 10 additions & 1 deletion frontend/src/types/Teacher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class Teacher extends User {
public create_time: Date,
public last_login: Date |null,
) {
super(id, username, email, first_name, last_name, last_enrolled, is_staff, roles, faculties, create_time, last_login);
super(id, username, email, first_name, last_name, last_enrolled, is_staff, roles, faculties, create_time, last_login, courses);
}

/**
Expand All @@ -42,4 +42,13 @@ export class Teacher extends User {
teacher.last_login ? new Date(teacher.last_login) : null
);
}

/**
* Check if the user is a teacher.
*
* @returns boolean
*/
public isTeacher(): boolean {
return true;
}
}
8 changes: 5 additions & 3 deletions frontend/src/types/User.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Course } from "./Course";
import { Faculty } from "./Faculty";

export type Role = 'user' | 'student' | 'assistant' | 'teacher';
Expand All @@ -15,6 +16,7 @@ export class User {
public faculties: Faculty[] = [],
public create_time: Date,
public last_login: Date |null,
public courses: Course[] = []
) {
}

Expand All @@ -33,7 +35,7 @@ export class User {
* @returns boolean
*/
public isStudent(): boolean {
return this.roles.includes('student');
return false;
}

/**
Expand All @@ -42,7 +44,7 @@ export class User {
* @returns boolean
*/
public isAssistant(): boolean {
return this.roles.includes('assistant');
return false;
}

/**
Expand All @@ -51,7 +53,7 @@ export class User {
* @returns boolean
*/
public isTeacher(): boolean {
return this.roles.includes('teacher');
return false;
}

/**
Expand Down
Loading

0 comments on commit 2d01f62

Please sign in to comment.