Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

Commit

Permalink
Merge pull request #195 from SELab-2/149-latest-group-submission
Browse files Browse the repository at this point in the history
Latest group submissions
  • Loading branch information
PJDeSmijter authored May 23, 2024
2 parents adaf04c + ed98348 commit 504d52f
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 7 deletions.
4 changes: 3 additions & 1 deletion backend/pigeonhole/apps/projects/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ 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',
'get_submissions']:
return user.is_teacher or user.is_admin or user.is_superuser
elif view.action in ['get_group']:
return True
Expand Down
22 changes: 22 additions & 0 deletions backend/pigeonhole/apps/projects/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def test_update_self(self):
'course': [self.course.course_id]
}
response = self.client.put(f'{API_ENDPOINT}{self.teacher.id}/', updated_data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

def test_delete_self(self):
response = self.client.delete(f'{API_ENDPOINT}{self.teacher.id}/')
Expand Down
42 changes: 42 additions & 0 deletions frontend/app/[locale]/components/LatestSubmissionList.tsx
Original file line number Diff line number Diff line change
@@ -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 = [
<React.Fragment key="group_nr"><GroupsIcon style={{ fontSize: '20px', verticalAlign: 'middle', marginBottom: '3px' }}/>{" " + t('group_number')}</React.Fragment>
,
<React.Fragment key="submission_date"><CalendarMonthIcon style={{ fontSize: '20px', verticalAlign: 'middle', marginBottom: '3px' }}/>{" " + t('submission_date')}</React.Fragment>
,
<React.Fragment key="status"><CheckCircleOutlineIcon style={{ fontSize: '20px', verticalAlign: 'middle', marginBottom: '3px' }}/>{" " + t('Status')}</React.Fragment>
, ""]
const headers_backend = ["group_nr", "submission_date", "status", ""]
const sortable = [true, true, false]

return (
<ListView
admin={true}
headers={headers}
headers_backend={headers_backend}
get={'submissions_latest'}
get_id={project_id}
sortable={sortable}
action_name={'download_submission'}
page_size={page_size}
search_text={search}
/>
)
}

export default LatestSubmissionList;
8 changes: 6 additions & 2 deletions frontend/app/[locale]/components/ListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import {
getUsers,
postData,
getOpenCourses,
fetchUserData
fetchUserData, getLatestSubmissions
} from '@lib/api';
import baseTheme from "../../../styles/theme";
import {useTranslation} from "react-i18next";
Expand Down Expand Up @@ -191,6 +191,7 @@ const ListView: NextPage<ListViewProps> = ({
},
'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],
};

Expand Down Expand Up @@ -219,6 +220,9 @@ const ListView: NextPage<ListViewProps> = ({
'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));
}
Expand Down Expand Up @@ -591,7 +595,7 @@ const ListView: NextPage<ListViewProps> = ({
</Button>
</StyledTableCell>
)}
{(get == 'submissions' || get == 'submissions_group') && (
{(get == 'submissions' || get == 'submissions_group' || get == 'submissions_latest') && (
<StyledTableCell>
<Button onClick={() => window.location.href = '/submission/' + row[0]}>
{t('View')}
Expand Down
3 changes: 2 additions & 1 deletion frontend/app/[locale]/components/ProjectDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"];

Expand Down Expand Up @@ -287,7 +288,7 @@ const ProjectDetailsPage: React.FC<ProjectDetailsPageProps> = ({
search={t("submission_search")}
/>
) : (
<ProjectSubmissionsList
<LatestSubmissionList
project_id={project_id}
page_size={8}
search={t("submission_search")}
Expand Down
22 changes: 20 additions & 2 deletions frontend/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,24 @@ export async function getGroupSubmissions(id: number, page = 1, pageSize = 5, ke
return (await getRequest(url))
}

export async function getLatestSubmissions(id: number, page = 1, pageSize = 5, keyword?: string, orderBy?: string, sortOrder?: string): Promise<Submission[]> {
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<UserData> {
Expand All @@ -506,7 +524,7 @@ export async function getUserData(): Promise<UserData> {
}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;
Expand All @@ -530,7 +548,7 @@ export async function fetchUserData() : Promise<UserData> {
window.location.href = "/";
return userData!;
}

}

export async function logOut() {
Expand Down

0 comments on commit 504d52f

Please sign in to comment.