feat: init activities

This commit is contained in:
swve 2023-11-14 22:30:39 +01:00
parent ee07f49139
commit 727f17ba7c
9 changed files with 358 additions and 370 deletions

View file

@ -1,55 +1,69 @@
from typing import Literal, Optional from typing import Literal, Optional
from click import Option
from sqlalchemy import JSON, Column from sqlalchemy import JSON, Column
from sqlmodel import Field, Session, SQLModel, create_engine, select from sqlmodel import Field, Session, SQLModel, create_engine, select
from enum import Enum from enum import Enum
class ActivityTypeEnum(str, Enum): class ActivityTypeEnum(str, Enum):
VIDEO = "VIDEO" TYPE_VIDEO = "TYPE_VIDEO"
DOCUMENT = "DOCUMENT" TYPE_DOCUMENT = "TYPE_DOCUMENT"
DYNAMIC = "DYNAMIC" TYPE_DYNAMIC = "TYPE_DYNAMIC"
ASSESSMENT = "ASSESSMENT" TYPE_ASSESSMENT = "TYPE_ASSESSMENT"
CUSTOM = "CUSTOM" TYPE_CUSTOM = "TYPE_CUSTOM"
class ActivitySubTypeEnum(str, Enum): class ActivitySubTypeEnum(str, Enum):
# Dynamic # Dynamic
DYNAMIC_PAGE = "DYNAMIC_PAGE" SUBTYPE_DYNAMIC_PAGE = "SUBTYPE_DYNAMIC_PAGE"
# Video # Video
VIDEO_YOUTUBE = "VIDEO_YOUTUBE" SUBTYPE_VIDEO_YOUTUBE = "SUBTYPE_VIDEO_YOUTUBE"
VIDEO_HOSTED = "VIDEO_HOSTED" SUBTYPE_VIDEO_HOSTED = "SUBTYPE_VIDEO_HOSTED"
# Document # Document
DOCUMENT_PDF = "DOCUMENT_PDF" SUBTYPE_DOCUMENT_PDF = "SUBTYPE_DOCUMENT_PDF"
DOCUMENT_DOC = "DOCUMENT_GDOC" SUBTYPE_DOCUMENT_DOC = "SUBTYPE_DOCUMENT_DOC"
# Assessment # Assessment
ASSESSMENT_QUIZ = "ASSESSMENT_QUIZ" SUBTYPE_ASSESSMENT_QUIZ = "SUBTYPE_ASSESSMENT_QUIZ"
# Custom # Custom
CUSTOM = "CUSTOM" SUBTYPE_CUSTOM = "SUBTYPE_CUSTOM"
class ActivityBase(SQLModel): class ActivityBase(SQLModel):
name: str name: str
activity_type: ActivityTypeEnum = ActivityTypeEnum.CUSTOM activity_type: ActivityTypeEnum = ActivityTypeEnum.TYPE_CUSTOM
activity_sub_type: ActivitySubTypeEnum = ActivitySubTypeEnum.CUSTOM activity_sub_type: ActivitySubTypeEnum = ActivitySubTypeEnum.SUBTYPE_CUSTOM
slug: str
content: dict = Field(default={}, sa_column=Column(JSON)) content: dict = Field(default={}, sa_column=Column(JSON))
published_version: int published_version: int
version: int version: int
org_id: int = Field(default=None, foreign_key="organization.id")
course_id: int = Field(default=None, foreign_key="course.id") course_id: int = Field(default=None, foreign_key="course.id")
class Activity(ActivityBase, table=True): class Activity(ActivityBase, table=True):
id: Optional[int] = Field(default=None, primary_key=True) id: Optional[int] = Field(default=None, primary_key=True)
activity_uuid: str org_id: int = Field(default=None, foreign_key="organization.id")
creation_date: str activity_uuid: str = ""
update_date: str creation_date: str = ""
update_date: str = ""
class ActivityCreate(ActivityBase): class ActivityCreate(ActivityBase):
order: int
org_id: int = Field(default=None, foreign_key="organization.id")
course_id: int = Field(default=None, foreign_key="course.id")
chapter_id : int
pass pass
class ActivityUpdate(ActivityBase):
activity_id: int
name: Optional[str]
activity_type: Optional[ActivityTypeEnum]
activity_sub_type: Optional[ActivitySubTypeEnum]
content: dict = Field(default={}, sa_column=Column(JSON))
published_version: Optional[int]
version: Optional[int]
class ActivityRead(ActivityBase): class ActivityRead(ActivityBase):
id: int id: int
activity_uuid: str activity_uuid: str

View file

@ -4,10 +4,10 @@ from sqlmodel import Field, SQLModel
class OrganizationBase(SQLModel): class OrganizationBase(SQLModel):
name: str name: str
description: Optional[str] = "" description: Optional[str]
slug: str slug: str
email: str email: str
logo_image: Optional[str] = "" logo_image: Optional[str]
class Organization(OrganizationBase, table=True): class Organization(OrganizationBase, table=True):

View file

@ -5,9 +5,9 @@ from sqlmodel import Field, SQLModel
class RoleTypeEnum(str, Enum): class RoleTypeEnum(str, Enum):
ORGANIZATION = "ORGANIZATION" TYPE_ORGANIZATION = "TYPE_ORGANIZATION"
ORGANIZATION_API_TOKEN = "ORGANIZATION_API_TOKEN" TYPE_ORGANIZATION_API_TOKEN = "TYPE_ORGANIZATION_API_TOKEN"
GLOBAL = "GLOBAL" TYPE_GLOBAL = "TYPE_GLOBAL"
class RoleBase(SQLModel): class RoleBase(SQLModel):
@ -19,7 +19,7 @@ class RoleBase(SQLModel):
class Role(RoleBase, table=True): class Role(RoleBase, table=True):
id: Optional[int] = Field(default=None, primary_key=True) id: Optional[int] = Field(default=None, primary_key=True)
org_id: int = Field(default=None, foreign_key="organization.id") org_id: int = Field(default=None, foreign_key="organization.id")
role_type: RoleTypeEnum = RoleTypeEnum.ORGANIZATION role_type: RoleTypeEnum = RoleTypeEnum.TYPE_GLOBAL
role_uuid: str = "" role_uuid: str = ""
creation_date: str = "" creation_date: str = ""
update_date: str = "" update_date: str = ""

View file

@ -1,4 +1,7 @@
from fastapi import APIRouter, Depends, UploadFile, Form, Request from fastapi import APIRouter, Depends, UploadFile, Form, Request
from src.db.activities import ActivityCreate, ActivityUpdate
from src.db.users import PublicUser
from src.core.events.database import get_db_session
from src.services.courses.activities.activities import ( from src.services.courses.activities.activities import (
Activity, Activity,
create_activity, create_activity,
@ -14,7 +17,6 @@ from src.services.courses.activities.video import (
create_external_video_activity, create_external_video_activity,
create_video_activity, create_video_activity,
) )
from src.services.users.schemas.users import PublicUser
router = APIRouter() router = APIRouter()
@ -22,17 +24,14 @@ router = APIRouter()
@router.post("/") @router.post("/")
async def api_create_activity( async def api_create_activity(
request: Request, request: Request,
activity_object: Activity, activity_object: ActivityCreate,
org_id: str,
coursechapter_id: str,
current_user: PublicUser = Depends(get_current_user), current_user: PublicUser = Depends(get_current_user),
db_session=Depends(get_db_session),
): ):
""" """
Create new activity Create new activity
""" """
return await create_activity( return await create_activity(request, activity_object, current_user, db_session)
request, activity_object, org_id, coursechapter_id, current_user
)
@router.get("/{activity_id}") @router.get("/{activity_id}")
@ -40,11 +39,14 @@ async def api_get_activity(
request: Request, request: Request,
activity_id: str, activity_id: str,
current_user: PublicUser = Depends(get_current_user), current_user: PublicUser = Depends(get_current_user),
db_session=Depends(get_db_session),
): ):
""" """
Get single activity by activity_id Get single activity by activity_id
""" """
return await get_activity(request, activity_id, current_user=current_user) return await get_activity(
request, activity_id, current_user=current_user, db_session=db_session
)
@router.get("/coursechapter/{coursechapter_id}") @router.get("/coursechapter/{coursechapter_id}")
@ -52,24 +54,25 @@ async def api_get_activities(
request: Request, request: Request,
coursechapter_id: str, coursechapter_id: str,
current_user: PublicUser = Depends(get_current_user), current_user: PublicUser = Depends(get_current_user),
db_session=Depends(get_db_session),
): ):
""" """
Get CourseChapter activities Get CourseChapter activities
""" """
return await get_activities(request, coursechapter_id, current_user) return await get_activities(request, coursechapter_id, current_user, db_session)
@router.put("/{activity_id}") @router.put("/")
async def api_update_activity( async def api_update_activity(
request: Request, request: Request,
activity_object: Activity, activity_object: ActivityUpdate,
activity_id: str,
current_user: PublicUser = Depends(get_current_user), current_user: PublicUser = Depends(get_current_user),
db_session=Depends(get_db_session),
): ):
""" """
Update activity by activity_id Update activity by activity_id
""" """
return await update_activity(request, activity_object, activity_id, current_user) return await update_activity(request, activity_object, current_user, db_session)
@router.delete("/{activity_id}") @router.delete("/{activity_id}")
@ -77,11 +80,12 @@ async def api_delete_activity(
request: Request, request: Request,
activity_id: str, activity_id: str,
current_user: PublicUser = Depends(get_current_user), current_user: PublicUser = Depends(get_current_user),
db_session=Depends(get_db_session),
): ):
""" """
Delete activity by activity_id Delete activity by activity_id
""" """
return await delete_activity(request, activity_id, current_user) return await delete_activity(request, activity_id, current_user, db_session)
# Video activity # Video activity
@ -91,15 +95,21 @@ async def api_delete_activity(
async def api_create_video_activity( async def api_create_video_activity(
request: Request, request: Request,
name: str = Form(), name: str = Form(),
coursechapter_id: str = Form(), chapter_id: str = Form(),
current_user: PublicUser = Depends(get_current_user), current_user: PublicUser = Depends(get_current_user),
video_file: UploadFile | None = None, video_file: UploadFile | None = None,
db_session=Depends(get_db_session),
): ):
""" """
Create new activity Create new activity
""" """
return await create_video_activity( return await create_video_activity(
request, name, coursechapter_id, current_user, video_file request,
name,
chapter_id,
current_user,
db_session,
video_file,
) )
@ -108,24 +118,28 @@ async def api_create_external_video_activity(
request: Request, request: Request,
external_video: ExternalVideo, external_video: ExternalVideo,
current_user: PublicUser = Depends(get_current_user), current_user: PublicUser = Depends(get_current_user),
db_session=Depends(get_db_session),
): ):
""" """
Create new activity Create new activity
""" """
return await create_external_video_activity(request, current_user, external_video) return await create_external_video_activity(
request, current_user, external_video, db_session
)
@router.post("/documentpdf") @router.post("/documentpdf")
async def api_create_documentpdf_activity( async def api_create_documentpdf_activity(
request: Request, request: Request,
name: str = Form(), name: str = Form(),
coursechapter_id: str = Form(), chapter_id: str = Form(),
current_user: PublicUser = Depends(get_current_user), current_user: PublicUser = Depends(get_current_user),
pdf_file: UploadFile | None = None, pdf_file: UploadFile | None = None,
db_session=Depends(get_db_session),
): ):
""" """
Create new activity Create new activity
""" """
return await create_documentpdf_activity( return await create_documentpdf_activity(
request, name, coursechapter_id, current_user, pdf_file request, name, chapter_id, current_user, db_session, pdf_file
) )

View file

@ -1,35 +1,21 @@
import stat
from typing import Literal from typing import Literal
from pydantic import BaseModel from pydantic import BaseModel
from sqlmodel import Session, select
from src.db.organizations import Organization
from src import db
from src.db.activities import ActivityCreate, Activity, ActivityRead, ActivityUpdate
from src.db.chapter_activities import ChapterActivity
from src.security.rbac.rbac import ( from src.security.rbac.rbac import (
authorization_verify_based_on_roles, authorization_verify_based_on_roles,
authorization_verify_if_element_is_public, authorization_verify_if_element_is_public,
authorization_verify_if_user_is_anon, authorization_verify_if_user_is_anon,
) )
from src.services.users.schemas.users import AnonymousUser, PublicUser from src.db.users import AnonymousUser, PublicUser
from fastapi import HTTPException, status, Request from fastapi import HTTPException, status, Request
from uuid import uuid4 from uuid import uuid4
from datetime import datetime from datetime import datetime
#### Classes ####################################################
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
#### Classes ####################################################
#################################################### ####################################################
# CRUD # CRUD
@ -38,148 +24,130 @@ class ActivityInDB(Activity):
async def create_activity( async def create_activity(
request: Request, request: Request,
activity_object: Activity, activity_object: ActivityCreate,
org_id: str,
coursechapter_id: str,
current_user: PublicUser, current_user: PublicUser,
db_session: Session,
): ):
activities = request.app.db["activities"] activity = Activity.from_orm(activity_object)
courses = request.app.db["courses"]
users = request.app.db["users"]
# get user # CHeck if org exists
user = await users.find_one({"user_id": current_user.user_id}) statement = select(Organization).where(Organization.id == activity_object.org_id)
org = db_session.exec(statement).first()
# generate activity_id if not org:
activity_id = str(f"activity_{uuid4()}") raise HTTPException(
status_code=404,
detail="Organization not found",
)
# verify activity rights activity.activity_uuid = str(f"activity_{uuid4()}")
await authorization_verify_based_on_roles( activity.creation_date = str(datetime.now())
request, activity.update_date = str(datetime.now())
current_user.user_id,
"create", # Insert Activity in DB
user["roles"], db_session.add(activity)
activity_id, db_session.commit()
db_session.refresh(activity)
# Add activity to chapter
activity_chapter = ChapterActivity(
chapter_id=activity_object.chapter_id,
activity_id=activity.id is not None,
course_id=activity_object.course_id,
org_id=activity_object.org_id,
creation_date=str(datetime.now()),
update_date=str(datetime.now()),
order=activity_object.order,
) )
# get course_id from activity # Insert ChapterActivity link in DB
course = await courses.find_one({"chapters": coursechapter_id}) db_session.add(activity_chapter)
db_session.commit()
db_session.refresh(activity_chapter)
# create activity return ActivityRead.from_orm(activity)
activity = ActivityInDB(
**activity_object.dict(),
creationDate=str(datetime.now()),
coursechapter_id=coursechapter_id,
updateDate=str(datetime.now()),
activity_id=activity_id,
org_id=org_id,
course_id=course["course_id"],
)
await activities.insert_one(activity.dict())
# update chapter
await courses.update_one(
{"chapters_content.coursechapter_id": coursechapter_id},
{"$addToSet": {"chapters_content.$.activities": activity_id}},
)
return activity
async def get_activity(request: Request, activity_id: str, current_user: PublicUser): async def get_activity(
activities = request.app.db["activities"] request: Request,
courses = request.app.db["courses"] activity_id: str,
current_user: PublicUser,
activity = await activities.find_one({"activity_id": activity_id}) db_session: Session,
):
# get course_id from activity statement = select(Activity).where(Activity.id == activity_id)
coursechapter_id = activity["coursechapter_id"] activity = db_session.exec(statement).first()
await courses.find_one({"chapters": coursechapter_id})
# verify course rights
await verify_rights(request, activity["course_id"], current_user, "read")
if not activity: if not activity:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist" status_code=404,
detail="Activity not found",
) )
activity = ActivityInDB(**activity)
return activity return activity
async def update_activity( async def update_activity(
request: Request, request: Request,
activity_object: Activity, activity_object: ActivityUpdate,
activity_id: str,
current_user: PublicUser, current_user: PublicUser,
db_session: Session,
): ):
activities = request.app.db["activities"] statement = select(Activity).where(Activity.id == activity_object.activity_id)
activity = db_session.exec(statement).first()
activity = await activities.find_one({"activity_id": activity_id})
# verify course rights
await verify_rights(request, activity_id, current_user, "update")
if activity:
creationDate = activity["creationDate"]
# get today's date
datetime_object = datetime.now()
updated_course = ActivityInDB(
activity_id=activity_id,
coursechapter_id=activity["coursechapter_id"],
creationDate=creationDate,
updateDate=str(datetime_object),
course_id=activity["course_id"],
org_id=activity["org_id"],
**activity_object.dict(),
)
await activities.update_one(
{"activity_id": activity_id}, {"$set": updated_course.dict()}
)
return ActivityInDB(**updated_course.dict())
else:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="activity does not exist"
)
async def delete_activity(request: Request, activity_id: str, current_user: PublicUser):
activities = request.app.db["activities"]
activity = await activities.find_one({"activity_id": activity_id})
# verify course rights
await verify_rights(request, activity_id, current_user, "delete")
if not activity: if not activity:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="activity does not exist" status_code=404,
detail="Activity not found",
) )
# Remove Activity del activity_object.activity_id
isDeleted = await activities.delete_one({"activity_id": activity_id})
# Remove Activity from chapter # Update only the fields that were passed in
courses = request.app.db["courses"] for var, value in vars(activity_object).items():
isDeletedFromChapter = await courses.update_one( if value is not None:
{"chapters_content.activities": activity_id}, setattr(activity, var, value)
{"$pull": {"chapters_content.$.activities": activity_id}},
)
if isDeleted and isDeletedFromChapter: db_session.add(activity)
return {"detail": "Activity deleted"} db_session.commit()
else: db_session.refresh(activity)
return activity
async def delete_activity(
request: Request,
activity_id: str,
current_user: PublicUser,
db_session: Session,
):
statement = select(Activity).where(Activity.id == activity_id)
activity = db_session.exec(statement).first()
if not activity:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE, status_code=404,
detail="Unavailable database", detail="Activity not found",
) )
# Delete activity from chapter
statement = select(ChapterActivity).where(
ChapterActivity.activity_id == activity_id
)
activity_chapter = db_session.exec(statement).first()
if not activity_chapter:
raise HTTPException(
status_code=404,
detail="Activity not found in chapter",
)
db_session.delete(activity_chapter)
db_session.delete(activity)
db_session.commit()
return {"detail": "Activity deleted"}
#################################################### ####################################################
# Misc # Misc
@ -187,64 +155,20 @@ async def delete_activity(request: Request, activity_id: str, current_user: Publ
async def get_activities( async def get_activities(
request: Request, coursechapter_id: str, current_user: PublicUser request: Request,
coursechapter_id: str,
current_user: PublicUser,
db_session: Session,
): ):
activities = request.app.db["activities"] statement = select(ChapterActivity).where(
ChapterActivity.chapter_id == coursechapter_id
activities = activities.find({"coursechapter_id": coursechapter_id}) )
activities = db_session.exec(statement).all()
if not activities: if not activities:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist" status_code=404,
detail="No activities found",
) )
activities = [
ActivityInDB(**activity) for activity in await activities.to_list(length=100)
]
return activities return activities
#### Security ####################################################
async def verify_rights(
request: Request,
activity_id: str, # course_id in case of read
current_user: PublicUser | AnonymousUser,
action: Literal["create", "read", "update", "delete"],
):
if action == "read":
if current_user.user_id == "anonymous":
await authorization_verify_if_element_is_public(
request, activity_id, current_user.user_id, action
)
else:
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)
await authorization_verify_based_on_roles(
request,
current_user.user_id,
action,
user["roles"],
activity_id,
)
else:
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)
await authorization_verify_based_on_roles(
request,
current_user.user_id,
action,
user["roles"],
activity_id,
)
#### Security ####################################################

View file

@ -1,7 +1,16 @@
from sqlmodel import Session, select
from src.db.chapters import Chapter
from src.db.activities import (
Activity,
ActivityRead,
ActivitySubTypeEnum,
ActivityTypeEnum,
)
from src.db.chapter_activities import ChapterActivity
from src.db.course_chapters import CourseChapter
from src.db.users import PublicUser
from src.security.rbac.rbac import authorization_verify_based_on_roles from src.security.rbac.rbac import authorization_verify_based_on_roles
from src.services.courses.activities.uploads.pdfs import upload_pdf from src.services.courses.activities.uploads.pdfs import upload_pdf
from src.services.users.users import PublicUser
from src.services.courses.activities.activities import ActivityInDB
from fastapi import HTTPException, status, UploadFile, Request from fastapi import HTTPException, status, UploadFile, Request
from uuid import uuid4 from uuid import uuid4
from datetime import datetime from datetime import datetime
@ -10,26 +19,35 @@ from datetime import datetime
async def create_documentpdf_activity( async def create_documentpdf_activity(
request: Request, request: Request,
name: str, name: str,
coursechapter_id: str, chapter_id: str,
current_user: PublicUser, current_user: PublicUser,
db_session: Session,
pdf_file: UploadFile | None = None, pdf_file: UploadFile | None = None,
): ):
activities = request.app.db["activities"] # get chapter_id
courses = request.app.db["courses"] statement = select(Chapter).where(Chapter.id == chapter_id)
users = request.app.db["users"] chapter = db_session.exec(statement).first()
# get user if not chapter:
user = await users.find_one({"user_id": current_user.user_id}) raise HTTPException(
status_code=404,
detail="Chapter not found",
)
# generate activity_id statement = select(CourseChapter).where(CourseChapter.chapter_id == chapter_id)
activity_id = str(f"activity_{uuid4()}") coursechapter = db_session.exec(statement).first()
# get org_id from course if not coursechapter:
coursechapter = await courses.find_one( raise HTTPException(
{"chapters_content.coursechapter_id": coursechapter_id} status_code=404,
) detail="CourseChapter not found",
)
org_id = coursechapter["org_id"] # get org_id
org_id = coursechapter.id
# create activity uuid
activity_uuid = f"activity_{uuid4()}"
# check if pdf_file is not None # check if pdf_file is not None
if not pdf_file: if not pdf_file:
@ -51,45 +69,48 @@ async def create_documentpdf_activity(
status_code=status.HTTP_409_CONFLICT, detail="Pdf : No pdf file provided" status_code=status.HTTP_409_CONFLICT, detail="Pdf : No pdf file provided"
) )
activity_object = ActivityInDB( # Create activity
org_id=org_id, activity = Activity(
activity_id=activity_id,
coursechapter_id=coursechapter_id,
name=name, name=name,
type="documentpdf", activity_type=ActivityTypeEnum.TYPE_DOCUMENT,
course_id=coursechapter["course_id"], activity_sub_type=ActivitySubTypeEnum.SUBTYPE_DOCUMENT_PDF,
content={ content={
"documentpdf": { "filename": "documentpdf." + pdf_format,
"filename": "documentpdf." + pdf_format, "activity_uuid": activity_uuid,
"activity_id": activity_id,
}
}, },
creationDate=str(datetime.now()), published_version=1,
updateDate=str(datetime.now()), version=1,
org_id=org_id is not None,
course_id=coursechapter.course_id,
activity_uuid=activity_uuid,
creation_date=str(datetime.now()),
update_date=str(datetime.now()),
) )
await authorization_verify_based_on_roles( # Insert Activity in DB
request, db_session.add(activity)
current_user.user_id, db_session.commit()
"create", db_session.refresh(activity)
user["roles"],
activity_id,
)
# create activity # Add activity to chapter
activity = ActivityInDB(**activity_object.dict()) activity_chapter = ChapterActivity(
await activities.insert_one(activity.dict()) chapter_id=(int(chapter_id)),
activity_id=activity.id is not None,
course_id=coursechapter.course_id,
org_id=coursechapter.org_id,
creation_date=str(datetime.now()),
update_date=str(datetime.now()),
order=1,
)
# upload pdf # upload pdf
if pdf_file: if pdf_file:
# get pdffile format # get pdffile format
await upload_pdf(pdf_file, activity_id, org_id, coursechapter["course_id"]) await upload_pdf(pdf_file, activity.id, org_id, coursechapter.course_id)
# todo : choose whether to update the chapter or not # Insert ChapterActivity link in DB
# update chapter db_session.add(activity_chapter)
await courses.update_one( db_session.commit()
{"chapters_content.coursechapter_id": coursechapter_id}, db_session.refresh(activity_chapter)
{"$addToSet": {"chapters_content.$.activities": activity_id}},
)
return activity return ActivityRead.from_orm(activity)

View file

@ -1,12 +1,16 @@
from typing import Literal from typing import Literal
from pydantic import BaseModel from pydantic import BaseModel
from sqlmodel import Session, select
from src.db.chapters import Chapter
from src.db.activities import Activity, ActivityRead, ActivitySubTypeEnum, ActivityTypeEnum
from src.db.chapter_activities import ChapterActivity
from src.db.course_chapters import CourseChapter
from src.db.users import PublicUser
from src.security.rbac.rbac import ( from src.security.rbac.rbac import (
authorization_verify_based_on_roles, authorization_verify_based_on_roles,
) )
from src.services.courses.activities.uploads.videos import upload_video from src.services.courses.activities.uploads.videos import upload_video
from src.services.users.users import PublicUser
from src.services.courses.activities.activities import ActivityInDB
from fastapi import HTTPException, status, UploadFile, Request from fastapi import HTTPException, status, UploadFile, Request
from uuid import uuid4 from uuid import uuid4
from datetime import datetime from datetime import datetime
@ -15,32 +19,32 @@ from datetime import datetime
async def create_video_activity( async def create_video_activity(
request: Request, request: Request,
name: str, name: str,
coursechapter_id: str, chapter_id: str,
current_user: PublicUser, current_user: PublicUser,
db_session: Session,
video_file: UploadFile | None = None, video_file: UploadFile | None = None,
): ):
activities = request.app.db["activities"] # get chapter_id
courses = request.app.db["courses"] statement = select(Chapter).where(Chapter.id == chapter_id)
users = request.app.db["users"] chapter = db_session.exec(statement).first()
# get user if not chapter:
user = await users.find_one({"user_id": current_user.user_id}) raise HTTPException(
status_code=404,
detail="Chapter not found",
)
# generate activity_id statement = select(CourseChapter).where(CourseChapter.chapter_id == chapter_id)
activity_id = str(f"activity_{uuid4()}") coursechapter = db_session.exec(statement).first()
# get org_id from course
coursechapter = await courses.find_one(
{"chapters_content.coursechapter_id": coursechapter_id}
)
if not coursechapter: if not coursechapter:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, status_code=404,
detail="CourseChapter : No coursechapter found", detail="CourseChapter not found",
) )
org_id = coursechapter["org_id"] # generate activity_uuid
activity_uuid = str(f"activity_{uuid4()}")
# check if video_file is not None # check if video_file is not None
if not video_file: if not video_file:
@ -64,55 +68,57 @@ async def create_video_activity(
detail="Video : No video file provided", detail="Video : No video file provided",
) )
activity_object = ActivityInDB( activity_object = Activity(
org_id=org_id,
activity_id=activity_id,
coursechapter_id=coursechapter_id,
course_id=coursechapter["course_id"],
name=name, name=name,
type="video", activity_type=ActivityTypeEnum.TYPE_VIDEO,
activity_sub_type=ActivitySubTypeEnum.SUBTYPE_VIDEO_HOSTED,
activity_uuid=activity_uuid,
published_version=1,
content={ content={
"video": { "filename": "video." + video_format,
"filename": "video." + video_format, "activity_uuid": activity_uuid,
"activity_id": activity_id,
}
}, },
creationDate=str(datetime.now()), version=1,
updateDate=str(datetime.now()), creation_date=str(datetime.now()),
) update_date=str(datetime.now()),
await authorization_verify_based_on_roles(
request,
current_user.user_id,
"create",
user["roles"],
activity_id,
) )
# create activity # create activity
activity = ActivityInDB(**activity_object.dict()) activity = Activity.from_orm(activity_object)
await activities.insert_one(activity.dict()) db_session.add(activity)
db_session.commit()
db_session.refresh(activity)
# upload video # upload video
if video_file: if video_file:
# get videofile format # get videofile format
await upload_video(video_file, activity_id, org_id, coursechapter["course_id"]) await upload_video(
video_file, activity.id, coursechapter.org_id, coursechapter.course_id
)
# todo : choose whether to update the chapter or not
# update chapter # update chapter
await courses.update_one( chapter_activity_object = ChapterActivity(
{"chapters_content.coursechapter_id": coursechapter_id}, chapter_id=coursechapter.id is not None,
{"$addToSet": {"chapters_content.$.activities": activity_id}}, activity_id=activity.id is not None,
course_id=coursechapter.course_id,
org_id=coursechapter.org_id,
creation_date=str(datetime.now()),
update_date=str(datetime.now()),
order=1,
) )
return activity # Insert ChapterActivity link in DB
db_session.add(chapter_activity_object)
db_session.commit()
db_session.refresh(chapter_activity_object)
return ActivityRead.from_orm(activity)
class ExternalVideo(BaseModel): class ExternalVideo(BaseModel):
name: str name: str
uri: str uri: str
type: Literal["youtube", "vimeo"] type: Literal["youtube", "vimeo"]
coursechapter_id: str chapter_id: str
class ExternalVideoInDB(BaseModel): class ExternalVideoInDB(BaseModel):
@ -123,65 +129,63 @@ async def create_external_video_activity(
request: Request, request: Request,
current_user: PublicUser, current_user: PublicUser,
data: ExternalVideo, data: ExternalVideo,
db_session: Session,
): ):
activities = request.app.db["activities"] # get chapter_id
courses = request.app.db["courses"] statement = select(Chapter).where(Chapter.id == data.chapter_id)
users = request.app.db["users"] chapter = db_session.exec(statement).first()
# get user if not chapter:
user = await users.find_one({"user_id": current_user.user_id}) raise HTTPException(
status_code=404,
detail="Chapter not found",
)
# generate activity_id statement = select(CourseChapter).where(CourseChapter.chapter_id == data.chapter_id)
activity_id = str(f"activity_{uuid4()}") coursechapter = db_session.exec(statement).first()
# get org_id from course
coursechapter = await courses.find_one(
{"chapters_content.coursechapter_id": data.coursechapter_id}
)
if not coursechapter: if not coursechapter:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, status_code=404,
detail="CourseChapter : No coursechapter found", detail="CourseChapter not found",
) )
org_id = coursechapter["org_id"] # generate activity_uuid
activity_uuid = str(f"activity_{uuid4()}")
activity_object = ActivityInDB( activity_object = Activity(
org_id=org_id,
activity_id=activity_id,
coursechapter_id=data.coursechapter_id,
name=data.name, name=data.name,
type="video", activity_type=ActivityTypeEnum.TYPE_VIDEO,
activity_sub_type=ActivitySubTypeEnum.SUBTYPE_VIDEO_YOUTUBE,
activity_uuid=activity_uuid,
published_version=1,
content={ content={
"external_video": { "uri": data.uri,
"uri": data.uri, "type": data.type,
"activity_id": activity_id, "activity_uuid": activity_uuid,
"type": data.type,
}
}, },
course_id=coursechapter["course_id"], version=1,
creationDate=str(datetime.now()), creation_date=str(datetime.now()),
updateDate=str(datetime.now()), update_date=str(datetime.now()),
)
await authorization_verify_based_on_roles(
request,
current_user.user_id,
"create",
user["roles"],
activity_id,
) )
# create activity # create activity
activity = ActivityInDB(**activity_object.dict()) activity = Activity.from_orm(activity_object)
await activities.insert_one(activity.dict()) db_session.add(activity)
db_session.commit()
db_session.refresh(activity)
# todo : choose whether to update the chapter or not
# update chapter # update chapter
await courses.update_one( chapter_activity_object = ChapterActivity(
{"chapters_content.coursechapter_id": data.coursechapter_id}, chapter_id=coursechapter.id is not None,
{"$addToSet": {"chapters_content.$.activities": activity_id}}, activity_id=activity.id is not None,
creation_date=str(datetime.now()),
update_date=str(datetime.now()),
order=1,
) )
return activity # Insert ChapterActivity link in DB
db_session.add(chapter_activity_object)
db_session.commit()
return ActivityRead.from_orm(activity)

View file

@ -10,11 +10,23 @@ from src.security.rbac.rbac import (
authorization_verify_if_user_is_anon, authorization_verify_if_user_is_anon,
) )
from src.services.courses.courses import Course from src.services.courses.courses import Course
from src.services.courses.activities.activities import ActivityInDB
from src.services.users.users import PublicUser 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): class CourseChapter(BaseModel):
name: str name: str
description: str description: str

View file

@ -12,7 +12,6 @@ from src.security.rbac.rbac import (
authorization_verify_if_element_is_public, authorization_verify_if_element_is_public,
authorization_verify_if_user_is_anon, authorization_verify_if_user_is_anon,
) )
from src.services.courses.activities.activities import ActivityInDB
from src.services.courses.thumbnails import upload_thumbnail from src.services.courses.thumbnails import upload_thumbnail
from fastapi import HTTPException, Request, status, UploadFile from fastapi import HTTPException, Request, status, UploadFile
from datetime import datetime from datetime import datetime