mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
wip: courses
This commit is contained in:
parent
afa8e4ea98
commit
ee07f49139
8 changed files with 334 additions and 378 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
fastapi==0.101.1
|
fastapi==0.104.1
|
||||||
pydantic>=1.8.0,<2.0.0
|
pydantic>=1.8.0,<2.0.0
|
||||||
sqlmodel==0.0.10
|
sqlmodel==0.0.10
|
||||||
uvicorn==0.23.2
|
uvicorn==0.23.2
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,40 @@
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from sqlmodel import Field, SQLModel
|
from sqlmodel import Field, SQLModel
|
||||||
|
|
||||||
|
|
||||||
class CourseBase(SQLModel):
|
class CourseBase(SQLModel):
|
||||||
name: str
|
name: str
|
||||||
description: Optional[str] = ""
|
description: Optional[str]
|
||||||
about: Optional[str] = ""
|
about: Optional[str]
|
||||||
course_slug: str
|
learnings: Optional[str]
|
||||||
learnings: Optional[str] = ""
|
tags: Optional[str]
|
||||||
tags: Optional[str] = ""
|
thumbnail_image: Optional[str]
|
||||||
thumbnail_image: Optional[str] = ""
|
|
||||||
public: bool
|
public: bool
|
||||||
|
|
||||||
|
|
||||||
class Course(CourseBase, table=True):
|
class Course(CourseBase, 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")
|
||||||
course_uuid: str
|
course_uuid: str = ""
|
||||||
creation_date: str
|
creation_date: str = ""
|
||||||
update_date: str
|
update_date: str = ""
|
||||||
|
|
||||||
|
|
||||||
class CourseCreate(CourseBase):
|
class CourseCreate(CourseBase):
|
||||||
|
org_id: int = Field(default=None, foreign_key="organization.id")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CourseUpdate(CourseBase):
|
||||||
|
course_id: int
|
||||||
|
name: str
|
||||||
|
description: Optional[str]
|
||||||
|
about: Optional[str]
|
||||||
|
learnings: Optional[str]
|
||||||
|
tags: Optional[str]
|
||||||
|
public: Optional[bool]
|
||||||
|
|
||||||
|
|
||||||
class CourseRead(CourseBase):
|
class CourseRead(CourseBase):
|
||||||
id: int
|
id: int
|
||||||
course_uuid: str
|
course_uuid: str
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ class RoleTypeEnum(str, Enum):
|
||||||
|
|
||||||
class RoleBase(SQLModel):
|
class RoleBase(SQLModel):
|
||||||
name: str
|
name: str
|
||||||
description: Optional[str] = ""
|
description: Optional[str]
|
||||||
rights: dict = Field(default={}, sa_column=Column(JSON))
|
rights: dict = Field(default={}, sa_column=Column(JSON))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -31,6 +31,6 @@ class RoleCreate(RoleBase):
|
||||||
|
|
||||||
class RoleUpdate(SQLModel):
|
class RoleUpdate(SQLModel):
|
||||||
role_id: int = Field(default=None, foreign_key="role.id")
|
role_id: int = Field(default=None, foreign_key="role.id")
|
||||||
name: Optional[str] = ""
|
name: Optional[str]
|
||||||
description: Optional[str] = ""
|
description: Optional[str]
|
||||||
rights: Optional[dict] = Field(default={}, sa_column=Column(JSON))
|
rights: Optional[dict] = Field(default={}, sa_column=Column(JSON))
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,10 @@ class UserRead(UserBase):
|
||||||
class PublicUser(UserRead):
|
class PublicUser(UserRead):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class AnonymousUser(UserRead):
|
||||||
|
id: str = "anonymous"
|
||||||
|
username: str = "anonymous"
|
||||||
|
|
||||||
class User(UserBase, table=True):
|
class User(UserBase, table=True):
|
||||||
id: Optional[int] = Field(default=None, primary_key=True)
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
password: str = ""
|
password: str = ""
|
||||||
|
|
|
||||||
|
|
@ -1,66 +1,139 @@
|
||||||
from fastapi import APIRouter, Depends, UploadFile, Form, Request
|
from fastapi import APIRouter, Depends, UploadFile, Form, Request
|
||||||
|
from sqlmodel import Session
|
||||||
|
from src.core.events.database import get_db_session
|
||||||
|
from src.db.users import PublicUser
|
||||||
|
from src.db.courses import Course, CourseCreate, CourseUpdate
|
||||||
from src.security.auth import get_current_user
|
from src.security.auth import get_current_user
|
||||||
|
|
||||||
from src.services.courses.courses import Course, create_course, get_course, get_course_meta, get_courses_orgslug, update_course, delete_course, update_course_thumbnail
|
from src.services.courses.courses import (
|
||||||
from src.services.users.users import PublicUser
|
create_course,
|
||||||
|
get_course,
|
||||||
|
get_course_meta,
|
||||||
|
get_courses_orgslug,
|
||||||
|
update_course,
|
||||||
|
delete_course,
|
||||||
|
update_course_thumbnail,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.post("/")
|
@router.post("/")
|
||||||
async def api_create_course(request: Request, org_id: str, name: str = Form(), mini_description: str = Form(), description: str = Form(), public: bool = Form(), current_user: PublicUser = Depends(get_current_user), thumbnail: UploadFile | None = None):
|
async def api_create_course(
|
||||||
|
request: Request,
|
||||||
|
org_id: int,
|
||||||
|
name: str = Form(),
|
||||||
|
description: str = Form(),
|
||||||
|
public: bool = Form(),
|
||||||
|
learnings: str = Form(),
|
||||||
|
tags: str = Form(),
|
||||||
|
about: str = Form(),
|
||||||
|
current_user: PublicUser = Depends(get_current_user),
|
||||||
|
db_session: Session = Depends(get_db_session),
|
||||||
|
thumbnail: UploadFile | None = None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Create new Course
|
Create new Course
|
||||||
"""
|
"""
|
||||||
course = Course(name=name, mini_description=mini_description, description=description,
|
course = CourseCreate(
|
||||||
org_id=org_id, public=public, thumbnail="", chapters=[], chapters_content=[], learnings=[])
|
name=name,
|
||||||
return await create_course(request, course, org_id, current_user, thumbnail)
|
description=description,
|
||||||
|
org_id=org_id,
|
||||||
|
public=public,
|
||||||
|
thumbnail_image="",
|
||||||
|
about=about,
|
||||||
|
learnings=learnings,
|
||||||
|
tags=tags,
|
||||||
|
)
|
||||||
|
return await create_course(
|
||||||
|
request, course, org_id, current_user, db_session, thumbnail
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.put("/thumbnail/{course_id}")
|
@router.put("/thumbnail/{course_id}")
|
||||||
async def api_create_course_thumbnail(request: Request, course_id: str, thumbnail: UploadFile | None = None, current_user: PublicUser = Depends(get_current_user)):
|
async def api_create_course_thumbnail(
|
||||||
|
request: Request,
|
||||||
|
course_id: str,
|
||||||
|
thumbnail: UploadFile | None = None,
|
||||||
|
db_session: Session = Depends(get_db_session),
|
||||||
|
current_user: PublicUser = Depends(get_current_user),
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Update new Course Thumbnail
|
Update new Course Thumbnail
|
||||||
"""
|
"""
|
||||||
return await update_course_thumbnail(request, course_id, current_user, thumbnail)
|
return await update_course_thumbnail(
|
||||||
|
request, course_id, current_user, db_session, thumbnail
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{course_id}")
|
@router.get("/{course_id}")
|
||||||
async def api_get_course(request: Request, course_id: str, current_user: PublicUser = Depends(get_current_user)):
|
async def api_get_course(
|
||||||
|
request: Request,
|
||||||
|
course_id: str,
|
||||||
|
db_session: Session = Depends(get_db_session),
|
||||||
|
current_user: PublicUser = Depends(get_current_user),
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Get single Course by course_id
|
Get single Course by course_id
|
||||||
"""
|
"""
|
||||||
return await get_course(request, course_id, current_user=current_user)
|
return await get_course(
|
||||||
|
request, course_id, current_user=current_user, db_session=db_session
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/meta/{course_id}")
|
@router.get("/meta/{course_id}")
|
||||||
async def api_get_course_meta(request: Request, course_id: str, current_user: PublicUser = Depends(get_current_user)):
|
async def api_get_course_meta(
|
||||||
|
request: Request,
|
||||||
|
course_id: str,
|
||||||
|
db_session: Session = Depends(get_db_session),
|
||||||
|
current_user: PublicUser = Depends(get_current_user),
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Get single Course Metadata (chapters, activities) by course_id
|
Get single Course Metadata (chapters, activities) by course_id
|
||||||
"""
|
"""
|
||||||
return await get_course_meta(request, course_id, current_user=current_user)
|
return await get_course_meta(
|
||||||
|
request, course_id, current_user=current_user, db_session=db_session
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/org_slug/{org_slug}/page/{page}/limit/{limit}")
|
@router.get("/org_slug/{org_slug}/page/{page}/limit/{limit}")
|
||||||
async def api_get_course_by_orgslug(request: Request, page: int, limit: int, org_slug: str, current_user: PublicUser = Depends(get_current_user)):
|
async def api_get_course_by_orgslug(
|
||||||
|
request: Request,
|
||||||
|
page: int,
|
||||||
|
limit: int,
|
||||||
|
org_slug: str,
|
||||||
|
db_session: Session = Depends(get_db_session),
|
||||||
|
current_user: PublicUser = Depends(get_current_user),
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Get houses by page and limit
|
Get houses by page and limit
|
||||||
"""
|
"""
|
||||||
return await get_courses_orgslug(request, current_user, page, limit, org_slug)
|
return await get_courses_orgslug(request, current_user, page, limit, org_slug)
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{course_id}")
|
@router.put("/")
|
||||||
async def api_update_course(request: Request, course_object: Course, course_id: str, current_user: PublicUser = Depends(get_current_user)):
|
async def api_update_course(
|
||||||
|
request: Request,
|
||||||
|
course_object: CourseUpdate,
|
||||||
|
db_session: Session = Depends(get_db_session),
|
||||||
|
current_user: PublicUser = Depends(get_current_user),
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Update Course by course_id
|
Update Course by course_id
|
||||||
"""
|
"""
|
||||||
return await update_course(request, course_object, course_id, current_user)
|
return await update_course(request, course_object, current_user, db_session)
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{course_id}")
|
@router.delete("/{course_id}")
|
||||||
async def api_delete_course(request: Request, course_id: str, current_user: PublicUser = Depends(get_current_user)):
|
async def api_delete_course(
|
||||||
|
request: Request,
|
||||||
|
course_id: str,
|
||||||
|
db_session: Session = Depends(get_db_session),
|
||||||
|
current_user: PublicUser = Depends(get_current_user),
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Delete Course by ID
|
Delete Course by ID
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return await delete_course(request, course_id, current_user)
|
return await delete_course(request, course_id, current_user, db_session)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,10 @@ import json
|
||||||
from typing import List, Literal, Optional
|
from typing import List, Literal, Optional
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
from sqlmodel import Session, select
|
||||||
|
from src.db.course_authors import CourseAuthor, CourseAuthorshipEnum
|
||||||
|
from src.db.users import PublicUser, AnonymousUser
|
||||||
|
from src.db.courses import Course, CourseCreate, CourseRead, CourseUpdate
|
||||||
from src.security.rbac.rbac import (
|
from src.security.rbac.rbac import (
|
||||||
authorization_verify_based_on_roles,
|
authorization_verify_based_on_roles,
|
||||||
authorization_verify_based_on_roles_and_authorship,
|
authorization_verify_based_on_roles_and_authorship,
|
||||||
|
|
@ -10,320 +14,183 @@ from src.security.rbac.rbac import (
|
||||||
)
|
)
|
||||||
from src.services.courses.activities.activities import ActivityInDB
|
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 src.services.users.schemas.users import AnonymousUser
|
|
||||||
from src.services.users.users import PublicUser
|
|
||||||
from fastapi import HTTPException, Request, status, UploadFile
|
from fastapi import HTTPException, Request, status, UploadFile
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
#### Classes ####################################################
|
|
||||||
|
|
||||||
|
async def get_course(
|
||||||
class Course(BaseModel):
|
request: Request, course_id: str, current_user: PublicUser, db_session: Session
|
||||||
name: str
|
):
|
||||||
mini_description: str
|
statement = select(Course).where(Course.id == course_id)
|
||||||
description: str
|
course = db_session.exec(statement).first()
|
||||||
learnings: List[str]
|
|
||||||
thumbnail: str
|
|
||||||
public: bool
|
|
||||||
chapters: List[str]
|
|
||||||
chapters_content: Optional[List]
|
|
||||||
org_id: str
|
|
||||||
|
|
||||||
|
|
||||||
class CourseInDB(Course):
|
|
||||||
course_id: str
|
|
||||||
creationDate: str
|
|
||||||
updateDate: str
|
|
||||||
authors: List[str]
|
|
||||||
|
|
||||||
|
|
||||||
# TODO : wow terrible, fix this
|
|
||||||
# those models need to be available only in the chapters service
|
|
||||||
class CourseChapter(BaseModel):
|
|
||||||
name: str
|
|
||||||
description: str
|
|
||||||
activities: list
|
|
||||||
|
|
||||||
|
|
||||||
class CourseChapterInDB(CourseChapter):
|
|
||||||
coursechapter_id: str
|
|
||||||
course_id: str
|
|
||||||
creationDate: str
|
|
||||||
updateDate: str
|
|
||||||
|
|
||||||
|
|
||||||
#### Classes ####################################################
|
|
||||||
|
|
||||||
# TODO : Add courses photo & cover upload and delete
|
|
||||||
|
|
||||||
|
|
||||||
####################################################
|
|
||||||
# CRUD
|
|
||||||
####################################################
|
|
||||||
|
|
||||||
|
|
||||||
async def get_course(request: Request, course_id: str, current_user: PublicUser):
|
|
||||||
courses = request.app.db["courses"]
|
|
||||||
|
|
||||||
course = await courses.find_one({"course_id": course_id})
|
|
||||||
|
|
||||||
# verify course rights
|
|
||||||
await verify_rights(request, course_id, current_user, "read")
|
|
||||||
|
|
||||||
if not course:
|
if not course:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist"
|
status_code=404,
|
||||||
|
detail="Course not found",
|
||||||
)
|
)
|
||||||
|
|
||||||
course = Course(**course)
|
|
||||||
return course
|
return course
|
||||||
|
|
||||||
|
|
||||||
async def get_course_meta(request: Request, course_id: str, current_user: PublicUser):
|
async def get_course_meta(
|
||||||
courses = request.app.db["courses"]
|
request: Request, course_id: str, current_user: PublicUser, db_session: Session
|
||||||
trails = request.app.db["trails"]
|
):
|
||||||
|
course_statement = select(Course).where(Course.id == course_id)
|
||||||
course = await courses.find_one({"course_id": course_id})
|
course = db_session.exec(course_statement).first()
|
||||||
activities = request.app.db["activities"]
|
|
||||||
|
|
||||||
# verify course rights
|
|
||||||
await verify_rights(request, course_id, current_user, "read")
|
|
||||||
|
|
||||||
if not course:
|
if not course:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist"
|
status_code=404,
|
||||||
|
detail="Course not found",
|
||||||
)
|
)
|
||||||
|
|
||||||
coursechapters = await courses.find_one(
|
# todo : get course chapters
|
||||||
{"course_id": course_id}, {"chapters_content": 1, "_id": 0}
|
# todo : get course activities
|
||||||
)
|
# todo : get trail
|
||||||
|
|
||||||
# activities
|
return course
|
||||||
coursechapter_activityIds_global = []
|
|
||||||
|
|
||||||
# chapters
|
|
||||||
chapters = {}
|
|
||||||
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)
|
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
chapters_list_with_activities = []
|
|
||||||
for chapter in chapters:
|
|
||||||
chapters_list_with_activities.append(
|
|
||||||
{
|
|
||||||
"id": chapters[chapter]["id"],
|
|
||||||
"name": chapters[chapter]["name"],
|
|
||||||
"activities": [
|
|
||||||
activities_list[activity]
|
|
||||||
for activity in chapters[chapter]["activityIds"]
|
|
||||||
],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
course = CourseInDB(**course)
|
|
||||||
|
|
||||||
# Get activity by user
|
|
||||||
trail = await trails.find_one(
|
|
||||||
{"courses.course_id": course_id, "user_id": current_user.user_id}
|
|
||||||
)
|
|
||||||
if trail:
|
|
||||||
# get only the course where course_id == course_id
|
|
||||||
trail_course = next(
|
|
||||||
(course for course in trail["courses"] if course["course_id"] == course_id),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
trail_course = ""
|
|
||||||
|
|
||||||
return {
|
|
||||||
"course": course,
|
|
||||||
"chapters": chapters_list_with_activities,
|
|
||||||
"trail": trail_course,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def create_course(
|
async def create_course(
|
||||||
request: Request,
|
request: Request,
|
||||||
course_object: Course,
|
course_object: CourseCreate,
|
||||||
org_id: str,
|
org_id: int,
|
||||||
current_user: PublicUser,
|
current_user: PublicUser,
|
||||||
|
db_session: Session,
|
||||||
thumbnail_file: UploadFile | None = None,
|
thumbnail_file: UploadFile | None = None,
|
||||||
):
|
):
|
||||||
courses = request.app.db["courses"]
|
course = Course.from_orm(course_object)
|
||||||
users = request.app.db["users"]
|
|
||||||
user = await users.find_one({"user_id": current_user.user_id})
|
|
||||||
|
|
||||||
# generate course_id with uuid4
|
|
||||||
course_id = str(f"course_{uuid4()}")
|
|
||||||
|
|
||||||
# TODO(fix) : the implementation here is clearly not the best one (this entire function)
|
|
||||||
course_object.org_id = org_id
|
|
||||||
course_object.chapters_content = []
|
|
||||||
|
|
||||||
await authorization_verify_based_on_roles(
|
|
||||||
request,
|
|
||||||
current_user.user_id,
|
|
||||||
"create",
|
|
||||||
user["roles"],
|
|
||||||
course_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
# Complete course object
|
||||||
|
course.org_id = org_id
|
||||||
|
course.course_uuid = str(uuid4())
|
||||||
|
course.creation_date = str(datetime.now())
|
||||||
|
course.update_date = str(datetime.now())
|
||||||
|
|
||||||
|
# Upload thumbnail
|
||||||
if thumbnail_file and thumbnail_file.filename:
|
if thumbnail_file and thumbnail_file.filename:
|
||||||
name_in_disk = (
|
name_in_disk = f"{course.course_uuid}_thumbnail_{uuid4()}.{thumbnail_file.filename.split('.')[-1]}"
|
||||||
f"{course_id}_thumbnail_{uuid4()}.{thumbnail_file.filename.split('.')[-1]}"
|
await upload_thumbnail(thumbnail_file, name_in_disk, org_id, course.course_uuid)
|
||||||
)
|
|
||||||
await upload_thumbnail(
|
|
||||||
thumbnail_file, name_in_disk, course_object.org_id, course_id
|
|
||||||
)
|
|
||||||
course_object.thumbnail = name_in_disk
|
course_object.thumbnail = name_in_disk
|
||||||
|
|
||||||
course = CourseInDB(
|
# Insert course
|
||||||
course_id=course_id,
|
db_session.add(course)
|
||||||
authors=[current_user.user_id],
|
db_session.commit()
|
||||||
creationDate=str(datetime.now()),
|
db_session.refresh(course)
|
||||||
updateDate=str(datetime.now()),
|
|
||||||
**course_object.dict(),
|
# Make the user the creator of the course
|
||||||
|
course_author = CourseAuthor(
|
||||||
|
course_id=course.id is not None,
|
||||||
|
user_id=current_user.id,
|
||||||
|
authorship=CourseAuthorshipEnum.CREATOR,
|
||||||
|
creation_date=str(datetime.now()),
|
||||||
|
update_date=str(datetime.now()),
|
||||||
)
|
)
|
||||||
|
|
||||||
course_in_db = await courses.insert_one(course.dict())
|
# Insert course author
|
||||||
|
db_session.add(course_author)
|
||||||
|
db_session.commit()
|
||||||
|
db_session.refresh(course_author)
|
||||||
|
|
||||||
if not course_in_db:
|
return CourseRead.from_orm(course)
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
||||||
detail="Unavailable database",
|
|
||||||
)
|
|
||||||
|
|
||||||
return course.dict()
|
|
||||||
|
|
||||||
|
|
||||||
async def update_course_thumbnail(
|
async def update_course_thumbnail(
|
||||||
request: Request,
|
request: Request,
|
||||||
course_id: str,
|
course_id: str,
|
||||||
current_user: PublicUser,
|
current_user: PublicUser,
|
||||||
|
db_session: Session,
|
||||||
thumbnail_file: UploadFile | None = None,
|
thumbnail_file: UploadFile | None = None,
|
||||||
):
|
):
|
||||||
courses = request.app.db["courses"]
|
statement = select(Course).where(Course.id == course_id)
|
||||||
|
course = db_session.exec(statement).first()
|
||||||
|
|
||||||
course = await courses.find_one({"course_id": course_id})
|
name_in_disk = None
|
||||||
|
|
||||||
# verify course rights
|
|
||||||
await verify_rights(request, course_id, current_user, "update")
|
|
||||||
|
|
||||||
# TODO(fix) : the implementation here is clearly not the best one
|
|
||||||
if course:
|
|
||||||
creationDate = course["creationDate"]
|
|
||||||
authors = course["authors"]
|
|
||||||
if thumbnail_file and thumbnail_file.filename:
|
|
||||||
name_in_disk = f"{course_id}_thumbnail_{uuid4()}.{thumbnail_file.filename.split('.')[-1]}"
|
|
||||||
course = Course(**course).copy(update={"thumbnail": name_in_disk})
|
|
||||||
await upload_thumbnail(
|
|
||||||
thumbnail_file, name_in_disk, course.org_id, course_id
|
|
||||||
)
|
|
||||||
|
|
||||||
updated_course = CourseInDB(
|
|
||||||
course_id=course_id,
|
|
||||||
creationDate=creationDate,
|
|
||||||
authors=authors,
|
|
||||||
updateDate=str(datetime.now()),
|
|
||||||
**course.dict(),
|
|
||||||
)
|
|
||||||
|
|
||||||
await courses.update_one(
|
|
||||||
{"course_id": course_id}, {"$set": updated_course.dict()}
|
|
||||||
)
|
|
||||||
|
|
||||||
return CourseInDB(**updated_course.dict())
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def update_course(
|
|
||||||
request: Request, course_object: Course, course_id: str, current_user: PublicUser
|
|
||||||
):
|
|
||||||
courses = request.app.db["courses"]
|
|
||||||
|
|
||||||
course = await courses.find_one({"course_id": course_id})
|
|
||||||
|
|
||||||
# verify course rights
|
|
||||||
await verify_rights(request, course_id, current_user, "update")
|
|
||||||
|
|
||||||
if course:
|
|
||||||
creationDate = course["creationDate"]
|
|
||||||
authors = course["authors"]
|
|
||||||
|
|
||||||
# get today's date
|
|
||||||
datetime_object = datetime.now()
|
|
||||||
|
|
||||||
updated_course = CourseInDB(
|
|
||||||
course_id=course_id,
|
|
||||||
creationDate=creationDate,
|
|
||||||
authors=authors,
|
|
||||||
updateDate=str(datetime_object),
|
|
||||||
**course_object.dict(),
|
|
||||||
)
|
|
||||||
|
|
||||||
await courses.update_one(
|
|
||||||
{"course_id": course_id}, {"$set": updated_course.dict()}
|
|
||||||
)
|
|
||||||
|
|
||||||
return CourseInDB(**updated_course.dict())
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def delete_course(request: Request, course_id: str, current_user: PublicUser):
|
|
||||||
courses = request.app.db["courses"]
|
|
||||||
|
|
||||||
course = await courses.find_one({"course_id": course_id})
|
|
||||||
|
|
||||||
# verify course rights
|
|
||||||
await verify_rights(request, course_id, current_user, "delete")
|
|
||||||
|
|
||||||
if not course:
|
if not course:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist"
|
status_code=404,
|
||||||
|
detail="Course not found",
|
||||||
)
|
)
|
||||||
|
|
||||||
isDeleted = await courses.delete_one({"course_id": course_id})
|
# Upload thumbnail
|
||||||
|
if thumbnail_file and thumbnail_file.filename:
|
||||||
|
name_in_disk = (
|
||||||
|
f"{course_id}_thumbnail_{uuid4()}.{thumbnail_file.filename.split('.')[-1]}"
|
||||||
|
)
|
||||||
|
await upload_thumbnail(
|
||||||
|
thumbnail_file, name_in_disk, course.org_id, course.course_uuid
|
||||||
|
)
|
||||||
|
|
||||||
if isDeleted:
|
# Update course
|
||||||
return {"detail": "Course deleted"}
|
if name_in_disk:
|
||||||
|
course.thumbnail_image = name_in_disk
|
||||||
else:
|
else:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
status_code=500,
|
||||||
detail="Unavailable database",
|
detail="Issue with thumbnail upload",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Complete the course object
|
||||||
|
course.update_date = str(datetime.now())
|
||||||
|
|
||||||
|
db_session.add(course)
|
||||||
|
db_session.commit()
|
||||||
|
db_session.refresh(course)
|
||||||
|
|
||||||
|
return course
|
||||||
|
|
||||||
|
|
||||||
|
async def update_course(
|
||||||
|
request: Request,
|
||||||
|
course_object: CourseUpdate,
|
||||||
|
current_user: PublicUser,
|
||||||
|
db_session: Session,
|
||||||
|
):
|
||||||
|
statement = select(Course).where(Course.id == course_object.course_id)
|
||||||
|
course = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
if not course:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="Course not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
del course_object.course_id
|
||||||
|
|
||||||
|
# Update only the fields that were passed in
|
||||||
|
for var, value in vars(course_object).items():
|
||||||
|
if value is not None:
|
||||||
|
setattr(course, var, value)
|
||||||
|
|
||||||
|
# Complete the course object
|
||||||
|
course.update_date = str(datetime.now())
|
||||||
|
|
||||||
|
db_session.add(course)
|
||||||
|
db_session.commit()
|
||||||
|
db_session.refresh(course)
|
||||||
|
|
||||||
|
return course
|
||||||
|
|
||||||
|
|
||||||
|
async def delete_course(
|
||||||
|
request: Request, course_id: str, 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=404,
|
||||||
|
detail="Course not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
db_session.delete(course)
|
||||||
|
db_session.commit()
|
||||||
|
|
||||||
|
return {"detail": "Course deleted"}
|
||||||
|
|
||||||
|
|
||||||
####################################################
|
####################################################
|
||||||
# Misc
|
# Misc
|
||||||
|
|
@ -349,7 +216,7 @@ async def get_courses_orgslug(
|
||||||
)
|
)
|
||||||
|
|
||||||
# show only public courses if user is not logged in
|
# show only public courses if user is not logged in
|
||||||
if current_user.user_id == "anonymous":
|
if current_user.id == "anonymous":
|
||||||
all_courses = (
|
all_courses = (
|
||||||
courses.find({"org_id": org["org_id"], "public": True})
|
courses.find({"org_id": org["org_id"], "public": True})
|
||||||
.sort("name", 1)
|
.sort("name", 1)
|
||||||
|
|
@ -380,30 +247,30 @@ async def verify_rights(
|
||||||
action: Literal["create", "read", "update", "delete"],
|
action: Literal["create", "read", "update", "delete"],
|
||||||
):
|
):
|
||||||
if action == "read":
|
if action == "read":
|
||||||
if current_user.user_id == "anonymous":
|
if current_user.id == "anonymous":
|
||||||
await authorization_verify_if_element_is_public(
|
await authorization_verify_if_element_is_public(
|
||||||
request, course_id, current_user.user_id, action
|
request, course_id, str(current_user.id), action
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
users = request.app.db["users"]
|
users = request.app.db["users"]
|
||||||
user = await users.find_one({"user_id": current_user.user_id})
|
user = await users.find_one({"user_id": str(current_user.id)})
|
||||||
|
|
||||||
await authorization_verify_based_on_roles_and_authorship(
|
await authorization_verify_based_on_roles_and_authorship(
|
||||||
request,
|
request,
|
||||||
current_user.user_id,
|
str(current_user.id),
|
||||||
action,
|
action,
|
||||||
user["roles"],
|
user["roles"],
|
||||||
course_id,
|
course_id,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
users = request.app.db["users"]
|
users = request.app.db["users"]
|
||||||
user = await users.find_one({"user_id": current_user.user_id})
|
user = await users.find_one({"user_id": str(current_user.id)})
|
||||||
|
|
||||||
await authorization_verify_if_user_is_anon(current_user.user_id)
|
await authorization_verify_if_user_is_anon(str(current_user.id))
|
||||||
|
|
||||||
await authorization_verify_based_on_roles_and_authorship(
|
await authorization_verify_based_on_roles_and_authorship(
|
||||||
request,
|
request,
|
||||||
current_user.user_id,
|
str(current_user.id),
|
||||||
action,
|
action,
|
||||||
user["roles"],
|
user["roles"],
|
||||||
course_id,
|
course_id,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ from src.services.users.users import PublicUser
|
||||||
|
|
||||||
from src.services.orgs.orgs import Organization, create_org
|
from src.services.orgs.orgs import Organization, create_org
|
||||||
from src.services.roles.schemas.roles import Permission, Elements, RoleInDB
|
from src.services.roles.schemas.roles import Permission, Elements, RoleInDB
|
||||||
from src.services.courses.courses import CourseInDB
|
|
||||||
from faker import Faker
|
from faker import Faker
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -128,16 +127,16 @@ async def create_initial_data(request: Request):
|
||||||
company = fake.company()
|
company = fake.company()
|
||||||
# remove whitespace and special characters and make lowercase
|
# remove whitespace and special characters and make lowercase
|
||||||
slug = ''.join(e for e in company if e.isalnum()).lower()
|
slug = ''.join(e for e in company if e.isalnum()).lower()
|
||||||
org = Organization(
|
# org = Organization(
|
||||||
name=company,
|
# name=company,
|
||||||
description=fake.unique.text(),
|
# description=fake.unique.text(),
|
||||||
email=fake.unique.email(),
|
# email=fake.unique.email(),
|
||||||
slug=slug,
|
# slug=slug,
|
||||||
logo="",
|
# logo="",
|
||||||
default=False
|
# default=False
|
||||||
)
|
# )
|
||||||
organizations.append(org)
|
# organizations.append(org)
|
||||||
await create_org(request, org, current_user)
|
# await create_org(request, org, current_user)
|
||||||
|
|
||||||
|
|
||||||
# Generate Courses and CourseChapters
|
# Generate Courses and CourseChapters
|
||||||
|
|
@ -160,22 +159,22 @@ async def create_initial_data(request: Request):
|
||||||
f.write(image.content)
|
f.write(image.content)
|
||||||
|
|
||||||
course_id = f"course_{uuid4()}"
|
course_id = f"course_{uuid4()}"
|
||||||
course = CourseInDB(
|
# course = CourseInDB(
|
||||||
name=fake_multilang.unique.sentence(),
|
# name=fake_multilang.unique.sentence(),
|
||||||
description=fake_multilang.unique.text(),
|
# description=fake_multilang.unique.text(),
|
||||||
mini_description=fake_multilang.unique.text(),
|
# mini_description=fake_multilang.unique.text(),
|
||||||
thumbnail="thumbnail",
|
# thumbnail="thumbnail",
|
||||||
org_id=org['org_id'],
|
# org_id=org['org_id'],
|
||||||
learnings=[fake_multilang.unique.sentence()
|
# learnings=[fake_multilang.unique.sentence()
|
||||||
for i in range(0, 5)],
|
# for i in range(0, 5)],
|
||||||
public=True,
|
# public=True,
|
||||||
chapters=[],
|
# chapters=[],
|
||||||
course_id=course_id,
|
# course_id=course_id,
|
||||||
creationDate=str(datetime.now()),
|
# creationDate=str(datetime.now()),
|
||||||
updateDate=str(datetime.now()),
|
# updateDate=str(datetime.now()),
|
||||||
authors=[current_user.user_id],
|
# authors=[current_user.user_id],
|
||||||
chapters_content=[],
|
# chapters_content=[],
|
||||||
)
|
# )
|
||||||
|
|
||||||
courses = request.app.db["courses"]
|
courses = request.app.db["courses"]
|
||||||
name_in_disk = f"test_mock{course_id}.jpeg"
|
name_in_disk = f"test_mock{course_id}.jpeg"
|
||||||
|
|
@ -191,10 +190,10 @@ async def create_initial_data(request: Request):
|
||||||
with open(f"content/uploads/img/{name_in_disk}", "wb") as f:
|
with open(f"content/uploads/img/{name_in_disk}", "wb") as f:
|
||||||
f.write(image.content)
|
f.write(image.content)
|
||||||
|
|
||||||
course.thumbnail = name_in_disk
|
# course.thumbnail = name_in_disk
|
||||||
|
|
||||||
course = CourseInDB(**course.dict())
|
# course = CourseInDB(**course.dict())
|
||||||
await courses.insert_one(course.dict())
|
# await courses.insert_one(course.dict())
|
||||||
|
|
||||||
# create chapters
|
# create chapters
|
||||||
for i in range(0, 5):
|
for i in range(0, 5):
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ from config.config import get_learnhouse_config
|
||||||
from src.security.security import security_hash_password
|
from src.security.security import security_hash_password
|
||||||
from src.services.courses.activities.activities import Activity, create_activity
|
from src.services.courses.activities.activities import Activity, create_activity
|
||||||
from src.services.courses.chapters import create_coursechapter, CourseChapter
|
from src.services.courses.chapters import create_coursechapter, CourseChapter
|
||||||
from src.services.courses.courses import CourseInDB
|
|
||||||
|
|
||||||
from src.services.orgs.schemas.orgs import Organization, OrganizationInDB
|
from src.services.orgs.schemas.orgs import Organization, OrganizationInDB
|
||||||
from faker import Faker
|
from faker import Faker
|
||||||
|
|
@ -370,50 +369,50 @@ async def create_sample_data(org_slug: str, username: str, request: Request):
|
||||||
with open("thumbnail.jpg", "wb") as f:
|
with open("thumbnail.jpg", "wb") as f:
|
||||||
f.write(image.content)
|
f.write(image.content)
|
||||||
|
|
||||||
course_id = f"course_{uuid4()}"
|
# course_id = f"course_{uuid4()}"
|
||||||
course = CourseInDB(
|
# course = CourseInDB(
|
||||||
name=fake_multilang.unique.sentence(),
|
# name=fake_multilang.unique.sentence(),
|
||||||
description=fake_multilang.unique.text(),
|
# description=fake_multilang.unique.text(),
|
||||||
mini_description=fake_multilang.unique.text(),
|
# mini_description=fake_multilang.unique.text(),
|
||||||
thumbnail="thumbnail",
|
# thumbnail="thumbnail",
|
||||||
org_id=org_id,
|
# org_id=org_id,
|
||||||
learnings=[fake_multilang.unique.sentence() for i in range(0, 5)],
|
# learnings=[fake_multilang.unique.sentence() for i in range(0, 5)],
|
||||||
public=True,
|
# public=True,
|
||||||
chapters=[],
|
# chapters=[],
|
||||||
course_id=course_id,
|
# course_id=course_id,
|
||||||
creationDate=str(datetime.now()),
|
# creationDate=str(datetime.now()),
|
||||||
updateDate=str(datetime.now()),
|
# updateDate=str(datetime.now()),
|
||||||
authors=[user_id],
|
# authors=[user_id],
|
||||||
chapters_content=[],
|
# chapters_content=[],
|
||||||
)
|
# )
|
||||||
|
|
||||||
courses = request.app.db["courses"]
|
# courses = request.app.db["courses"]
|
||||||
|
|
||||||
course = CourseInDB(**course.dict())
|
# course = CourseInDB(**course.dict())
|
||||||
await courses.insert_one(course.dict())
|
# await courses.insert_one(course.dict())
|
||||||
|
|
||||||
# create chapters
|
# # create chapters
|
||||||
for i in range(0, 5):
|
# for i in range(0, 5):
|
||||||
coursechapter = CourseChapter(
|
# coursechapter = CourseChapter(
|
||||||
name=fake_multilang.unique.sentence(),
|
# name=fake_multilang.unique.sentence(),
|
||||||
description=fake_multilang.unique.text(),
|
# description=fake_multilang.unique.text(),
|
||||||
activities=[],
|
# activities=[],
|
||||||
)
|
# )
|
||||||
coursechapter = await create_coursechapter(
|
# coursechapter = await create_coursechapter(
|
||||||
request, coursechapter, course_id, current_user
|
# request, coursechapter, course_id, current_user
|
||||||
)
|
# )
|
||||||
if coursechapter:
|
# if coursechapter:
|
||||||
# create activities
|
# # create activities
|
||||||
for i in range(0, 5):
|
# for i in range(0, 5):
|
||||||
activity = Activity(
|
# activity = Activity(
|
||||||
name=fake_multilang.unique.sentence(),
|
# name=fake_multilang.unique.sentence(),
|
||||||
type="dynamic",
|
# type="dynamic",
|
||||||
content={},
|
# content={},
|
||||||
)
|
# )
|
||||||
activity = await create_activity(
|
# activity = await create_activity(
|
||||||
request,
|
# request,
|
||||||
activity,
|
# activity,
|
||||||
org_id,
|
# org_id,
|
||||||
coursechapter["coursechapter_id"],
|
# coursechapter["coursechapter_id"],
|
||||||
current_user,
|
# current_user,
|
||||||
)
|
# )
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue