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

Real-time evaluation step status updates for client #714

Merged
merged 9 commits into from Jan 11, 2021
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -21,6 +21,7 @@
* Admins can see the author of each assignment (#691)
* Individual evaluation steps are now run in parallel to make evaluation faster (#710)
* Make the IDE liveliness check work with URLs from other origin
* Evaluation step status is shown in real time (#714)
* If an evaluation step is marked inactive, export this too (#748)
* The options of evaluation steps are exported regardless of whether they are the default options or not (#750)

Expand Down
4 changes: 4 additions & 0 deletions client/src/App.less
Expand Up @@ -68,3 +68,7 @@
.ant-message-notice-content {
white-space: pre-wrap;
}

.spin-slow .anticon-spin {
animation-duration: 2s;
}
9 changes: 2 additions & 7 deletions client/src/App.tsx
@@ -1,5 +1,4 @@
import { MenuDataItem } from '@ant-design/pro-layout/lib/typings'
import { Spin } from 'antd'
import { useEffect, useState } from 'react'
import {
BrowserRouter as Router,
Expand All @@ -8,7 +7,6 @@ import {
Switch
} from 'react-router-dom'
import './App.less'
import Centered from './components/Centered'
import DefaultLayout from './components/DefaultLayout'
import ScrollToHash from './components/ScrollToHash'
import {
Expand All @@ -32,6 +30,7 @@ import { messageService } from './services/message'
import { displayName } from './services/user'
import { noop } from './services/util'
import { HideNavigationProvider } from './hooks/useHideNavigation'
import LoadingIndicator from './components/LoadingIndicator'

const App: React.FC<{ onUserChanged?: () => void }> = props => {
const onUserChanged = props.onUserChanged || noop
Expand Down Expand Up @@ -63,11 +62,7 @@ const App: React.FC<{ onUserChanged?: () => void }> = props => {
}, [logoutSucceeded, onUserChanged])

if (loading || timeOffset === undefined) {
return (
<Centered>
<Spin size="large" />
</Centered>
)
return <LoadingIndicator />
}

if (authenticatedUser === undefined) {
Expand Down
9 changes: 3 additions & 6 deletions client/src/components/AsyncContainer.tsx
@@ -1,6 +1,7 @@
import { ApolloError } from '@apollo/client'
import { Result, Spin } from 'antd'
import { Result } from 'antd'
import React from 'react'
import LoadingIndicator from './LoadingIndicator'

interface AsyncPlaceholderProps {
result: {
Expand All @@ -11,11 +12,7 @@ interface AsyncPlaceholderProps {

const AsyncPlaceholder: React.FC<AsyncPlaceholderProps> = props => {
if (props.result.loading) {
return (
<div style={{ textAlign: 'center' }}>
<Spin size="large" />
</div>
)
return <LoadingIndicator />
}
if (props.result.error) {
return (
Expand Down
13 changes: 7 additions & 6 deletions client/src/components/EvaluationHistory.tsx
Expand Up @@ -3,7 +3,6 @@ import { Button, Card, Modal, Timeline } from 'antd'
import React from 'react'
import {
EvaluationStepResult,
EvaluationStepStatus,
GetEvaluationHistoryQueryResult,
useGetEvaluationHistoryQuery
} from '../generated/graphql'
Expand All @@ -12,17 +11,14 @@ import './EvaluationHistory.less'
import EvaluationResult from './EvaluationResult'
import { EvaluationErrorIcon } from './Icons'
import useEvaluationStatus from '../hooks/useEvaluationStatus'
import { isEvaluationInProgress } from '../services/evaluation'

const EvaluationHistory: React.FC<{ answerId: string }> = ({ answerId }) => {
const result = useGetEvaluationHistoryQuery({ variables: { answerId } })
const apolloClient = useApolloClient()

const evaluationStatus = useEvaluationStatus(answerId)

const isPending =
evaluationStatus === EvaluationStepStatus.Running ||
evaluationStatus === EvaluationStepStatus.Queued

if (result.data === undefined) {
return <AsyncPlaceholder result={result} />
}
Expand All @@ -33,7 +29,12 @@ const EvaluationHistory: React.FC<{ answerId: string }> = ({ answerId }) => {

return (
<Card className="evaluation-history">
<Timeline reverse pending={isPending ? 'Running...' : undefined}>
<Timeline
reverse
pending={
isEvaluationInProgress(evaluationStatus) ? 'Running...' : undefined
}
>
{evaluations.map(renderEvaluation(apolloClient))}
</Timeline>
</Card>
Expand Down
20 changes: 8 additions & 12 deletions client/src/components/EvaluationIndicator.tsx
@@ -1,9 +1,9 @@
import { Badge } from 'antd'
import React from 'react'
import useLatestEvaluation from '../hooks/useLatestEvaluation'
import { EvaluationErrorIcon } from './Icons'
import useEvaluationStatus from '../hooks/useEvaluationStatus'
import { EvaluationStepStatus } from '../generated/graphql'
import { EvaluationStepResult } from '../generated/graphql'
import useAnswerEvaluation from '../hooks/useAnswerEvaluation'
import { isEvaluationInProgress } from '../services/evaluation'

interface EvaluationIndicatorProps {
style?: React.CSSProperties
Expand All @@ -12,25 +12,21 @@ interface EvaluationIndicatorProps {

const EvaluationIndicator: React.FC<EvaluationIndicatorProps> = props => {
const { answerId, style } = props
const latest = useLatestEvaluation(answerId)
const status = useEvaluationStatus(answerId)
const { latestEvaluation, evaluationStatus } = useAnswerEvaluation(answerId)

if (
status === EvaluationStepStatus.Running ||
status === EvaluationStepStatus.Queued
) {
if (isEvaluationInProgress(evaluationStatus)) {
return <Badge style={style} status="processing" />
}

if (latest.summary === 'SUCCESS') {
if (latestEvaluation?.stepsResultSummary === EvaluationStepResult.Success) {
return <Badge style={style} status="success" />
}

if (latest.summary === 'FAILED') {
if (latestEvaluation?.stepsResultSummary === EvaluationStepResult.Failed) {
return <Badge style={style} status="error" />
}

if (latest.summary === 'ERRORED') {
if (latestEvaluation?.stepsResultSummary === EvaluationStepResult.Errored) {
return <EvaluationErrorIcon style={style} />
}

Expand Down
12 changes: 12 additions & 0 deletions client/src/components/EvaluationProcessingIcon.tsx
@@ -0,0 +1,12 @@
import { Icon } from 'antd'
import { IconProps } from 'antd/es/icon'

const EvaluationProcessingIcon: React.FC<IconProps> = ({
className,
...props
}) => {
const classes = 'spin-slow ' + (className ? className : '')
return <Icon type="setting" spin className={classes} {...props} />
}

export default EvaluationProcessingIcon