✨ 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:
|
if values["type"] == QuizQuestionType.RANGE and type(v) != RangeQuizAnswer:
|
||||||
raise ValueError("Answer must be from type RangeQuizAnswer if type is RANGE")
|
raise ValueError("Answer must be from type RangeQuizAnswer if type is RANGE")
|
||||||
if values["type"] == QuizQuestionType.VOTING and type(v[0]) != VotingQuizAnswer:
|
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
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import socketio
|
|||||||
from cryptography.fernet import Fernet
|
from cryptography.fernet import Fernet
|
||||||
|
|
||||||
from classquiz.config import redis, settings
|
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 pydantic import BaseModel, ValidationError, validator
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
@@ -113,8 +113,10 @@ async def join_game(sid: str, data: dict):
|
|||||||
{"username": data.username, "sid": sid},
|
{"username": data.username, "sid": sid},
|
||||||
room=redis_res.admin,
|
room=redis_res.admin,
|
||||||
)
|
)
|
||||||
lol = fernet.encrypt(datetime.now().isoformat().encode("utf-8")).decode("utf-8")
|
# +++ Time-Sync +++
|
||||||
await sio.emit("time_sync", lol, room=sid)
|
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)
|
sio.enter_room(sid, data.game_pin)
|
||||||
|
|
||||||
|
|
||||||
@@ -183,14 +185,16 @@ class RangeQuizAnswerWithoutSolution(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class ReturnQuestion(QuizQuestion):
|
class ReturnQuestion(QuizQuestion):
|
||||||
answers: list[ABCDQuizAnswerWithoutSolution] | RangeQuizAnswerWithoutSolution
|
answers: list[ABCDQuizAnswerWithoutSolution] | RangeQuizAnswerWithoutSolution | list[VotingQuizAnswer]
|
||||||
|
|
||||||
@validator("answers")
|
@validator("answers")
|
||||||
def answers_not_none_if_abcd_type(cls, v, values):
|
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")
|
raise ValueError("Answers can't be none if type is ABCD")
|
||||||
if values["type"] == QuizQuestionType.RANGE and type(v) != RangeQuizAnswerWithoutSolution:
|
if values["type"] == QuizQuestionType.RANGE and type(v) != RangeQuizAnswerWithoutSolution:
|
||||||
raise ValueError("Answer must be from type RangeQuizAnswer if type is RANGE")
|
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
|
return v
|
||||||
|
|
||||||
|
|
||||||
@@ -204,13 +208,17 @@ async def set_question_number(sid, data: str):
|
|||||||
game_data.current_question = int(float(data))
|
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']}", game_data.json())
|
||||||
await redis.set(f"game:{session['game_pin']}:current_time", datetime.now().isoformat())
|
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(
|
await sio.emit(
|
||||||
"set_question_number",
|
"set_question_number",
|
||||||
{
|
{
|
||||||
"question_index": int(float(data)),
|
"question_index": int(float(data)),
|
||||||
"question": ReturnQuestion(
|
"question": ReturnQuestion(**temp_return).dict(),
|
||||||
**game_data.dict(include={"questions"})["questions"][int(float(data))]
|
|
||||||
).dict(),
|
|
||||||
},
|
},
|
||||||
room=game_pin,
|
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
|
<= game_data.questions[int(float(data.question_index))].answers.max_correct
|
||||||
):
|
):
|
||||||
answer_right = True
|
answer_right = True
|
||||||
|
elif game_data.questions[int(float(data.question_index))].type == QuizQuestionType.VOTING:
|
||||||
|
answer_right = False
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
latency = int(float((await sio.get_session(sid))["ping"]))
|
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):
|
async def get_final_results(sid: str, _data: dict):
|
||||||
session: dict = await sio.get_session(sid)
|
session: dict = await sio.get_session(sid)
|
||||||
game_data = PlayGame(**json.loads(await redis.get(f"game:{session['game_pin']}")))
|
game_data = PlayGame(**json.loads(await redis.get(f"game:{session['game_pin']}")))
|
||||||
results = {}
|
|
||||||
if not session["admin"]:
|
if not session["admin"]:
|
||||||
return
|
return
|
||||||
results = await generate_final_results(game_data, session["game_pin"])
|
results = await generate_final_results(game_data, session["game_pin"])
|
||||||
|
|||||||
Generated
+26
-64
@@ -77,7 +77,7 @@ devDependencies:
|
|||||||
'@sentry/tracing': 7.11.1
|
'@sentry/tracing': 7.11.1
|
||||||
'@sveltejs/adapter-auto': 1.0.0-next.80
|
'@sveltejs/adapter-auto': 1.0.0-next.80
|
||||||
'@sveltejs/adapter-node': 1.0.0-next.86
|
'@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
|
'@tailwindcss/typography': 0.5.4_tailwindcss@3.1.8
|
||||||
'@types/canvas-confetti': 1.4.3
|
'@types/canvas-confetti': 1.4.3
|
||||||
'@types/cookie': 0.5.1
|
'@types/cookie': 0.5.1
|
||||||
@@ -579,10 +579,10 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
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:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-KrhlSHT3aCVnhRgUoN6aGIjIw3nWEdNwfoZcE1x65F5D7Ju/K9D8dQwyal9v0aBAZyN9nFuXxsYGaMLE9sppfw==
|
integrity: sha512-qUnuhuL82meE0lSwrsS/FIvtDoE83Au4SxVLCim3FzoCnJc7bFZ9vYRbRUYxAjfYKRMjKVUrp9iHFObn9LmnUg==
|
||||||
}
|
}
|
||||||
engines: { node: '>=16.14' }
|
engines: { node: '>=16.14' }
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -598,13 +598,12 @@ packages:
|
|||||||
kleur: 4.1.5
|
kleur: 4.1.5
|
||||||
magic-string: 0.26.3
|
magic-string: 0.26.3
|
||||||
mime: 3.0.0
|
mime: 3.0.0
|
||||||
node-fetch: 3.2.10
|
|
||||||
sade: 1.8.1
|
sade: 1.8.1
|
||||||
set-cookie-parser: 2.5.1
|
set-cookie-parser: 2.5.1
|
||||||
sirv: 2.0.2
|
sirv: 2.0.2
|
||||||
svelte: 3.49.0
|
svelte: 3.49.0
|
||||||
tiny-glob: 0.2.9
|
tiny-glob: 0.2.9
|
||||||
undici: 5.9.1
|
undici: 5.11.0
|
||||||
vite: 3.1.2_sass@1.54.4
|
vite: 3.1.2_sass@1.54.4
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- diff-match-patch
|
- diff-match-patch
|
||||||
@@ -1398,6 +1397,16 @@ packages:
|
|||||||
}
|
}
|
||||||
dev: true
|
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:
|
/callsites/3.1.0:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
@@ -1717,14 +1726,6 @@ packages:
|
|||||||
css-tree: 1.1.3
|
css-tree: 1.1.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/data-uri-to-buffer/4.0.0:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==
|
|
||||||
}
|
|
||||||
engines: { node: '>= 12' }
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/debug/4.3.4:
|
/debug/4.3.4:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
@@ -2503,17 +2504,6 @@ packages:
|
|||||||
svelte: 3.49.0
|
svelte: 3.49.0
|
||||||
dev: true
|
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:
|
/file-entry-cache/6.0.1:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
@@ -2570,16 +2560,6 @@ packages:
|
|||||||
}
|
}
|
||||||
dev: true
|
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:
|
/fraction.js/4.2.0:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
@@ -3358,14 +3338,6 @@ packages:
|
|||||||
}
|
}
|
||||||
dev: true
|
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:
|
/node-fetch/2.6.7:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
@@ -3381,18 +3353,6 @@ packages:
|
|||||||
whatwg-url: 5.0.0
|
whatwg-url: 5.0.0
|
||||||
dev: true
|
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:
|
/node-gyp-build/4.5.0:
|
||||||
resolution:
|
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'
|
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
|
dev: true
|
||||||
|
|
||||||
|
/streamsearch/1.1.0:
|
||||||
|
resolution:
|
||||||
|
{
|
||||||
|
integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
|
||||||
|
}
|
||||||
|
engines: { node: '>=10.0.0' }
|
||||||
|
dev: true
|
||||||
|
|
||||||
/string-width/4.2.3:
|
/string-width/4.2.3:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
@@ -4960,12 +4928,14 @@ packages:
|
|||||||
}
|
}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/undici/5.9.1:
|
/undici/5.11.0:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-6fB3a+SNnWEm4CJbgo0/CWR8RGcOCQP68SF4X0mxtYTq2VNN8T88NYrWVBAeSX+zb7bny2dx2iYhP3XHi00omg==
|
integrity: sha512-oWjWJHzFet0Ow4YZBkyiJwiK5vWqEYoH7BINzJAJOLedZ++JpAlCbUktW2GQ2DS2FpKmxD/JMtWUUWl1BtghGw==
|
||||||
}
|
}
|
||||||
engines: { node: '>=12.18' }
|
engines: { node: '>=12.18' }
|
||||||
|
dependencies:
|
||||||
|
busboy: 1.6.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/update-browserslist-db/1.0.5_browserslist@4.21.3:
|
/update-browserslist-db/1.0.5_browserslist@4.21.3:
|
||||||
@@ -5057,14 +5027,6 @@ packages:
|
|||||||
pbf: 3.2.1
|
pbf: 3.2.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/web-streams-polyfill/3.2.1:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==
|
|
||||||
}
|
|
||||||
engines: { node: '>= 8' }
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/webidl-conversions/3.0.1:
|
/webidl-conversions/3.0.1:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -114,8 +114,8 @@
|
|||||||
{#if selected_question + 1 === quiz_data.questions.length && timer_res === '0' && question_results !== null}
|
{#if selected_question + 1 === quiz_data.questions.length && timer_res === '0' && question_results !== null}
|
||||||
{#if JSON.stringify(final_results) === JSON.stringify([null])}
|
{#if JSON.stringify(final_results) === JSON.stringify([null])}
|
||||||
<button on:click={get_final_results} class="admin-button"
|
<button on:click={get_final_results} class="admin-button"
|
||||||
>Get final results</button
|
>Get final results
|
||||||
>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
{:else if timer_res === '0' || selected_question === -1}
|
{:else if timer_res === '0' || selected_question === -1}
|
||||||
{#if (selected_question + 1 !== quiz_data.questions.length && question_results !== null) || selected_question === -1}
|
{#if (selected_question + 1 !== quiz_data.questions.length && question_results !== null) || selected_question === -1}
|
||||||
@@ -129,13 +129,13 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{#if question_results === null && selected_question !== -1}
|
{#if question_results === null && selected_question !== -1}
|
||||||
<button on:click={get_question_results} class="admin-button"
|
<button on:click={get_question_results} class="admin-button"
|
||||||
>Show results</button
|
>Show results
|
||||||
>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
{:else if selected_question !== -1}
|
{:else if selected_question !== -1}
|
||||||
<button on:click={show_solutions} class="admin-button"
|
<button on:click={show_solutions} class="admin-button"
|
||||||
>Stop time and show solutions</button
|
>Stop time and show solutions
|
||||||
>
|
</button>
|
||||||
{:else}
|
{:else}
|
||||||
<!-- <button
|
<!-- <button
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
@@ -181,20 +181,28 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if game_mode === 'kahoot'}
|
{#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">
|
<div class="grid grid-cols-2 gap-2 w-full p-4">
|
||||||
{#each quiz_data.questions[selected_question].answers as answer, i}
|
{#each quiz_data.questions[selected_question].answers as answer, i}
|
||||||
<div
|
<div
|
||||||
class="rounded-lg h-fit flex"
|
class="rounded-lg h-fit flex"
|
||||||
style="background-color: {answer.color ?? '#B45309'}"
|
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]} />
|
<img class="w-14 inline-block pl-4" alt="icon" src={kahoot_icons[i]} />
|
||||||
<span
|
<span
|
||||||
class="text-center text-2xl px-2 py-4 w-full text-black"
|
class="text-center text-2xl px-2 py-4 w-full text-black"
|
||||||
class:text-4xl={answer.right && timer_res === '0'}
|
class:text-4xl={answer.right &&
|
||||||
class:underline={answer.right && timer_res === '0'}
|
timer_res === '0' &&
|
||||||
>{answer.answer}</span
|
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" />
|
<span class="pl-4 w-10" />
|
||||||
</div>
|
</div>
|
||||||
@@ -221,6 +229,16 @@
|
|||||||
<h1 class="text-3xl">{$t('admin_page.no_answers')}</h1>
|
<h1 class="text-3xl">{$t('admin_page.no_answers')}</h1>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/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}
|
{:else}
|
||||||
{#await import('$lib/play/admin/results.svelte')}
|
{#await import('$lib/play/admin/results.svelte')}
|
||||||
<Spinner />
|
<Spinner />
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
import { reach } from 'yup';
|
import { reach } from 'yup';
|
||||||
import { getLocalization } from '$lib/i18n';
|
import { getLocalization } from '$lib/i18n';
|
||||||
import { VotingQuestionSchema } from '$lib/yupSchemas';
|
import { VotingQuestionSchema } from '$lib/yupSchemas';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
const { t } = getLocalization();
|
const { t } = getLocalization();
|
||||||
|
|
||||||
@@ -19,9 +20,19 @@
|
|||||||
|
|
||||||
export let selected_question: number;
|
export let selected_question: number;
|
||||||
export let data: EditorData;
|
export let data: EditorData;
|
||||||
if (!Array.isArray(data.questions[selected_question].answers)) {
|
try {
|
||||||
|
if (typeof data.questions[selected_question].answers[0].right === 'boolean') {
|
||||||
data.questions[selected_question].answers = [];
|
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>
|
</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>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if timer_res !== '0'}
|
{#if timer_res !== '0'}
|
||||||
{#if question.type === QuizQuestionType.ABCD}
|
{#if question.type === QuizQuestionType.ABCD || QuizQuestionType.VOTING}
|
||||||
{#if game_mode === 'normal'}
|
{#if game_mode === 'normal'}
|
||||||
<div class="grid grid-cols-2 gap-2 w-full p-4">
|
<div class="grid grid-cols-2 gap-2 w-full p-4">
|
||||||
{#each question.answers as answer}
|
{#each question.answers as answer}
|
||||||
@@ -202,4 +202,11 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/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}
|
{/if}
|
||||||
|
|||||||
@@ -91,7 +91,8 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{:else if timer_res === '0'}
|
{:else if question.type === QuizQuestionType.RANGE}
|
||||||
|
{#if timer_res === '0'}
|
||||||
{#await import('svelte-range-slider-pips')}
|
{#await import('svelte-range-slider-pips')}
|
||||||
<Spinner />
|
<Spinner />
|
||||||
{:then c}
|
{:then c}
|
||||||
@@ -135,4 +136,19 @@
|
|||||||
</div>
|
</div>
|
||||||
{/await}
|
{/await}
|
||||||
{/if}
|
{/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>
|
</div>
|
||||||
|
|||||||
@@ -63,9 +63,12 @@ export const dataSchema = yup.object({
|
|||||||
.lowercase(),
|
.lowercase(),
|
||||||
answers: yup.lazy((v) => {
|
answers: yup.lazy((v) => {
|
||||||
if (Array.isArray(v)) {
|
if (Array.isArray(v)) {
|
||||||
|
console.log(v);
|
||||||
if (typeof v[0].right === 'boolean') {
|
if (typeof v[0].right === 'boolean') {
|
||||||
|
console.log('Yup, ABCD');
|
||||||
return ABCDQuestionSchema;
|
return ABCDQuestionSchema;
|
||||||
} else {
|
} else {
|
||||||
|
console.log('Yup, Poll');
|
||||||
return VotingQuestionSchema;
|
return VotingQuestionSchema;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
import { getLocalization } from '$lib/i18n';
|
import { getLocalization } from '$lib/i18n';
|
||||||
import { navbarVisible } from '$lib/stores';
|
import { navbarVisible } from '$lib/stores';
|
||||||
import { QuizQuestionType } from '$lib/quiz_types';
|
import { QuizQuestionType } from '$lib/quiz_types';
|
||||||
|
import type { Question } from '$lib/quiz_types';
|
||||||
|
|
||||||
navbarVisible.set(false);
|
navbarVisible.set(false);
|
||||||
|
|
||||||
@@ -21,18 +22,6 @@
|
|||||||
questions: Question[];
|
questions: Question[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Question {
|
|
||||||
question: string;
|
|
||||||
time: string;
|
|
||||||
type?: QuizQuestionType;
|
|
||||||
answers: Answer[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Answer {
|
|
||||||
right: boolean;
|
|
||||||
answer: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
let responseData = {
|
let responseData = {
|
||||||
open: false
|
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