init : more chapters

This commit is contained in:
swve 2023-11-20 19:53:39 +01:00
parent 4fcc8aa77c
commit d95497e804

View file

@ -1,7 +1,19 @@
from datetime import datetime from datetime import datetime
from typing import List, Literal from typing import List
from uuid import uuid4 from uuid import uuid4
from pydantic import BaseModel from sqlmodel import Session, select
from src import db
from src.db.course_chapters import CourseChapter
from src.db.activities import Activity, ActivityRead
from src.db.chapter_activities import ChapterActivity
from src.db.chapters import (
Chapter,
ChapterCreate,
ChapterRead,
ChapterUpdate,
ChapterUpdateOrder,
DepreceatedChaptersRead,
)
from src.security.auth import non_public_endpoint from src.security.auth import non_public_endpoint
from src.security.rbac.rbac import ( from src.security.rbac.rbac import (
authorization_verify_based_on_roles, authorization_verify_based_on_roles,
@ -14,366 +26,454 @@ from src.services.users.users import PublicUser
from fastapi import HTTPException, status, Request from fastapi import HTTPException, status, Request
class Activity(BaseModel):
name: str
type: str
content: object
class ActivityInDB(Activity):
activity_id: str
course_id: str
coursechapter_id: str
org_id: str
creationDate: str
updateDate: str
class CourseChapter(BaseModel):
name: str
description: str
activities: list
class CourseChapterInDB(CourseChapter):
coursechapter_id: str
course_id: str
creationDate: str
updateDate: str
# Frontend
class CourseChapterMetaData(BaseModel):
chapterOrder: List[str]
chapters: dict
activities: object
#### Classes ####################################################
#################################################### ####################################################
# CRUD # CRUD
#################################################### ####################################################
async def create_coursechapter( async def create_chapter(
request: Request, request: Request,
coursechapter_object: CourseChapter, chapter_object: ChapterCreate,
course_id: str,
current_user: PublicUser, current_user: PublicUser,
): db_session: Session,
courses = request.app.db["courses"] ) -> ChapterRead:
users = request.app.db["users"] chapter = Chapter.from_orm(chapter_object)
# get course org_id and verify rights
await courses.find_one({"course_id": course_id})
user = await users.find_one({"user_id": current_user.user_id})
# generate coursechapter_id with uuid4 # complete chapter object
coursechapter_id = str(f"coursechapter_{uuid4()}") chapter.course_id = chapter_object.course_id
chapter.chapter_uuid = f"chapter_{uuid4()}"
chapter.creation_date = str(datetime.now())
chapter.update_date = str(datetime.now())
hasRoleRights = await authorization_verify_based_on_roles( # Find the last chapter in the course and add it to the list
request, current_user.user_id, "create", user["roles"], course_id statement = (
select(CourseChapter)
.where(CourseChapter.course_id == chapter.course_id)
.order_by(CourseChapter.order)
)
course_chapters = db_session.exec(statement).all()
# get last chapter order
last_order = course_chapters[-1].order if course_chapters else 0
to_be_used_order = last_order + 1
# Add chapter to database
db_session.add(chapter)
db_session.commit()
db_session.refresh(chapter)
chapter = ChapterRead(**chapter.dict(), activities=[])
# Check if COurseChapter link exists
statement = (
select(CourseChapter)
.where(CourseChapter.chapter_id == chapter.id)
.where(CourseChapter.course_id == chapter.course_id)
.where(CourseChapter.order == to_be_used_order)
)
course_chapter = db_session.exec(statement).first()
if not course_chapter:
# Add CourseChapter link
course_chapter = CourseChapter(
course_id=chapter.course_id,
chapter_id=chapter.id,
org_id=chapter.org_id,
creation_date=str(datetime.now()),
update_date=str(datetime.now()),
order=to_be_used_order,
) )
if not hasRoleRights: # Insert CourseChapter link in DB
raise HTTPException( db_session.add(course_chapter)
status_code=status.HTTP_409_CONFLICT, db_session.commit()
detail="Roles : Insufficient rights to perform this action",
)
coursechapter = CourseChapterInDB( return chapter
coursechapter_id=coursechapter_id,
creationDate=str(datetime.now()),
updateDate=str(datetime.now()),
course_id=course_id,
**coursechapter_object.dict(),
)
courses.update_one(
{"course_id": course_id},
{
"$addToSet": {
"chapters": coursechapter_id,
"chapters_content": coursechapter.dict(),
}
},
)
return coursechapter.dict()
async def get_coursechapter( async def get_chapter(
request: Request, coursechapter_id: str, current_user: PublicUser
):
courses = request.app.db["courses"]
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, "read")
coursechapter = CourseChapter(**coursechapter)
return coursechapter
else:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="CourseChapter does not exist"
)
async def update_coursechapter(
request: Request, request: Request,
coursechapter_object: CourseChapter, chapter_id: int,
coursechapter_id: str,
current_user: PublicUser, current_user: PublicUser,
): db_session: Session,
courses = request.app.db["courses"] ) -> ChapterRead:
statement = select(Chapter).where(Chapter.id == chapter_id)
chapter = db_session.exec(statement).first()
coursechapter = await courses.find_one( if not chapter:
{"chapters_content.coursechapter_id": coursechapter_id}
)
if coursechapter:
# verify course rights
await verify_rights(request, coursechapter["course_id"], current_user, "update")
coursechapter = CourseChapterInDB(
coursechapter_id=coursechapter_id,
creationDate=str(datetime.now()),
updateDate=str(datetime.now()),
course_id=coursechapter["course_id"],
**coursechapter_object.dict(),
)
courses.update_one(
{"chapters_content.coursechapter_id": coursechapter_id},
{"$set": {"chapters_content.$": coursechapter.dict()}},
)
return coursechapter
else:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Coursechapter does not exist" status_code=status.HTTP_409_CONFLICT, detail="Chapter does not exist"
) )
# Get activities for this chapter
async def delete_coursechapter( statement = (
request: Request, coursechapter_id: str, current_user: PublicUser select(Activity)
): .join(ChapterActivity, Activity.id == ChapterActivity.activity_id)
courses = request.app.db["courses"] .where(ChapterActivity.chapter_id == chapter_id)
.distinct(Activity.id)
course = await courses.find_one(
{"chapters_content.coursechapter_id": coursechapter_id}
) )
if course: activities = db_session.exec(statement).all()
# verify course rights
await verify_rights(request, course["course_id"], current_user, "delete")
# Remove coursechapter from course chapter = ChapterRead(
await courses.update_one( **chapter.dict(),
{"course_id": course["course_id"]}, activities=[ActivityRead(**activity.dict()) for activity in activities],
{"$pull": {"chapters": coursechapter_id}},
) )
await courses.update_one( return chapter
{"chapters_content.coursechapter_id": coursechapter_id},
{"$pull": {"chapters_content": {"coursechapter_id": coursechapter_id}}},
)
return {"message": "Coursechapter deleted"}
else: async def update_chapter(
request: Request,
chapter_object: ChapterUpdate,
current_user: PublicUser,
db_session: Session,
) -> ChapterRead:
statement = select(Chapter).where(Chapter.id == chapter_object.chapter_id)
chapter = db_session.exec(statement).first()
if not chapter:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist" status_code=status.HTTP_409_CONFLICT, detail="Chapter does not exist"
) )
# Update only the fields that were passed in
for var, value in vars(chapter_object).items():
if value is not None:
setattr(chapter, var, value)
chapter.update_date = str(datetime.now())
db_session.commit()
db_session.refresh(chapter)
chapter = ChapterRead(**chapter.dict())
return chapter
async def delete_chapter(
request: Request,
chapter_id: str,
current_user: PublicUser,
db_session: Session,
):
statement = select(Chapter).where(Chapter.id == chapter_id)
chapter = db_session.exec(statement).first()
if not chapter:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Chapter does not exist"
)
db_session.delete(chapter)
db_session.commit()
# Remove all linked activities
statement = select(ChapterActivity).where(ChapterActivity.chapter_id == chapter_id)
chapter_activities = db_session.exec(statement).all()
for chapter_activity in chapter_activities:
db_session.delete(chapter_activity)
db_session.commit()
return {"detail": "chapter deleted"}
#################################################### ####################################################
# Misc # Misc
#################################################### ####################################################
async def get_coursechapters( async def get_course_chapters(
request: Request, course_id: str, page: int = 1, limit: int = 10 request: Request,
): course_id: int,
courses = request.app.db["courses"] db_session: Session,
page: int = 1,
limit: int = 10,
) -> List[ChapterRead]:
statement = select(Chapter).where(Chapter.course_id == course_id)
chapters = db_session.exec(statement).all()
course = await courses.find_one({"course_id": course_id}) if not chapters:
if course:
course = Course(**course)
coursechapters = course.chapters_content
return coursechapters
async def get_coursechapters_meta(
request: Request, course_id: str, current_user: PublicUser
):
courses = request.app.db["courses"]
activities = request.app.db["activities"]
await non_public_endpoint(current_user)
await verify_rights(request, course_id, current_user, "read")
coursechapters = await courses.find_one(
{"course_id": course_id}, {"chapters": 1, "chapters_content": 1, "_id": 0}
)
coursechapters = coursechapters
if not coursechapters:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist" status_code=status.HTTP_409_CONFLICT, detail="Course do not have chapters"
) )
# activities chapters = [ChapterRead(**chapter.dict(), activities=[]) for chapter in chapters]
coursechapter_activityIds_global = []
# chapters # Get activities for each chapter
chapters = {} for chapter in chapters:
if coursechapters["chapters_content"]: statement = (
for coursechapter in coursechapters["chapters_content"]: select(ChapterActivity)
coursechapter = CourseChapterInDB(**coursechapter) .where(ChapterActivity.chapter_id == chapter.id)
coursechapter_activityIds = [] .order_by(ChapterActivity.order)
.distinct(ChapterActivity.id, ChapterActivity.order)
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 = {}
for activity in await activities.find(
{"activity_id": {"$in": coursechapter_activityIds_global}}
).to_list(length=100):
activity = ActivityInDB(**activity)
activities_list[activity.activity_id] = {
"id": activity.activity_id,
"name": activity.name,
"type": activity.type,
"content": activity.content,
}
final = {
"chapters": 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,
):
courses = request.app.db["courses"]
await verify_rights(request, course_id, current_user, "update")
# update chapters in course
await courses.update_one(
{"course_id": course_id},
{"$set": {"chapters": coursechapters_metadata.chapterOrder}},
) )
chapter_activities = db_session.exec(statement).all()
if coursechapters_metadata.chapters is not None: for chapter_activity in chapter_activities:
for ( statement = (
coursechapter_id, select(Activity)
chapter_metadata, .where(Activity.id == chapter_activity.activity_id)
) in coursechapters_metadata.chapters.items(): .distinct(Activity.id)
filter_query = {"chapters_content.coursechapter_id": coursechapter_id} )
update_query = { activity = db_session.exec(statement).first()
"$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}")
# update activities in coursechapters if activity:
activity = request.app.db["activities"] chapter.activities.append(ActivityRead(**activity.dict()))
if coursechapters_metadata.chapters is not None:
for (
coursechapter_id,
chapter_metadata,
) in coursechapters_metadata.chapters.items():
# Update coursechapter_id in activities
filter_query = {"activity_id": {"$in": chapter_metadata["activityIds"]}}
update_query = {"$set": {"coursechapter_id": coursechapter_id}}
result = await activity.update_many(filter_query, update_query) return chapters
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}")
return {"detail": "coursechapters metadata updated"}
#### Security #################################################### async def get_depreceated_course_chapters(
async def verify_rights(
request: Request, request: Request,
course_id: str, course_id: int,
current_user: PublicUser, current_user: PublicUser,
action: Literal["read", "update", "delete"], db_session: Session,
): ) -> DepreceatedChaptersRead:
courses = request.app.db["courses"] statement = select(Course).where(Course.id == course_id)
users = request.app.db["users"] course = db_session.exec(statement).first()
user = await users.find_one({"user_id": current_user.user_id})
course = await courses.find_one({"course_id": course_id})
if not course: if not course:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist" status_code=status.HTTP_409_CONFLICT, detail="Course does not exist"
) )
if action == "read": # Get chapters that are linked to his course and order them by order, using the order field in the CourseChapter table
if current_user.user_id == "anonymous": statement = (
await authorization_verify_if_element_is_public( select(Chapter)
request, course_id, current_user.user_id, action .join(CourseChapter, Chapter.id == CourseChapter.chapter_id)
.where(CourseChapter.course_id == course_id)
.order_by(CourseChapter.order)
.group_by(Chapter.id, CourseChapter.order)
) )
else: print("ded", statement)
users = request.app.db["users"] chapters = db_session.exec(statement).all()
user = await users.find_one({"user_id": current_user.user_id})
await authorization_verify_if_user_is_anon(current_user.user_id) chapters = [ChapterRead(**chapter.dict(), activities=[]) for chapter in chapters]
await authorization_verify_based_on_roles_and_authorship( # Get activities for each chapter
request, for chapter in chapters:
current_user.user_id, statement = (
action, select(Activity)
user["roles"], .join(ChapterActivity, Activity.id == ChapterActivity.activity_id)
course_id, .where(ChapterActivity.chapter_id == chapter.id)
.order_by(ChapterActivity.order)
.distinct(Activity.id, ChapterActivity.order)
) )
else: chapter_activities = db_session.exec(statement).all()
users = request.app.db["users"]
user = await users.find_one({"user_id": current_user.user_id})
await authorization_verify_if_user_is_anon(current_user.user_id) for chapter_activity in chapter_activities:
statement = (
select(Activity)
.join(ChapterActivity, Activity.id == ChapterActivity.activity_id)
.where(Activity.id == chapter_activity.id)
.distinct(Activity.id, ChapterActivity.order)
.order_by(ChapterActivity.order)
)
activity = db_session.exec(statement).first()
await authorization_verify_based_on_roles_and_authorship( if activity:
request, chapter.activities.append(ActivityRead(**activity.dict()))
current_user.user_id,
action, # Get a list of chapter ids
user["roles"], chapter_order: List[str] = [str(chapter.id) for chapter in chapters]
course_id,
# Get activities for each chapter
activities = []
for chapter_id in chapter_order:
# order by activity order
statement = (
select(Activity)
.join(ChapterActivity, Activity.id == ChapterActivity.activity_id)
.where(ChapterActivity.chapter_id == chapter_id)
.order_by(ChapterActivity.order)
.distinct(Activity.id, ChapterActivity.order)
)
chapter_activities = db_session.exec(statement).all()
activities.extend(chapter_activities)
result = DepreceatedChaptersRead(
chapter_order=chapter_order, chapters=chapters, activities=activities
) )
return result
#### Security ####################################################
async def reorder_chapters_and_activities(
request: Request,
course_id: int,
chapters_order: ChapterUpdateOrder,
current_user: PublicUser,
db_session: Session,
):
statement = select(Course).where(Course.id == course_id)
course = db_session.exec(statement).first()
if not course:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist"
)
###########
# Chapters
###########
# Delete CourseChapters that are not linked to chapter_id and activity_id and org_id and course_id
statement = (
select(CourseChapter)
.where(
CourseChapter.course_id == course_id, CourseChapter.org_id == course.org_id
)
.order_by(CourseChapter.order)
)
course_chapters = db_session.exec(statement).all()
chapter_ids_to_keep = [
chapter_order.chapter_id
for chapter_order in chapters_order.chapter_order_by_ids
]
for course_chapter in course_chapters:
if course_chapter.chapter_id not in chapter_ids_to_keep:
db_session.delete(course_chapter)
db_session.commit()
# Delete Chapters that are not in the list of chapters_order
statement = select(Chapter).where(Chapter.course_id == course_id)
chapters = db_session.exec(statement).all()
chapter_ids_to_keep = [
chapter_order.chapter_id
for chapter_order in chapters_order.chapter_order_by_ids
]
for chapter in chapters:
if chapter.id not in chapter_ids_to_keep:
db_session.delete(chapter)
db_session.commit()
# If links do not exists, create them
for chapter_order in chapters_order.chapter_order_by_ids:
statement = (
select(CourseChapter)
.where(
CourseChapter.chapter_id == chapter_order.chapter_id,
CourseChapter.course_id == course_id,
)
.order_by(CourseChapter.order)
)
course_chapter = db_session.exec(statement).first()
if not course_chapter:
# Add CourseChapter link
course_chapter = CourseChapter(
chapter_id=chapter_order.chapter_id,
course_id=course_id,
org_id=course.org_id,
creation_date=str(datetime.now()),
update_date=str(datetime.now()),
order=chapter_order.chapter_id,
)
# Insert CourseChapter link in DB
db_session.add(course_chapter)
db_session.commit()
# Update order of chapters
for chapter_order in chapters_order.chapter_order_by_ids:
statement = (
select(CourseChapter)
.where(
CourseChapter.chapter_id == chapter_order.chapter_id,
CourseChapter.course_id == course_id,
)
.order_by(CourseChapter.order)
)
course_chapter = db_session.exec(statement).first()
if course_chapter:
# Get the order from the index of the chapter_order_by_ids list
course_chapter.order = chapters_order.chapter_order_by_ids.index(
chapter_order
)
db_session.commit()
###########
# Activities
###########
# Delete ChapterActivities that are not linked to chapter_id and activity_id and org_id and course_id
statement = (
select(ChapterActivity)
.where(
ChapterActivity.course_id == course_id,
ChapterActivity.org_id == course.org_id,
)
.order_by(ChapterActivity.order)
)
chapter_activities = db_session.exec(statement).all()
activity_ids_to_keep = [
activity_order.activity_id
for chapter_order in chapters_order.chapter_order_by_ids
for activity_order in chapter_order.activities_order_by_ids
]
for chapter_activity in chapter_activities:
if chapter_activity.activity_id not in activity_ids_to_keep:
db_session.delete(chapter_activity)
db_session.commit()
# If links do not exists, create them
for chapter_order in chapters_order.chapter_order_by_ids:
for activity_order in chapter_order.activities_order_by_ids:
statement = (
select(ChapterActivity)
.where(
ChapterActivity.chapter_id == chapter_order.chapter_id,
ChapterActivity.activity_id == activity_order.activity_id,
)
.order_by(ChapterActivity.order)
)
chapter_activity = db_session.exec(statement).first()
if not chapter_activity:
# Add ChapterActivity link
chapter_activity = ChapterActivity(
chapter_id=chapter_order.chapter_id,
activity_id=activity_order.activity_id,
org_id=course.org_id,
course_id=course_id,
creation_date=str(datetime.now()),
update_date=str(datetime.now()),
order=activity_order.activity_id,
)
# Insert ChapterActivity link in DB
db_session.add(chapter_activity)
db_session.commit()
# Update order of activities
for chapter_order in chapters_order.chapter_order_by_ids:
for activity_order in chapter_order.activities_order_by_ids:
statement = (
select(ChapterActivity)
.where(
ChapterActivity.chapter_id == chapter_order.chapter_id,
ChapterActivity.activity_id == activity_order.activity_id,
)
.order_by(ChapterActivity.order)
)
chapter_activity = db_session.exec(statement).first()
if chapter_activity:
# Get the order from the index of the chapter_order_by_ids list
chapter_activity.order = chapter_order.activities_order_by_ids.index(
activity_order
)
db_session.commit()
return {"detail": "Chapters reordered"}