From e78f51121cb9e73e9d24faa7aee03b9242dd905f Mon Sep 17 00:00:00 2001 From: "Hans Gabriel B. Daduya" Date: Wed, 6 Dec 2023 19:51:47 +0800 Subject: [PATCH 1/2] Fix true or false question (#229) --- apps/expo/src/screens/create-question/index.tsx | 4 ++-- packages/schema/src/question.ts | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/expo/src/screens/create-question/index.tsx b/apps/expo/src/screens/create-question/index.tsx index adc24bc..9d15171 100644 --- a/apps/expo/src/screens/create-question/index.tsx +++ b/apps/expo/src/screens/create-question/index.tsx @@ -258,13 +258,13 @@ export const CreateQuestionScreen: FC = () => { { id: 0, text: "True", - isCorrect: data.answer, + isCorrect: data.choices[0]!.isCorrect, styles: choiceStyles[0]!.styles, }, { id: 1, text: "False", - isCorrect: !data.answer, + isCorrect: data.choices[1]!.isCorrect, styles: choiceStyles[1]!.styles, }, ]); diff --git a/packages/schema/src/question.ts b/packages/schema/src/question.ts index 22423c6..b5a5cc5 100644 --- a/packages/schema/src/question.ts +++ b/packages/schema/src/question.ts @@ -61,7 +61,12 @@ export const questionSchema = z.discriminatedUnion("type", [ }), z.object({ question: z.string(), - answer: z.boolean(), + choices: z.array( + z.object({ + isCorrect: z.boolean(), + text: z.string(), + }), + ), type: z.literal("trueOrFalse"), timeLimit: timeLimitSchema, points: pointsSchema, From a34004c93e82ad43a1003b0417458c7dac00abb7 Mon Sep 17 00:00:00 2001 From: Edmel John Linaugo <68092712+EdmelKun@users.noreply.github.com> Date: Wed, 6 Dec 2023 20:07:38 +0800 Subject: [PATCH 2/2] Elinaugo137/tes 262 (#230) * fix: handle failing question generation * fix: changed default number of questions to 5 --- .../bottom-sheet/ChoiceBottomSheet.tsx | 3 ++ .../components/dropdowns/DeleteDropdown.tsx | 5 ++- .../src/components/headers/ReusableHeader.tsx | 2 +- apps/expo/src/forms/CreateTestForm/index.tsx | 2 +- .../screens/create-question/ChoiceCard.tsx | 2 +- .../src/screens/create-question/index.tsx | 32 +++++++--------- .../question-options-dropdown.tsx | 21 ++--------- .../src/screens/create-reviewer/index.tsx | 37 ++++++++----------- .../src/functions/randomQuestionsHandlers.ts | 7 +++- packages/api/src/router/gptApi.ts | 15 +++++++- 10 files changed, 61 insertions(+), 65 deletions(-) diff --git a/apps/expo/src/components/bottom-sheet/ChoiceBottomSheet.tsx b/apps/expo/src/components/bottom-sheet/ChoiceBottomSheet.tsx index 190399a..6ca4a67 100644 --- a/apps/expo/src/components/bottom-sheet/ChoiceBottomSheet.tsx +++ b/apps/expo/src/components/bottom-sheet/ChoiceBottomSheet.tsx @@ -9,6 +9,7 @@ import { Foundation } from "@expo/vector-icons"; import type { FC } from "react"; import type { QuestionType } from "../../stores/useQuestionStore"; +import useError from "../../screens/create-question/hooks"; interface Props { goToCreateQuestion: (questionType: QuestionType) => void; @@ -19,7 +20,9 @@ const ChoiceBottomSheet: FC = ({ goToCreateQuestion, closeBottomSheet, }) => { + const { resetErrors } = useError(); const handleChoicePress = (questionType: QuestionType) => () => { + resetErrors(); goToCreateQuestion(questionType); closeBottomSheet?.(); }; diff --git a/apps/expo/src/components/dropdowns/DeleteDropdown.tsx b/apps/expo/src/components/dropdowns/DeleteDropdown.tsx index 5ea737a..dcaf611 100644 --- a/apps/expo/src/components/dropdowns/DeleteDropdown.tsx +++ b/apps/expo/src/components/dropdowns/DeleteDropdown.tsx @@ -53,7 +53,10 @@ const DeleteDropdown: FC = ({ onCancel={() => { setOpenAlert(false); }} - onConfirm={onDelete} + onConfirm={() => { + onDelete(); + setOpenAlert(false); + }} /> ); diff --git a/apps/expo/src/components/headers/ReusableHeader.tsx b/apps/expo/src/components/headers/ReusableHeader.tsx index a85ed39..f35781b 100644 --- a/apps/expo/src/components/headers/ReusableHeader.tsx +++ b/apps/expo/src/components/headers/ReusableHeader.tsx @@ -17,7 +17,7 @@ interface HeaderProps extends ViewProps { } export const ReusableHeader: FC = ({ - screenName, + screenName = "", optionIcon, onIconPress, backIcon = , diff --git a/apps/expo/src/forms/CreateTestForm/index.tsx b/apps/expo/src/forms/CreateTestForm/index.tsx index 0c0d6d8..1f11eee 100644 --- a/apps/expo/src/forms/CreateTestForm/index.tsx +++ b/apps/expo/src/forms/CreateTestForm/index.tsx @@ -308,7 +308,7 @@ const CreateTestForm: FC = ({ const createMultipleQuestions = (inputMessage: string) => { const numOfQuestions = - numberOfQuestionOptions.find((option) => option.isSelected)?.value ?? 1; + numberOfQuestionOptions.find((option) => option.isSelected)?.value ?? 5; if (inputMessage.length <= 0) { setErrorInAIQuestion(true); diff --git a/apps/expo/src/screens/create-question/ChoiceCard.tsx b/apps/expo/src/screens/create-question/ChoiceCard.tsx index 6ea7585..bddc7b3 100644 --- a/apps/expo/src/screens/create-question/ChoiceCard.tsx +++ b/apps/expo/src/screens/create-question/ChoiceCard.tsx @@ -669,7 +669,7 @@ export const IdentificationCards = ({ borderRadius="full" fontStyle="bold" textColor="white" - TOwidth="[50%]" + TOwidth="[45%]" Vwidth="full" Vheight="12" onPress={addChoice} diff --git a/apps/expo/src/screens/create-question/index.tsx b/apps/expo/src/screens/create-question/index.tsx index 9d15171..e6265d1 100644 --- a/apps/expo/src/screens/create-question/index.tsx +++ b/apps/expo/src/screens/create-question/index.tsx @@ -115,7 +115,7 @@ export const CreateQuestionScreen: FC = () => { const [selectedQuestionId, setSelectedQuestionId] = useState(0); const [choices, setChoices] = useState([]); const [isDeleting, setIsDeleting] = useState(false); - const [openDeleteAlert, setOpenDeleteAlert] = useState(false); + const [isChoiceModalTouched, setIsChoiceModalTouched] = useState(false); const { height, width } = Dimensions.get("window"); @@ -194,6 +194,8 @@ export const CreateQuestionScreen: FC = () => { }; const goToCreateQuestion = (selectedQuestionType: QuestionType) => { + resetErrors(); + setIsChoiceModalTouched(false); if (isLastQuestionInEdit()) { deleteLastQuestion(); addEmptyQuestion(selectedQuestionType); @@ -357,6 +359,7 @@ export const CreateQuestionScreen: FC = () => { const handleOpenModal = (index: number) => () => { setSelectedQuestionId(index); + setIsChoiceModalTouched(true); setShowModal(true); }; @@ -452,12 +455,11 @@ export const CreateQuestionScreen: FC = () => { time: timeLimitOptions.find((option) => option.isSelected)?.value ?? 0, }; } - if (!question) return false; - resetErrors(); editQuestion(selectedIndex!, question); resetQuestionImage(); + resetErrors(); successToast({ title: "Success", message: "Question saved successfully", @@ -472,7 +474,10 @@ export const CreateQuestionScreen: FC = () => { title: "Missing field", message: "Title cannot be empty", }); - } else if (!errorState.choicesError.every((item) => item === undefined)) { + } else if ( + isChoiceModalTouched && + !errorState.choicesError.every((item) => item === undefined) + ) { errorToast({ title: "Missing field", message: "Choices cannot be empty", @@ -493,7 +498,7 @@ export const CreateQuestionScreen: FC = () => { ) { errorToast({ title: "Missing field", - message: "Please select a correct answer", + message: "Please assign a correct answer", }); } } @@ -619,7 +624,6 @@ export const CreateQuestionScreen: FC = () => { } setIsDeleting(false); - setOpenDeleteAlert(false); }; const handleGenerateQuestion = () => { @@ -667,14 +671,15 @@ export const CreateQuestionScreen: FC = () => { screenName={"Create Question"} optionIcon={ setOpenDeleteAlert(true)} isSaved={!isSaved} handleSaveAnswer={handleSaveAnswer(() => { setIsSaved(false); })} /> } + showDeleteIcon={true} + onDeletePress={handleDelete} + isDeleting={isDeleting} backIcon={} handleExit={handleExitScreen} /> @@ -1001,17 +1006,6 @@ export const CreateQuestionScreen: FC = () => { }} onConfirm={goBack} /> - - setOpenDeleteAlert(false)} - onConfirm={handleDelete} - /> ); diff --git a/apps/expo/src/screens/create-question/question-options-dropdown.tsx b/apps/expo/src/screens/create-question/question-options-dropdown.tsx index 8b2a152..dd7acc6 100644 --- a/apps/expo/src/screens/create-question/question-options-dropdown.tsx +++ b/apps/expo/src/screens/create-question/question-options-dropdown.tsx @@ -1,31 +1,16 @@ import React from "react"; -import { TouchableOpacity, View, ActivityIndicator } from "react-native"; -import { AntDesign } from "@expo/vector-icons"; +import { TouchableOpacity, View } from "react-native"; import { Entypo } from "@expo/vector-icons"; import type { FC } from "react"; interface Props { - isDeleting: boolean; - setOpenDeleteAlert: () => void; isSaved: boolean; handleSaveAnswer: () => void; } -const QuestionOptionsDropdown: FC = ({ - isDeleting, - setOpenDeleteAlert, - isSaved, - handleSaveAnswer, -}) => { +const QuestionOptionsDropdown: FC = ({ isSaved, handleSaveAnswer }) => { return ( - - - {isDeleting ? ( - - ) : ( - - )} - + diff --git a/apps/expo/src/screens/create-reviewer/index.tsx b/apps/expo/src/screens/create-reviewer/index.tsx index 5b24a8f..8287cf5 100644 --- a/apps/expo/src/screens/create-reviewer/index.tsx +++ b/apps/expo/src/screens/create-reviewer/index.tsx @@ -40,13 +40,12 @@ import { AlertModal } from "../../components/modals/AlertModal"; import ChoiceModal from "../../components/modals/ChoiceModal"; import type { Option } from "./types"; import { NUMBER_OF_QUESTIONS_OPTIONS } from "./constants"; -import useQuestionStore, { QuestionType } from "../../stores/useQuestionStore"; +import useQuestionStore from "../../stores/useQuestionStore"; import { AppButton } from "../../components/buttons/AppButton"; import * as DocumentPicker from "expo-document-picker"; import * as FileSystem from "expo-file-system"; import { extractHighlightedText, - mapQuestionType, } from "../../utils/helpers/strings"; interface CacheOptions { @@ -70,17 +69,6 @@ export const CreateReviewerScreen = ({ type: "create", }; const richText = useRef(null); - const types: QuestionType[] = [ - "multiple_choice", - "true_or_false", - "multi_select", - "identification", - ]; - const randomizeQuestionType = Math.floor(Math.random() * types.length); - const questionType = types[randomizeQuestionType]; - const mappedQuestionType = mapQuestionType( - questionType !== undefined ? questionType : "multiple_choice", - ); const [isHighlighterToggled, setIsHighlighterToggled] = useState(false); const [isChoiceModalToggled, setIsChoiceModalToggled] = useState(false); const [isPDFUploading, setIsPDFUploading] = useState(false); @@ -95,8 +83,6 @@ export const CreateReviewerScreen = ({ ); const [showNumberofQuestionsModal, setShowNumberOfQuestionsModal] = useState(false); - - const addEmptyQuestion = useQuestionStore((state) => state.addEmptyQuestion); const setLastIndex = useQuestionStore((state) => state.setLastIndex); const { addQuestions, removeBlankQuestions } = useQuestionStore(); @@ -116,7 +102,7 @@ export const CreateReviewerScreen = ({ const { mutate: readFile } = trpc.pdfTextExtraction.extractText.useMutation(); const { mutate: generateMultipleQuestions, isLoading: isGenerating } = - trpc.gptApi.generateMultipleQuestions.useMutation(); + trpc.gptApi.generateMultipleRandomQuestions.useMutation(); const { mutate: createReviewer, @@ -367,7 +353,7 @@ export const CreateReviewerScreen = ({ const createMultipleQuestions = (inputMessage: string) => { const numOfQuestions = - numberOfQuestionOptions.find((option) => option.isSelected)?.value ?? 1; + numberOfQuestionOptions.find((option) => option.isSelected)?.value ?? 5; if (inputMessage === " ") { setShowNumberOfQuestionsModal(false); @@ -379,9 +365,7 @@ export const CreateReviewerScreen = ({ generateMultipleQuestions( { message: inputMessage, - questionType: mappedQuestionType, numOfQuestions: numOfQuestions, - numOfChoicesPerQuestion: 4, }, { onSuccess: (data) => { @@ -413,11 +397,11 @@ export const CreateReviewerScreen = ({ choices: [ { text: "True", - isCorrect: question.answer, + isCorrect: question.choices[0]?.isCorrect ?? false, }, { text: "False", - isCorrect: !question.answer, + isCorrect: question.choices[1]?.isCorrect ?? false, }, ], inEdit: false, @@ -426,6 +410,16 @@ export const CreateReviewerScreen = ({ points: question.points, }; } + if (question.type === "identification") { + return { + type: "identification", + choices: question.choices, + inEdit: false, + title: question.question, + time: question.timeLimit, + points: question.points, + }; + } return { type: "multiple_choice", choices: [], @@ -435,7 +429,6 @@ export const CreateReviewerScreen = ({ }), ); removeBlankQuestions(); - addEmptyQuestion("multiple_choice"); setLastIndex(); setShowNumberOfQuestionsModal(false); successToast({ diff --git a/packages/api/src/functions/randomQuestionsHandlers.ts b/packages/api/src/functions/randomQuestionsHandlers.ts index 7e62dcc..89068f3 100644 --- a/packages/api/src/functions/randomQuestionsHandlers.ts +++ b/packages/api/src/functions/randomQuestionsHandlers.ts @@ -85,6 +85,8 @@ export const generateCombinedQuestionPrompts = ( type ]()}\n`; } + + typeCounts[type] = 0; }); return combinedPrompts.trim(); @@ -92,6 +94,7 @@ export const generateCombinedQuestionPrompts = ( export const processGeneratedQuestions = ( generatedMessage: string, + numOfQuestions: number, ): ParsedQuestion[] => { const questionBlocks = generatedMessage .split("separator") @@ -116,5 +119,7 @@ export const processGeneratedQuestions = ( } }); - return parsedQuestions; + const sliceParsedQuestions = parsedQuestions.slice(0, numOfQuestions); + + return sliceParsedQuestions; }; diff --git a/packages/api/src/router/gptApi.ts b/packages/api/src/router/gptApi.ts index b01c52e..af8f24f 100644 --- a/packages/api/src/router/gptApi.ts +++ b/packages/api/src/router/gptApi.ts @@ -131,7 +131,20 @@ export const gptApiRouter = router({ const generatedMessage = await fetchGPT(promptText); - const processedQuestions = processGeneratedQuestions(generatedMessage); + const processedQuestions = processGeneratedQuestions( + generatedMessage, + numOfQuestions, + ); + + if (processedQuestions.length < numOfQuestions) { + const regenerateMessage = await fetchGPT(promptText); + const reprocessedQuestions = processGeneratedQuestions( + regenerateMessage, + numOfQuestions, + ); + + return reprocessedQuestions; + } return processedQuestions; }),