Skip to content

Commit fb90e68

Browse files
(wip) begin migration to serverless online judge
1 parent 6583592 commit fb90e68

File tree

5 files changed

+134
-146
lines changed

5 files changed

+134
-146
lines changed

src/components/Groups/OnlineJudgeSubmission/OnlineJudgeSubmission.tsx

+37-42
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,47 @@
11
import React from 'react';
2-
import {
3-
ExecutionStatus,
4-
Submission,
5-
SubmissionType,
6-
} from '../../../models/groups/problem';
7-
import TestCaseResult from './TestCaseResult';
82

93
const OnlineJudgeSubmission = ({
104
submission,
115
}: {
12-
submission: Submission & { type: SubmissionType.ONLINE_JUDGE };
6+
submission: any;
137
}): JSX.Element => {
148
return (
15-
<div>
16-
<div>
17-
{submission.gradingStatus === 'waiting' &&
18-
'Waiting for an available grading server...'}
19-
{submission.gradingStatus === 'in_progress' && 'Grading in progress'}
20-
{submission.gradingStatus === 'done' &&
21-
(submission.compilationError ? 'Compilation Error' : 'Done')}
22-
{submission.gradingStatus === 'error' &&
23-
(submission.errorMessage
24-
? 'Error: ' + submission.errorMessage
25-
: 'Error')}
26-
</div>
27-
{submission.gradingStatus === 'done' &&
28-
submission.status !== ExecutionStatus.AC && (
29-
<p>
30-
Common issues: Java users should name their class Main. Remember to
31-
use standard input/output instead of file input/output. Finally, our
32-
grader currently has an issue that requires you to output an endline
33-
(ex. \n or endl) at the end of the output. This is a bug (it works
34-
on USACO without the endline) that we're working on fixing.
35-
</p>
36-
)}
37-
{submission.compilationError === true && (
38-
<pre className="text-red-800 dark:text-red-200 overflow-auto">
39-
{submission.compilationErrorMessage}
40-
</pre>
41-
)}
42-
{submission.compilationError === false && (
43-
<div>
44-
{submission.testCases.map(tc => (
45-
<TestCaseResult data={tc} key={tc.caseId} />
46-
))}
47-
</div>
48-
)}
49-
</div>
9+
<pre>{JSON.stringify(submission, null, 2)}</pre>
10+
// <div>
11+
// <div>
12+
// {submission.gradingStatus === 'waiting' &&
13+
// 'Waiting for an available grading server...'}
14+
// {submission.gradingStatus === 'in_progress' && 'Grading in progress'}
15+
// {submission.gradingStatus === 'done' &&
16+
// (submission.compilationError ? 'Compilation Error' : 'Done')}
17+
// {submission.gradingStatus === 'error' &&
18+
// (submission.errorMessage
19+
// ? 'Error: ' + submission.errorMessage
20+
// : 'Error')}
21+
// </div>
22+
// {submission.gradingStatus === 'done' &&
23+
// submission.status !== ExecutionStatus.AC && (
24+
// <p>
25+
// Common issues: Java users should name their class Main. Remember to
26+
// use standard input/output instead of file input/output. Finally, our
27+
// grader currently has an issue that requires you to output an endline
28+
// (ex. \n or endl) at the end of the output. This is a bug (it works
29+
// on USACO without the endline) that we're working on fixing.
30+
// </p>
31+
// )}
32+
// {submission.compilationError === true && (
33+
// <pre className="text-red-800 dark:text-red-200 overflow-auto">
34+
// {submission.compilationErrorMessage}
35+
// </pre>
36+
// )}
37+
// {submission.compilationError === false && (
38+
// <div>
39+
// {submission.testCases.map(tc => (
40+
// <TestCaseResult data={tc} key={tc.caseId} />
41+
// ))}
42+
// </div>
43+
// )}
44+
// </div>
5045
);
5146
};
5247

src/components/Groups/ProblemPage/ProblemSubmissionInterface.tsx

+15-75
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
import type firebaseType from 'firebase/firestore';
2-
import { doc, getFirestore, onSnapshot } from 'firebase/firestore';
31
import * as React from 'react';
42
import { useReducer } from 'react';
53
import { useDropzone } from 'react-dropzone';
64
import { LANGUAGE_LABELS } from '../../../context/UserDataContext/properties/userLang';
75
import UserDataContext from '../../../context/UserDataContext/UserDataContext';
86
import { useActiveGroup } from '../../../hooks/groups/useActiveGroup';
97
import { usePostActions } from '../../../hooks/groups/usePostActions';
10-
import { useFirebaseApp } from '../../../hooks/useFirebase';
8+
import useProblemSubmissionResult from '../../../hooks/useProblemSubmissionResult';
119
import {
1210
ExecutionStatus,
1311
GroupProblemData,
@@ -76,26 +74,7 @@ export default function ProblemSubmissionInterface({
7674
},
7775
});
7876

79-
const firebaseApp = useFirebaseApp();
80-
const [onlineJudgeSubmissionDoc, setOnlineJudgeSubmissionDoc] =
81-
React.useState<firebaseType.DocumentReference | null>(null);
82-
const [submissionResult, setSubmissionResult] = React.useState<
83-
(Submission & { type: SubmissionType.ONLINE_JUDGE }) | null
84-
>(null);
85-
86-
React.useEffect(() => {
87-
if (!onlineJudgeSubmissionDoc || !firebaseApp) {
88-
setSubmissionResult(null);
89-
return;
90-
}
91-
92-
const unsub = onSnapshot(onlineJudgeSubmissionDoc, doc => {
93-
setSubmissionResult(
94-
doc.data() as Submission & { type: SubmissionType.ONLINE_JUDGE }
95-
);
96-
});
97-
return unsub;
98-
}, [onlineJudgeSubmissionDoc, firebaseApp]);
77+
const [submissionResult, setSubmissionID] = useProblemSubmissionResult();
9978

10079
if (activeGroup.activeUserId !== firebaseUser?.uid) {
10180
// this suggests the parent is viewing the child's account
@@ -139,30 +118,20 @@ export default function ProblemSubmissionInterface({
139118

140119
const handleSubmitSolution = async e => {
141120
e.preventDefault();
142-
if (isCPIClass) {
143-
const submissionID = await submitSolution(problem, {
144-
...submission,
145-
type: SubmissionType.ONLINE_JUDGE,
146-
judgeProblemId: problem.usacoGuideId, // todo update
147-
gradingStatus: 'waiting',
148-
result: -1, // todo write a rule for this?
121+
try {
122+
const submissionID = await submitSolution({
123+
problemID: problem.usacoGuideId,
124+
language: submission.language,
125+
filename: {
126+
cpp: 'main.cpp',
127+
java: 'Main.java',
128+
py: 'main.py',
129+
}[submission.language],
130+
sourceCode: submission.code,
149131
});
150-
setOnlineJudgeSubmissionDoc(
151-
doc(
152-
getFirestore(firebaseApp),
153-
'groups',
154-
activeGroup.activeGroupId,
155-
'posts',
156-
problem.postId,
157-
'problems',
158-
problem.id,
159-
'submissions',
160-
submissionID
161-
)
162-
);
163-
} else {
164-
submitSolution(problem, submission);
165-
editSubmission(emptySubmission);
132+
setSubmissionID(submissionID);
133+
} catch (error) {
134+
alert('Failed to submit solution: ' + error.message);
166135
}
167136
};
168137

@@ -211,35 +180,6 @@ export default function ProblemSubmissionInterface({
211180
<OnlineJudgeSubmission submission={submissionResult} />
212181
</div>
213182
)}
214-
{/*<div className="mt-4">*/}
215-
{/* <label*/}
216-
{/* htmlFor="score"*/}
217-
{/* className="block text-sm font-medium text-gray-700 dark:text-gray-300"*/}
218-
{/* >*/}
219-
{/* Score*/}
220-
{/* </label>*/}
221-
{/* <div className="mt-1 relative rounded-md shadow-sm w-24">*/}
222-
{/* <ScoreInput*/}
223-
{/* type="number"*/}
224-
{/* name="score"*/}
225-
{/* id="score"*/}
226-
{/* min={0}*/}
227-
{/* max={100}*/}
228-
{/* value={*/}
229-
{/* submission.result === null*/}
230-
{/* ? ''*/}
231-
{/* : Math.round((submission.result as number) * 100)*/}
232-
{/* }*/}
233-
{/* onChange={e =>*/}
234-
{/* editSubmission({ result: parseInt(e.target.value) / 100 })*/}
235-
{/* }*/}
236-
{/* className="input"*/}
237-
{/* placeholder="0 - 100"*/}
238-
{/* aria-describedby="price-currency"*/}
239-
{/* required*/}
240-
{/* />*/}
241-
{/* </div>*/}
242-
{/*</div>*/}
243183
<div className="mt-4">
244184
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
245185
Language

src/context/UserDataContext/UserDataContext.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,8 @@ export const UserDataProvider = ({
261261
// If the user is signed in, sync remote data with local data
262262
React.useEffect(() => {
263263
if (firebaseUser) {
264+
firebaseUser.getIdToken(true).then(token => console.log(token));
265+
264266
const userDoc = doc(getFirestore(firebaseApp), 'users', firebaseUser.uid);
265267
return onSnapshot(userDoc, {
266268
next: snapshot => {

src/hooks/groups/usePostActions.ts

+44-29
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import {
2-
addDoc,
32
arrayRemove,
43
arrayUnion,
54
collection,
@@ -13,18 +12,27 @@ import {
1312
import { useContext } from 'react';
1413
import UserDataContext from '../../context/UserDataContext/UserDataContext';
1514
import { PostData } from '../../models/groups/posts';
16-
import {
17-
GroupProblemData,
18-
Submission,
19-
SubmissionType,
20-
} from '../../models/groups/problem';
15+
import { GroupProblemData, SubmissionType } from '../../models/groups/problem';
2116
import { useFirebaseApp } from '../useFirebase';
2217

18+
// Duplicated from online-judge... online-judge should be the only version of this
19+
export interface ProblemSubmissionRequestData {
20+
problemID: string;
21+
language: 'cpp' | 'java' | 'py';
22+
filename: string;
23+
sourceCode: string;
24+
submissionID?: string; // if given, uses this as the submission ID. must be uuidv4
25+
wait?: boolean; // if true, request will wait until the submission finishes grading.
26+
firebase?: {
27+
idToken: string; // used to authenticate REST api
28+
collectionPath: string;
29+
};
30+
}
31+
2332
export function usePostActions(groupId: string) {
2433
const firebaseApp = useFirebaseApp();
25-
const { firebaseUser, setUserProgressOnProblems } = useContext(
26-
UserDataContext
27-
);
34+
const { firebaseUser, setUserProgressOnProblems } =
35+
useContext(UserDataContext);
2836

2937
const updatePost = async (postId: string, updatedData: Partial<PostData>) => {
3038
await updateDoc(
@@ -181,30 +189,37 @@ export function usePostActions(groupId: string) {
181189
});
182190
},
183191
submitSolution: async (
184-
problem: GroupProblemData,
185-
submission: Partial<Submission>
192+
submission: Omit<
193+
ProblemSubmissionRequestData,
194+
'submissionID' | 'wait' | 'firebase'
195+
>
186196
) => {
187-
if (problem.usacoGuideId) {
188-
setUserProgressOnProblems(problem.usacoGuideId, 'Solved');
189-
}
190-
const doc = await addDoc(
191-
collection(
192-
getFirestore(firebaseApp),
193-
'groups',
194-
groupId,
195-
'posts',
196-
problem.postId,
197-
'problems',
198-
problem.id,
199-
'submissions'
200-
),
197+
const idToken = await firebaseUser.getIdToken();
198+
const reqData: ProblemSubmissionRequestData = {
199+
problemID: submission.problemID,
200+
language: submission.language,
201+
filename: submission.filename,
202+
sourceCode: submission.sourceCode,
203+
// firebase: {
204+
// collectionPath: `usaco-guide/databases/(default)/documents/__test`,
205+
// idToken,
206+
// },
207+
};
208+
const resp = await fetch(
209+
`https://oh2kjsg6kh.execute-api.us-west-1.amazonaws.com/Prod/submissions`,
201210
{
202-
...submission,
203-
timestamp: serverTimestamp(),
204-
userId: firebaseUser.uid,
211+
method: 'POST',
212+
headers: {
213+
'Content-Type': 'application/json',
214+
},
215+
body: JSON.stringify(reqData),
205216
}
206217
);
207-
return doc.id;
218+
const respData = await resp.json();
219+
if (!resp.ok) {
220+
throw new Error(respData.message);
221+
}
222+
return respData.submissionID;
208223
},
209224
};
210225
}
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { useEffect, useRef, useState } from 'react';
2+
3+
export default function useProblemSubmissionResult() {
4+
const [submissionID, setSubmissionID] = useState(null);
5+
const [result, setResult] = useState(null);
6+
const currentSubmission = useRef(0);
7+
8+
useEffect(() => {
9+
const queryResult = async (curSubmission, submissionID) => {
10+
const res = await fetch(
11+
`https://oh2kjsg6kh.execute-api.us-west-1.amazonaws.com/Prod/submissions/${submissionID}`
12+
);
13+
const data = await res.json();
14+
15+
if (currentSubmission.current !== curSubmission) return;
16+
17+
setResult(data);
18+
19+
if (res.ok && data.status === 'executing') {
20+
setTimeout(() => queryResult(curSubmission, submissionID), 1000);
21+
}
22+
};
23+
24+
if (submissionID) {
25+
queryResult(++currentSubmission.current, submissionID);
26+
}
27+
}, [submissionID]);
28+
29+
useEffect(() => {
30+
return () => {
31+
currentSubmission.current = -1;
32+
};
33+
}, []);
34+
35+
return [result, setSubmissionID];
36+
}

0 commit comments

Comments
 (0)