diff --git a/classquiz/db/models.py b/classquiz/db/models.py index 6c6e3c9..3105930 100644 --- a/classquiz/db/models.py +++ b/classquiz/db/models.py @@ -338,3 +338,39 @@ class QuizTivity(ormar.Model): tablename = "quiztivitys" metadata = metadata database = database + + +class QuizTivityShare(ormar.Model): + id: uuid.UUID = ormar.UUID(primary_key=True) + name: str | None = ormar.Text(nullable=True) + expire_at: datetime | None = ormar.DateTime(nullable=True) + quiztivity: QuizTivity | None = ormar.ForeignKey(QuizTivity) + user: User | None = ormar.ForeignKey(User) + + class Meta: + tablename = "quiztivityshares" + metadata = metadata + database = database + + +class OnlyId(BaseModel): + id: uuid.UUID + + +class PublicQuizTivityShare(BaseModel): + id: uuid.UUID + name: str | None + expire_in: int + quiztivity: OnlyId + user: OnlyId + + @classmethod + def from_db_model(cls, data: QuizTivityShare): + expire_in = int((data.expire_at - datetime.now()).seconds / 60) + return cls( + id=data.id, + name=data.name, + expire_in=expire_in, + quiztivity=OnlyId(id=data.quiztivity.id), + user=OnlyId(id=data.user.id), + ) diff --git a/classquiz/routers/quiztivity.py b/classquiz/routers/quiztivity/__init__.py similarity index 78% rename from classquiz/routers/quiztivity.py rename to classquiz/routers/quiztivity/__init__.py index e725909..5f3717b 100644 --- a/classquiz/routers/quiztivity.py +++ b/classquiz/routers/quiztivity/__init__.py @@ -1,16 +1,20 @@ # 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/. + from uuid import UUID, uuid4 from fastapi import APIRouter, Depends, HTTPException from classquiz.auth import get_current_user from datetime import datetime -from classquiz.db.models import User, QuizTivityInput, QuizTivity +from classquiz.db.models import User, QuizTivityInput, QuizTivity, QuizTivityShare, PublicQuizTivityShare +from classquiz.routers.quiztivity.shares import router as shares_router router = APIRouter() +router.include_router(shares_router, prefix="/shares") + @router.post("/create") async def create_quiztivity(data: QuizTivityInput, user: User = Depends(get_current_user)) -> QuizTivity: @@ -48,3 +52,12 @@ async def delete_quiztivity(uuid: UUID): 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 + + +@router.get("/{uuid}/shares") +async def get_shares(uuid: UUID, user: User = Depends(get_current_user)) -> list[PublicQuizTivityShare]: + shares = await QuizTivityShare.objects.filter(quiztivity=uuid, user=user).all() + resp_shares = [] + for share in shares: + resp_shares.append(PublicQuizTivityShare.from_db_model(share)) + return resp_shares diff --git a/classquiz/routers/quiztivity/shares.py b/classquiz/routers/quiztivity/shares.py new file mode 100644 index 0000000..54848ad --- /dev/null +++ b/classquiz/routers/quiztivity/shares.py @@ -0,0 +1,80 @@ +# 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/. +from uuid import UUID, uuid4 + +from fastapi import APIRouter, Depends, HTTPException +from pydantic import BaseModel +from datetime import datetime, timedelta + +from classquiz.auth import get_current_user +from classquiz.db.models import User, QuizTivityShare, QuizTivity, PublicQuizTivityShare + +router = APIRouter() + + +@router.get("/") +async def get_shares(user: User = Depends(get_current_user)) -> list[PublicQuizTivityShare]: + shares = await QuizTivityShare.objects.filter(user=user).all() + resp_shares = [] + for share in shares: + resp_shares.append(PublicQuizTivityShare.from_db_model(share)) + return resp_shares + + +class CreateShareInput(BaseModel): + name: str | None + quiztivity: UUID + expire_in: int | None + + +@router.post("/") +async def create_share(data: CreateShareInput, user: User = Depends(get_current_user)) -> PublicQuizTivityShare: + quiztivity = await QuizTivity.objects.get_or_none(id=data.quiztivity, user=user) + expire_at = None + if data.expire_in is not None: + expire_at = (datetime.now() + timedelta(minutes=data.expire_in)).replace(tzinfo=None) + if quiztivity is None: + raise HTTPException(status_code=400, detail="Quiztivity wasn't found") + share = await QuizTivityShare.objects.create( + id=uuid4(), name=data.name, expire_at=expire_at, quiztivity=quiztivity, user=user + ) + share = PublicQuizTivityShare.from_db_model(share) + return share + + +@router.delete("/{uuid}") +async def delete_share(uuid: UUID, user: User = Depends(get_current_user)): + share = await QuizTivityShare.objects.get_or_none(id=uuid, user=user) + if share is None: + raise HTTPException(status_code=404, detail="Share wasn't found") + await share.delete() + return + + +class UpdateShareInput(BaseModel): + name: str | None + expire_in: int | None + + +@router.put("/{uuid}") +async def update_share( + data: UpdateShareInput, uuid: UUID, user: User = Depends(get_current_user) +) -> PublicQuizTivityShare: + share = await QuizTivityShare.objects.get_or_none(id=uuid, user=user) + if share is None: + raise HTTPException(status_code=404, detail="Share wasn't found") + expire_at = None + if data.expire_in is not None: + expire_at = (datetime.now() + timedelta(minutes=data.expire_in)).replace(tzinfo=None) + share.name = data.name + share.expire_at = expire_at + return PublicQuizTivityShare.from_db_model(await share.update()) + + +@router.get("/{uuid}") +async def get_share(uuid: UUID) -> QuizTivity: + share = await QuizTivityShare.objects.select_related("quiztivity").get_or_none(id=uuid) + if share is None: + raise HTTPException(status_code=404, detail="Share not found") + return share.quiztivity diff --git a/frontend/src/lib/components/popover/smalltop.svelte b/frontend/src/lib/components/popover/smalltop.svelte new file mode 100644 index 0000000..7765d34 --- /dev/null +++ b/frontend/src/lib/components/popover/smalltop.svelte @@ -0,0 +1,61 @@ + + + +{#if open} +
Error!!!
+ {/if} ++ {#if share.expire_in} + {$t('quiztivity.editor.shares.expires_on', { + date: DateTime.now() + .plus({ minutes: share.expire_in }) + .toJSDate() + .toLocaleString() + })} + {:else} + {$t('quiztivity.editor.shares.never_expires')} + {/if} +
+