Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

Commit

Permalink
feat : class implementation problem
Browse files Browse the repository at this point in the history
  • Loading branch information
kasterra committed May 26, 2024
1 parent 5ac7ed2 commit 02ec5dd
Show file tree
Hide file tree
Showing 6 changed files with 322 additions and 19 deletions.
72 changes: 69 additions & 3 deletions app/API/problem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,76 @@ export async function postBlankProblem(
return await response.json();
}

export async function postClassImplementationProblem(
file: File,
memory_limit: number,
prepared_main: { content: string; name: string; language: string },
language: string,
practice_id: number,
time_limit: number,
title: string,
token: string
): Promise<ProblemDetailResponse> {
if (0 > memory_limit || memory_limit > 2048) {
throw new BadRequestError("메모리 제한은 0 ~ 2048 사이 값을 넣어야 합니다");
}
if (!title) {
throw new BadRequestError("제목은 필수 입력 필드입니다");
}
if (0 > time_limit || time_limit > 10000) {
throw new BadRequestError("시간 제한은 0~10,000 사이의 값을 넣어야 합니다");
}
if (!prepared_main.content) {
throw new BadRequestError("Main 파일은 반드시 있어야 합니다");
}
const fileUploadResponse = await uploadFile(file, token);
const file_path = fileUploadResponse.data.path;
const response = await fetch(`${API_SERVER_URL}/problem`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
file_path,
memory_limit,
prepared_main: {
code: {
name: prepared_main.name,
content: prepared_main.content,
},
language: prepared_main.language,
},
practice_id,
time_limit,
title,
type: "class_implementation",
}),
});
switch (response.status) {
case 400:
throw new BadRequestError("입력값 검증 실패");
break;
case 401:
handle401();
break;
case 403:
throw new ForbiddenError("강의 소유 권한이 없습니다. 다시 확인해 주세요");
}
return await response.json();
}

export async function updateProblem(
problemType: "solving" | "blank",
problemType: "solving" | "blank" | "class_implementation",
problemId: number,
memory_limit: number,
time_limit: number,
title: string,
token: string,
file_path: string,
parsed_code_elements?: parsedCodeElement[][],
language?: string
language?: string,
prepared_main?: { content: string; name: string; language: string }
): Promise<EmptyResponse> {
if (0 > memory_limit || memory_limit > 2048) {
throw new BadRequestError("메모리 제한은 0 ~ 2048 사이 값을 넣어야 합니다");
Expand All @@ -132,7 +192,13 @@ export async function updateProblem(
throw new BadRequestError("빈칸 문제에는 빈칸정보가 필요합니다");
}
}
console.log(parsed_code_elements);
if (problemType === "class_implementation") {
if (!prepared_main) {
throw new BadRequestError(
"클래스/함수 구현 문제에는 Main 파일이 필요합니다"
);
}
}
const response = await fetch(`${API_SERVER_URL}/problem/${problemId}`, {
method: "PUT",
headers: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ const SubmitModal = ({ isOpen, onClose }: Props) => {
<button
className={formStyles["primary-button"]}
type="submit"
disabled={code.length === 0}
disabled={!fileList && code.length === 0}
>
제출
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,19 @@ import SingleFileInput from "~/components/Input/SingleFileInput";
import CodeBlock from "~/components/CodeBlock";
import { codeHole, parsedCodeElement } from "~/util/codeHole";
import toast from "react-hot-toast";
import { postBlankProblem, postSolveProblem } from "~/API/problem";
import {
postBlankProblem,
postClassImplementationProblem,
postSolveProblem,
} from "~/API/problem";
import { useAuth } from "~/contexts/AuthContext";
import BlankPreviewModal from "./BlankPreviewModal";
import { lanugage } from "~/types";
import {
getCodeFileExtension,
problemTitles,
readFileAsServerFormat,
} from "~/util";

interface Props {
lectureName: string;
Expand All @@ -28,19 +37,19 @@ const ProblemAddModal = ({
isOpen,
onClose,
}: Props) => {
const [problemType, setProblemType] = useState<"blank" | "solving">(
"solving"
);
const [problemType, setProblemType] = useState<
"blank" | "solving" | "class_implementation"
>("solving");
const [language, setLanguage] = useState<lanugage>("c");
const [codeString, setCodeString] = useState("");
const [isPreviewModalOpen, setIsPreviewModalOpen] = useState(false);
const auth = useAuth();
const [dragFile, setDragFile] = useState<File | null>(null);

const [mainFile, setMainFile] = useState<File | null>(null);
return (
<Modal
title={`문제 추가 - ${
problemType === "blank" ? "빈칸 채우기" : "문제 해결"
}`}
title={`문제 추가 - ${problemTitles[problemType] || "알 수 없음"}`}
subtitle={`${lectureName} ${practiceName}에 문제를 추가합니다`}
isOpen={isOpen}
onClose={onClose}
Expand Down Expand Up @@ -106,6 +115,42 @@ const ProblemAddModal = ({
}
);
break;
case "class_implementation":
let mainFileObj: {
content: string;
name: string;
} = {} as { content: string; name: string };
if (mainFile) {
mainFileObj = await readFileAsServerFormat(mainFile);
}
await toast.promise(
postClassImplementationProblem(
file,
memory,
mainFile
? { ...mainFileObj, language }
: {
content: codeString,
name: `Main.${getCodeFileExtension(language)}`,
language,
},
language,
practiceId,
time,
name,
auth.token
),
{
loading: "문제를 추가하는중...",
success: () => {
onClose();
return "문제를 성공적으로 추가했습니다!";
},
error: (err) =>
`Error: ${err.message} - ${err.responseMessage}`,
}
);
break;
}
}}
>
Expand All @@ -120,8 +165,8 @@ const ProblemAddModal = ({
<RadioGroup
title="문제 유형"
name="problemType"
valueList={["solving", "blank"]}
textList={["문제 해결", "빈칸 채우기"]}
valueList={["solving", "blank", "class_implementation"]}
textList={["문제 해결", "빈칸 채우기", "클래스/함수 구현"]}
onChange={setProblemType as (value: string) => void}
/>
<TextInput
Expand Down Expand Up @@ -196,6 +241,42 @@ const ProblemAddModal = ({
)}
</div>
) : null}
{problemType === "class_implementation" ? (
<div className={styles["blank-section"]}>
<label htmlFor="language">Main 파일 언어</label>
<select
name="language"
id="language"
onChange={(e) => setLanguage(e.target.value as lanugage)}
>
<option value="c">C</option>
<option value="java">Java</option>
<option value="javascript">JavaScript</option>
<option value="python">Python</option>
<option value="plaintext">Text</option>
</select>
<span>언어에 맞는 Main 파일을 준비해 주세요.</span>
<span>
직접 입력하거나(이 경우엔 파일 이름은
"Main.언어에_맞는_확장자"로 설정됨) 파일을 업로드 해주세요
</span>
<SingleFileInput
title="Main 파일"
name="main"
onFileUpload={(file) => {
setMainFile(file);
}}
/>
{!mainFile && (
<CodeBlock
height={500}
language={language}
value={codeString}
onChange={setCodeString}
/>
)}
</div>
) : null}
</div>
</div>
</form>
Expand Down
Loading

0 comments on commit 02ec5dd

Please sign in to comment.