diff --git a/classquiz/db/models.py b/classquiz/db/models.py index f977539..1955e14 100644 --- a/classquiz/db/models.py +++ b/classquiz/db/models.py @@ -115,21 +115,19 @@ class QuizQuestionType(str, Enum): RANGE = "RANGE" VOTING = "VOTING" SLIDE = "SLIDE" - - -class SlideElementTypes(str, Enum): TEXT = "TEXT" - HEADLINE = "HEADLINE" - IMAGE = "IMAGE" - RECTANGLE = "RECTANGLE" - CIRCLE = "CIRCLE" + + +class TextQuizAnswer(BaseModel): + answer: str + case_sensitive: bool class QuizQuestion(BaseModel): question: str time: str # in Secs type: None | QuizQuestionType = QuizQuestionType.ABCD - answers: list[ABCDQuizAnswer] | RangeQuizAnswer | list[VotingQuizAnswer] | str + answers: list[ABCDQuizAnswer] | RangeQuizAnswer | list[TextQuizAnswer] | list[VotingQuizAnswer] | str image: str | None = None @validator("answers") @@ -140,6 +138,8 @@ class QuizQuestion(BaseModel): raise ValueError("Answer must be from type RangeQuizAnswer if type is RANGE") if values["type"] == QuizQuestionType.VOTING and type(v[0]) != VotingQuizAnswer: raise ValueError("Answer must be from type VotingQuizAnswer if type is VOTING") + if values["type"] == QuizQuestionType.TEXT and type(v[0]) != TextQuizAnswer: + raise ValueError("Answer must be from type TextQuizAnswer if type is TEXT") if values["type"] == QuizQuestionType.SLIDE and type(v[0]) != str: raise ValueError("Answer must be from type SlideElement if type is SLIDE") return v diff --git a/classquiz/socket_server/__init__.py b/classquiz/socket_server/__init__.py index 4c7dfda..6e9a9c4 100644 --- a/classquiz/socket_server/__init__.py +++ b/classquiz/socket_server/__init__.py @@ -209,6 +209,7 @@ class RangeQuizAnswerWithoutSolution(BaseModel): class ReturnQuestion(QuizQuestion): answers: list[ABCDQuizAnswerWithoutSolution] | RangeQuizAnswerWithoutSolution | list[VotingQuizAnswer] + type: QuizQuestionType = QuizQuestionType.ABCD @validator("answers") def answers_not_none_if_abcd_type(cls, v, values): @@ -226,7 +227,7 @@ class ReturnQuestion(QuizQuestion): async def set_question_number(sid, data: str): # data is just a number (as a str) of the question session = await sio.get_session(sid) - print("set_question_number", data, session) + # print("set_question_number", data, session) if session["admin"]: game_pin = session["game_pin"] game_data = PlayGame.parse_raw(await redis.get(f"game:{session['game_pin']}")) @@ -246,7 +247,8 @@ async def set_question_number(sid, data: str): if game_data.questions[int(float(data))].type == QuizQuestionType.VOTING: for i in range(len(temp_return["answers"])): temp_return["answers"][i] = VotingQuizAnswer(**temp_return["answers"][i]) - print("emitting") + temp_return["type"] = game_data.questions[int(float(data))].type + # print("emitting") await sio.emit( "set_question_number", { @@ -301,6 +303,17 @@ async def submit_answer(sid: str, data: dict): answer_right = True elif game_data.questions[int(float(data.question_index))].type == QuizQuestionType.VOTING: answer_right = False + elif game_data.questions[int(float(data.question_index))].type == QuizQuestionType.TEXT: + answer_right = False + for q in game_data.questions[int(float(data.question_index))].answers: + if q.case_sensitive: + if data.answer == q.answer: + answer_right = True + break + else: + if data.answer.lower() == q.answer.lower(): + answer_right = True + break else: raise NotImplementedError latency = int(float((await sio.get_session(sid))["ping"])) diff --git a/frontend/src/lib/admin.svelte b/frontend/src/lib/admin.svelte index 2904734..6511a71 100644 --- a/frontend/src/lib/admin.svelte +++ b/frontend/src/lib/admin.svelte @@ -248,6 +248,21 @@ {/each} + {:else if quiz_data.questions[selected_question].type === QuizQuestionType.TEXT} + {#if timer_res === '0'} +
+ {#each quiz_data.questions[selected_question].answers as answer, i} +
+ {answer.answer} + +
+ {/each} +
+ {:else} +

Enter your answer into the input field!

+ {/if} {/if} {/if} {/if} diff --git a/frontend/src/lib/editor/TextEditorPart.svelte b/frontend/src/lib/editor/TextEditorPart.svelte new file mode 100644 index 0000000..389ad91 --- /dev/null +++ b/frontend/src/lib/editor/TextEditorPart.svelte @@ -0,0 +1,136 @@ + + + +
+ {#if Array.isArray(data.questions[selected_question].answers)} + {#each data.questions[selected_question].answers as answer, index} +
+ + + +
+ {/each} + {/if} + {#if data.questions[selected_question].answers.length < 4} + + {/if} +
diff --git a/frontend/src/lib/editor/card.svelte b/frontend/src/lib/editor/card.svelte index 355b000..6b4293c 100644 --- a/frontend/src/lib/editor/card.svelte +++ b/frontend/src/lib/editor/card.svelte @@ -182,6 +182,7 @@ +
@@ -199,6 +200,12 @@ {:then c} {/await} + {:else if data.questions[selected_question].type === QuizQuestionType.TEXT} + {#await import('$lib/editor/TextEditorPart.svelte')} + + {:then c} + + {/await} {/if}
diff --git a/frontend/src/lib/play/question.svelte b/frontend/src/lib/play/question.svelte index 1f24217..ae715f0 100644 --- a/frontend/src/lib/play/question.svelte +++ b/frontend/src/lib/play/question.svelte @@ -72,6 +72,8 @@ }); }; + let text_input = ''; + let slider_value = [0]; if (question.type === QuizQuestionType.RANGE) { slider_value[0] = (question.answers.max - question.answers.min) / 2 + question.answers.min; @@ -187,6 +189,21 @@ {/await} + {:else if question.type === QuizQuestionType.TEXT} + + +
+ +
{/if} {:else if question.type === QuizQuestionType.ABCD} {#if solution === undefined} diff --git a/frontend/src/lib/practice/question.svelte b/frontend/src/lib/practice/question.svelte index 37118e1..9b22577 100644 --- a/frontend/src/lib/practice/question.svelte +++ b/frontend/src/lib/practice/question.svelte @@ -39,6 +39,7 @@ } let slider_values = [question.answers.min_correct ?? 0, question.answers.max_correct ?? 0]; + let text_input; timer(question.time); @@ -125,7 +126,7 @@
{/await} + {:else if question.type === QuizQuestionType.TEXT} + {#if timer_res === '0'} + {#each question.answers as answer, i} +
+ {answer.answer} +
+ {/each} + {:else} +
+ +
+
+ +
+ {/if} {/if} diff --git a/frontend/src/lib/quiz_types.ts b/frontend/src/lib/quiz_types.ts index 45d623e..91a6f0f 100644 --- a/frontend/src/lib/quiz_types.ts +++ b/frontend/src/lib/quiz_types.ts @@ -29,7 +29,8 @@ export enum QuizQuestionType { ABCD = 'ABCD', // eslint-disable-line no-unused-vars RANGE = 'RANGE', // eslint-disable-line no-unused-vars VOTING = 'VOTING', // eslint-disable-line no-unused-vars - SLIDE = 'SLIDE' // eslint-disable-line no-unused-vars + SLIDE = 'SLIDE', // eslint-disable-line no-unused-vars + TEXT = 'TEXT' // eslint-disable-line no-unused-vars } export interface RangeQuizAnswer { @@ -39,12 +40,17 @@ export interface RangeQuizAnswer { max_correct: number; } +export interface TextQuizAnswer { + answer: string; + case_sensitive: boolean; +} + export interface Question { time: string; question: string; type?: QuizQuestionType; image?: string; - answers: Answer[] | RangeQuizAnswer | VotingAnswer[] | string; + answers: Answer[] | RangeQuizAnswer | VotingAnswer[] | string | TextQuizAnswer[]; } export interface Answer { diff --git a/frontend/src/lib/yupSchemas.ts b/frontend/src/lib/yupSchemas.ts index 3c0eddf..d904873 100644 --- a/frontend/src/lib/yupSchemas.ts +++ b/frontend/src/lib/yupSchemas.ts @@ -35,17 +35,17 @@ export const RangeQuestionSchema = yup.object({ max_correct: yup.number() }); -export const SlideQuestionSchema = yup.array().of( - yup.object({ - type: yup.string().required(), - x: yup.number(), - y: yup.number(), - height: yup.number(), - width: yup.number(), - data: yup.string().optional(), - id: yup.number().required() - }) -); +export const TextQuestionSchema = yup + .array() + .of( + yup.object({ + case_sensitive: yup.boolean().required(), + answer: yup.string().required('You need an answer') + }) + ) + .min(2, 'You need at least 2 answers') + .max(16, "You can't have more than 16 answers"); + export const dataSchema = yup.object({ public: yup.boolean().required(), type: yup.string(), @@ -75,20 +75,15 @@ export const dataSchema = yup.object({ .lowercase(), answers: yup.lazy((v) => { if (Array.isArray(v)) { - try { - if (typeof v[0].right === 'boolean') { - console.log('ABCDQuestionSchema'); - return ABCDQuestionSchema; - } else if (v[0].answer !== undefined) { - console.log('VotingQuestionSchema'); - return VotingQuestionSchema; - } else { - console.log('SlideQuestionSchema'); - return yup.string().required().nullable(); - } - } catch { - console.log('SlideQuestionSchema'); - return yup.string().required("The slide mustn't be empty").nullable(); + if (typeof v[0].right === 'boolean') { + console.log('ABCDQuestionSchema'); + return ABCDQuestionSchema; + } else if (typeof v[0].case_sensitive === 'boolean') { + console.log('TextQuestionSchema'); + return TextQuestionSchema; + } else if (v[0].answer !== undefined) { + console.log('VotingQuestionSchema'); + return VotingQuestionSchema; } } else if (typeof v === 'string' || v instanceof String) { return yup.string().required("The slide mustn't be empty").nullable(); diff --git a/frontend/src/routes/view/[quiz_id]/+page.svelte b/frontend/src/routes/view/[quiz_id]/+page.svelte index b91c208..ebb0666 100644 --- a/frontend/src/routes/view/[quiz_id]/+page.svelte +++ b/frontend/src/routes/view/[quiz_id]/+page.svelte @@ -218,7 +218,7 @@ and {question.answers.max_correct} are correct, where numbers between {question .answers.min} and {question.answers.max} can be selected.

- {:else if question.type === QuizQuestionType.VOTING} + {:else if question.type === QuizQuestionType.VOTING || question.type === QuizQuestionType.TEXT}
{#each question.answers as answer, index_answer}