diff --git a/src/dependencies/__init__.py b/src/dependencies/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/main.py b/src/main.py index 1124cff7..c291b269 100644 --- a/src/main.py +++ b/src/main.py @@ -1,6 +1,6 @@ from fastapi import APIRouter from src.routers import users, auth, houses, orgs, roles -from src.routers.courses import chapters, collections, courses +from src.routers.courses import chapters, collections, courses,elements global_router = APIRouter(prefix="/api") @@ -14,5 +14,6 @@ global_router.include_router(orgs.router, prefix="/orgs", tags=["orgs"]) global_router.include_router(roles.router, prefix="/roles", tags=["roles"]) global_router.include_router(courses.router, prefix="/courses", tags=["courses"]) global_router.include_router(chapters.router, prefix="/chapters", tags=["chapters"]) +global_router.include_router(elements.router, prefix="/elements", tags=["elements"]) global_router.include_router(collections.router, prefix="/collections", tags=["collections"]) diff --git a/src/routers/courses/elements.py b/src/routers/courses/elements.py new file mode 100644 index 00000000..6d2e3d99 --- /dev/null +++ b/src/routers/courses/elements.py @@ -0,0 +1,45 @@ +from fastapi import APIRouter, Depends, UploadFile, Form +from src.services.courses.elements import * +from src.dependencies.auth import get_current_user + +router = APIRouter() + + +@router.post("/") +async def api_create_element(element_object: Element, coursechapter_id: str, current_user: PublicUser = Depends(get_current_user)): + """ + Create new Element + """ + return await create_element(element_object, coursechapter_id, current_user) + + +@router.get("/{element_id}") +async def api_get_element(element_id: str, current_user: PublicUser = Depends(get_current_user)): + """ + Get single Element by element_id + """ + return await get_element(element_id, current_user=current_user) + +@router.get("/coursechapter/{coursechapter_id}") +async def api_get_elements(coursechapter_id: str, current_user: PublicUser = Depends(get_current_user)): + """ + Get CourseChapter Elements + """ + return await get_elements(coursechapter_id, current_user) + +@router.put("/{element_id}") +async def api_update_element(element_object: Element, element_id: str, current_user: PublicUser = Depends(get_current_user)): + """ + Update Element by element_id + """ + return await update_element(element_object, element_id, current_user) + + +@router.delete("/{element_id}") +async def api_delete_element(element_id: str, current_user: PublicUser = Depends(get_current_user)): + """ + Delete Element by element_id + """ + return await delete_element(element_id, current_user) + + diff --git a/src/services/courses/chapters.py b/src/services/courses/chapters.py index d903eab8..706b4904 100644 --- a/src/services/courses/chapters.py +++ b/src/services/courses/chapters.py @@ -32,7 +32,11 @@ class CourseChapterMetaData(BaseModel): chapterOrder: List[str] chapters: List -# CoursesChapters +#### Classes #################################################### + +#################################################### +# CRUD +#################################################### async def create_coursechapter(coursechapter_object: CourseChapter, course_id: str, current_user: PublicUser): @@ -82,55 +86,6 @@ async def get_coursechapter(coursechapter_id: str, current_user: PublicUser): status_code=status.HTTP_409_CONFLICT, detail="CourseChapter does not exist") -async def get_coursechapters_meta(course_id: str, current_user: PublicUser): - await check_database() - coursechapters = learnhouseDB["coursechapters"] - courses = learnhouseDB["courses"] - - coursechapters = coursechapters.find( - {"course_id": course_id}).sort("name", 1) - - course = courses.find_one({"course_id": course_id}) - course = Course(**course) # type: ignore - - # chapters - chapters = {} - for coursechapter in coursechapters: - coursechapter = CourseChapterInDB(**coursechapter) - coursechapter_elementIds = [] - - for element in coursechapter.elements: - coursechapter_elementIds.append(element.element_id) - - chapters[coursechapter.coursechapter_id] = { - "id": coursechapter.coursechapter_id, "name": coursechapter.name, "elementIds": coursechapter_elementIds - } - - final = { - "chapters": chapters, - "chapterOrder": course.chapters - } - - return final - - -async def update_coursechapters_meta(course_id: str, coursechapters_metadata: CourseChapterMetaData, current_user: PublicUser): - await check_database() - coursechapters = learnhouseDB["coursechapters"] - courses = learnhouseDB["courses"] - - course = courses.find_one({"course_id": course_id}) - course = Course(**course) # type: ignore - - # update chapters in course - courseInDB = courses.update_one({"course_id": course_id}, { - "$set": {"chapters": coursechapters_metadata.chapterOrder}}) - - # TODO : update chapters in coursechapters - - return {courseInDB} - - async def update_coursechapter(coursechapter_object: CourseChapter, coursechapter_id: str, current_user: PublicUser): await check_database() coursechapters = learnhouseDB["coursechapters"] @@ -189,6 +144,10 @@ async def delete_coursechapter(coursechapter_id: str, current_user: PublicUser) raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="Course does not exist") +#################################################### +# Misc +#################################################### + async def get_coursechapters(course_id: str, page: int = 1, limit: int = 10): await check_database() @@ -201,6 +160,54 @@ async def get_coursechapters(course_id: str, page: int = 1, limit: int = 10): return [json.loads(json.dumps(coursechapter, default=str)) for coursechapter in all_coursechapters] +async def get_coursechapters_meta(course_id: str, current_user: PublicUser): + await check_database() + coursechapters = learnhouseDB["coursechapters"] + courses = learnhouseDB["courses"] + + coursechapters = coursechapters.find( + {"course_id": course_id}).sort("name", 1) + + course = courses.find_one({"course_id": course_id}) + course = Course(**course) # type: ignore + + # chapters + chapters = {} + for coursechapter in coursechapters: + coursechapter = CourseChapterInDB(**coursechapter) + coursechapter_elementIds = [] + + for element in coursechapter.elements: + coursechapter_elementIds.append(element.element_id) + + chapters[coursechapter.coursechapter_id] = { + "id": coursechapter.coursechapter_id, "name": coursechapter.name, "elementIds": coursechapter_elementIds + } + + final = { + "chapters": chapters, + "chapterOrder": course.chapters + } + + return final + + +async def update_coursechapters_meta(course_id: str, coursechapters_metadata: CourseChapterMetaData, current_user: PublicUser): + await check_database() + coursechapters = learnhouseDB["coursechapters"] + courses = learnhouseDB["courses"] + + course = courses.find_one({"course_id": course_id}) + course = Course(**course) # type: ignore + + # update chapters in course + courseInDB = courses.update_one({"course_id": course_id}, { + "$set": {"chapters": coursechapters_metadata.chapterOrder}}) + + # TODO : update chapters in coursechapters + + return {courseInDB} + #### Security #################################################### diff --git a/src/services/courses/collections.py b/src/services/courses/collections.py index a2a5118b..95cdea39 100644 --- a/src/services/courses/collections.py +++ b/src/services/courses/collections.py @@ -23,6 +23,9 @@ class CollectionInDB(Collection): #### Classes #################################################### +#################################################### +# CRUD +#################################################### async def get_collection(collection_id: str, current_user: PublicUser): await check_database() @@ -112,6 +115,9 @@ async def delete_collection(collection_id: str, current_user: PublicUser): raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database") +#################################################### +# Misc +#################################################### async def get_collections(page: int = 1, limit: int = 10): ## TODO : auth diff --git a/src/services/courses/courses.py b/src/services/courses/courses.py index 18eb3e21..5f15b1af 100644 --- a/src/services/courses/courses.py +++ b/src/services/courses/courses.py @@ -5,7 +5,7 @@ from uuid import uuid4 from pydantic import BaseModel from src.services.uploads import upload_thumbnail from src.services.users import PublicUser, User -from src.services.database import create_config_collection, check_database, create_database, learnhouseDB, learnhouseDB +from src.services.database import create_config_collection, check_database, create_database, learnhouseDB from src.services.security import * from fastapi import FastAPI, HTTPException, status, Request, Response, BackgroundTasks, UploadFile, File from datetime import datetime @@ -30,17 +30,14 @@ class CourseInDB(Course): updateDate: str authors: List[str] -##### - - - - #### Classes #################################################### # TODO : Add courses photo & cover upload and delete -# Courses +#################################################### +# CRUD +#################################################### async def get_course(course_id: str, current_user: PublicUser): await check_database() @@ -59,7 +56,7 @@ async def get_course(course_id: str, current_user: PublicUser): return course -async def create_course(course_object: Course, org_id: str, current_user: PublicUser, thumbnail_file: UploadFile | None = None): +async def create_course(course_object: Course, org_id: str, current_user: PublicUser, thumbnail_file: UploadFile | None = None): await check_database() courses = learnhouseDB["courses"] @@ -174,6 +171,10 @@ async def delete_course(course_id: str, current_user: PublicUser): raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database") +#################################################### +# Misc +#################################################### + async def get_courses(page: int = 1, limit: int = 10, org_id: str | None = None): await check_database() @@ -186,7 +187,6 @@ async def get_courses(page: int = 1, limit: int = 10, org_id: str | None = None) return [json.loads(json.dumps(course, default=str)) for course in all_courses] - #### Security #################################################### diff --git a/src/services/courses/elements.py b/src/services/courses/elements.py index e69de29b..3b4de715 100644 --- a/src/services/courses/elements.py +++ b/src/services/courses/elements.py @@ -0,0 +1,148 @@ +from pydantic import BaseModel +from src.services.database import create_config_collection, check_database, create_database, learnhouseDB +from src.services.security import verify_user_rights_with_roles +from src.services.users import PublicUser, User +from fastapi import FastAPI, HTTPException, status, Request, Response, BackgroundTasks, UploadFile, File +from uuid import uuid4 +from datetime import datetime + +#### Classes #################################################### + +class Element(BaseModel): + name: str + element_type: str + content: str + + + +class ElementInDB(Element): + element_id: str + coursechapter_id: str + creationDate: str + updateDate: str + +#### Classes #################################################### + + +#################################################### +# CRUD +#################################################### + + +async def create_element(element_object: Element, coursechapter_id : str , current_user: PublicUser): + await check_database() + elements = learnhouseDB["elements"] + + # generate element_id + element_id = str(f"element_{uuid4()}") + + hasRoleRights = await verify_user_rights_with_roles("create", current_user.user_id, element_id) + + if not hasRoleRights: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, detail="Roles : Insufficient rights to perform this action") + + # create element + element = ElementInDB(**element_object.dict(), creationDate=str( + datetime.now()), coursechapter_id=coursechapter_id, updateDate=str(datetime.now()), element_id=element_id) + elements.insert_one(element.dict()) + + return element + + +async def get_element(element_id: str, current_user: PublicUser): + await check_database() + elements = learnhouseDB["elements"] + + element = elements.find_one({"element_id": element_id}) + + # verify course rights + hasRoleRights = await verify_user_rights_with_roles("read", current_user.user_id, element_id) + + if not hasRoleRights: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, detail="Roles : Insufficient rights to perform this action") + + if not element: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, detail="Course does not exist") + + element = ElementInDB(**element) + return element + + +async def update_element(element_object: Element, element_id: str, current_user: PublicUser): + await check_database() + + # verify course rights + await verify_user_rights_with_roles("update", current_user.user_id, element_id) + + elements = learnhouseDB["elements"] + + element = elements.find_one({"element_id": element_id}) + + if element: + creationDate = element["creationDate"] + + # get today's date + datetime_object = datetime.now() + + updated_course = ElementInDB( + element_id=element_id,coursechapter_id=element["coursechapter_id"] ,creationDate=creationDate, updateDate=str(datetime_object), **element_object.dict()) + + elements.update_one({"element_id": element_id}, { + "$set": updated_course.dict()}) + + return ElementInDB(**updated_course.dict()) + + else: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, detail="Element does not exist") + + +async def delete_element(element_id: str, current_user: PublicUser): + await check_database() + + # verify course rights + await verify_user_rights_with_roles("delete", current_user.user_id, element_id) + + elements = learnhouseDB["elements"] + + element = elements.find_one({"element_id": element_id}) + + if not element: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, detail="Element does not exist") + + isDeleted = elements.delete_one({"element_id": element_id}) + + if isDeleted: + return {"detail": "Element deleted"} + else: + raise HTTPException( + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database") + +#################################################### +# Misc +#################################################### + + +async def get_elements(coursechapter_id: str, current_user: PublicUser): + await check_database() + elements = learnhouseDB["elements"] + + # verify course rights + await verify_user_rights_with_roles("read", current_user.user_id, coursechapter_id) + + elements = elements.find({"coursechapter_id": coursechapter_id}) + + if not elements: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, detail="No elements found") + + elements_list = [] + + for element in elements: + elements_list.append(Element(**element)) + + return elements_list diff --git a/src/services/security.py b/src/services/security.py index 4a4b0e65..a6998ff3 100644 --- a/src/services/security.py +++ b/src/services/security.py @@ -77,6 +77,8 @@ async def check_element_type(element_id): return "coursechapters" elif element_id.startswith("collection_"): return "collections" + elif element_id.startswith("element_"): + return "elements" else: raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="Issue verifying element nature")