Skip to content
This repository has been archived by the owner on Sep 27, 2024. It is now read-only.

Commit

Permalink
Merge branch 'main' into frontend-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rvdkeere committed Apr 18, 2024
2 parents 3bd8da6 + 3ccb6c8 commit d81fbe7
Show file tree
Hide file tree
Showing 25 changed files with 315 additions and 81 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ Volg deze stappen om de backend van het project op te zetten:
```
6. Voer het `fill_database_mock.py` script uit als een module om de database te vullen met mock data:
```bash
python -m db.fill_database_mock
python fill_database_mock.py
```
Je kan ook een lege databank initialiseren met het `create_database_tables.py` script als volgt:
```bash
python -m db.create_database_tables
python create_database_tables.py
```
*Opgelet: beide scripts zullen de huidige databankinhoud verwijderen indien die bestaat en daarna de tabellen opnieuw aanmaken.*
7. Start de API door het `app.py` script uit te voeren:
Expand Down
14 changes: 0 additions & 14 deletions frontend/package-lock.json

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

2 changes: 1 addition & 1 deletion frontend/src/components/ProjectStudentComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import {useTranslation} from 'react-i18next';

export default function ProjectStudentComponent(props: { project: ProjectStudent }): JSX.Element {


// true als er een groep is, anders false.
const is_in_group = props.project.groupMembers && props.project.groupMembers.length > 0;


const { t } = useTranslation();

return (
Expand Down
13 changes: 11 additions & 2 deletions frontend/src/components/ProjectTeacherComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import SimpleTests from "./SimpleTests/SimpleTests.tsx";
import {TeacherOrStudent} from "./SimpleTests/TeacherOrStudentEnum.tsx";
import Calendar from "react-calendar";
import {useTranslation} from 'react-i18next';
import {RegularButton} from "./RegularButton.tsx";

// SimpleTests
const CHECK_SIMPLE_TESTS = false
Expand Down Expand Up @@ -54,6 +55,10 @@ export function ProjectTeacherComponent(props: { project: ProjectTeacher }): JSX
const [data, setData] = useState<object>(calledData);
const [hasChanged, setHasChanged] = useState(false);

const save = () => {
// TODO save button
}

return (
<div className={"create-project"}>
{/* PROJECT NAME FIELD */}
Expand Down Expand Up @@ -181,7 +186,8 @@ export function ProjectTeacherComponent(props: { project: ProjectTeacher }): JSX
<input type="checkbox"/>
</div>
<div className="field-body">
<label className="label is-fullwidth">{t('create_project.teamwork.changes')}</label>
<label
className="label is-fullwidth">{t('create_project.teamwork.changes')}</label>
</div>
</div>
<br/>
Expand Down Expand Up @@ -215,8 +221,11 @@ export function ProjectTeacherComponent(props: { project: ProjectTeacher }): JSX
}
</label>
</div>
<div className={"mx-5"}>
<RegularButton placeholder={t('create_project.save_button')} add={false} onClick={save}/>
</div>
</div>
</div>
)
;
}
}
6 changes: 3 additions & 3 deletions frontend/src/components/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {MdLanguage, MdOutlineKeyboardArrowDown} from "react-icons/md";
import useAuth from "../hooks/useAuth.ts";
import {User} from "../utils/ApiInterfaces.ts";
import {useTranslation} from "react-i18next";
import i18n from "../i18n.tsx";
import {modify_language} from "../utils/api/User.ts";

function DropdownLanguage(): JSX.Element {
const [isOpen, setIsOpen] = useState<boolean>(false);
Expand Down Expand Up @@ -35,7 +35,7 @@ function DropdownLanguage(): JSX.Element {
<a
className="dropdown-item"
onClick={() => {
void i18n.changeLanguage("en");
modify_language("en")
toggle()
}}
>
Expand All @@ -44,7 +44,7 @@ function DropdownLanguage(): JSX.Element {
<a
className="dropdown-item"
onClick={() => {
void i18n.changeLanguage("nl");
modify_language("nl")
toggle()
}}
>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function Sidebar(props: {home: string, buttons?: SidebarButton[]}): JSX.E
if (buttons == undefined) {
buttons = [SidebarButton.COURSES, SidebarButton.PROJECTS];
}

return (
<>
<aside className={"menu is-flex is-flex-direction-column is-justify-content-space-between"}>
Expand Down
9 changes: 6 additions & 3 deletions frontend/src/dataloaders/AdminLoader.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import apiFetch from "../utils/ApiFetch.ts";
import apiFetch, {ApiFetchResponse} from "../utils/ApiFetch.ts";
import {Backend_user} from "../utils/BackendInterfaces.ts";
import {mapUser} from "../utils/ApiTypesMapper.ts";
import {User} from "../utils/ApiInterfaces.ts";
Expand All @@ -17,8 +17,11 @@ function filter_on_role(users: User[], role: string): User[] {
}

export default async function adminLoader(): Promise<AdminLoaderObject> {
const users_backend: Backend_user[] = (await apiFetch("/users")) as Backend_user[];
const users: User[] = users_backend.map((user) => mapUser(user));
const users_backend: ApiFetchResponse<Backend_user[]> = await apiFetch<Backend_user[]>("/users");
if (!users_backend.ok) {
// TODO error handling
}
const users = users_backend.content.map((user) => mapUser(user));
const students = filter_on_role(users, "STUDENT");
const teachers = filter_on_role(users, "TEACHER");
const admins = filter_on_role(users, "ADMIN");
Expand Down
8 changes: 5 additions & 3 deletions frontend/src/dataloaders/LoginLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ const isUser = (data?: Backend_user) => {
export default async function loginLoader(): Promise<loginLoaderObject> {
const token = localStorage.getItem('token')
if (token) {
const user = (await apiFetch('/user')) as Backend_user;
if (isUser(user)) {
return {user: mapUser(user)}
const user_request = (await apiFetch<Backend_user>('/user'));
if (user_request.ok && isUser(user_request.content)) {
return {user: mapUser(user_request.content)}
}else{
// TODO error handling
}
}
return {user: undefined}
Expand Down
29 changes: 21 additions & 8 deletions frontend/src/dataloaders/ProjectsStudentLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,36 +45,49 @@ export async function LoadProjectsForStudent(filter_on_current: boolean = false,
const project_ids = projects.map(project => project.project_id);

const apiGroups = project_ids.map(async project_id => {
const apiGroup = await apiFetch(`/projects/${project_id}/group`) as Backend_group;
const apiGroupData = await apiFetch<Backend_group>(`/projects/${project_id}/group`);
if (!apiGroupData.ok){
// TODO: error handling
}
const apiGroup = apiGroupData.content;
if (apiGroup) {
return mapGroup(apiGroup);
} else {
return null
}
return null;
})

const groups = (await Promise.all(apiGroups))
const groups_without_null = groups.filter(group => group !== null) as Group[];
const group_ids = groups_without_null.map(group => group?.group_id);

const submissionPromises: Promise<Submission>[] = group_ids.map(async group_id => {
const apiSubmission = await apiFetch(`/groups/${group_id}/submission`) as Backend_submission;
return mapSubmission(apiSubmission);
const apiSubmission = await apiFetch<Backend_submission>(`/groups/${group_id}/submission`);
if (!apiSubmission.ok) {
// TODO error handling
}
return mapSubmission(apiSubmission.content);
});

const submissions: Submission[] = (await Promise.all(submissionPromises));

const groupMemberIdsPromises: Promise<groupInfo>[] = groups_without_null.map(async group => {
const members = await apiFetch(`/groups/${group.group_id}/members`) as memberInfo[]
const membersData = await apiFetch<memberInfo[]>(`/groups/${group.group_id}/members`)
if (!membersData.ok) {
// TODO error handling
}
const members = membersData.content;
return {group_id: group.group_id, project_id: group.project_id, member_ids: members};
});

const groupMemberInfo: groupInfo[] = (await Promise.all(groupMemberIdsPromises));

const groupMembersPromises: Promise<groupInfo>[] = groupMemberInfo.map(async (groupinfo) => {
const memberPromises = groupinfo.member_ids.map(async (memberId) => {
const apiMember = await apiFetch(`/users/${memberId.id}`) as Backend_user;
return mapUser(apiMember);
const apiMemberData = await apiFetch<Backend_user>(`/users/${memberId.id}`);
if (!apiMemberData.ok) {
// TODO error handling
}
return mapUser(apiMemberData.content);
});
const users = await Promise.all(memberPromises);
return {
Expand Down
28 changes: 20 additions & 8 deletions frontend/src/dataloaders/loader_helpers/SharedFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,17 @@ export async function coursesLoader(role: teacherStudentRole, course_id?: number
}

const teachers = (await Promise.all(courses.map(async course => {
const teacher_ids = await apiFetch(`/subjects/${course.subject_id}/teachers`) as TeacherIdInfo[];

const teacher_ids_data = await apiFetch<TeacherIdInfo[]>(`/subjects/${course.subject_id}/teachers`);
if (!teacher_ids_data.ok){
// TODO error handling
}
const teacher_ids = teacher_ids_data.content
const teachers_promises = teacher_ids.map(async teacher_id => {
const user = await apiFetch(`/users/${teacher_id.id}`) as Backend_user
return mapUser(user);
const userData = await apiFetch<Backend_user>(`/users/${teacher_id.id}`);
if (!userData.ok){
// TODO error handling
}
return mapUser(userData.content);
});

const teachers = await Promise.all(teachers_promises);
Expand Down Expand Up @@ -121,12 +127,18 @@ export interface projectsAndSubjects {
}

export async function getAllProjectsAndSubjects(role: teacherStudentRole, filter_on_current: boolean = false): Promise<projectsAndSubjects> {
const apiSubjects = (await apiFetch(`/${role}/subjects`)) as Backend_Subject[];
const apiProjects = (await apiFetch(`/${role}/projects`)) as Backend_Project[];
let projects: Project[] = mapProjectList(apiProjects);
const apiProjects = (await apiFetch<Backend_Project[]>(`/${role}/projects`));
const apiSubjects = (await apiFetch<Backend_Subject[]>(`/${role}/subjects`));

if (!apiProjects.ok || !apiSubjects.ok) {
// TODO error handling
// throw ...
}
let projects = mapProjectList(apiProjects.content);
if (filter_on_current) {
projects = projects.filter(project => project.project_visible && !project.project_archived)
}
const subjects: Subject[] = mapSubjectList(apiSubjects);

const subjects = mapSubjectList(apiSubjects.content);
return {projects, subjects}
}
17 changes: 12 additions & 5 deletions frontend/src/dataloaders/projectsTeacherLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@ export async function LoadProjectsForTeacher(filter_on_current: boolean = false,
}

const groupPromises: Promise<Group[][]> = Promise.all(projects.map(async project => {
const groups = await apiFetch(`/projects/${project.project_id}/groups`) as Backend_group[];
return mapGroupList(groups);
const groups = await apiFetch<Backend_group[]>(`/projects/${project.project_id}/groups`);
if (!groups.ok) {
// TODO: error handling
}
return mapGroupList(groups.content);
}));

const statistics: { [key: number]: number } = {
Expand All @@ -47,9 +50,13 @@ export async function LoadProjectsForTeacher(filter_on_current: boolean = false,
let amount = 0
for (const group of groupArray) {
try {
const submissionBackend = await apiFetch(`/groups/${group.group_id}/submission`) as Backend_submission;
const submission = mapSubmission(submissionBackend);
if (submission) {
const submissionData = await apiFetch<Backend_submission>(`/groups/${group.group_id}/submission`);
if (!submissionData.ok){
// TODO error handling
}
const submissionBackend = submissionData.content
if (submissionBackend) {
const submission = mapSubmission(submissionData.content)
console.log(submission)
statistics[submission.submission_state] += 1;
amount++;
Expand Down
27 changes: 11 additions & 16 deletions frontend/src/pages/login/LoginScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,27 @@ import {Navigate, useLocation, useRouteLoaderData} from 'react-router-dom';
import useAuth from "../../hooks/useAuth.ts";
import loginLoader, {LOGIN_ROUTER_ID, loginLoaderObject} from "../../dataloaders/LoginLoader.ts";
import LoginForm from "../../components/authentication/LoginForm.tsx";
import {DEBUG} from "../root.tsx";
import {Token, User} from "../../utils/ApiInterfaces.ts";
import {post_ticket} from "../../utils/api/Login.ts";

interface location_type {
search?: { ticket?: string },
state?: { from?: { pathname: string } },
pathname: string
}

const ticketLogin = async (ticket: string, setUser: React.Dispatch<React.SetStateAction<User | undefined>>) => {
let url = '/api/login?ticket=' + ticket
if (DEBUG) {
url = 'http://127.0.0.1:8000/api/login?ticket=' + ticket
}
const token = await (await fetch(url, {method: 'POST', headers: {'Content-Type': 'application/json'}}))
.json() as Token

if (token.token) {
async function ticketLogin (ticket: string, setUser: React.Dispatch<React.SetStateAction<User | undefined>>) {
const token: Token | undefined = await post_ticket(ticket)
if (token?.token) {
localStorage.setItem('token', token.token)
const result: loginLoaderObject = await loginLoader()
if (result.user) {
setUser(result.user)
}else{
} else {
localStorage.removeItem('token')
setUser(undefined)
}
}

return token;
}

export default function LoginScreen(): JSX.Element {
Expand All @@ -53,9 +45,12 @@ export default function LoginScreen(): JSX.Element {
// If the saved token is valid => the user will be logged in
if (data && data.user) {
setUser(data.user)
}
else if (!user && ticket) {
void ticketLogin(ticket, setUser);
} else if (!user && ticket) {
try {
void ticketLogin(ticket, setUser);
} catch (error) {
console.log("Ticket wasn't accepted")
}
}
}, [data, setUser, ticket, user]);

Expand Down
21 changes: 15 additions & 6 deletions frontend/src/pages/teacher/CreateCourse.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@ import Inputfield from "../../components/Inputfield.tsx";
import {Header} from "../../components/Header.tsx";
import {Sidebar} from "../../components/Sidebar.tsx";
import {RegularButton} from "../../components/RegularButton.tsx";
import { useTranslation } from 'react-i18next';
import {useTranslation} from 'react-i18next';
import {createSubject} from "../../utils/api/Teacher.ts";

export default function CreateCourse(): JSX.Element {
const [projectName, setProjectName] = useState<string>("");
const [courseName, setCourseName] = useState<string>("");

const { t } = useTranslation();
const {t} = useTranslation();

const createCourse = () => {
// todo: what after succes/failure
// Default: archived false
void createSubject(courseName)
}

return (
<>
Expand All @@ -25,13 +32,15 @@ export default function CreateCourse(): JSX.Element {
<label className="label">{t('create_course.name.tag')}</label>
</div>
<div className="field-body field">
<Inputfield placeholder={t('create_course.name.placeholder')} value={projectName}
setValue={setProjectName}/>
<Inputfield placeholder={t('create_course.name.placeholder')} value={courseName}
setValue={setCourseName}/>
</div>
</div>
<div className={"is-flex is-justify-content-center"}>
{/*Waiting for the post requests to implement the on click*/}
<RegularButton placeholder={t('create_course.confirm')} add={false} onClick={() => {}}/>
<RegularButton placeholder={t('create_course.confirm')} add={false} onClick={() => {
createCourse
}}/>
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit d81fbe7

Please sign in to comment.