, id: number) => {
+ setSubmissionId(id);
+ setOpen(true);
+ };
+ const handleCloseLogs = () => {
+ setSubmissionId(null);
+ setOpen(false);
};
const updateSubmissions = (filter: 'none' | 'latest' | 'best') => {
@@ -513,7 +488,10 @@ export default function GradingTable() {
label={row.auto_status.split('_').join(' ')}
color={getColor(row.auto_status)}
clickable={true}
- onClick={event => openLogs(event, row.id)}
+ onClick={event => {
+ event.stopPropagation(); // prevents the event from bubbling up to the TableRow
+ handleOpenLogs(event, row.id);
+ }}
/>
{getManualChip(row)}
@@ -546,6 +524,15 @@ export default function GradingTable() {
)}
+ {submissionId && (
+
+ )}
-
);
}
diff --git a/src/components/coursemanage/grading/manual-grading.tsx b/src/components/coursemanage/grading/manual-grading.tsx
index 637ab12..5b9ba00 100644
--- a/src/components/coursemanage/grading/manual-grading.tsx
+++ b/src/components/coursemanage/grading/manual-grading.tsx
@@ -172,6 +172,14 @@ export const ManualGrading = () => {
try {
await autogradeSubmission(lecture, assignment, submission).then(() => {
refetchSubmission();
+ queryClient.invalidateQueries({
+ queryKey: [
+ 'submissionLogs',
+ lecture.id,
+ assignment.id,
+ submission.id
+ ]
+ });
});
enqueueSnackbar('Autograding submission!', {
variant: 'success'
diff --git a/src/components/coursemanage/grading/table-toolbar.tsx b/src/components/coursemanage/grading/table-toolbar.tsx
index 7f67e65..59f6fcd 100644
--- a/src/components/coursemanage/grading/table-toolbar.tsx
+++ b/src/components/coursemanage/grading/table-toolbar.tsx
@@ -31,7 +31,6 @@ import { Submission } from '../../../model/submission';
import { showDialog } from '../../util/dialog-provider';
import AddIcon from '@mui/icons-material/Add';
import { Link } from 'react-router-dom';
-import { openBrowser } from '../overview/util';
import SearchIcon from '@mui/icons-material/Search';
import ClearIcon from '@mui/icons-material/Clear';
import { queryClient } from '../../../widgets/assignmentmanage';
@@ -159,6 +158,9 @@ export function EnhancedTableToolbar(props: EnhancedTableToolbarProps) {
const row = rows.find(value => value.id === id);
row.auto_status = 'pending';
await autogradeSubmission(lecture, assignment, row);
+ await queryClient.invalidateQueries({
+ queryKey: ['submissionLogs', lecture.id, assignment.id, row.id]
+ });
})
);
enqueueSnackbar(`Autograding ${numSelected} submissions!`, {
diff --git a/src/components/util/submission-logs.tsx b/src/components/util/submission-logs.tsx
new file mode 100644
index 0000000..aedf058
--- /dev/null
+++ b/src/components/util/submission-logs.tsx
@@ -0,0 +1,123 @@
+import {
+ Button,
+ Card,
+ CardActions,
+ CardContent,
+ CardHeader,
+ LinearProgress,
+ Modal,
+ Paper,
+ Typography
+} from '@mui/material';
+import * as React from 'react';
+import { useQuery } from '@tanstack/react-query';
+import { getLogs } from '../../services/submissions.service';
+
+interface SubmissionLogsProps {
+ lectureId: number;
+ assignmentId: number;
+ submissionId: number;
+ onClose: () => void;
+ open: boolean;
+}
+
+const formatLogs = (logs: string): JSX.Element[] => {
+ return logs.split('\n').map((line, index) => {
+ const timestampMatch = line.match(
+ /^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}]/
+ );
+ const logLevelMatch = line.match(/\[(INFO|WARNING|ERROR|DEBUG)]/);
+
+ const timestamp = timestampMatch ? timestampMatch[0] : '';
+ const logLevel = logLevelMatch ? logLevelMatch[0] : '';
+ const message = line.replace(timestamp, '').replace(logLevel, '').trim();
+
+ let logLevelColor = 'white';
+ if (logLevelMatch) {
+ switch (logLevelMatch[1]) {
+ case 'INFO':
+ logLevelColor = 'lightGreen';
+ break;
+ case 'WARNING':
+ logLevelColor = 'orange';
+ break;
+ case 'ERROR':
+ logLevelColor = 'red';
+ break;
+ case 'DEBUG':
+ logLevelColor = 'lightBlue';
+ break;
+ default:
+ logLevelColor = 'white';
+ }
+ }
+
+ return (
+
+ {timestamp && {timestamp}}{' '}
+ {logLevel && {logLevel}}{' '}
+ {message}
+
+ );
+ });
+};
+
+export const SubmissionLogs = (props: SubmissionLogsProps) => {
+ const { data: logs, isLoading: isLoadingLogs } = useQuery({
+ queryKey: [
+ 'submissionLogs',
+ props.lectureId,
+ props.assignmentId,
+ props.submissionId
+ ],
+ queryFn: () =>
+ getLogs(props.lectureId, props.assignmentId, props.submissionId)
+ });
+
+ if (isLoadingLogs) {
+ return (
+
+
+
+
+
+ );
+ }
+ return (
+
+
+
+
+ {logs && logs.length > 0 ? (
+ formatLogs(logs)
+ ) : (
+ No Logs found.
+ )}
+
+
+
+
+
+
+
+ );
+};