Basic Quiztivity working

This commit is contained in:
Mawoka
2023-05-13 19:49:38 +02:00
parent 2750e7539d
commit f23fb4f625
15 changed files with 295 additions and 14 deletions
+6
View File
@@ -42,3 +42,9 @@ async def delete_quiztivity(uuid: UUID):
if quiztivity is None:
raise HTTPException(status_code=404, detail="QuizTivity not found")
await quiztivity.delete()
@router.get("/")
async def get_all_quiztivities(user: User = Depends(get_current_user)) -> list[QuizTivity]:
quiztivities = await QuizTivity.objects.filter(user=user).order_by(QuizTivity.created_at.desc()).all()
return quiztivities
+2
View File
@@ -33,6 +33,7 @@
"@tailwindcss/typography": "^0.5.9",
"@types/canvas-confetti": "^1.6.0",
"@types/cookie": "^0.5.1",
"@types/dompurify": "^3.0.2",
"@types/js-cookie": "^3.0.3",
"@types/luxon": "^3.3.0",
"@types/marked": "^4.3.0",
@@ -56,6 +57,7 @@
"cookie": "^0.5.0",
"crypto-js": "^4.1.1",
"cssnano": "^6.0.0",
"dompurify": "^3.0.3",
"eslint": "^8.38.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-svelte3": "^4.0.0",
+27
View File
@@ -20,6 +20,7 @@ specifiers:
'@tailwindcss/typography': ^0.5.9
'@types/canvas-confetti': ^1.6.0
'@types/cookie': ^0.5.1
'@types/dompurify': ^3.0.2
'@types/js-cookie': ^3.0.3
'@types/luxon': ^3.3.0
'@types/marked': ^4.3.0
@@ -43,6 +44,7 @@ specifiers:
cookie: ^0.5.0
crypto-js: ^4.1.1
cssnano: ^6.0.0
dompurify: ^3.0.3
eslint: ^8.38.0
eslint-config-prettier: ^8.8.0
eslint-plugin-svelte3: ^4.0.0
@@ -106,6 +108,7 @@ devDependencies:
'@tailwindcss/typography': 0.5.9_tailwindcss@3.3.1
'@types/canvas-confetti': 1.6.0
'@types/cookie': 0.5.1
'@types/dompurify': 3.0.2
'@types/js-cookie': 3.0.3
'@types/luxon': 3.3.0
'@types/marked': 4.3.0
@@ -129,6 +132,7 @@ devDependencies:
cookie: 0.5.0
crypto-js: 4.1.1
cssnano: 6.0.0_postcss@8.4.22
dompurify: 3.0.3
eslint: 8.38.0
eslint-config-prettier: 8.8.0_eslint@8.38.0
eslint-plugin-svelte3: 4.0.0_a4ckjr7darsrglbip3svgxgxc4
@@ -1438,6 +1442,15 @@ packages:
}
dev: true
/@types/dompurify/3.0.2:
resolution:
{
integrity: sha512-YBL4ziFebbbfQfH5mlC+QTJsvh0oJUrWbmxKMyEdL7emlHJqGR2Qb34TEFKj+VCayBvjKy3xczMFNhugThUsfQ==
}
dependencies:
'@types/trusted-types': 2.0.3
dev: true
/@types/estree/1.0.1:
resolution:
{
@@ -1517,6 +1530,13 @@ packages:
}
dev: true
/@types/trusted-types/2.0.3:
resolution:
{
integrity: sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==
}
dev: true
/@types/ua-parser-js/0.7.36:
resolution:
{
@@ -2620,6 +2640,13 @@ packages:
domelementtype: 2.3.0
dev: true
/dompurify/3.0.3:
resolution:
{
integrity: sha512-axQ9zieHLnAnHh0sfAamKYiqXMJAVwu+LM/alQ7WDagoWessyWvMSFyW65CqF3owufNu8HBcE4cM2Vflu7YWcQ==
}
dev: true
/domutils/3.0.1:
resolution:
{
@@ -5,13 +5,9 @@
-->
<script lang="ts">
import type { Markdown } from '../../types';
import { getLocalization } from '$lib/i18n';
import BrownButton from '$lib/components/buttons/brown.svelte';
import { marked } from 'marked';
import { browser } from '$app/environment';
const { t } = getLocalization();
export let data: Markdown | undefined;
if (!data) {
@@ -0,0 +1,21 @@
<!--
- 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 { Markdown } from '$lib/quiztivity/types';
import DOMPurify from 'dompurify';
import { marked } from 'marked';
import { browser } from '$app/environment';
export let data: Markdown | undefined;
let rendered_html = '';
$: rendered_html = browser ? DOMPurify.sanitize(marked.parse(data.markdown ?? '')) : '';
</script>
<div class="prose dark:prose-invert">
{@html rendered_html}
</div>
@@ -7,6 +7,7 @@
import type { Memory } from '../../types';
import { getLocalization } from '$lib/i18n';
import BrownButton from '$lib/components/buttons/brown.svelte';
import { flip } from 'svelte/animate';
const { t } = getLocalization();
@@ -21,6 +22,20 @@
cards: []
};
}
const arraymove = (arr: any[], fromI: number, toI: number) => {
const el = arr[fromI];
arr.splice(fromI, 1);
arr.splice(toI, 0, el);
};
const move_card_left = (selected_slide: number) => {
arraymove(data.cards, selected_slide, selected_slide - 1);
data.cards = data.cards;
};
const move_card_right = (selected_slide: number) => {
arraymove(data.cards, selected_slide, selected_slide + 1);
data.cards = data.cards;
};
const add_card = () => {
if (!new_pair_data.text_1 || !new_pair_data.text_2) {
@@ -81,8 +96,11 @@
>
</div>
</div>
{#each data.cards as card_pair}
<div class="border-[#B07156] border-2 rounded">
{#each data.cards as card_pair, i (card_pair[0].id)}
<div
class="border-[#B07156] border-2 rounded group flex flex-col"
animate:flip={{ duration: 200 }}
>
<div class="grid grid-cols-2 py-2 h-full">
{#each card_pair as card}
<div class="px-2 flex h-full">
@@ -90,6 +108,21 @@
</div>
{/each}
</div>
<div class="grid grid-cols-2 p-2 gap-2">
<BrownButton
on:click={() => {
move_card_left(i);
}}
disabled={i === 0}>{$t('quiztivity.editor.move_left')}</BrownButton
>
<BrownButton
on:click={() => {
move_card_right(i);
}}
disabled={i + 1 === data.cards.length}
>{$t('quiztivity.editor.move_right')}</BrownButton
>
</div>
</div>
{/each}
</div>
@@ -0,0 +1,101 @@
<!--
- 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 { Memory, MemoryCard } from '$lib/quiztivity/types';
import { getLocalization } from '$lib/i18n';
const { t } = getLocalization();
export let data: Memory | undefined;
const shuffle = <Type>(a: Array<Type>): Array<Type> => {
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
};
let card_opened = {};
const get_all_cards_as_single_array = (): MemoryCard[] => {
console.log('data', data.cards);
let final_arr: MemoryCard[] = [];
for (let i = 0; i < data.cards.length; i++) {
const pair = data.cards[i];
console.log('pair', pair);
for (let c = 0; c < pair.length; c++) {
final_arr.push({ ...pair[c], id: `${i}:${c}` });
card_opened[`${i}:${c}`] = false;
}
}
console.log(final_arr);
return final_arr;
};
const all_cards_array: MemoryCard[] = get_all_cards_as_single_array();
const random_card_order = shuffle(all_cards_array);
console.log(random_card_order);
const found_cards: string[] = [];
let opened_active_cards: string[] = [];
let try_count = 0;
let game_finished = false;
const flip_card = (id: string) => {
if (found_cards.includes(id) || game_finished) {
return;
}
card_opened[id] = true;
opened_active_cards.push(id);
if (opened_active_cards.length === 2) {
const [pair_1_id, card_1_id] = opened_active_cards[0].split(':');
if (!pair_1_id || !card_1_id) {
throw "Mustn't happen";
}
const [pair_2_id, card_2_id] = opened_active_cards[1].split(':');
if (!pair_2_id || !card_2_id) {
throw "Mustn't happen";
}
if (pair_2_id === pair_1_id) {
found_cards.push(opened_active_cards[0]);
found_cards.push(opened_active_cards[1]);
opened_active_cards = [];
}
}
if (opened_active_cards.length === 3) {
card_opened[opened_active_cards[0]] = false;
card_opened[opened_active_cards[1]] = false;
opened_active_cards.splice(0, 2);
try_count += 1;
}
game_finished = random_card_order.length === found_cards.length;
};
</script>
<div>
<p class="text-center">{$t('quiztivity.play.memory.try_count', { try_count })}</p>
<div class="grid lg:grid-cols-6 grid-cols-2 gap-2 m-4">
{#each random_card_order as card}
<button
class="aspect-square flex border-[#B07156] border-2 rounded"
on:click={() => {
flip_card(card.id);
}}
>
{#if card_opened[card.id]}
<p class="m-auto transition-all">{card.text}</p>
{:else}
<img
src="/android-chrome-512x512.png"
alt="ClassQuiz logo"
class="m-4 opacity-50 hover:opacity-80 transition-all"
/>
{/if}
</button>
{/each}
</div>
</div>
+1 -1
View File
@@ -19,7 +19,7 @@ export interface Pdf {
url: string;
}
interface MemoryCard {
export interface MemoryCard {
image?: string;
text?: string;
id: string;
@@ -4,9 +4,8 @@
- file, You can obtain one at https://mozilla.org/MPL/2.0/.
-->
<script lang="ts">
import type { Data } from '../../../lib/quiztivity/types';
import Editor from '../../../lib/quiztivity/editor.svelte';
import { goto } from '$app/navigation';
import type { Data } from '$lib/quiztivity/types';
import Editor from '$lib/quiztivity/editor.svelte';
let data: Data = { pages: [], id: undefined, title: '' };
let saving = false;
@@ -5,9 +5,7 @@
-->
<script lang="ts">
import type { PageData } from './$types';
import type { Data } from '../../../lib/quiztivity/types';
import Editor from '../../../lib/quiztivity/editor.svelte';
import { goto } from '$app/navigation';
export let data: PageData;
+1 -1
View File
@@ -6,7 +6,7 @@
import type { PageLoad } from './$types';
import { error } from '@sveltejs/kit';
import type { Data } from '../../../lib/quiztivity/types';
import type { Data } from '$lib/quiztivity/types';
export const load = (async ({ url, fetch }) => {
const id = url.searchParams.get('id');
@@ -0,0 +1,38 @@
<!--
- 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 { PageData } from './$types';
import type { QuizTivityPage } from '$lib/quiztivity/types';
import { QuizTivityTypes } from '$lib/quiztivity/types';
import NavigationBar from './navigation_bar.svelte';
export let data: PageData;
let quiztivity = data.quiztivity;
for (let i = 0; i < quiztivity.pages.length; i++) {
const id = (Math.random() + 1).toString(36).substring(7);
const type: QuizTivityTypes = quiztivity.pages[i].type as QuizTivityTypes;
quiztivity.pages[i] = { ...quiztivity.pages[i], id, type };
}
let current_slide_index = 0;
let current_slide: QuizTivityPage = quiztivity.pages[current_slide_index];
$: current_slide = quiztivity.pages[current_slide_index];
</script>
<div class="w-full h-full flex flex-col overflow-scroll">
<div class="w-full h-full">
{#if current_slide.type === QuizTivityTypes.MARKDOWN}
{#await import('$lib/quiztivity/components/markdown/play.svelte') then c}
<svelte:component this={c.default} data={current_slide.data} />
{/await}
{:else if current_slide.type === QuizTivityTypes.MEMORY}
{#await import('$lib/quiztivity/components/memory/play.svelte') then c}
<svelte:component this={c.default} data={current_slide.data} />
{/await}
{/if}
</div>
<NavigationBar bind:current_slide_index question_count={quiztivity.pages.length} />
</div>
@@ -0,0 +1,24 @@
/*
* 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/.
*/
import type { PageLoad } from './$types';
import { error } from '@sveltejs/kit';
import type { Data } from '$lib/quiztivity/types';
export const load = (async ({ url, fetch }) => {
const id = url.searchParams.get('id');
if (!id) {
throw error(400, 'id missing');
}
const resp = await fetch(`/api/v1/quiztivity/${id}`);
if (!resp.ok) {
throw error(404, 'quiztivity not found');
}
const data: Data = await resp.json();
return {
quiztivity: data
};
}) satisfies PageLoad;
@@ -0,0 +1,36 @@
<!--
- 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 BrownButton from '$lib/components/buttons/brown.svelte';
import { getLocalization } from '$lib/i18n';
export let current_slide_index: number;
export let question_count: number;
const { t } = getLocalization();
</script>
<div class="mt-auto h-[7vh] bg-opacity-50 bg-black shadow-2xl flex justify-between">
<div class="my-auto justify-start ml-4">
<BrownButton
disabled={current_slide_index < 1}
on:click={() => {
current_slide_index -= 1;
}}>{$t('words.back')}</BrownButton
>
</div>
<div class="my-auto">
{current_slide_index + 1}/{question_count}
</div>
<div class="my-auto justify-end mr-4">
<BrownButton
disabled={current_slide_index === 1}
on:click={() => {
current_slide_index += 1;
}}>{$t('words.next')}</BrownButton
>
</div>
</div>