From 44eb39ec004a246f3ab4063640d065f50c43a686 Mon Sep 17 00:00:00 2001 From: John McCann Cunniff Jr Date: Wed, 21 Feb 2024 15:37:19 -0500 Subject: [PATCH] CHG make autograde page submission tests same as what students see --- .../core/Submission/SubmissionTests.jsx | 306 +++++++++++++----- .../pages/core/admin/Autograde/Submission.jsx | 10 +- .../core/public/Submission/Submission.jsx | 223 +------------ .../public/Submission/Submission.styles.jsx | 66 ---- 4 files changed, 231 insertions(+), 374 deletions(-) delete mode 100644 web/src/pages/core/public/Submission/Submission.styles.jsx diff --git a/web/src/components/core/Submission/SubmissionTests.jsx b/web/src/components/core/Submission/SubmissionTests.jsx index 50097e102..b24580868 100644 --- a/web/src/components/core/Submission/SubmissionTests.jsx +++ b/web/src/components/core/Submission/SubmissionTests.jsx @@ -1,19 +1,26 @@ -import React from 'react'; +import React, {useState} from 'react'; + import makeStyles from '@mui/styles/makeStyles'; -import Typography from '@mui/material/Typography'; -import Accordion from '@mui/material/Accordion'; -import AccordionSummary from '@mui/material/AccordionSummary'; -import AccordionDetails from '@mui/material/AccordionDetails'; -import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; -import CircularProgress from '@mui/material/CircularProgress'; -import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import green from '@mui/material/colors/green'; -import CancelIcon from '@mui/icons-material/Cancel'; -import Fab from '@mui/material/Fab'; -import AssessmentIcon from '@mui/icons-material/Assessment'; -import HighlightOffIcon from '@mui/icons-material/HighlightOff'; import {Tooltip} from '@mui/material'; -import IconButton from '@mui/material/IconButton'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import DeleteForeverIcon from '@mui/icons-material/DeleteForever'; + +import {useHistory} from 'react-router-dom'; +import {useSnackbar} from 'notistack'; +import axios from 'axios'; +import clsx from 'clsx'; + +import SubmissionTestExpanded from '../SubmissionTestExpanded/SubmissionTestExpanded'; +import SubmissionContent from '../SubmissionContent/SubmissionContent'; +import SubmissionTest from '../SubmissionTest/SubmissionTest'; +import SubmissionHeader from '../SubmissionHeader/SubmissionHeader'; +import {submissionUpdateSubscribe} from '../../../constant'; +import standardStatusHandler from '../../../utils/standardStatusHandler'; +import {translateSubmission} from '../../../utils/submission'; +import standardErrorHandler from '../../../utils/standardErrorHandler'; + const useStyles = makeStyles((theme) => ({ heading: { @@ -37,83 +44,222 @@ const useStyles = makeStyles((theme) => ({ left: -6, zIndex: 1, }, + submissionContentContainer: { + width: '100%', + marginTop: theme.spacing(2), + }, })); +const regrade = ( + {submission, setSubmission, setStep, setErrorStop}, + continueSubscribe, + enqueueSnackbar, +) => () => { + if (!submission.processed) { + return enqueueSnackbar('Submission must first finish tests before regrading.', {variant: 'warning'}); + } + + axios + .get(`/api/public/submissions/regrade/${submission.commit}`) + .then((response) => { + const data = standardStatusHandler(response, enqueueSnackbar); + if (data) { + setErrorStop(false); + setStep(-1); + setSubmission(null); + continueSubscribe(); + enqueueSnackbar('Regrading submission', {variant: 'success'}); + } else { + enqueueSnackbar(`Unable to regrade`, {variant: 'error'}); + } + }) + .catch((error) => { + enqueueSnackbar(error.toString(), {variant: 'error'}); + }); +}; + -export default function SubmissionTests({tests, stop}) { +export default function SubmissionTests({submissionId}) { const classes = useStyles(); + const [step, setStep] = useState(0); + const [modalTest, setModalTest] = useState(null); + const [isExpanded, setIsExpanded] = useState(false); + const [submission, setSubmission] = useState(null); + const {enqueueSnackbar} = useSnackbar(); + const history = useHistory(); + + const continueSubscribe = () => setTimeout(() => { + if (step < submissionUpdateSubscribe) { + setStep((state) => ++state); + } + }, 1000); + + React.useEffect(() => { + if (!submissionId) { + return; + } + axios.get( + `/api/public/submissions/get/${submissionId}`, + ).then((response) => { + const data = standardStatusHandler(response, enqueueSnackbar); + if (!data) { + return; + } + + const newSubmission = translateSubmission(data.submission); + + // sort all the tests in Alpha Order + newSubmission.tests.sort(function(a, b) { + return a.test.order > b.test.order; + }); - if (!tests) { + setSubmission(newSubmission); + + + if (!submission) { + return continueSubscribe(); + } + + if (submission.build.passed !== newSubmission.build.passed) { + if (newSubmission.build.passed === true) { + enqueueSnackbar('Build passed', {variant: 'success'}); + } else if (newSubmission.build.passed === false) { + return enqueueSnackbar('Build failed', {variant: 'error'}); + } + } + + for (let index = 0; index < submission.tests.length; index++) { + const oldTest = submission.tests[index]; + const newTest = newSubmission.tests[index]; + + if (oldTest.result.passed === null && newTest.result.passed !== null) { + enqueueSnackbar( + `${newTest.test.name} ${newTest.result.passed ? 'passed' : 'failed'}`, + {variant: (newTest.result.passed ? 'success' : 'error')}); + } + } + + if (!submission.processed) { + continueSubscribe(); + } + }).catch((error) => enqueueSnackbar(error.toString(), {variant: 'error'})); + }, [step, submissionId]); + + const expandModal = (test) => { + setModalTest(test); + setIsExpanded(true); + }; + + const closeModal = () => { + setIsExpanded(false); + setModalTest(null); + }; + + if (!submissionId || !submission) { return null; } + const pipelineLogTest = { + test: { + name: 'Pipeline Log', + }, + result: { + test_name: 'Pipeline Log', + passed: !!submission?.build?.passed, + message: 'Not Visible to Students', + output_type: 'text', + output: submission?.pipeline_log ?? null, + }, + }; + + const buildTest = { + test: { + name: 'Build', + }, + result: { + test_name: 'Build', + passed: !!submission?.build?.passed, + message: !!submission?.build?.passed ? 'Build Succeeded' : 'Build Failed', + output_type: 'text', + output: submission?.build?.stdout ?? null, + }, + }; + + return ( - {tests.map((test, index) => ( - - } - aria-controls="panel2a-content" - id="panel2a-header" - > -
- - {stop ? ( - - ) : ( - - {test.result.passed === null ? ( - - ) : (test.result.passed === true) ? ( - - ) : (test.result.passed === false) ? ( - - ) : null} - - )} - - {stop ? null : ( - test.result.passed === null && - )} -
- {test.test.name} - {test.test.hidden ? ( -
- -
- ) : null} -
- -
- - {test.result.message} - - {test.result.passed !== null && !!test.result.stdout ? - test.result.stdout.trim().split('\n') - .map((line, index) => ( - line.trim().length !== 0 ? - - {line} - : -
- )) : - null} -
-
-
- ))} + {!!submission?.pipeline_log && ( + + + + + + )} + + + + + + {submission?.pipeline_log && ( + expandModal(pipelineLogTest)} + /> + )} + {!submission.commit.startsWith('fake-') && ( + expandModal(buildTest)} + /> + )} + {submission?.tests && submission.tests.map((test, index) => ( + expandModal(test)} + /> + ))} + + + {modalTest && + closeModal()} + /> + }
); } diff --git a/web/src/pages/core/admin/Autograde/Submission.jsx b/web/src/pages/core/admin/Autograde/Submission.jsx index 261a9ad78..d3aee1b02 100644 --- a/web/src/pages/core/admin/Autograde/Submission.jsx +++ b/web/src/pages/core/admin/Autograde/Submission.jsx @@ -81,14 +81,8 @@ export default function Submission() { - - - - - - - - + + diff --git a/web/src/pages/core/public/Submission/Submission.jsx b/web/src/pages/core/public/Submission/Submission.jsx index b10c807cb..5a0a0ad13 100644 --- a/web/src/pages/core/public/Submission/Submission.jsx +++ b/web/src/pages/core/public/Submission/Submission.jsx @@ -1,232 +1,15 @@ -import React, {useState} from 'react'; -import {useSnackbar} from 'notistack'; -import axios from 'axios'; +import React from 'react'; import {useParams} from 'react-router-dom'; -import {useHistory} from 'react-router-dom'; - -import Grid from '@mui/material/Grid'; -import Box from '@mui/material/Box'; -import {useStyles} from './Submission.styles'; -import {translateSubmission} from '../../../../utils/submission'; import StandardLayout from '../../../../components/shared/Layouts/StandardLayout'; -import standardStatusHandler from '../../../../utils/standardStatusHandler'; -import SubmissionContent from '../../../../components/core/SubmissionContent/SubmissionContent'; -import SubmissionHeader from '../../../../components/core/SubmissionHeader/SubmissionHeader'; -import SubmissionTest from '../../../../components/core/SubmissionTest/SubmissionTest'; -import SubmissionTestExpanded from '../../../../components/core/SubmissionTestExpanded/SubmissionTestExpanded'; -import {submissionUpdateSubscribe} from '../../../../constant'; -import clsx from 'clsx'; -import Button from '@mui/material/Button'; -import DeleteForeverIcon from '@mui/icons-material/DeleteForever'; -import standardErrorHandler from '../../../../utils/standardErrorHandler'; - -const regrade = ( - {submission, setSubmission, setStep, setErrorStop}, - continueSubscribe, - enqueueSnackbar, -) => () => { - if (!submission.processed) { - return enqueueSnackbar('Submission must first finish tests before regrading.', {variant: 'warning'}); - } - - axios - .get(`/api/public/submissions/regrade/${submission.commit}`) - .then((response) => { - const data = standardStatusHandler(response, enqueueSnackbar); - if (data) { - setErrorStop(false); - setStep(-1); - setSubmission(null); - continueSubscribe(); - enqueueSnackbar('Regrading submission', {variant: 'success'}); - } else { - enqueueSnackbar(`Unable to regrade`, {variant: 'error'}); - } - }) - .catch((error) => { - enqueueSnackbar(error.toString(), {variant: 'error'}); - }); -}; +import SubmissionTests from '../../../../components/core/Submission/SubmissionTests'; export default function Submission() { - const classes = useStyles(); - const history = useHistory(); - const {enqueueSnackbar} = useSnackbar(); - const [step, setStep] = useState(0); - const [submission, setSubmission] = useState(null); - const [modalTest, setModalTest] = useState(null); - const [isExpanded, setIsExpanded] = useState(false); const {submissionId} = useParams(); - const continueSubscribe = () => setTimeout(() => { - if (step < submissionUpdateSubscribe) { - setStep((state) => ++state); - } - }, 1000); - - React.useEffect(() => { - axios.get( - `/api/public/submissions/get/${submissionId}`, - ).then((response) => { - const data = standardStatusHandler(response, enqueueSnackbar); - if (!data) { - return; - } - - const newSubmission = translateSubmission(data.submission); - - // sort all the tests in Alpha Order - newSubmission.tests.sort(function(a, b) { - return a.test.order > b.test.order; - }); - - setSubmission(newSubmission); - - - if (!submission) { - return continueSubscribe(); - } - - if (submission.build.passed !== newSubmission.build.passed) { - if (newSubmission.build.passed === true) { - enqueueSnackbar('Build passed', {variant: 'success'}); - } else if (newSubmission.build.passed === false) { - return enqueueSnackbar('Build failed', {variant: 'error'}); - } - } - - for (let index = 0; index < submission.tests.length; index++) { - const oldTest = submission.tests[index]; - const newTest = newSubmission.tests[index]; - - if (oldTest.result.passed === null && newTest.result.passed !== null) { - enqueueSnackbar( - `${newTest.test.name} ${newTest.result.passed ? 'passed' : 'failed'}`, - {variant: (newTest.result.passed ? 'success' : 'error')}); - } - } - - if (!submission.processed) { - continueSubscribe(); - } - }).catch((error) => enqueueSnackbar(error.toString(), {variant: 'error'})); - }, [step]); - - if (!submission) { - return null; - } - - const expandModal = (test) => { - setModalTest(test); - setIsExpanded(true); - }; - - const closeModal = () => { - setIsExpanded(false); - setModalTest(null); - }; - - const pipelineLogTest = { - test: { - name: 'Pipeline Log', - }, - result: { - test_name: 'Pipeline Log', - passed: !!submission?.build?.passed, - message: 'Not Visible to Students', - output_type: 'text', - output: submission?.pipeline_log ?? null, - }, - }; - - const buildTest = { - test: { - name: 'Build', - }, - result: { - test_name: 'Build', - passed: !!submission?.build?.passed, - message: !!submission?.build?.passed ? 'Build Succeeded' : 'Build Failed', - output_type: 'text', - output: submission?.build?.stdout ?? null, - }, - }; - return ( - - {!!submission?.pipeline_log && ( - - - - )} - - - - - - {submission?.pipeline_log && ( - expandModal(pipelineLogTest)} - /> - )} - {!submission.commit.startsWith('fake-') && ( - expandModal(buildTest)} - /> - )} - {submission?.tests && submission.tests.map((test, index) => ( - expandModal(test)} - /> - ))} - - - {modalTest && - closeModal()} - /> - } - + ); } diff --git a/web/src/pages/core/public/Submission/Submission.styles.jsx b/web/src/pages/core/public/Submission/Submission.styles.jsx deleted file mode 100644 index 285b86aa4..000000000 --- a/web/src/pages/core/public/Submission/Submission.styles.jsx +++ /dev/null @@ -1,66 +0,0 @@ -import makeStyles from '@mui/styles/makeStyles'; - -export const useStyles = makeStyles((theme) => ({ - root: { - width: '100%', - position: 'relative', - }, - heading: { - padding: theme.spacing(2), - display: 'flex', - }, - wrapper: { - margin: theme.spacing(1), - position: 'relative', - }, - buttonSuccess: { - 'backgroundColor': theme.palette.color.green, - '&:hover': { - backgroundColor: theme.palette.color.green, - }, - }, - fabProgress: { - color: theme.palette.color.green, - position: 'absolute', - top: -6, - left: -6, - zIndex: 1, - }, - buttonProgress: { - color: theme.palette.color.green, - position: 'absolute', - top: '50%', - left: '50%', - marginTop: -12, - marginLeft: -12, - }, - submissionContentContainer: { - width: '100%', - marginTop: theme.spacing(2), - }, - expandedContainer: { - zIndex: 599, - position: 'absolute', - display: 'flex', - justifyContent: 'center', - alignItems: 'flex-start', - width: '100%', - height: '100%', - }, - headerContainer: { - width: '100%', - }, - backDrop: { - zIndex: 500, - position: 'absolute', - top: 0, - left: 0, - height: '100%', - width: '100%', - opacity: '7%', - backgroundColor: '#C4C4C4', - }, - blur: { - filter: 'blur(3px)', - }, -}));