-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a3eec97
commit c3a095b
Showing
10 changed files
with
259 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
"""empty message | ||
Revision ID: 4f7559aa1483 | ||
Create Date: 2024-07-06 20:09:48.351518 | ||
""" | ||
|
||
from alembic import op | ||
|
||
import sqlalchemy as sa | ||
|
||
|
||
revision = "4f7559aa1483" | ||
down_revision = "1a9e957978e2" | ||
branch_labels = None | ||
depends_on = None | ||
|
||
|
||
def upgrade() -> None: | ||
op.create_table( | ||
"skills_sub_skill_bookmark", | ||
sa.Column("bookmark_id", sa.Integer(), autoincrement=True, nullable=False), | ||
sa.Column("user_id", sa.String(length=36), nullable=True), | ||
sa.Column("root_skill_id", sa.String(length=256), nullable=True), | ||
sa.Column("sub_skill_id", sa.String(length=256), nullable=True), | ||
sa.PrimaryKeyConstraint("bookmark_id"), | ||
mysql_collate="utf8mb4_bin", | ||
) | ||
|
||
|
||
def downgrade() -> None: | ||
op.drop_table("skills_sub_skill_bookmark") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
"""Endpoints related to bookmarking""" | ||
|
||
from typing import Any | ||
|
||
from fastapi import APIRouter | ||
|
||
from api import models | ||
from api.auth import require_verified_email, user_auth | ||
from api.database import db | ||
from api.exceptions.skill import SkillNotFoundException, SubSkillNotFoundException | ||
from api.schemas.user import User | ||
from api.utils.docs import responses | ||
|
||
|
||
router = APIRouter() | ||
|
||
|
||
@router.post( | ||
"/bookmark/{root_skill_id}", | ||
dependencies=[require_verified_email], | ||
responses=responses(bool, SkillNotFoundException), | ||
) | ||
async def create_bookmark(user: User = user_auth, root_skill_id: str = None) -> Any: | ||
""" | ||
Create a new bookmark on a root skill. | ||
This bookmarks all subskills under the root skill. | ||
*Requirements:* **VERIFIED** | ||
""" | ||
root_skill: models.RootSkill | None = await db.get(models.RootSkill, id=root_skill_id) | ||
if root_skill is None: | ||
raise SkillNotFoundException | ||
|
||
for subskill in root_skill.sub_skills: | ||
try: | ||
await create_sub_skill_bookmark(user, root_skill_id, subskill.id) | ||
except Exception: | ||
pass | ||
|
||
return True | ||
|
||
|
||
@router.delete( | ||
"/bookmark/{root_skill_id}", | ||
dependencies=[require_verified_email], | ||
responses=responses(bool, SkillNotFoundException), | ||
) | ||
async def delete_bookmark(user: User = user_auth, root_skill_id: str = None) -> Any: | ||
""" | ||
Delete a bookmark on a root skill. | ||
This deletes all subskill bookmarks under the root skill. | ||
*Requirements:* **VERIFIED** | ||
""" | ||
root_skill: models.RootSkill | None = await db.get(models.RootSkill, id=root_skill_id) | ||
if root_skill is None: | ||
raise SkillNotFoundException | ||
|
||
for subskill in root_skill.sub_skills: | ||
try: | ||
await delete_sub_skill_bookmark(user, root_skill_id, subskill.id) | ||
except Exception: | ||
pass | ||
|
||
return True | ||
|
||
|
||
@router.post( | ||
"/bookmark/{root_skill_id}/{sub_skill_id}", | ||
dependencies=[require_verified_email], | ||
responses=responses(bool, SkillNotFoundException), | ||
) | ||
async def create_sub_skill_bookmark(user: User = user_auth, root_skill_id: str = None, sub_skill_id: str = None) -> Any: | ||
""" | ||
Create a new bookmark on a sub skill. | ||
This also bookmarks the root skill. | ||
*Requirements:* **VERIFIED** | ||
""" | ||
root_skill: models.RootSkill | None = await db.get(models.RootSkill, id=root_skill_id) | ||
if root_skill is None: | ||
raise SkillNotFoundException | ||
|
||
sub_skill: models.SubSkill | None = await db.get(models.SubSkill, id=sub_skill_id, parent_id=root_skill_id) | ||
if sub_skill is None: | ||
raise SubSkillNotFoundException | ||
|
||
return await models.SubSkillBookmark.create(user_id=user.id, root_skill_id=root_skill_id, sub_skill_id=sub_skill_id) | ||
|
||
|
||
@router.delete( | ||
"/bookmark/{root_skill_id}/{sub_skill_id}", | ||
dependencies=[require_verified_email], | ||
responses=responses(bool, SkillNotFoundException), | ||
) | ||
async def delete_sub_skill_bookmark(user: User = user_auth, root_skill_id: str = None, sub_skill_id: str = None) -> Any: | ||
""" | ||
Delete a bookmark on a sub skill. | ||
This also deletes the root skill bookmark. | ||
*Requirements:* **VERIFIED** | ||
""" | ||
root_skill: models.RootSkill | None = await db.get(models.RootSkill, id=root_skill_id) | ||
if root_skill is None: | ||
raise SkillNotFoundException | ||
|
||
sub_skill: models.SubSkill | None = await db.get(models.SubSkill, id=sub_skill_id, parent_id=root_skill_id) | ||
if sub_skill is None: | ||
raise SubSkillNotFoundException | ||
|
||
return await models.SubSkillBookmark.delete(user_id=user.id, root_skill_id=root_skill_id, sub_skill_id=sub_skill_id) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
from starlette import status | ||
|
||
from api.exceptions.api_exception import APIException | ||
|
||
|
||
class SubSkillAlreadyBookmarkedException(APIException): | ||
status_code = status.HTTP_409_CONFLICT | ||
detail = "Subskill already bookmarked" | ||
description = "The requested sub skill is already bookmarked." | ||
|
||
|
||
class BookmarkNotFoundException(APIException): | ||
status_code = status.HTTP_404_NOT_FOUND | ||
detail = "Bookmark not found" | ||
description = "The requested bookmark was not found." |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
from __future__ import annotations | ||
|
||
from sqlalchemy import Column, Integer, String | ||
|
||
from api.database import Base, db, filter_by | ||
from api.exceptions.bookmarks import BookmarkNotFoundException, SubSkillAlreadyBookmarkedException | ||
from api.utils.cache import clear_cache | ||
|
||
|
||
class SubSkillBookmark(Base): | ||
__tablename__ = "skills_sub_skill_bookmark" | ||
|
||
bookmark_id = Column(Integer, primary_key=True, autoincrement=True) | ||
user_id = Column(String(36)) | ||
root_skill_id = Column(String(256)) | ||
sub_skill_id = Column(String(256)) | ||
|
||
@staticmethod | ||
async def create(user_id: str, root_skill_id: str, sub_skill_id: str) -> bool: | ||
if await db.exists( | ||
filter_by(SubSkillBookmark, user_id=user_id, sub_skill_id=sub_skill_id, root_skill_id=root_skill_id) | ||
): | ||
raise SubSkillAlreadyBookmarkedException | ||
|
||
bookmark = SubSkillBookmark(user_id=user_id, sub_skill_id=sub_skill_id, root_skill_id=root_skill_id) | ||
response = await db.add(bookmark) | ||
await clear_cache("skills") | ||
|
||
return bool(response) | ||
|
||
@staticmethod | ||
async def delete(user_id: str, root_skill_id: str, sub_skill_id: str) -> bool: | ||
bookmark = await db.get( | ||
SubSkillBookmark, user_id=user_id, sub_skill_id=sub_skill_id, root_skill_id=root_skill_id | ||
) | ||
if bookmark is None: | ||
raise BookmarkNotFoundException | ||
|
||
response = await db.delete(bookmark) | ||
await clear_cache("skills") | ||
|
||
return bool(response) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters