diff --git a/src/services/courses/activities/activities.py b/src/services/courses/activities/activities.py index 1adf49b6..92c5750b 100644 --- a/src/services/courses/activities/activities.py +++ b/src/services/courses/activities/activities.py @@ -31,7 +31,7 @@ class ActivityInDB(Activity): async def create_activity(request: Request, activity_object: Activity, org_id: str, coursechapter_id: str, current_user: PublicUser): activities = request.app.db["activities"] - coursechapters = request.app.db["coursechapters"] + courses = request.app.db["courses"] # generate activity_id activity_id = str(f"activity_{uuid4()}") @@ -48,8 +48,8 @@ async def create_activity(request: Request, activity_object: Activity, org_id: s await activities.insert_one(activity.dict()) # update chapter - await coursechapters.update_one({"coursechapter_id": coursechapter_id}, { - "$addToSet": {"activities": activity_id}}) + await courses.update_one({"chapters_content.coursechapter_id": coursechapter_id}, { + "$addToSet": {"chapters_content.$.activities": activity_id}}) return activity diff --git a/src/services/courses/chapters.py b/src/services/courses/chapters.py index a2a4e1c1..46512220 100644 --- a/src/services/courses/chapters.py +++ b/src/services/courses/chapters.py @@ -1,5 +1,6 @@ from datetime import datetime import json +import pprint from typing import List from uuid import uuid4 from pydantic import BaseModel @@ -36,10 +37,9 @@ class CourseChapterMetaData(BaseModel): #################################################### -async def create_coursechapter(request: Request,coursechapter_object: CourseChapter, course_id: str, current_user: PublicUser): - coursechapters = request.app.db["coursechapters"] +async def create_coursechapter(request: Request, coursechapter_object: CourseChapter, course_id: str, current_user: PublicUser): courses = request.app.db["courses"] - + print(course_id) # get course org_id and verify rights course = await courses.find_one({"course_id": course_id}) @@ -55,22 +55,17 @@ async def create_coursechapter(request: Request,coursechapter_object: CourseChap coursechapter = CourseChapterInDB(coursechapter_id=coursechapter_id, creationDate=str( datetime.now()), updateDate=str(datetime.now()), course_id=course_id, **coursechapter_object.dict()) - coursechapter_in_db = await coursechapters.insert_one(coursechapter.dict()) courses.update_one({"course_id": course_id}, { - "$addToSet": {"chapters": coursechapter_id}}) - - if not coursechapter_in_db: - raise HTTPException( - status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database") + "$addToSet": {"chapters": coursechapter_id, "chapters_content": coursechapter.dict()}}) return coursechapter.dict() -async def get_coursechapter(request: Request,coursechapter_id: str, current_user: PublicUser): - coursechapters = request.app.db["coursechapters"] +async def get_coursechapter(request: Request, coursechapter_id: str, current_user: PublicUser): + courses = request.app.db["courses"] - coursechapter = await coursechapters.find_one( - {"coursechapter_id": coursechapter_id}) + coursechapter = await courses.find_one( + {"chapters_content.coursechapter_id": coursechapter_id}) if coursechapter: # verify course rights @@ -84,57 +79,44 @@ async def get_coursechapter(request: Request,coursechapter_id: str, current_user status_code=status.HTTP_409_CONFLICT, detail="CourseChapter does not exist") -async def update_coursechapter(request: Request,coursechapter_object: CourseChapter, coursechapter_id: str, current_user: PublicUser): - coursechapters = request.app.db["coursechapters"] +async def update_coursechapter(request: Request, coursechapter_object: CourseChapter, coursechapter_id: str, current_user: PublicUser): + courses = request.app.db["courses"] - coursechapter = await coursechapters.find_one( - {"coursechapter_id": coursechapter_id}) + coursechapter = await courses.find_one( + {"chapters_content.coursechapter_id": coursechapter_id}) if coursechapter: + # verify course rights await verify_rights(request, coursechapter["course_id"], current_user, "update") - creationDate = coursechapter["creationDate"] - # get today's date - datetime_object = datetime.now() + coursechapter = CourseChapterInDB(coursechapter_id=coursechapter_id, creationDate=str( + datetime.now()), updateDate=str(datetime.now()), course_id=coursechapter["course_id"], **coursechapter_object.dict()) - updated_coursechapter = CourseChapterInDB( - coursechapter_id=coursechapter_id, creationDate=creationDate, course_id=coursechapter["course_id"], updateDate=str(datetime_object), **coursechapter_object.dict()) + courses.update_one({"chapters_content.coursechapter_id": coursechapter_id}, { + "$set": {"chapters_content.$": coursechapter.dict()}}) - await coursechapters.update_one({"coursechapter_id": coursechapter_id}, { - "$set": updated_coursechapter.dict()}) - - return CourseChapterInDB(**updated_coursechapter.dict()) + return coursechapter else: raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="Coursechapter does not exist") -async def delete_coursechapter(request: Request,coursechapter_id: str, current_user: PublicUser): - - coursechapters = request.app.db["coursechapters"] +async def delete_coursechapter(request: Request, coursechapter_id: str, current_user: PublicUser): courses = request.app.db["courses"] - coursechapter = await coursechapters.find_one( - {"coursechapter_id": coursechapter_id}) + course = await courses.find_one( + {"chapters_content.coursechapter_id": coursechapter_id}) - if coursechapter: + if course: # verify course rights - await verify_rights(request, coursechapter["course_id"], current_user, "delete") + await verify_rights(request, course["course_id"], current_user, "delete") - isDeleted = await coursechapters.delete_one( - {"coursechapter_id": coursechapter_id}) + courses.update_one({"chapters_content.coursechapter_id": coursechapter_id}, { + "$pull": {"chapters_content": {"coursechapter_id": coursechapter_id}}}) - # Remove coursechapter from course - await courses.update_one({"course_id": coursechapter["course_id"]}, { - "$pull": {"chapters": coursechapter_id}}) - - if isDeleted: - return {"detail": "coursechapter deleted"} - else: - raise HTTPException( - status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database") + return {"message": "Coursechapter deleted"} else: raise HTTPException( @@ -145,43 +127,44 @@ async def delete_coursechapter(request: Request,coursechapter_id: str, current_ #################################################### -async def get_coursechapters(request: Request,course_id: str, page: int = 1, limit: int = 10): - courses = request.app.db["coursechapters"] - # TODO : Get only courses that user is admin/has roles of - # get all courses from database - all_coursechapters = courses.find({"course_id": course_id}).sort( - "name", 1).skip(10 * (page - 1)).limit(limit) +async def get_coursechapters(request: Request, course_id: str, page: int = 1, limit: int = 10): + courses = request.app.db["courses"] - return [json.loads(json.dumps(coursechapter, default=str)) for coursechapter in await all_coursechapters.to_list(length=100)] + course = await courses.find_one({"course_id": course_id}) + + if course: + course = Course(**course) + coursechapters = course.chapters_content + + return coursechapters async def get_coursechapters_meta(request: Request, course_id: str, current_user: PublicUser): - coursechapters = request.app.db["coursechapters"] courses = request.app.db["courses"] activities = request.app.db["activities"] - coursechapters = coursechapters.find( - {"course_id": course_id}).sort("name", 1) + coursechapters = await courses.find_one({"course_id": course_id}, {"chapters": 1, "chapters_content": 1, "_id": 0}) - course = await courses.find_one({"course_id": course_id}) - course = Course(**course) # type: ignore + coursechapters = coursechapters # activities coursechapter_activityIds_global = [] # chapters chapters = {} - for coursechapter in await coursechapters.to_list(length=100): - coursechapter = CourseChapterInDB(**coursechapter) - coursechapter_activityIds = [] + if coursechapters["chapters_content"]: + for coursechapter in coursechapters["chapters_content"]: - for activity in coursechapter.activities: - coursechapter_activityIds.append(activity) - coursechapter_activityIds_global.append(activity) + coursechapter = CourseChapterInDB(**coursechapter) + coursechapter_activityIds = [] - chapters[coursechapter.coursechapter_id] = { - "id": coursechapter.coursechapter_id, "name": coursechapter.name, "activityIds": coursechapter_activityIds - } + for activity in coursechapter.activities: + coursechapter_activityIds.append(activity) + coursechapter_activityIds_global.append(activity) + + chapters[coursechapter.coursechapter_id] = { + "id": coursechapter.coursechapter_id, "name": coursechapter.name, "activityIds": coursechapter_activityIds + } # activities activities_list = {} @@ -193,37 +176,41 @@ async def get_coursechapters_meta(request: Request, course_id: str, current_user final = { "chapters": chapters, - "chapterOrder": course.chapters, + "chapterOrder": coursechapters["chapters"], "activities": activities_list } return final -async def update_coursechapters_meta(request: Request,course_id: str, coursechapters_metadata: CourseChapterMetaData, current_user: PublicUser): - coursechapters = request.app.db["coursechapters"] +async def update_coursechapters_meta(request: Request, course_id: str, coursechapters_metadata: CourseChapterMetaData, current_user: PublicUser): courses = request.app.db["courses"] # update chapters in course courseInDB = await courses.update_one({"course_id": course_id}, { - "$set": {"chapters": coursechapters_metadata.chapterOrder}}) + "$set": {"chapters": coursechapters_metadata.chapterOrder}}) if coursechapters_metadata.chapters is not None: for coursechapter_id, chapter_metadata in coursechapters_metadata.chapters.items(): - filter_query = {"coursechapter_id": coursechapter_id} - update_query = {"$set": {"activities": chapter_metadata["activityIds"]}} - result = await coursechapters.update_one(filter_query, update_query) + filter_query = { + "chapters_content.coursechapter_id": coursechapter_id} + update_query = { + "$set": { + "chapters_content.$.activities": chapter_metadata["activityIds"] + } + } + result = await courses.update_one(filter_query, update_query) if result.matched_count == 0: # handle error when no documents are matched by the filter query - print(f"No documents found for course chapter ID {coursechapter_id}") - + print( + f"No documents found for course chapter ID {coursechapter_id}") return {"detail": "coursechapters metadata updated"} #### Security #################################################### -async def verify_rights(request: Request,course_id: str, current_user: PublicUser, action: str): +async def verify_rights(request: Request, course_id: str, current_user: PublicUser, action: str): courses = request.app.db["courses"] course = await courses.find_one({"course_id": course_id}) diff --git a/src/services/courses/courses.py b/src/services/courses/courses.py index d0682379..d4ea6b4e 100644 --- a/src/services/courses/courses.py +++ b/src/services/courses/courses.py @@ -1,5 +1,5 @@ import json -from typing import List +from typing import List, Optional from uuid import uuid4 from pydantic import BaseModel from src.services.courses.activities.activities import ActivityInDB @@ -20,6 +20,7 @@ class Course(BaseModel): thumbnail: str public: bool chapters: List[str] + chapters_content: Optional[List] org_id: str @@ -71,7 +72,6 @@ async def get_course(request: Request, course_id: str, current_user: PublicUser) async def get_course_meta(request: Request, course_id: str, current_user: PublicUser): courses = request.app.db["courses"] - coursechapters = request.app.db["coursechapters"] trails = request.app.db["trails"] course = await courses.find_one({"course_id": course_id}) @@ -84,25 +84,26 @@ async def get_course_meta(request: Request, course_id: str, current_user: Public raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="Course does not exist") - coursechapters = coursechapters.find( - {"course_id": course_id}).sort("name", 1) + coursechapters = await courses.find_one({"course_id": course_id}, { + "chapters_content": 1, "_id": 0}) # activities coursechapter_activityIds_global = [] # chapters chapters = {} - for coursechapter in await coursechapters.to_list(length=100): - coursechapter = CourseChapterInDB(**coursechapter) - coursechapter_activityIds = [] + if coursechapters["chapters_content"]: + for coursechapter in coursechapters["chapters_content"]: + coursechapter = CourseChapterInDB(**coursechapter) + coursechapter_activityIds = [] - for activity in coursechapter.activities: - coursechapter_activityIds.append(activity) - coursechapter_activityIds_global.append(activity) + for activity in coursechapter.activities: + coursechapter_activityIds.append(activity) + coursechapter_activityIds_global.append(activity) - chapters[coursechapter.coursechapter_id] = { - "id": coursechapter.coursechapter_id, "name": coursechapter.name, "activityIds": coursechapter_activityIds - } + chapters[coursechapter.coursechapter_id] = { + "id": coursechapter.coursechapter_id, "name": coursechapter.name, "activityIds": coursechapter_activityIds + } # activities activities_list = {} @@ -144,16 +145,16 @@ async def create_course(request: Request, course_object: Course, org_id: str, cu # TODO(fix) : the implementation here is clearly not the best one (this entire function) course_object.org_id = org_id - await verify_user_rights_with_roles(request, "create", current_user.user_id, course_id,org_id) + course_object.chapters_content = [] + await verify_user_rights_with_roles(request, "create", current_user.user_id, course_id, org_id) - if thumbnail_file: name_in_disk = f"{course_id}_thumbnail_{uuid4()}.{thumbnail_file.filename.split('.')[-1]}" await upload_thumbnail(thumbnail_file, name_in_disk) course_object.thumbnail = name_in_disk course = CourseInDB(course_id=course_id, authors=[ - current_user.user_id], creationDate=str(datetime.now()), updateDate=str(datetime.now()), **course_object.dict()) + current_user.user_id], creationDate=str(datetime.now()), updateDate=str(datetime.now()), **course_object.dict()) course_in_db = await courses.insert_one(course.dict()) @@ -258,6 +259,7 @@ async def get_courses(request: Request, page: int = 1, limit: int = 10, org_id: return [json.loads(json.dumps(course, default=str)) for course in await all_courses.to_list(length=100)] + async def get_courses_orgslug(request: Request, page: int = 1, limit: int = 10, org_slug: str | None = None): courses = request.app.db["courses"] orgs = request.app.db["organizations"] @@ -269,7 +271,7 @@ async def get_courses_orgslug(request: Request, page: int = 1, limit: int = 10, if not org: raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail=f"Organization does not exist") - + # get all courses from database all_courses = courses.find({"org_id": org['org_id']}).sort( "name", 1).skip(10 * (page - 1)).limit(limit) @@ -277,7 +279,6 @@ async def get_courses_orgslug(request: Request, page: int = 1, limit: int = 10, return [json.loads(json.dumps(course, default=str)) for course in await all_courses.to_list(length=100)] - #### Security #################################################### diff --git a/src/services/trail.py b/src/services/trail.py index ee71c870..2e04df5e 100644 --- a/src/services/trail.py +++ b/src/services/trail.py @@ -181,10 +181,13 @@ async def add_course_to_trail(request: Request, user: PublicUser, orgslug: str, trail = await trails.find_one( {"user_id": user.user_id, "org_id": org["org_id"]}) + + if not trail: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Trail not found") + trail = TrailInDB(trail_id=f"trail_{uuid4()}", user_id=user.user_id, org_id=org["org_id"], courses=[]) + trail = await trails.insert_one(trail.dict()) + trail = await trails.find_one({"trail_id": trail.inserted_id}) # check if course is already present in the trail for element in trail["courses"]: