Added button to view imported quiz on Kahoot!.

This commit is contained in:
Mawoka
2023-01-27 23:02:53 +01:00
parent 2d05ee26bd
commit 76311ed0ee
8 changed files with 124 additions and 83 deletions
+1
View File
@@ -181,6 +181,7 @@ class Quiz(ormar.Model):
cover_image: Optional[str] = ormar.Text(nullable=True, unique=False)
background_color: str | None = ormar.Text(nullable=True, unique=False)
background_image: str | None = ormar.Text(nullable=True, unique=False)
kahoot_id: uuid.UUID | None = ormar.UUID(nullable=True, default=None)
@validator("background_image")
def must_come_from_local_cdn(cls, v):
+3 -2
View File
@@ -30,7 +30,8 @@ async def import_quiz(quiz_id: str, user: User) -> Quiz | str:
:param quiz_id: The ID of the quiz to import.
:return: True if the import was successful, False otherwise.
"""
quiz = await get_quiz(quiz_id)
kahoot_quiz_id = quiz_id
quiz = await get_quiz(kahoot_quiz_id)
if quiz is None:
return "quiz not found"
quiz_questions: list[dict] = []
@@ -60,7 +61,6 @@ async def import_quiz(quiz_id: str, user: User) -> Quiz | str:
).dict()
)
cover = None
print(quiz.kahoot.cover)
if quiz.kahoot.cover != "" and quiz.kahoot.cover is not None:
image_bytes = await _download_image(quiz.kahoot.cover)
image_name = f"{quiz_id}--{uuid.uuid4()}"
@@ -77,6 +77,7 @@ async def import_quiz(quiz_id: str, user: User) -> Quiz | str:
questions=json.dumps(quiz_questions),
imported_from_kahoot=True,
cover_image=cover,
kahoot_id=uuid.UUID(kahoot_quiz_id),
)
meilisearch.index(settings.meilisearch_index).add_documents([await get_meili_data(quiz_data)])
return await quiz_data.save()
-1
View File
@@ -105,7 +105,6 @@ async def auth(request: Request, response: Response):
if user_data.email is None:
return RedirectResponse("/account/oauth-error?error=email")
user_in_db = await User.objects.get_or_none(email=user_data.email)
print(user_data.id)
if user_in_db is None:
# REGISTER USER
try:
-1
View File
@@ -92,7 +92,6 @@ async def get_public_quiz(quiz_id: str):
except ValueError:
raise HTTPException(status_code=400, detail="badly formed quiz id")
quiz = await Quiz.objects.select_related("user_id").get_or_none(id=quiz_id)
print(quiz.dict(exclude={"user_id": {"avatar"}}))
if quiz is None:
return JSONResponse(status_code=404, content={"detail": "quiz not found"})
else:
+1 -2
View File
@@ -331,8 +331,7 @@ async def get_email_from_jwt(data: GetEmailFromJWT):
try:
payload = jwt.decode(data.jwt, settings.secret_key, algorithms=["HS256"])
return payload.get("sub")
except JWTError as e:
print(e)
except JWTError:
raise HTTPException(status_code=401)
@@ -3,14 +3,17 @@
export let flex = false;
export let href: undefined | string = undefined;
export let target: undefined | string = '_self';
</script>
{#if href}
<a
{href}
{target}
class="w-full px-4 py-2 leading-5 text-black dark:text-white transition-colors duration-200 transform bg-gray-50 dark:bg-gray-700 rounded text-center hover:bg-gray-300 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 dark:hover:bg-gray-600"
on:click
class:flex
class:block={!flex}
class:justify-center={flex}
>
<slot />
+87 -77
View File
@@ -12,6 +12,7 @@
import StartGamePopup from '$lib/dashboard/start_game.svelte';
import { onMount } from 'svelte';
import Spinner from '$lib/Spinner.svelte';
import GrayButton from '$lib/components/buttons/gray.svelte';
const tippy = createTippy({
arrow: true,
@@ -56,6 +57,7 @@
user_id: string;
imported_from_kahoot?: boolean;
questions: Question[];
kahoot_id?: string;
}
</script>
@@ -82,87 +84,95 @@
</div>
</div>
{/if}
<div class="text-center text-sm pt-1">
<div class="text-center text-sm pt-1 mb-4">
<ImportedOrNot imported={quiz.imported_from_kahoot} />
</div>
<div class="flex justify-center mt-8 mb-4">
{#if logged_in}
<button
class="admin-button"
on:click={() => {
start_game = quiz.id;
}}
>
{$t('words.start')}
</button>
{:else}
<div use:tippy={{ content: 'You need to be logged in to start a game' }}>
<button
class="px-4 py-2 leading-5 text-white transition-colors duration-200 transform bg-gray-700 rounded text-center hover:bg-gray-600 focus:outline-none cursor-not-allowed opacity-50"
disabled
>
{$t('words.start')}
</button>
</div>
{/if}
</div>
<div class="flex justify-center m-2">
<a class="admin-button" href="/practice?quiz_id={quiz.id}">
{$t('words.practice')}
</a>
</div>
<div class="flex justify-center">
{#if logged_in}
<a
href="/api/v1/eximport/{quiz.id}"
class="px-4 py-2 leading-5 text-white transition-colors duration-200 transform bg-gray-700 rounded text-center hover:bg-gray-600 focus:outline-none"
>
<svg
class="w-5 h-5 inline-block"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
/>
</svg>
{$t('words.download')}
</a>
{:else}
<div use:tippy={{ content: 'You need to be logged in to download a game' }}>
<button
class="px-4 py-2 leading-5 text-white transition-colors duration-200 transform bg-gray-700 rounded text-center hover:bg-gray-600 focus:outline-none cursor-not-allowed opacity-50"
disabled
>
<svg
class="w-5 h-5 inline-block"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
<div class="flex flex-col justify-center">
<div class="mx-auto flex flex-col gap-2 justify-center w-fit">
{#if quiz.imported_from_kahoot && quiz.kahoot_id}
<div class="w-full">
<GrayButton
href="https://create.kahoot.it/details/{quiz.kahoot_id}"
target="_blank"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
/>
</svg>
{$t('words.download')}
</button>
View on <i>Kahoot!</i>
</GrayButton>
</div>
{/if}
{#if logged_in}
<div class="w-full">
<GrayButton
on:click={() => {
start_game = quiz.id;
}}
>
{$t('words.start')}
</GrayButton>
</div>
{:else}
<div use:tippy={{ content: 'You need to be logged in to start a game' }}>
<div class="w-full">
<GrayButton disabled={true}>
{$t('words.start')}
</GrayButton>
</div>
</div>
{/if}
<div class="w-full">
<GrayButton href="/practice?quiz_id={quiz.id}">
{$t('words.practice')}
</GrayButton>
</div>
{/if}
</div>
<div class="flex justify-center">
<a href="mailto:report@mawoka.eu?subject=Report quiz {quiz.id}" class="text-sm underline">
{$t('words.report')}
</a>
<div class="w-full">
{#if logged_in}
<GrayButton flex={true} href="/api/v1/eximport/{quiz.id}">
<svg
class="w-5 h-5 inline-block"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
/>
</svg>
{$t('words.download')}
</GrayButton>
{:else}
<div use:tippy={{ content: 'You need to be logged in to download a game' }}>
<GrayButton disabled={true} flex={true}>
<svg
class="w-5 h-5 inline-block"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
/>
</svg>
{$t('words.download')}
</GrayButton>
</div>
{/if}
</div>
</div>
<div class="flex justify-center">
<a
href="mailto:report@mawoka.eu?subject=Report quiz {quiz.id}"
class="text-sm underline"
>
{$t('words.report')}
</a>
</div>
</div>
{#each quiz.questions as question, index_question}
@@ -0,0 +1,29 @@
"""Added kahoot_id
Revision ID: 7ad8502af419
Revises: 820e06ef2c2a
Create Date: 2023-01-27 22:30:00.331395
"""
from alembic import op
import sqlalchemy as sa
import ormar
# revision identifiers, used by Alembic.
revision = "7ad8502af419"
down_revision = "820e06ef2c2a"
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("quiz", sa.Column("kahoot_id", ormar.fields.sqlalchemy_uuid.CHAR(32), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("quiz", "kahoot_id")
# ### end Alembic commands ###