From c2f5ef55c85e97e473b66527724776a8b43eea6c Mon Sep 17 00:00:00 2001 From: Axel Lorreyne Date: Thu, 23 May 2024 18:43:22 +0200 Subject: [PATCH 1/5] add latest group submissions endpoint --- .../pigeonhole/apps/projects/permissions.py | 2 +- backend/pigeonhole/apps/projects/views.py | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/backend/pigeonhole/apps/projects/permissions.py b/backend/pigeonhole/apps/projects/permissions.py index 5e03ef11..d6f20305 100644 --- a/backend/pigeonhole/apps/projects/permissions.py +++ b/backend/pigeonhole/apps/projects/permissions.py @@ -24,7 +24,7 @@ def has_permission(self, request, view): return False elif view.action in ['get_group_submissions']: return True - elif view.action in ['download_submissions', 'download_testfiles']: + elif view.action in ['download_submissions', 'download_testfiles', 'get_last_group_submissions']: return user.is_teacher or user.is_admin or user.is_superuser elif view.action in ['get_group']: return True diff --git a/backend/pigeonhole/apps/projects/views.py b/backend/pigeonhole/apps/projects/views.py index dae5b424..8f606cab 100644 --- a/backend/pigeonhole/apps/projects/views.py +++ b/backend/pigeonhole/apps/projects/views.py @@ -222,6 +222,28 @@ def get_last_submission(self, request, *args, **kwargs): SubmissionsSerializer(submissions.first()).data, status=status.HTTP_200_OK ) + @action(detail=True, methods=["get"]) + def get_last_group_submissions(self, request, *args, **kwargs): + project = self.get_object() + groups = Group.objects.filter(project_id=project) + latest_submission_ids = [] + + for group in groups: + latest_submission = Submissions.objects.filter(group_id=group).order_by('-timestamp').first() + if latest_submission: + latest_submission_ids.append(latest_submission.submission_id) + + queryset = Submissions.objects.filter(submission_id__in=latest_submission_ids) + submissions_filter = SubmissionFilter(request.GET, queryset=queryset) + filtered_submissions = submissions_filter.qs + paginator = CustomPageNumberPagination() + paginated_submissions = paginator.paginate_queryset( + filtered_submissions, request + ) + + serializer = SubmissionsSerializer(paginated_submissions, many=True) + return paginator.get_paginated_response(serializer.data) + @action(detail=True, methods=["get"]) def download_submissions(self, request, *args, **kwargs): project = self.get_object() From 670d6d133dfaa92e97f6aa94001958afeafba96f Mon Sep 17 00:00:00 2001 From: Axel Lorreyne Date: Thu, 23 May 2024 18:43:35 +0200 Subject: [PATCH 2/5] add latest group submissions view --- .../components/LatestSubmissionList.tsx | 42 +++++++++++++++++++ frontend/app/[locale]/components/ListView.tsx | 6 ++- .../components/ProjectDetailsPage.tsx | 3 +- frontend/lib/api.ts | 22 +++++++++- 4 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 frontend/app/[locale]/components/LatestSubmissionList.tsx diff --git a/frontend/app/[locale]/components/LatestSubmissionList.tsx b/frontend/app/[locale]/components/LatestSubmissionList.tsx new file mode 100644 index 00000000..01644829 --- /dev/null +++ b/frontend/app/[locale]/components/LatestSubmissionList.tsx @@ -0,0 +1,42 @@ +"use client" + +import ListView from "@app/[locale]/components/ListView"; +import React from "react"; +import {useTranslation} from "react-i18next"; +import GroupsIcon from '@mui/icons-material/Groups'; +import CalendarMonthIcon from '@mui/icons-material/CalendarMonth'; +import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; + + +const LatestSubmissionList = ({project_id, page_size = 5, search}: { + project_id: number, + page_size: number, + search: string +}) => { + const {t} = useTranslation() + const headers = [ + {" " + t('group_number')} + , + {" " + t('submission_date')} + , + {" " + t('Status')} + , ""] + const headers_backend = ["group_nr", "submission_date", "status", ""] + const sortable = [true, true, false] + + return ( + + ) +} + +export default LatestSubmissionList; \ No newline at end of file diff --git a/frontend/app/[locale]/components/ListView.tsx b/frontend/app/[locale]/components/ListView.tsx index d2b40b73..c87131aa 100644 --- a/frontend/app/[locale]/components/ListView.tsx +++ b/frontend/app/[locale]/components/ListView.tsx @@ -46,7 +46,7 @@ import { getUsers, postData, getOpenCourses, - fetchUserData + fetchUserData, getLatestSubmissions } from '@lib/api'; import baseTheme from "../../../styles/theme"; import {useTranslation} from "react-i18next"; @@ -191,6 +191,7 @@ const ListView: NextPage = ({ }, 'submissions': (data) => [data.submission_id, data.group_id, convertDate(t, data.timestamp), data?.output_simple_test && data?.eval_result], 'submissions_group': (data) => [data.submission_id, data.group_id, convertDate(t, data.timestamp), data?.output_simple_test && data?.eval_result], + 'submissions_latest': (data) => [data.submission_id, data.group_id, convertDate(t, data.timestamp), data?.output_simple_test && data?.eval_result], 'archived_courses': (data) => [data.course_id, data.name, data.description, data.open_course], }; @@ -219,6 +220,9 @@ const ListView: NextPage = ({ 'submissions_group': async () => { return parse_pages(await getGroupSubmissions(get_id, currentPage, page_size, searchTerm, sortConfig.key.toLowerCase(), sortConfig.direction === 'asc' ? 'asc' : 'desc')); }, + 'submissions_latest': async () => { + return parse_pages(await getLatestSubmissions(get_id, currentPage, page_size, searchTerm, sortConfig.key.toLowerCase(), sortConfig.direction === 'asc' ? 'asc' : 'desc')); + }, 'archived_courses': async () => { return parse_pages(await getArchivedCourses(currentPage, page_size, searchTerm)); } diff --git a/frontend/app/[locale]/components/ProjectDetailsPage.tsx b/frontend/app/[locale]/components/ProjectDetailsPage.tsx index f0c4b093..0e6b71fd 100644 --- a/frontend/app/[locale]/components/ProjectDetailsPage.tsx +++ b/frontend/app/[locale]/components/ProjectDetailsPage.tsx @@ -18,6 +18,7 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import ExpandLessIcon from "@mui/icons-material/ExpandLess"; import DownloadIcon from "@mui/icons-material/Download"; import AccessTimeIcon from "@mui/icons-material/AccessTime"; +import LatestSubmissionList from "@app/[locale]/components/LatestSubmissionList"; const backend_url = process.env["NEXT_PUBLIC_BACKEND_URL"]; @@ -287,7 +288,7 @@ const ProjectDetailsPage: React.FC = ({ search={t("submission_search")} /> ) : ( - { + let url = `/projects/${id}/get_last_group_submissions?page=${page}&page_size=${pageSize}` + + if (keyword) { + url += `&keyword=${keyword}`; + } + + if (orderBy) { + url += `&order_by=${orderBy}`; + } + + if (sortOrder) { + url += `&sort_order=${sortOrder}`; + } + + return (await getRequest(url)) +} + let userData: UserData | undefined = undefined; export async function getUserData(): Promise { @@ -506,7 +524,7 @@ export async function getUserData(): Promise { }else if(localStorage.getItem('user')){ const userobj = JSON.parse(localStorage.getItem('user') as string); const lastcache : string | undefined = userobj?.lastcache; - + if(lastcache && Date.now() - parseInt(lastcache) < 2 * 60 * 1000){ console.log(Date.now() - parseInt(lastcache)); let user : UserData = userobj.data; @@ -530,7 +548,7 @@ export async function fetchUserData() : Promise { window.location.href = "/"; return userData!; } - + } export async function logOut() { From 3b8ae71e5cec8682b492ea5447266201f26d214a Mon Sep 17 00:00:00 2001 From: Pieter-Jan De Smijter Date: Thu, 23 May 2024 19:03:48 +0200 Subject: [PATCH 3/5] fix listview submission list --- frontend/app/[locale]/components/ListView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app/[locale]/components/ListView.tsx b/frontend/app/[locale]/components/ListView.tsx index c87131aa..84be9180 100644 --- a/frontend/app/[locale]/components/ListView.tsx +++ b/frontend/app/[locale]/components/ListView.tsx @@ -595,7 +595,7 @@ const ListView: NextPage = ({ )} - {(get == 'submissions' || get == 'submissions_group') && ( + {(get == 'submissions' || get == 'submissions_group' || get == 'submissions_latest') && (