Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Latest group submissions #195

Merged
merged 6 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading