✨ Implemented in Live-Game
This commit is contained in:
@@ -111,7 +111,7 @@ class QuizQuestion(BaseModel):
|
||||
if values["type"] == QuizQuestionType.RANGE and type(v) != RangeQuizAnswer:
|
||||
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 RangeQuizAnswer if type is RANGE")
|
||||
raise ValueError("Answer must be from type VotingQuizAnswer if type is VOTING")
|
||||
return v
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import socketio
|
||||
from cryptography.fernet import Fernet
|
||||
|
||||
from classquiz.config import redis, settings
|
||||
from classquiz.db.models import PlayGame, QuizQuestionType, GameSession, GamePlayer, QuizQuestion
|
||||
from classquiz.db.models import PlayGame, QuizQuestionType, GameSession, GamePlayer, QuizQuestion, VotingQuizAnswer
|
||||
from pydantic import BaseModel, ValidationError, validator
|
||||
from datetime import datetime
|
||||
|
||||
@@ -113,8 +113,10 @@ async def join_game(sid: str, data: dict):
|
||||
{"username": data.username, "sid": sid},
|
||||
room=redis_res.admin,
|
||||
)
|
||||
lol = fernet.encrypt(datetime.now().isoformat().encode("utf-8")).decode("utf-8")
|
||||
await sio.emit("time_sync", lol, room=sid)
|
||||
# +++ Time-Sync +++
|
||||
encrypted_datetime = fernet.encrypt(datetime.now().isoformat().encode("utf-8")).decode("utf-8")
|
||||
await sio.emit("time_sync", encrypted_datetime, room=sid)
|
||||
# --- Time-Sync ---
|
||||
sio.enter_room(sid, data.game_pin)
|
||||
|
||||
|
||||
@@ -183,14 +185,16 @@ class RangeQuizAnswerWithoutSolution(BaseModel):
|
||||
|
||||
|
||||
class ReturnQuestion(QuizQuestion):
|
||||
answers: list[ABCDQuizAnswerWithoutSolution] | RangeQuizAnswerWithoutSolution
|
||||
answers: list[ABCDQuizAnswerWithoutSolution] | RangeQuizAnswerWithoutSolution | list[VotingQuizAnswer]
|
||||
|
||||
@validator("answers")
|
||||
def answers_not_none_if_abcd_type(cls, v, values):
|
||||
if values["type"] == QuizQuestionType.ABCD and len(v) == 0:
|
||||
if values["type"] == QuizQuestionType.ABCD and type(v[0]) != ABCDQuizAnswerWithoutSolution:
|
||||
raise ValueError("Answers can't be none if type is ABCD")
|
||||
if values["type"] == QuizQuestionType.RANGE and type(v) != RangeQuizAnswerWithoutSolution:
|
||||
raise ValueError("Answer must be from type RangeQuizAnswer if type is RANGE")
|
||||
if values["type"] == QuizQuestionType.VOTING and type(v[0]) != VotingQuizAnswer:
|
||||
print("Answer must be from type VotingQuizAnswer if type is VOTING")
|
||||
return v
|
||||
|
||||
|
||||
@@ -204,13 +208,17 @@ async def set_question_number(sid, data: str):
|
||||
game_data.current_question = int(float(data))
|
||||
await redis.set(f"game:{session['game_pin']}", game_data.json())
|
||||
await redis.set(f"game:{session['game_pin']}:current_time", datetime.now().isoformat())
|
||||
# print(game_data.dict(include={"questions"})["questions"][int(float(data))])
|
||||
temp_return = game_data.dict(include={"questions"})["questions"][int(float(data))]
|
||||
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(temp_return)
|
||||
await sio.emit(
|
||||
"set_question_number",
|
||||
{
|
||||
"question_index": int(float(data)),
|
||||
"question": ReturnQuestion(
|
||||
**game_data.dict(include={"questions"})["questions"][int(float(data))]
|
||||
).dict(),
|
||||
"question": ReturnQuestion(**temp_return).dict(),
|
||||
},
|
||||
room=game_pin,
|
||||
)
|
||||
@@ -258,6 +266,8 @@ async def submit_answer(sid: str, data: dict):
|
||||
<= game_data.questions[int(float(data.question_index))].answers.max_correct
|
||||
):
|
||||
answer_right = True
|
||||
elif game_data.questions[int(float(data.question_index))].type == QuizQuestionType.VOTING:
|
||||
answer_right = False
|
||||
else:
|
||||
raise NotImplementedError
|
||||
latency = int(float((await sio.get_session(sid))["ping"]))
|
||||
@@ -317,7 +327,6 @@ async def submit_answer(sid: str, data: dict):
|
||||
async def get_final_results(sid: str, _data: dict):
|
||||
session: dict = await sio.get_session(sid)
|
||||
game_data = PlayGame(**json.loads(await redis.get(f"game:{session['game_pin']}")))
|
||||
results = {}
|
||||
if not session["admin"]:
|
||||
return
|
||||
results = await generate_final_results(game_data, session["game_pin"])
|
||||
|
||||
Generated
+26
-64
@@ -77,7 +77,7 @@ devDependencies:
|
||||
'@sentry/tracing': 7.11.1
|
||||
'@sveltejs/adapter-auto': 1.0.0-next.80
|
||||
'@sveltejs/adapter-node': 1.0.0-next.86
|
||||
'@sveltejs/kit': 1.0.0-next.504_svelte@3.49.0+vite@3.1.2
|
||||
'@sveltejs/kit': 1.0.0-next.508_svelte@3.49.0+vite@3.1.2
|
||||
'@tailwindcss/typography': 0.5.4_tailwindcss@3.1.8
|
||||
'@types/canvas-confetti': 1.4.3
|
||||
'@types/cookie': 0.5.1
|
||||
@@ -579,10 +579,10 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@sveltejs/kit/1.0.0-next.504_svelte@3.49.0+vite@3.1.2:
|
||||
/@sveltejs/kit/1.0.0-next.508_svelte@3.49.0+vite@3.1.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-KrhlSHT3aCVnhRgUoN6aGIjIw3nWEdNwfoZcE1x65F5D7Ju/K9D8dQwyal9v0aBAZyN9nFuXxsYGaMLE9sppfw==
|
||||
integrity: sha512-qUnuhuL82meE0lSwrsS/FIvtDoE83Au4SxVLCim3FzoCnJc7bFZ9vYRbRUYxAjfYKRMjKVUrp9iHFObn9LmnUg==
|
||||
}
|
||||
engines: { node: '>=16.14' }
|
||||
hasBin: true
|
||||
@@ -598,13 +598,12 @@ packages:
|
||||
kleur: 4.1.5
|
||||
magic-string: 0.26.3
|
||||
mime: 3.0.0
|
||||
node-fetch: 3.2.10
|
||||
sade: 1.8.1
|
||||
set-cookie-parser: 2.5.1
|
||||
sirv: 2.0.2
|
||||
svelte: 3.49.0
|
||||
tiny-glob: 0.2.9
|
||||
undici: 5.9.1
|
||||
undici: 5.11.0
|
||||
vite: 3.1.2_sass@1.54.4
|
||||
transitivePeerDependencies:
|
||||
- diff-match-patch
|
||||
@@ -1398,6 +1397,16 @@ packages:
|
||||
}
|
||||
dev: true
|
||||
|
||||
/busboy/1.6.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==
|
||||
}
|
||||
engines: { node: '>=10.16.0' }
|
||||
dependencies:
|
||||
streamsearch: 1.1.0
|
||||
dev: true
|
||||
|
||||
/callsites/3.1.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -1717,14 +1726,6 @@ packages:
|
||||
css-tree: 1.1.3
|
||||
dev: true
|
||||
|
||||
/data-uri-to-buffer/4.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==
|
||||
}
|
||||
engines: { node: '>= 12' }
|
||||
dev: true
|
||||
|
||||
/debug/4.3.4:
|
||||
resolution:
|
||||
{
|
||||
@@ -2503,17 +2504,6 @@ packages:
|
||||
svelte: 3.49.0
|
||||
dev: true
|
||||
|
||||
/fetch-blob/3.2.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==
|
||||
}
|
||||
engines: { node: ^12.20 || >= 14.13 }
|
||||
dependencies:
|
||||
node-domexception: 1.0.0
|
||||
web-streams-polyfill: 3.2.1
|
||||
dev: true
|
||||
|
||||
/file-entry-cache/6.0.1:
|
||||
resolution:
|
||||
{
|
||||
@@ -2570,16 +2560,6 @@ packages:
|
||||
}
|
||||
dev: true
|
||||
|
||||
/formdata-polyfill/4.0.10:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==
|
||||
}
|
||||
engines: { node: '>=12.20.0' }
|
||||
dependencies:
|
||||
fetch-blob: 3.2.0
|
||||
dev: true
|
||||
|
||||
/fraction.js/4.2.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -3358,14 +3338,6 @@ packages:
|
||||
}
|
||||
dev: true
|
||||
|
||||
/node-domexception/1.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
|
||||
}
|
||||
engines: { node: '>=10.5.0' }
|
||||
dev: true
|
||||
|
||||
/node-fetch/2.6.7:
|
||||
resolution:
|
||||
{
|
||||
@@ -3381,18 +3353,6 @@ packages:
|
||||
whatwg-url: 5.0.0
|
||||
dev: true
|
||||
|
||||
/node-fetch/3.2.10:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==
|
||||
}
|
||||
engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
|
||||
dependencies:
|
||||
data-uri-to-buffer: 4.0.0
|
||||
fetch-blob: 3.2.0
|
||||
formdata-polyfill: 4.0.10
|
||||
dev: true
|
||||
|
||||
/node-gyp-build/4.5.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -4540,6 +4500,14 @@ packages:
|
||||
deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility'
|
||||
dev: true
|
||||
|
||||
/streamsearch/1.1.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
|
||||
}
|
||||
engines: { node: '>=10.0.0' }
|
||||
dev: true
|
||||
|
||||
/string-width/4.2.3:
|
||||
resolution:
|
||||
{
|
||||
@@ -4960,12 +4928,14 @@ packages:
|
||||
}
|
||||
dev: true
|
||||
|
||||
/undici/5.9.1:
|
||||
/undici/5.11.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-6fB3a+SNnWEm4CJbgo0/CWR8RGcOCQP68SF4X0mxtYTq2VNN8T88NYrWVBAeSX+zb7bny2dx2iYhP3XHi00omg==
|
||||
integrity: sha512-oWjWJHzFet0Ow4YZBkyiJwiK5vWqEYoH7BINzJAJOLedZ++JpAlCbUktW2GQ2DS2FpKmxD/JMtWUUWl1BtghGw==
|
||||
}
|
||||
engines: { node: '>=12.18' }
|
||||
dependencies:
|
||||
busboy: 1.6.0
|
||||
dev: true
|
||||
|
||||
/update-browserslist-db/1.0.5_browserslist@4.21.3:
|
||||
@@ -5057,14 +5027,6 @@ packages:
|
||||
pbf: 3.2.1
|
||||
dev: true
|
||||
|
||||
/web-streams-polyfill/3.2.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==
|
||||
}
|
||||
engines: { node: '>= 8' }
|
||||
dev: true
|
||||
|
||||
/webidl-conversions/3.0.1:
|
||||
resolution:
|
||||
{
|
||||
|
||||
@@ -114,8 +114,8 @@
|
||||
{#if selected_question + 1 === quiz_data.questions.length && timer_res === '0' && question_results !== null}
|
||||
{#if JSON.stringify(final_results) === JSON.stringify([null])}
|
||||
<button on:click={get_final_results} class="admin-button"
|
||||
>Get final results</button
|
||||
>
|
||||
>Get final results
|
||||
</button>
|
||||
{/if}
|
||||
{:else if timer_res === '0' || selected_question === -1}
|
||||
{#if (selected_question + 1 !== quiz_data.questions.length && question_results !== null) || selected_question === -1}
|
||||
@@ -129,13 +129,13 @@
|
||||
{/if}
|
||||
{#if question_results === null && selected_question !== -1}
|
||||
<button on:click={get_question_results} class="admin-button"
|
||||
>Show results</button
|
||||
>
|
||||
>Show results
|
||||
</button>
|
||||
{/if}
|
||||
{:else if selected_question !== -1}
|
||||
<button on:click={show_solutions} class="admin-button"
|
||||
>Stop time and show solutions</button
|
||||
>
|
||||
>Stop time and show solutions
|
||||
</button>
|
||||
{:else}
|
||||
<!-- <button
|
||||
on:click={() => {
|
||||
@@ -181,20 +181,28 @@
|
||||
</div>
|
||||
{/if}
|
||||
{#if game_mode === 'kahoot'}
|
||||
{#if quiz_data.questions[selected_question].type === QuizQuestionType.ABCD}
|
||||
{#if quiz_data.questions[selected_question].type === QuizQuestionType.ABCD || quiz_data.questions[selected_question].type === QuizQuestionType.VOTING}
|
||||
<div class="grid grid-cols-2 gap-2 w-full p-4">
|
||||
{#each quiz_data.questions[selected_question].answers as answer, i}
|
||||
<div
|
||||
class="rounded-lg h-fit flex"
|
||||
style="background-color: {answer.color ?? '#B45309'}"
|
||||
class:opacity-50={!answer.right && timer_res === '0'}
|
||||
class:opacity-50={!answer.right &&
|
||||
timer_res === '0' &&
|
||||
quiz_data.questions[selected_question].type !==
|
||||
QuizQuestionType.ABCD}
|
||||
>
|
||||
<img class="w-14 inline-block pl-4" alt="icon" src={kahoot_icons[i]} />
|
||||
<span
|
||||
class="text-center text-2xl px-2 py-4 w-full text-black"
|
||||
class:text-4xl={answer.right && timer_res === '0'}
|
||||
class:underline={answer.right && timer_res === '0'}
|
||||
>{answer.answer}</span
|
||||
class:text-4xl={answer.right &&
|
||||
timer_res === '0' &&
|
||||
quiz_data.questions[selected_question].type !==
|
||||
QuizQuestionType.ABCD}
|
||||
class:underline={answer.right &&
|
||||
timer_res === '0' &&
|
||||
quiz_data.questions[selected_question].type !==
|
||||
QuizQuestionType.ABCD}>{answer.answer}</span
|
||||
>
|
||||
<span class="pl-4 w-10" />
|
||||
</div>
|
||||
@@ -221,6 +229,16 @@
|
||||
<h1 class="text-3xl">{$t('admin_page.no_answers')}</h1>
|
||||
</div>
|
||||
{/if}
|
||||
{:else if quiz_data.questions[selected_question].type === QuizQuestionType.VOTING}
|
||||
{#await import('$lib/play/admin/voting_results.svelte')}
|
||||
<Spinner />
|
||||
{:then c}
|
||||
<svelte:component
|
||||
this={c.default}
|
||||
bind:data={question_results}
|
||||
bind:question={quiz_data.questions[selected_question]}
|
||||
/>
|
||||
{/await}
|
||||
{:else}
|
||||
{#await import('$lib/play/admin/results.svelte')}
|
||||
<Spinner />
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import { reach } from 'yup';
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
import { VotingQuestionSchema } from '$lib/yupSchemas';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
const { t } = getLocalization();
|
||||
|
||||
@@ -19,9 +20,19 @@
|
||||
|
||||
export let selected_question: number;
|
||||
export let data: EditorData;
|
||||
if (!Array.isArray(data.questions[selected_question].answers)) {
|
||||
data.questions[selected_question].answers = [];
|
||||
}
|
||||
try {
|
||||
if (typeof data.questions[selected_question].answers[0].right === 'boolean') {
|
||||
data.questions[selected_question].answers = [];
|
||||
}
|
||||
} catch {}
|
||||
|
||||
/*console.log(data.questions[selected_question].answers, 'moIn!', data.questions[selected_question].answers.length);
|
||||
onMount(() => {
|
||||
for (let i = 0; i < data.questions[selected_question].answers; i++) {
|
||||
console.log(data.questions[selected_question].answers[i], 'iterate');
|
||||
data.questions[selected_question].answers[i].right = undefined;
|
||||
}
|
||||
});*/
|
||||
</script>
|
||||
|
||||
<!--
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<!--
|
||||
- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Question } from '$lib/quiz_types';
|
||||
|
||||
export let data;
|
||||
export let question: Question;
|
||||
|
||||
let quiz_answers = [];
|
||||
|
||||
for (const i of question.answers) {
|
||||
quiz_answers.push(i.answer);
|
||||
}
|
||||
|
||||
console.log('mounted', question);
|
||||
let sorted_data = {};
|
||||
for (const i of quiz_answers) {
|
||||
sorted_data[i] = 0;
|
||||
}
|
||||
for (const i of data) {
|
||||
sorted_data[i.answer] += 1;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex justify-center h-full w-full">
|
||||
<div
|
||||
class="m-auto grid grid-rows-3 w-fit gap-4"
|
||||
style="grid-template-columns: repeat({quiz_answers.length}, minmax(0, 1fr));"
|
||||
>
|
||||
{#each quiz_answers as answer}
|
||||
<span class="text-center self-end"
|
||||
>{#if sorted_data[answer] > 0}{sorted_data[answer]}{/if}</span
|
||||
>
|
||||
{/each}
|
||||
{#each quiz_answers as answer}
|
||||
<div
|
||||
class="w-20 bg-black self-end flex justify-center"
|
||||
style="height: {(sorted_data[answer] * 20) / data.length}rem"
|
||||
/>
|
||||
{/each}
|
||||
{#each quiz_answers as answer}
|
||||
<div class="w-20">
|
||||
<p class="-rotate-45">{answer}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
@@ -113,7 +113,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
{#if timer_res !== '0'}
|
||||
{#if question.type === QuizQuestionType.ABCD}
|
||||
{#if question.type === QuizQuestionType.ABCD || QuizQuestionType.VOTING}
|
||||
{#if game_mode === 'normal'}
|
||||
<div class="grid grid-cols-2 gap-2 w-full p-4">
|
||||
{#each question.answers as answer}
|
||||
@@ -202,4 +202,11 @@
|
||||
{/if}
|
||||
</p>
|
||||
{/if}
|
||||
<!--{:else if question.type === QuizQuestionType.VOTING}
|
||||
{#await import('$lib/play/admin/voting_results.svelte')}
|
||||
<Spinner />
|
||||
{:then c}
|
||||
<svelte:component this={c.default} bind:data={question_results}
|
||||
bind:question={quiz_data.questions[selected_question]} />
|
||||
{/await}-->
|
||||
{/if}
|
||||
|
||||
@@ -91,48 +91,64 @@
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{:else if timer_res === '0'}
|
||||
{#await import('svelte-range-slider-pips')}
|
||||
<Spinner />
|
||||
{:then c}
|
||||
<div class="grayscale pointer-events-none w-full">
|
||||
<svelte:component
|
||||
this={c.default}
|
||||
bind:values={slider_values}
|
||||
bind:min={question.answers.min}
|
||||
bind:max={question.answers.max}
|
||||
pips
|
||||
float
|
||||
all="label"
|
||||
/>
|
||||
</div>
|
||||
{/await}
|
||||
{:else}
|
||||
{#await import('svelte-range-slider-pips')}
|
||||
<Spinner />
|
||||
{:then c}
|
||||
<div class:pointer-events-none={selected_answer !== undefined}>
|
||||
<svelte:component
|
||||
this={c.default}
|
||||
bind:values={slider_value}
|
||||
bind:min={question.answers.min}
|
||||
bind:max={question.answers.max}
|
||||
pips
|
||||
float
|
||||
all="label"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<button
|
||||
class="w-1/2 text-3xl bg-amber-700 my-2 disabled:opacity-60 border border-white"
|
||||
disabled={selected_answer !== undefined}
|
||||
on:click={() => {
|
||||
selected_answer = slider_value[0];
|
||||
timer_res = '0';
|
||||
}}
|
||||
>{$t('words.submit')}
|
||||
</button>
|
||||
</div>
|
||||
{/await}
|
||||
{:else if question.type === QuizQuestionType.RANGE}
|
||||
{#if timer_res === '0'}
|
||||
{#await import('svelte-range-slider-pips')}
|
||||
<Spinner />
|
||||
{:then c}
|
||||
<div class="grayscale pointer-events-none w-full">
|
||||
<svelte:component
|
||||
this={c.default}
|
||||
bind:values={slider_values}
|
||||
bind:min={question.answers.min}
|
||||
bind:max={question.answers.max}
|
||||
pips
|
||||
float
|
||||
all="label"
|
||||
/>
|
||||
</div>
|
||||
{/await}
|
||||
{:else}
|
||||
{#await import('svelte-range-slider-pips')}
|
||||
<Spinner />
|
||||
{:then c}
|
||||
<div class:pointer-events-none={selected_answer !== undefined}>
|
||||
<svelte:component
|
||||
this={c.default}
|
||||
bind:values={slider_value}
|
||||
bind:min={question.answers.min}
|
||||
bind:max={question.answers.max}
|
||||
pips
|
||||
float
|
||||
all="label"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<button
|
||||
class="w-1/2 text-3xl bg-amber-700 my-2 disabled:opacity-60 border border-white"
|
||||
disabled={selected_answer !== undefined}
|
||||
on:click={() => {
|
||||
selected_answer = slider_value[0];
|
||||
timer_res = '0';
|
||||
}}
|
||||
>{$t('words.submit')}
|
||||
</button>
|
||||
</div>
|
||||
{/await}
|
||||
{/if}
|
||||
{:else if question.type === QuizQuestionType.VOTING}
|
||||
{#each question.answers as answer, i}
|
||||
<button
|
||||
disabled={selected_answer !== undefined || timer_res === '0'}
|
||||
class="p-2 rounded-lg flex justify-center w-full transition bg-amber-300 my-5 disabled:grayscale text-black"
|
||||
on:click={() => {
|
||||
selected_answer = i;
|
||||
timer_res = '0';
|
||||
}}>{answer.answer}</button
|
||||
>
|
||||
{/each}
|
||||
{#if timer_res === '0'}
|
||||
<p>No correct answers, since this is a poll-question</p>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -63,9 +63,12 @@ export const dataSchema = yup.object({
|
||||
.lowercase(),
|
||||
answers: yup.lazy((v) => {
|
||||
if (Array.isArray(v)) {
|
||||
console.log(v);
|
||||
if (typeof v[0].right === 'boolean') {
|
||||
console.log('Yup, ABCD');
|
||||
return ABCDQuestionSchema;
|
||||
} else {
|
||||
console.log('Yup, Poll');
|
||||
return VotingQuestionSchema;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
import { navbarVisible } from '$lib/stores';
|
||||
import { QuizQuestionType } from '$lib/quiz_types';
|
||||
import type { Question } from '$lib/quiz_types';
|
||||
|
||||
navbarVisible.set(false);
|
||||
|
||||
@@ -21,18 +22,6 @@
|
||||
questions: Question[];
|
||||
}
|
||||
|
||||
interface Question {
|
||||
question: string;
|
||||
time: string;
|
||||
type?: QuizQuestionType;
|
||||
answers: Answer[];
|
||||
}
|
||||
|
||||
interface Answer {
|
||||
right: boolean;
|
||||
answer: string;
|
||||
}
|
||||
|
||||
let responseData = {
|
||||
open: false
|
||||
};
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
<!--
|
||||
- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { navbarVisible } from '$lib/stores';
|
||||
|
||||
navbarVisible.set(false);
|
||||
const data = [
|
||||
{
|
||||
username: 'Mawoka',
|
||||
answer: 'A very long answe-thing',
|
||||
right: false,
|
||||
time_taken: 1681.631,
|
||||
score: 0
|
||||
},
|
||||
{
|
||||
username: 'Mawoka',
|
||||
answer: 'A very long answe-thing',
|
||||
right: false,
|
||||
time_taken: 1681.631,
|
||||
score: 0
|
||||
},
|
||||
{
|
||||
username: 'Mawoka',
|
||||
answer: 'A very long answe-thing',
|
||||
right: false,
|
||||
time_taken: 1681.631,
|
||||
score: 0
|
||||
},
|
||||
{
|
||||
username: 'Mawoka',
|
||||
answer: 'A very long answe-thing',
|
||||
right: false,
|
||||
time_taken: 1681.631,
|
||||
score: 0
|
||||
},
|
||||
{
|
||||
username: 'Mawoka',
|
||||
answer: 'A very long answe-thing',
|
||||
right: false,
|
||||
time_taken: 1681.631,
|
||||
score: 0
|
||||
},
|
||||
{
|
||||
username: 'Mawoka',
|
||||
answer: 'A very long answe-thing',
|
||||
right: false,
|
||||
time_taken: 1681.631,
|
||||
score: 0
|
||||
},
|
||||
{
|
||||
username: 'Mawoka',
|
||||
answer: 'A very long answe-thing',
|
||||
right: false,
|
||||
time_taken: 1681.631,
|
||||
score: 0
|
||||
},
|
||||
{
|
||||
username: 'Mawoka',
|
||||
answer: 'A very long answe-thing',
|
||||
right: false,
|
||||
time_taken: 1681.631,
|
||||
score: 0
|
||||
},
|
||||
{
|
||||
username: 'Mawoka',
|
||||
answer: 'A very long answe-thing',
|
||||
right: false,
|
||||
time_taken: 1681.631,
|
||||
score: 0
|
||||
},
|
||||
|
||||
{
|
||||
username: 'Mawoka',
|
||||
answer: 'A very long answe-thing',
|
||||
right: false,
|
||||
time_taken: 1681.631,
|
||||
score: 0
|
||||
},
|
||||
{
|
||||
username: 'dewads',
|
||||
answer: 'A very long answe-thing',
|
||||
right: false,
|
||||
time_taken: 1681.631,
|
||||
score: 0
|
||||
},
|
||||
{
|
||||
username: 'dsadsa',
|
||||
answer: 'der',
|
||||
right: false,
|
||||
time_taken: 2613.532,
|
||||
score: 0
|
||||
}
|
||||
];
|
||||
const quiz_answers = ['A very long answe-thing', 'dieter', 'moin', 'der'];
|
||||
let sorted_data = {};
|
||||
for (const i of quiz_answers) {
|
||||
sorted_data[i] = 0;
|
||||
}
|
||||
for (const i of data) {
|
||||
sorted_data[i.answer] += 1;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex justify-center h-screen w-full">
|
||||
<div
|
||||
class="m-auto grid grid-rows-3 w-fit gap-4"
|
||||
style="grid-template-columns: repeat({quiz_answers.length}, minmax(0, 1fr));"
|
||||
>
|
||||
{#each quiz_answers as answer}
|
||||
<span class="text-center self-end"
|
||||
>{#if sorted_data[answer] > 0}{sorted_data[answer]}{/if}</span
|
||||
>
|
||||
{/each}
|
||||
{#each quiz_answers as answer}
|
||||
<div
|
||||
class="w-20 bg-black self-end flex justify-center"
|
||||
style="height: {(sorted_data[answer] * 20) / data.length}rem"
|
||||
/>
|
||||
{/each}
|
||||
{#each quiz_answers as answer}
|
||||
<div class="w-20">
|
||||
<p class="-rotate-45">{answer}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user