Skip to content

Commit

Permalink
feat(client,server): wait chapter problem submission until graded the…
Browse files Browse the repository at this point in the history
…n reload (#550)
  • Loading branch information
fushar authored Oct 28, 2023
1 parent aca7640 commit b006490
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,9 @@ public Response getSubmissionSourceDarkImage(@PathParam("submissionJid") String

@POST
@Consumes(MULTIPART_FORM_DATA)
@Produces(APPLICATION_JSON)
@UnitOfWork
public void createSubmission(@HeaderParam(AUTHORIZATION) AuthHeader authHeader, FormDataMultiPart parts) {
public Submission createSubmission(@HeaderParam(AUTHORIZATION) AuthHeader authHeader, FormDataMultiPart parts) {
actorChecker.check(authHeader);

String containerJid = checkNotNull(parts.getField("containerJid"), "containerJid").getValue();
Expand All @@ -255,6 +256,8 @@ public void createSubmission(@HeaderParam(AUTHORIZATION) AuthHeader authHeader,
Submission submission = submissionClient.submit(data, source, config);

submissionSourceBuilder.storeSubmissionSource(submission.getJid(), source);

return submission;
}

@POST
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HTMLTable, Button } from '@blueprintjs/core';
import { HTMLTable, Button, ProgressBar } from '@blueprintjs/core';
import { Download } from '@blueprintjs/icons';
import { Link } from 'react-router-dom';

Expand Down Expand Up @@ -30,6 +30,8 @@ export function SubmissionDetails({
problemUrl,
containerName,
onDownload,
hideSourceFilename,
showLoaderWhenPending,
}) {
const hasSubtasks = latestGrading && latestGrading.details && latestGrading.details.subtaskResults.length > 1;

Expand Down Expand Up @@ -146,6 +148,13 @@ export function SubmissionDetails({
));
};

const renderLoader = () => {
if (showLoaderWhenPending && latestGrading?.verdict.code === VerdictCode.PND) {
return <ProgressBar className="pending-loader" />;
}
return null;
};

const renderSampleTestDataResults = () => {
const details = latestGrading.details;
if (details.testDataResults.length < 1) {
Expand Down Expand Up @@ -302,9 +311,11 @@ export function SubmissionDetails({

const sourceFiles = Object.keys(submissionFiles).map(key => (
<ContentCard key={key}>
<h5>
{key === DEFAULT_SOURCE_KEY ? '' : key + ': '} {submissionFiles[key].name}
</h5>
{!hideSourceFilename && (
<h5>
{key === DEFAULT_SOURCE_KEY ? '' : key + ': '} {submissionFiles[key].name}
</h5>
)}
<SourceCode language={getGradingLanguageSyntaxHighlighterValue(gradingLanguage)}>
{decodeBase64(submissionFiles[key].content)}
</SourceCode>
Expand Down Expand Up @@ -343,7 +354,7 @@ export function SubmissionDetails({

const renderSourceFilesHeading = () => {
if (!onDownload) {
return <h4>Source Files</h4>;
return null;
}
return (
<div>
Expand All @@ -359,6 +370,7 @@ export function SubmissionDetails({
return (
<div className="programming-submission-details">
{renderGeneralInfo()}
{renderLoader()}
{renderDetails()}
{renderSourceFiles()}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@
float: left;
}

.pending-loader {
margin-bottom: 15px;
}

.submission-details-image {
overflow: auto;
}
Expand Down
2 changes: 2 additions & 0 deletions judgels-client/src/modules/jerahmeel/jerahmeelReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import storage from 'redux-persist/es/storage';
import courseReducer from '../../routes/courses/courses/modules/courseReducer';
import courseChapterReducer from '../../routes/courses/courses/single/chapters/modules/courseChapterReducer';
import courseChaptersReducer from '../../routes/courses/courses/single/chapters/modules/courseChaptersReducer';
import chapterProblemReducer from '../../routes/courses/courses/single/chapters/single/problems/single/modules/chapterProblemReducer';
import problemSetReducer from '../../routes/problems/problemsets/modules/problemSetReducer';
import problemSetProblemReducer from '../../routes/problems/problemsets/single/problems/modules/problemSetProblemReducer';

export default combineReducers({
course: persistReducer({ key: 'jerahmeelCourse', storage }, courseReducer),
courseChapter: persistReducer({ key: 'jerahmeelCourseChapter', storage }, courseChapterReducer),
courseChapters: courseChaptersReducer,
chapterProblem: chapterProblemReducer,
problemSet: persistReducer({ key: 'jerahmeelProblemSet', storage }, problemSetReducer),
problemSetProblem: persistReducer({ key: 'jerahmeelProblemSetProblem', storage }, problemSetProblemReducer),
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { ProblemType } from '../../../../../../../../../modules/api/sandalphon/p
import { selectCourse } from '../../../../../../modules/courseSelectors';
import { selectCourseChapter } from '../../../../modules/courseChapterSelectors';
import { selectCourseChapters } from '../../../../modules/courseChaptersSelectors';
import { selectChapterProblemKey } from '../modules/chapterProblemSelectors';
import { selectStatementLanguage } from '../../../../../../../../../modules/webPrefs/webPrefsSelectors';
import * as chapterProblemActions from '../../modules/chapterProblemActions';
import * as breadcrumbsActions from '../../../../../../../../../modules/breadcrumbs/breadcrumbsActions';
Expand All @@ -25,33 +26,17 @@ export class ChapterProblemPage extends Component {
response: undefined,
};

async componentDidMount() {
const response = await this.props.onGetProblemWorksheet(
this.props.chapter.jid,
this.props.match.params.problemAlias,
this.props.statementLanguage
);

this.setState({
response,
});

this.props.onPushBreadcrumb(this.props.match.url, response.problem.alias);

sendGAEvent({ category: 'Courses', action: 'View course problem', label: this.props.course.name });
sendGAEvent({ category: 'Courses', action: 'View chapter problem', label: this.props.chapter.name });
sendGAEvent({
category: 'Courses',
action: 'View problem',
label: this.props.chapterName + ': ' + this.props.match.params.problemAlias,
});
componentDidMount() {
this.refreshProblem();
}

async componentDidUpdate(prevProps) {
if (this.props.statementLanguage !== prevProps.statementLanguage) {
await this.componentDidMount();
} else if (this.props.match.params.problemAlias !== prevProps.match.params.problemAlias) {
await this.componentDidMount();
if (
this.props.statementLanguage !== prevProps.statementLanguage ||
this.props.chapterProblemKey !== prevProps.chapterProblemKey ||
this.props.match.params.problemAlias !== prevProps.match.params.problemAlias
) {
await this.refreshProblem();
}
}

Expand All @@ -69,6 +54,28 @@ export class ChapterProblemPage extends Component {
);
}

refreshProblem = async () => {
const response = await this.props.onGetProblemWorksheet(
this.props.chapter.jid,
this.props.match.params.problemAlias,
this.props.statementLanguage
);

this.setState({
response,
});

this.props.onPushBreadcrumb(this.props.match.url, response.problem.alias);

sendGAEvent({ category: 'Courses', action: 'View course problem', label: this.props.course.name });
sendGAEvent({ category: 'Courses', action: 'View chapter problem', label: this.props.chapter.name });
sendGAEvent({
category: 'Courses',
action: 'View problem',
label: this.props.chapterName + ': ' + this.props.match.params.problemAlias,
});
};

renderHeader = () => {
const { course, chapter, match } = this.props;

Expand Down Expand Up @@ -148,6 +155,7 @@ const mapStateToProps = state => ({
course: selectCourse(state),
chapter: selectCourseChapter(state),
chapters: selectCourseChapters(state),
chapterProblemKey: selectChapterProblemKey(state),
statementLanguage: selectStatementLanguage(state),
});
const mapDispatchToProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,20 @@ export function createSubmission(courseSlug, chapterJid, chapterAlias, problemJi
sourceFiles['sourceFiles.' + key] = data.sourceFiles[key];
});

await submissionProgrammingAPI.createSubmission(token, chapterJid, problemJid, data.gradingLanguage, sourceFiles);
const submission = await submissionProgrammingAPI.createSubmission(
token,
chapterJid,
problemJid,
data.gradingLanguage,
sourceFiles
);

toastActions.showSuccessToast('Solution submitted.');

window.scrollTo(0, 0);
dispatch(push(`/courses/${courseSlug}/chapters/${chapterAlias}/problems/${problemAlias}/submissions`));
dispatch(
push(`/courses/${courseSlug}/chapters/${chapterAlias}/problems/${problemAlias}/submissions/${submission.id}`)
);
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { SubmissionDetails } from '../../../../../../../../../../../../component
import { selectStatementLanguage } from '../../../../../../../../../../../../modules/webPrefs/webPrefsSelectors';
import { selectCourse } from '../../../../../../../../../modules/courseSelectors';
import { selectCourseChapter } from '../../../../../../../modules/courseChapterSelectors';
import { RefreshChapterProblem } from '../../../../modules/chapterProblemReducer';
import { VerdictCode } from '../../../../../../../../../../../../modules/api/gabriel/verdict';
import * as breadcrumbsActions from '../../../../../../../../../../../../modules/breadcrumbs/breadcrumbsActions';
import * as chapterProblemSubmissionActions from '../../modules/chapterProblemSubmissionActions';

Expand All @@ -22,20 +24,10 @@ export class ChapterProblemSubmissionPage extends Component {
containerName: undefined,
};

async componentDidMount() {
const { data, profile, problemName, containerName } = await this.props.onGetSubmissionWithSource(
+this.props.match.params.submissionId,
this.props.statementLanguage
);
const sourceImageUrl = data.source ? undefined : await this.props.onGetSubmissionSourceImage(data.submission.jid);
this.props.onPushBreadcrumb(this.props.match.url, '#' + data.submission.id);
this.setState({
submissionWithSource: data,
sourceImageUrl,
profile,
problemName,
containerName,
});
currentTimeout;

componentDidMount() {
this.refreshSubmission();
}

async componentWillUnmount() {
Expand Down Expand Up @@ -63,6 +55,36 @@ export class ChapterProblemSubmissionPage extends Component {
);
}

refreshSubmission = async () => {
const { data, profile, problemName, containerName } = await this.props.onGetSubmissionWithSource(
+this.props.match.params.submissionId,
this.props.statementLanguage
);
const sourceImageUrl = data.source ? undefined : await this.props.onGetSubmissionSourceImage(data.submission.jid);
this.props.onPushBreadcrumb(this.props.match.url, '#' + data.submission.id);
this.setState({
submissionWithSource: data,
sourceImageUrl,
profile,
problemName,
containerName,
});

if (sourceImageUrl) {
return;
}

const verdictCode = data.submission.latestGrading?.verdict.code || VerdictCode.PND;
if (verdictCode === VerdictCode.PND) {
this.currentTimeout = setTimeout(this.refreshSubmission, 1500);
} else {
if (this.currentTimeout) {
clearTimeout(this.currentTimeout);
this.props.onRefreshChapterProblem(Date.now());
}
}
};

renderSubmission = () => {
const { submissionWithSource, profile, sourceImageUrl } = this.state;
const { course, chapter } = this.props;
Expand All @@ -79,6 +101,8 @@ export class ChapterProblemSubmissionPage extends Component {
sourceImageUrl={sourceImageUrl}
profile={profile}
problemUrl={`/courses/${course.slug}/chapters/${chapter.alias}/problems/${problemAlias}`}
hideSourceFilename
showLoaderWhenPending
/>
);
};
Expand All @@ -91,6 +115,7 @@ const mapStateToProps = state => ({
});

const mapDispatchToProps = {
onRefreshChapterProblem: RefreshChapterProblem,
onGetSubmissionWithSource: chapterProblemSubmissionActions.getSubmissionWithSource,
onGetSubmissionSourceImage: chapterProblemSubmissionActions.getSubmissionSourceImage,
onPushBreadcrumb: breadcrumbsActions.pushBreadcrumb,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const initialState = {
value: undefined,
};

export function RefreshChapterProblem(key) {
return {
type: 'jerahmeel/chapterProblem/REFRESH',
payload: key,
};
}

export default function chapterProblemReducer(state = initialState, action) {
switch (action.type) {
case 'jerahmeel/chapterProblem/REFRESH':
return { ...state, value: action.payload };
default:
return state;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function selectChapterProblemKey(state) {
return state.jerahmeel.chapterProblem.value;
}

0 comments on commit b006490

Please sign in to comment.