1
- import React from 'react' ;
1
+ import React , { useState } from 'react' ;
2
+
2
3
import makeStyles from '@mui/styles/makeStyles' ;
3
- import Typography from '@mui/material/Typography' ;
4
- import Accordion from '@mui/material/Accordion' ;
5
- import AccordionSummary from '@mui/material/AccordionSummary' ;
6
- import AccordionDetails from '@mui/material/AccordionDetails' ;
7
- import ExpandMoreIcon from '@mui/icons-material/ExpandMore' ;
8
- import CircularProgress from '@mui/material/CircularProgress' ;
9
- import CheckCircleIcon from '@mui/icons-material/CheckCircle' ;
10
4
import green from '@mui/material/colors/green' ;
11
- import CancelIcon from '@mui/icons-material/Cancel' ;
12
- import Fab from '@mui/material/Fab' ;
13
- import AssessmentIcon from '@mui/icons-material/Assessment' ;
14
- import HighlightOffIcon from '@mui/icons-material/HighlightOff' ;
15
5
import { Tooltip } from '@mui/material' ;
16
- import IconButton from '@mui/material/IconButton' ;
6
+ import Box from '@mui/material/Box' ;
7
+ import Button from '@mui/material/Button' ;
8
+ import DeleteForeverIcon from '@mui/icons-material/DeleteForever' ;
9
+
10
+ import { useHistory } from 'react-router-dom' ;
11
+ import { useSnackbar } from 'notistack' ;
12
+ import axios from 'axios' ;
13
+ import clsx from 'clsx' ;
14
+
15
+ import SubmissionTestExpanded from '../SubmissionTestExpanded/SubmissionTestExpanded' ;
16
+ import SubmissionContent from '../SubmissionContent/SubmissionContent' ;
17
+ import SubmissionTest from '../SubmissionTest/SubmissionTest' ;
18
+ import SubmissionHeader from '../SubmissionHeader/SubmissionHeader' ;
19
+ import { submissionUpdateSubscribe } from '../../../constant' ;
20
+ import standardStatusHandler from '../../../utils/standardStatusHandler' ;
21
+ import { translateSubmission } from '../../../utils/submission' ;
22
+ import standardErrorHandler from '../../../utils/standardErrorHandler' ;
23
+
17
24
18
25
const useStyles = makeStyles ( ( theme ) => ( {
19
26
heading : {
@@ -37,83 +44,222 @@ const useStyles = makeStyles((theme) => ({
37
44
left : - 6 ,
38
45
zIndex : 1 ,
39
46
} ,
47
+ submissionContentContainer : {
48
+ width : '100%' ,
49
+ marginTop : theme . spacing ( 2 ) ,
50
+ } ,
40
51
} ) ) ;
41
52
53
+ const regrade = (
54
+ { submission, setSubmission, setStep, setErrorStop} ,
55
+ continueSubscribe ,
56
+ enqueueSnackbar ,
57
+ ) => ( ) => {
58
+ if ( ! submission . processed ) {
59
+ return enqueueSnackbar ( 'Submission must first finish tests before regrading.' , { variant : 'warning' } ) ;
60
+ }
61
+
62
+ axios
63
+ . get ( `/api/public/submissions/regrade/${ submission . commit } ` )
64
+ . then ( ( response ) => {
65
+ const data = standardStatusHandler ( response , enqueueSnackbar ) ;
66
+ if ( data ) {
67
+ setErrorStop ( false ) ;
68
+ setStep ( - 1 ) ;
69
+ setSubmission ( null ) ;
70
+ continueSubscribe ( ) ;
71
+ enqueueSnackbar ( 'Regrading submission' , { variant : 'success' } ) ;
72
+ } else {
73
+ enqueueSnackbar ( `Unable to regrade` , { variant : 'error' } ) ;
74
+ }
75
+ } )
76
+ . catch ( ( error ) => {
77
+ enqueueSnackbar ( error . toString ( ) , { variant : 'error' } ) ;
78
+ } ) ;
79
+ } ;
80
+
42
81
43
- export default function SubmissionTests ( { tests , stop } ) {
82
+ export default function SubmissionTests ( { submissionId } ) {
44
83
const classes = useStyles ( ) ;
84
+ const [ step , setStep ] = useState ( 0 ) ;
85
+ const [ modalTest , setModalTest ] = useState ( null ) ;
86
+ const [ isExpanded , setIsExpanded ] = useState ( false ) ;
87
+ const [ submission , setSubmission ] = useState ( null ) ;
88
+ const { enqueueSnackbar} = useSnackbar ( ) ;
89
+ const history = useHistory ( ) ;
90
+
91
+ const continueSubscribe = ( ) => setTimeout ( ( ) => {
92
+ if ( step < submissionUpdateSubscribe ) {
93
+ setStep ( ( state ) => ++ state ) ;
94
+ }
95
+ } , 1000 ) ;
96
+
97
+ React . useEffect ( ( ) => {
98
+ if ( ! submissionId ) {
99
+ return ;
100
+ }
101
+ axios . get (
102
+ `/api/public/submissions/get/${ submissionId } ` ,
103
+ ) . then ( ( response ) => {
104
+ const data = standardStatusHandler ( response , enqueueSnackbar ) ;
105
+ if ( ! data ) {
106
+ return ;
107
+ }
108
+
109
+ const newSubmission = translateSubmission ( data . submission ) ;
110
+
111
+ // sort all the tests in Alpha Order
112
+ newSubmission . tests . sort ( function ( a , b ) {
113
+ return a . test . order > b . test . order ;
114
+ } ) ;
45
115
46
- if ( ! tests ) {
116
+ setSubmission ( newSubmission ) ;
117
+
118
+
119
+ if ( ! submission ) {
120
+ return continueSubscribe ( ) ;
121
+ }
122
+
123
+ if ( submission . build . passed !== newSubmission . build . passed ) {
124
+ if ( newSubmission . build . passed === true ) {
125
+ enqueueSnackbar ( 'Build passed' , { variant : 'success' } ) ;
126
+ } else if ( newSubmission . build . passed === false ) {
127
+ return enqueueSnackbar ( 'Build failed' , { variant : 'error' } ) ;
128
+ }
129
+ }
130
+
131
+ for ( let index = 0 ; index < submission . tests . length ; index ++ ) {
132
+ const oldTest = submission . tests [ index ] ;
133
+ const newTest = newSubmission . tests [ index ] ;
134
+
135
+ if ( oldTest . result . passed === null && newTest . result . passed !== null ) {
136
+ enqueueSnackbar (
137
+ `${ newTest . test . name } ${ newTest . result . passed ? 'passed' : 'failed' } ` ,
138
+ { variant : ( newTest . result . passed ? 'success' : 'error' ) } ) ;
139
+ }
140
+ }
141
+
142
+ if ( ! submission . processed ) {
143
+ continueSubscribe ( ) ;
144
+ }
145
+ } ) . catch ( ( error ) => enqueueSnackbar ( error . toString ( ) , { variant : 'error' } ) ) ;
146
+ } , [ step , submissionId ] ) ;
147
+
148
+ const expandModal = ( test ) => {
149
+ setModalTest ( test ) ;
150
+ setIsExpanded ( true ) ;
151
+ } ;
152
+
153
+ const closeModal = ( ) => {
154
+ setIsExpanded ( false ) ;
155
+ setModalTest ( null ) ;
156
+ } ;
157
+
158
+ if ( ! submissionId || ! submission ) {
47
159
return null ;
48
160
}
49
161
162
+ const pipelineLogTest = {
163
+ test : {
164
+ name : 'Pipeline Log' ,
165
+ } ,
166
+ result : {
167
+ test_name : 'Pipeline Log' ,
168
+ passed : ! ! submission ?. build ?. passed ,
169
+ message : 'Not Visible to Students' ,
170
+ output_type : 'text' ,
171
+ output : submission ?. pipeline_log ?? null ,
172
+ } ,
173
+ } ;
174
+
175
+ const buildTest = {
176
+ test : {
177
+ name : 'Build' ,
178
+ } ,
179
+ result : {
180
+ test_name : 'Build' ,
181
+ passed : ! ! submission ?. build ?. passed ,
182
+ message : ! ! submission ?. build ?. passed ? 'Build Succeeded' : 'Build Failed' ,
183
+ output_type : 'text' ,
184
+ output : submission ?. build ?. stdout ?? null ,
185
+ } ,
186
+ } ;
187
+
188
+
50
189
return (
51
190
< React . Fragment >
52
- { tests . map ( ( test , index ) => (
53
- < Accordion key = { `test-${ index } ` } >
54
- < AccordionSummary
55
- expandIcon = { < ExpandMoreIcon /> }
56
- aria-controls = "panel2a-content"
57
- id = "panel2a-header"
58
- >
59
- < div className = { classes . wrapper } >
60
- < Fab
61
- aria-label = "save"
62
- color = { stop ? 'error' : ( test . result . passed === false ? 'error' : 'primary' ) }
63
- >
64
- { stop ? (
65
- < CancelIcon />
66
- ) : (
67
- < React . Fragment >
68
- { test . result . passed === null ? (
69
- < AssessmentIcon />
70
- ) : ( test . result . passed === true ) ? (
71
- < CheckCircleIcon />
72
- ) : ( test . result . passed === false ) ? (
73
- < CancelIcon />
74
- ) : null }
75
- </ React . Fragment >
76
- ) }
77
- </ Fab >
78
- { stop ? null : (
79
- test . result . passed === null && < CircularProgress size = { 68 } className = { classes . fabProgress } />
80
- ) }
81
- </ div >
82
- < Typography className = { classes . heading } > { test . test . name } </ Typography >
83
- { test . test . hidden ? (
84
- < div className = { classes . hiddenIcon } >
85
- < Tooltip title = { 'Test hidden to students' } >
86
- < IconButton size = "large" >
87
- < HighlightOffIcon />
88
- </ IconButton >
89
- </ Tooltip >
90
- </ div >
91
- ) : null }
92
- </ AccordionSummary >
93
- < AccordionDetails >
94
- < div >
95
- < Typography key = { 'message' } variant = { 'h5' } className = { classes . heading } >
96
- { test . result . message }
97
- </ Typography >
98
- { test . result . passed !== null && ! ! test . result . stdout ?
99
- test . result . stdout . trim ( ) . split ( '\n' )
100
- . map ( ( line , index ) => (
101
- line . trim ( ) . length !== 0 ?
102
- < Typography
103
- variant = { 'body1' }
104
- color = { 'textSecondary' }
105
- width = { 100 }
106
- key = { `line-${ index } ` }
107
- >
108
- { line }
109
- </ Typography > :
110
- < br />
111
- ) ) :
112
- null }
113
- </ div >
114
- </ AccordionDetails >
115
- </ Accordion >
116
- ) ) }
191
+ { ! ! submission ?. pipeline_log && (
192
+ < Box sx = { { m : 1 } } >
193
+ < Tooltip title = "Delete submission forever from Anubis" >
194
+ < Button
195
+ variant = { 'contained' }
196
+ color = { 'error' }
197
+ startIcon = { < DeleteForeverIcon /> }
198
+ className = { clsx ( classes . dataItem ) }
199
+ onClick = { ( ) => {
200
+ axios . delete ( `/api/admin/submissions/delete/${ submissionId } ` ) . then ( ( response ) => {
201
+ const data = standardStatusHandler ( response , enqueueSnackbar ) ;
202
+ if ( data ) {
203
+ history . go ( - 1 ) ;
204
+ }
205
+ } ) . catch ( standardErrorHandler ( enqueueSnackbar ) ) ;
206
+ } }
207
+ >
208
+ Delete
209
+ </ Button >
210
+ </ Tooltip >
211
+ </ Box >
212
+ ) }
213
+ < Box className = { classes . headerContainer } >
214
+ < SubmissionHeader
215
+ admin = { ! ! submission ?. pipeline_log }
216
+ regrade = { regrade (
217
+ { submission, setSubmission, setStep} ,
218
+ continueSubscribe ,
219
+ enqueueSnackbar ,
220
+ ) }
221
+ { ...submission }
222
+ />
223
+ </ Box >
224
+ < Box className = { classes . submissionContentContainer } >
225
+ < SubmissionContent submission = { submission } >
226
+ { submission ?. pipeline_log && (
227
+ < SubmissionTest
228
+ test = { pipelineLogTest }
229
+ processing = { submission . processing }
230
+ expandModal = { ( ) => expandModal ( pipelineLogTest ) }
231
+ />
232
+ ) }
233
+ { ! submission . commit . startsWith ( 'fake-' ) && (
234
+ < SubmissionTest
235
+ test = { buildTest }
236
+ processing = { submission . processing }
237
+ expandModal = { ( ) => expandModal ( buildTest ) }
238
+ />
239
+ ) }
240
+ { submission ?. tests && submission . tests . map ( ( test , index ) => (
241
+ < SubmissionTest
242
+ key = { index }
243
+ test = { test }
244
+ processing = { submission . processing }
245
+ expandModal = { ( ) => expandModal ( test ) }
246
+ />
247
+ ) ) }
248
+ </ SubmissionContent >
249
+ </ Box >
250
+ { modalTest &&
251
+ < SubmissionTestExpanded
252
+ open = { isExpanded }
253
+ submissionID = { submission . commit }
254
+ assignmentName = { submission . assignment_name }
255
+ testName = { modalTest . result . test_name }
256
+ testSuccess = { modalTest . result . passed }
257
+ testOutputType = { modalTest . result . output_type }
258
+ testOutput = { modalTest . result . output }
259
+ testMessage = { modalTest . result . message }
260
+ onClose = { ( ) => closeModal ( ) }
261
+ />
262
+ }
117
263
</ React . Fragment >
118
264
) ;
119
265
}
0 commit comments