mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: init trails
This commit is contained in:
parent
b04fd64c92
commit
eca819b896
5 changed files with 495 additions and 253 deletions
53
apps/api/src/db/trail_runs.py
Normal file
53
apps/api/src/db/trail_runs.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import JSON, Column
|
||||
from sqlmodel import Field, SQLModel
|
||||
from enum import Enum
|
||||
|
||||
from src.db.trail_steps import TrailStep
|
||||
|
||||
|
||||
class TrailRunEnum(str, Enum):
|
||||
RUN_TYPE_COURSE = "RUN_TYPE_COURSE"
|
||||
|
||||
|
||||
class StatusEnum(str, Enum):
|
||||
STATUS_IN_PROGRESS = "STATUS_IN_PROGRESS"
|
||||
STATUS_COMPLETED = "STATUS_COMPLETED"
|
||||
STATUS_PAUSED = "STATUS_PAUSED"
|
||||
STATUS_CANCELLED = "STATUS_CANCELLED"
|
||||
|
||||
|
||||
class TrailRun(SQLModel, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
data: dict = Field(default={}, sa_column=Column(JSON))
|
||||
status: StatusEnum = StatusEnum.STATUS_IN_PROGRESS
|
||||
# foreign keys
|
||||
trail_id: int = Field(default=None, foreign_key="trail.id")
|
||||
course_id: int = Field(default=None, foreign_key="course.id")
|
||||
org_id: int = Field(default=None, foreign_key="organization.id")
|
||||
user_id: int = Field(default=None, foreign_key="user.id")
|
||||
# timestamps
|
||||
creation_date: str
|
||||
update_date: str
|
||||
|
||||
|
||||
class TrailRunCreate(TrailRun):
|
||||
pass
|
||||
|
||||
|
||||
# trick because Lists are not supported in SQLModel (runs: list[TrailStep] )
|
||||
class TrailRunRead(BaseModel):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
data: dict = Field(default={}, sa_column=Column(JSON))
|
||||
status: StatusEnum = StatusEnum.STATUS_IN_PROGRESS
|
||||
# foreign keys
|
||||
trail_id: int = Field(default=None, foreign_key="trail.id")
|
||||
course_id: int = Field(default=None, foreign_key="course.id")
|
||||
org_id: int = Field(default=None, foreign_key="organization.id")
|
||||
user_id: int = Field(default=None, foreign_key="user.id")
|
||||
# timestamps
|
||||
creation_date: str
|
||||
update_date: str
|
||||
steps: list[TrailStep]
|
||||
pass
|
||||
32
apps/api/src/db/trail_steps.py
Normal file
32
apps/api/src/db/trail_steps.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
from enum import Enum
|
||||
from typing import Optional
|
||||
from sqlalchemy import JSON, Column
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
class TrailStepTypeEnum(str, Enum):
|
||||
STEP_TYPE_READABLE_ACTIVITY = "STEP_TYPE_READABLE_ACTIVITY"
|
||||
STEP_TYPE_ASSIGNMENT_ACTIVITY = "STEP_TYPE_ASSIGNMENT_ACTIVITY"
|
||||
STEP_TYPE_CUSTOM_ACTIVITY = "STEP_TYPE_CUSTOM_ACTIVITY"
|
||||
|
||||
|
||||
class TrailStep(SQLModel, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
complete: bool
|
||||
teacher_verified: bool
|
||||
grade: str
|
||||
data: dict = Field(default={}, sa_column=Column(JSON))
|
||||
# foreign keys
|
||||
trailrun_id: int = Field(default=None, foreign_key="trailrun.id")
|
||||
trail_id: int = Field(default=None, foreign_key="trail.id")
|
||||
activity_id: int = Field(default=None, foreign_key="activity.id")
|
||||
course_id: int = Field(default=None, foreign_key="course.id")
|
||||
org_id: int = Field(default=None, foreign_key="organization.id")
|
||||
user_id: int = Field(default=None, foreign_key="user.id")
|
||||
# timestamps
|
||||
creation_date: str
|
||||
update_date: str
|
||||
|
||||
|
||||
# note : prepare assignments support
|
||||
# an assignment object will be linked to a trail step object in the future
|
||||
34
apps/api/src/db/trails.py
Normal file
34
apps/api/src/db/trails.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
from sqlmodel import Field, SQLModel
|
||||
from enum import Enum
|
||||
from src.db.trail_runs import TrailRun, TrailRunRead
|
||||
|
||||
from src.db.trail_steps import TrailStep
|
||||
|
||||
|
||||
class TrailBase(SQLModel):
|
||||
org_id: int = Field(default=None, foreign_key="organization.id")
|
||||
user_id: int = Field(default=None, foreign_key="user.id")
|
||||
|
||||
|
||||
class Trail(TrailBase, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
trail_uuid: str = ""
|
||||
creation_date: str = ""
|
||||
update_date: str = ""
|
||||
|
||||
|
||||
class TrailCreate(TrailBase):
|
||||
pass
|
||||
|
||||
|
||||
# trick because Lists are not supported in SQLModel (runs: list[TrailRun] )
|
||||
class TrailRead(BaseModel):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
trail_uuid: str
|
||||
org_id: int = Field(default=None, foreign_key="organization.id")
|
||||
user_id: int = Field(default=None, foreign_key="user.id")
|
||||
creation_date: str
|
||||
update_date: str
|
||||
runs: list[TrailRunRead]
|
||||
|
|
@ -1,56 +1,103 @@
|
|||
from fastapi import APIRouter, Depends, Request
|
||||
from src.core.events.database import get_db_session
|
||||
from src.db.trails import TrailCreate, TrailRead
|
||||
from src.security.auth import get_current_user
|
||||
from src.services.trail.trail import Trail, add_activity_to_trail, add_course_to_trail, create_trail, get_user_trail_with_orgslug, get_user_trail, remove_course_from_trail
|
||||
from src.services.trail.trail import (
|
||||
Trail,
|
||||
add_activity_to_trail,
|
||||
add_course_to_trail,
|
||||
create_user_trail,
|
||||
get_user_trails,
|
||||
get_user_trail_with_orgid,
|
||||
remove_course_from_trail,
|
||||
)
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("/start")
|
||||
async def api_start_trail(request: Request, trail_object: Trail, org_id: str, user=Depends(get_current_user)) -> Trail:
|
||||
async def api_start_trail(
|
||||
request: Request,
|
||||
trail_object: TrailCreate,
|
||||
user=Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
) -> Trail:
|
||||
"""
|
||||
Start trail
|
||||
"""
|
||||
return await create_trail(request, user, org_id, trail_object)
|
||||
return await create_user_trail(request, user, trail_object, db_session)
|
||||
|
||||
|
||||
@router.get("/org_id/{org_id}/trail")
|
||||
async def api_get_trail_by_orgid(request: Request, org_slug: str, user=Depends(get_current_user)):
|
||||
@router.get("/")
|
||||
async def api_get_user_trail(
|
||||
request: Request,
|
||||
user=Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
) -> TrailRead:
|
||||
"""
|
||||
Get a user trails
|
||||
"""
|
||||
return await get_user_trail(request, user=user, org_slug=org_slug)
|
||||
return await get_user_trails(
|
||||
request, user=user, db_session=db_session
|
||||
)
|
||||
|
||||
|
||||
@router.get("/org_slug/{org_slug}/trail")
|
||||
async def api_get_trail_by_orgslug(request: Request, org_slug: str, user=Depends(get_current_user)):
|
||||
@router.get("/org_slug/{org_id}/trail")
|
||||
async def api_get_trail_by_org_id(
|
||||
request: Request,
|
||||
org_id: int,
|
||||
user=Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
) -> TrailRead:
|
||||
"""
|
||||
Get a user trails using org slug
|
||||
"""
|
||||
return await get_user_trail_with_orgslug(request, user, org_slug=org_slug)
|
||||
return await get_user_trail_with_orgid(
|
||||
request, user, org_id=org_id, db_session=db_session
|
||||
)
|
||||
|
||||
|
||||
# Courses in trail
|
||||
|
||||
|
||||
@router.post("/org_slug/{org_slug}/add_course/{course_id}")
|
||||
async def api_add_course_to_trail(request: Request, course_id: str, org_slug: str, user=Depends(get_current_user)):
|
||||
@router.post("/add_course/{course_id}")
|
||||
async def api_add_course_to_trail(
|
||||
request: Request,
|
||||
course_id: str,
|
||||
user=Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
) -> TrailRead:
|
||||
"""
|
||||
Add Course to trail
|
||||
"""
|
||||
return await add_course_to_trail(request, user, org_slug, course_id)
|
||||
return await add_course_to_trail(request, user, course_id, db_session)
|
||||
|
||||
|
||||
@router.post("/org_slug/{org_slug}/remove_course/{course_id}")
|
||||
async def api_remove_course_to_trail(request: Request, course_id: str, org_slug: str, user=Depends(get_current_user)):
|
||||
@router.post("/remove_course/{course_id}")
|
||||
async def api_remove_course_to_trail(
|
||||
request: Request,
|
||||
course_id: str,
|
||||
user=Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
) -> TrailRead:
|
||||
"""
|
||||
Remove Course from trail
|
||||
"""
|
||||
return await remove_course_from_trail(request, user, org_slug, course_id)
|
||||
return await remove_course_from_trail(request, user, course_id, db_session)
|
||||
|
||||
|
||||
@router.post("/org_slug/{org_slug}/add_activity/course_id/{course_id}/activity_id/{activity_id}")
|
||||
async def api_add_activity_to_trail(request: Request, activity_id: str, course_id: str, org_slug: str, user=Depends(get_current_user)):
|
||||
@router.post("/add_activity/course_id/{course_id}/activity_id/{activity_id}")
|
||||
async def api_add_activity_to_trail(
|
||||
request: Request,
|
||||
activity_id: int,
|
||||
course_id: int,
|
||||
user=Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
) -> TrailRead:
|
||||
"""
|
||||
Add Course to trail
|
||||
"""
|
||||
return await add_activity_to_trail(request, user, course_id, org_slug, activity_id)
|
||||
return await add_activity_to_trail(
|
||||
request, user, course_id, activity_id, db_session
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,286 +1,362 @@
|
|||
from datetime import datetime
|
||||
import stat
|
||||
from typing import List, Literal, Optional
|
||||
from uuid import uuid4
|
||||
from fastapi import HTTPException, Request, status
|
||||
from pydantic import BaseModel
|
||||
from sqlmodel import Session, select
|
||||
from src.db.courses import Course
|
||||
from src.db.trail_runs import TrailRun, TrailRunCreate, TrailRunRead
|
||||
from src.db.trail_steps import TrailStep
|
||||
from src.db.trails import Trail, TrailCreate, TrailRead
|
||||
from src.db.users import PublicUser
|
||||
from src.services.orgs.schemas.orgs import PublicOrganization
|
||||
from src.services.courses.chapters import get_coursechapters_meta
|
||||
|
||||
from src.services.users.users import PublicUser
|
||||
|
||||
#### Classes ####################################################
|
||||
|
||||
|
||||
class ActivityData(BaseModel):
|
||||
activity_id: str
|
||||
activity_type: str
|
||||
data: Optional[dict]
|
||||
|
||||
|
||||
class TrailCourse(BaseModel):
|
||||
course_id: str
|
||||
elements_type: Optional[Literal["course"]] = "course"
|
||||
status: Optional[Literal["ongoing", "done", "closed"]] = "ongoing"
|
||||
course_object: dict
|
||||
masked: Optional[bool] = False
|
||||
activities_marked_complete: Optional[List[str]]
|
||||
activities_data: Optional[List[ActivityData]]
|
||||
progress: Optional[int]
|
||||
|
||||
|
||||
class Trail(BaseModel):
|
||||
status: Optional[Literal["ongoing", "done", "closed"]] = "ongoing"
|
||||
masked: Optional[bool] = False
|
||||
courses: Optional[List[TrailCourse]]
|
||||
|
||||
|
||||
class TrailInDB(Trail):
|
||||
trail_id: str
|
||||
org_id: str
|
||||
user_id: str
|
||||
creationDate: str = datetime.now().isoformat()
|
||||
updateDate: str = datetime.now().isoformat()
|
||||
|
||||
|
||||
#### Classes ####################################################
|
||||
|
||||
|
||||
async def create_trail(
|
||||
request: Request, user: PublicUser, org_id: str, trail_object: Trail
|
||||
async def create_user_trail(
|
||||
request: Request,
|
||||
user: PublicUser,
|
||||
trail_object: TrailCreate,
|
||||
db_session: Session,
|
||||
) -> Trail:
|
||||
trails = request.app.db["trails"]
|
||||
statement = select(Trail).where(Trail.org_id == trail_object.org_id)
|
||||
trail = db_session.exec(statement).first()
|
||||
|
||||
# get list of courses
|
||||
if trail_object.courses:
|
||||
courses = trail_object.courses
|
||||
# get course ids
|
||||
course_ids = [course.course_id for course in courses]
|
||||
|
||||
# find if the user has already started the course
|
||||
existing_trail = await trails.find_one(
|
||||
{"user_id": user.user_id, "courses.course_id": {"$in": course_ids}}
|
||||
)
|
||||
if existing_trail:
|
||||
# update the status of the element with the matching course_id to "ongoing"
|
||||
for element in existing_trail["courses"]:
|
||||
if element["course_id"] in course_ids:
|
||||
element["status"] = "ongoing"
|
||||
# update the existing trail in the database
|
||||
await trails.replace_one(
|
||||
{"trail_id": existing_trail["trail_id"]}, existing_trail
|
||||
if trail:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Trail already exists",
|
||||
)
|
||||
|
||||
# create trail id
|
||||
trail_id = f"trail_{uuid4()}"
|
||||
trail = Trail.from_orm(trail_object)
|
||||
|
||||
trail.creation_date = str(datetime.now())
|
||||
trail.update_date = str(datetime.now())
|
||||
trail.org_id = trail_object.org_id
|
||||
trail.trail_uuid = str(f"trail_{uuid4()}")
|
||||
|
||||
# create trail
|
||||
trail = TrailInDB(
|
||||
**trail_object.dict(), trail_id=trail_id, user_id=user.user_id, org_id=org_id
|
||||
)
|
||||
|
||||
await trails.insert_one(trail.dict())
|
||||
db_session.add(trail)
|
||||
db_session.commit()
|
||||
db_session.refresh(trail)
|
||||
|
||||
return trail
|
||||
|
||||
|
||||
async def get_user_trail(request: Request, org_slug: str, user: PublicUser) -> Trail:
|
||||
trails = request.app.db["trails"]
|
||||
trail = await trails.find_one({"user_id": user.user_id})
|
||||
async def get_user_trails(
|
||||
request: Request,
|
||||
user: PublicUser,
|
||||
db_session: Session,
|
||||
) -> TrailRead:
|
||||
statement = select(Trail).where(Trail.user_id == user.id)
|
||||
trail = db_session.exec(statement).first()
|
||||
|
||||
if not trail:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Trail not found"
|
||||
)
|
||||
for element in trail["courses"]:
|
||||
course_id = element["course_id"]
|
||||
chapters_meta = await get_coursechapters_meta(request, course_id, user)
|
||||
activities = chapters_meta["activities"]
|
||||
num_activities = len(activities)
|
||||
|
||||
num_completed_activities = len(element.get("activities_marked_complete", []))
|
||||
element["progress"] = (
|
||||
round((num_completed_activities / num_activities) * 100, 2)
|
||||
if num_activities > 0
|
||||
else 0
|
||||
statement = select(TrailRun).where(TrailRun.trail_id == trail.id)
|
||||
trail_runs = db_session.exec(statement).all()
|
||||
|
||||
trail_runs = [
|
||||
TrailRunRead(**trail_run.__dict__, steps=[]) for trail_run in trail_runs
|
||||
]
|
||||
|
||||
for trail_run in trail_runs:
|
||||
statement = select(TrailStep).where(TrailStep.trailrun_id == trail_run.id)
|
||||
trail_steps = db_session.exec(statement).all()
|
||||
|
||||
trail_steps = [TrailStep(**trail_step.__dict__) for trail_step in trail_steps]
|
||||
trail_run.steps = trail_steps
|
||||
|
||||
for trail_step in trail_steps:
|
||||
statement = select(Course).where(Course.id == trail_step.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
trail_step.data = dict(course=course)
|
||||
|
||||
trail_read = TrailRead(
|
||||
**trail.dict(),
|
||||
runs=trail_runs,
|
||||
)
|
||||
|
||||
return Trail(**trail)
|
||||
return trail_read
|
||||
|
||||
|
||||
async def get_user_trail_with_orgslug(
|
||||
request: Request, user: PublicUser, org_slug: str
|
||||
) -> Trail:
|
||||
trails = request.app.db["trails"]
|
||||
orgs = request.app.db["organizations"]
|
||||
courses_mongo = request.app.db["courses"]
|
||||
|
||||
# get org_id from orgslug
|
||||
org = await orgs.find_one({"slug": org_slug})
|
||||
|
||||
trail = await trails.find_one({"user_id": user.user_id, "org_id": org["org_id"]})
|
||||
async def get_user_trail_with_orgid(
|
||||
request: Request, user: PublicUser, org_id: int, db_session: Session
|
||||
) -> TrailRead:
|
||||
statement = select(Trail).where(Trail.org_id == org_id, Trail.user_id == user.id)
|
||||
trail = db_session.exec(statement).first()
|
||||
|
||||
if not trail:
|
||||
return Trail(masked=False, courses=[])
|
||||
|
||||
course_ids = [course["course_id"] for course in trail["courses"]]
|
||||
|
||||
live_courses = await courses_mongo.find({"course_id": {"$in": course_ids}}).to_list(
|
||||
length=None
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Trail not found"
|
||||
)
|
||||
|
||||
for course in trail["courses"]:
|
||||
course_id = course["course_id"]
|
||||
statement = select(TrailRun).where(TrailRun.trail_id == trail.id)
|
||||
trail_runs = db_session.exec(statement).all()
|
||||
|
||||
if course_id not in [course["course_id"] for course in live_courses]:
|
||||
course["masked"] = True
|
||||
continue
|
||||
trail_runs = [
|
||||
TrailRunRead(**trail_run.__dict__, steps=[]) for trail_run in trail_runs
|
||||
]
|
||||
|
||||
chapters_meta = await get_coursechapters_meta(request, course_id, user)
|
||||
activities = chapters_meta["activities"]
|
||||
for trail_run in trail_runs:
|
||||
statement = select(TrailStep).where(TrailStep.trailrun_id == trail_run.id)
|
||||
trail_steps = db_session.exec(statement).all()
|
||||
|
||||
# get course object without _id
|
||||
course_object = await courses_mongo.find_one(
|
||||
{"course_id": course_id}, {"_id": 0}
|
||||
trail_steps = [TrailStep(**trail_step.__dict__) for trail_step in trail_steps]
|
||||
trail_run.steps = trail_steps
|
||||
|
||||
for trail_step in trail_steps:
|
||||
statement = select(Course).where(Course.id == trail_step.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
trail_step.data = dict(course=course)
|
||||
|
||||
trail_read = TrailRead(
|
||||
**trail.dict(),
|
||||
runs=trail_runs,
|
||||
)
|
||||
|
||||
course["course_object"] = course_object
|
||||
num_activities = len(activities)
|
||||
|
||||
num_completed_activities = len(course.get("activities_marked_complete", []))
|
||||
course["progress"] = (
|
||||
round((num_completed_activities / num_activities) * 100, 2)
|
||||
if num_activities > 0
|
||||
else 0
|
||||
)
|
||||
|
||||
return Trail(**trail)
|
||||
return trail_read
|
||||
|
||||
|
||||
async def add_activity_to_trail(
|
||||
request: Request, user: PublicUser, course_id: str, org_slug: str, activity_id: str
|
||||
) -> Trail:
|
||||
trails = request.app.db["trails"]
|
||||
orgs = request.app.db["organizations"]
|
||||
courseid = "course_" + course_id
|
||||
activityid = "activity_" + activity_id
|
||||
request: Request,
|
||||
user: PublicUser,
|
||||
course_id: int,
|
||||
activity_id: int,
|
||||
db_session: Session,
|
||||
) -> TrailRead:
|
||||
|
||||
# get org_id from orgslug
|
||||
org = await orgs.find_one({"slug": org_slug})
|
||||
org_id = org["org_id"]
|
||||
# check if run already exists
|
||||
statement = select(TrailRun).where(TrailRun.course_id == course_id)
|
||||
trailrun = db_session.exec(statement).first()
|
||||
|
||||
# find a trail with the user_id and course_id in the courses array
|
||||
trail = await trails.find_one(
|
||||
{"user_id": user.user_id, "courses.course_id": courseid, "org_id": org_id}
|
||||
)
|
||||
|
||||
if user.user_id == "anonymous":
|
||||
if trailrun:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Anonymous users cannot add activity to trail",
|
||||
status_code=status.HTTP_400_BAD_REQUEST, detail="TrailRun already exists"
|
||||
)
|
||||
|
||||
if not trail:
|
||||
return Trail(masked=False, courses=[])
|
||||
statement = select(Course).where(Course.id == course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
# if a trail has course_id in the courses array, then add the activity_id to the activities_marked_complete array
|
||||
for element in trail["courses"]:
|
||||
if element["course_id"] == courseid:
|
||||
if "activities_marked_complete" in element:
|
||||
# check if activity_id is already in the array
|
||||
if activityid not in element["activities_marked_complete"]:
|
||||
element["activities_marked_complete"].append(activityid)
|
||||
else:
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Activity already marked complete",
|
||||
)
|
||||
else:
|
||||
element["activities_marked_complete"] = [activity_id]
|
||||
|
||||
# modify trail object
|
||||
await trails.replace_one({"trail_id": trail["trail_id"]}, trail)
|
||||
|
||||
return Trail(**trail)
|
||||
|
||||
|
||||
async def add_course_to_trail(
|
||||
request: Request, user: PublicUser, orgslug: str, course_id: str
|
||||
) -> Trail:
|
||||
trails = request.app.db["trails"]
|
||||
orgs = request.app.db["organizations"]
|
||||
|
||||
if user.user_id == "anonymous":
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Anonymous users cannot add activity to trail",
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Course not found"
|
||||
)
|
||||
|
||||
org = await orgs.find_one({"slug": orgslug})
|
||||
|
||||
org = PublicOrganization(**org)
|
||||
|
||||
trail = await trails.find_one({"user_id": user.user_id, "org_id": org["org_id"]})
|
||||
|
||||
if not trail:
|
||||
trail_to_insert = TrailInDB(
|
||||
trail_id=f"trail_{uuid4()}",
|
||||
user_id=user.user_id,
|
||||
org_id=org["org_id"],
|
||||
courses=[],
|
||||
statement = select(Trail).where(
|
||||
Trail.org_id == course.org_id, Trail.user_id == user.id
|
||||
)
|
||||
trail_to_insert = await trails.insert_one(trail_to_insert.dict())
|
||||
|
||||
trail = await trails.find_one({"_id": trail_to_insert.inserted_id})
|
||||
|
||||
# check if course is already present in the trail
|
||||
for element in trail["courses"]:
|
||||
if element["course_id"] == course_id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Course already present in the trail",
|
||||
)
|
||||
|
||||
updated_trail = TrailCourse(
|
||||
course_id=course_id,
|
||||
activities_data=[],
|
||||
activities_marked_complete=[],
|
||||
progress=0,
|
||||
course_object={},
|
||||
status="ongoing",
|
||||
masked=False,
|
||||
)
|
||||
trail["courses"].append(updated_trail.dict())
|
||||
await trails.replace_one({"trail_id": trail["trail_id"]}, trail)
|
||||
return Trail(**trail)
|
||||
|
||||
|
||||
async def remove_course_from_trail(
|
||||
request: Request, user: PublicUser, orgslug: str, course_id: str
|
||||
) -> Trail:
|
||||
trails = request.app.db["trails"]
|
||||
orgs = request.app.db["organizations"]
|
||||
|
||||
if user.user_id == "anonymous":
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Anonymous users cannot add activity to trail",
|
||||
)
|
||||
|
||||
org = await orgs.find_one({"slug": orgslug})
|
||||
|
||||
org = PublicOrganization(**org)
|
||||
trail = await trails.find_one({"user_id": user.user_id, "org_id": org["org_id"]})
|
||||
trail = db_session.exec(statement).first()
|
||||
|
||||
if not trail:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Trail not found"
|
||||
)
|
||||
|
||||
# check if course is already present in the trail
|
||||
statement = select(TrailRun).where(
|
||||
TrailRun.trail_id == trail.id, TrailRun.course_id == course.id
|
||||
)
|
||||
trailrun = db_session.exec(statement).first()
|
||||
|
||||
for element in trail["courses"]:
|
||||
if element["course_id"] == course_id:
|
||||
trail["courses"].remove(element)
|
||||
break
|
||||
if not trailrun:
|
||||
trailrun = TrailRun(
|
||||
trail_id=trail.id is not None,
|
||||
course_id=course.id is not None,
|
||||
org_id=course.org_id,
|
||||
user_id=user.id,
|
||||
creation_date=str(datetime.now()),
|
||||
update_date=str(datetime.now()),
|
||||
)
|
||||
db_session.add(trailrun)
|
||||
db_session.commit()
|
||||
db_session.refresh(trailrun)
|
||||
|
||||
await trails.replace_one({"trail_id": trail["trail_id"]}, trail)
|
||||
return Trail(**trail)
|
||||
statement = select(TrailStep).where(
|
||||
TrailStep.trailrun_id == trailrun.id, TrailStep.activity_id == activity_id
|
||||
)
|
||||
trailstep = db_session.exec(statement).first()
|
||||
|
||||
if not trailstep:
|
||||
trailstep = TrailStep(
|
||||
trailrun_id=trailrun.id is not None,
|
||||
activity_id=activity_id,
|
||||
course_id=course.id is not None,
|
||||
org_id=course.org_id,
|
||||
complete=False,
|
||||
teacher_verified=False,
|
||||
grade="",
|
||||
user_id=user.id,
|
||||
creation_date=str(datetime.now()),
|
||||
update_date=str(datetime.now()),
|
||||
)
|
||||
db_session.add(trailstep)
|
||||
db_session.commit()
|
||||
db_session.refresh(trailstep)
|
||||
|
||||
statement = select(TrailRun).where(TrailRun.trail_id == trail.id)
|
||||
trail_runs = db_session.exec(statement).all()
|
||||
|
||||
trail_runs = [
|
||||
TrailRunRead(**trail_run.__dict__, steps=[]) for trail_run in trail_runs
|
||||
]
|
||||
|
||||
for trail_run in trail_runs:
|
||||
statement = select(TrailStep).where(TrailStep.trailrun_id == trail_run.id)
|
||||
trail_steps = db_session.exec(statement).all()
|
||||
|
||||
trail_steps = [TrailStep(**trail_step.__dict__) for trail_step in trail_steps]
|
||||
trail_run.steps = trail_steps
|
||||
|
||||
for trail_step in trail_steps:
|
||||
statement = select(Course).where(Course.id == trail_step.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
trail_step.data = dict(course=course)
|
||||
|
||||
trail_read = TrailRead(
|
||||
**trail.dict(),
|
||||
runs=trail_runs,
|
||||
)
|
||||
|
||||
return trail_read
|
||||
|
||||
|
||||
async def add_course_to_trail(
|
||||
request: Request,
|
||||
user: PublicUser,
|
||||
course_id: str,
|
||||
db_session: Session,
|
||||
) -> TrailRead:
|
||||
|
||||
# check if run already exists
|
||||
statement = select(TrailRun).where(TrailRun.course_id == course_id)
|
||||
trailrun = db_session.exec(statement).first()
|
||||
|
||||
if trailrun:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST, detail="TrailRun already exists"
|
||||
)
|
||||
|
||||
statement = select(Course).where(Course.id == course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Course not found"
|
||||
)
|
||||
|
||||
statement = select(Trail).where(
|
||||
Trail.org_id == course.org_id, Trail.user_id == user.id
|
||||
)
|
||||
trail = db_session.exec(statement).first()
|
||||
|
||||
if not trail:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Trail not found"
|
||||
)
|
||||
|
||||
statement = select(TrailRun).where(
|
||||
TrailRun.trail_id == trail.id, TrailRun.course_id == course.id
|
||||
)
|
||||
trail_run = db_session.exec(statement).first()
|
||||
|
||||
if not trail_run:
|
||||
trail_run = TrailRun(
|
||||
trail_id=trail.id is not None,
|
||||
course_id=course.id is not None,
|
||||
org_id=course.org_id,
|
||||
user_id=user.id,
|
||||
creation_date=str(datetime.now()),
|
||||
update_date=str(datetime.now()),
|
||||
)
|
||||
db_session.add(trail_run)
|
||||
db_session.commit()
|
||||
db_session.refresh(trail_run)
|
||||
|
||||
statement = select(TrailRun).where(TrailRun.trail_id == trail.id)
|
||||
trail_runs = db_session.exec(statement).all()
|
||||
|
||||
trail_runs = [
|
||||
TrailRunRead(**trail_run.__dict__, steps=[]) for trail_run in trail_runs
|
||||
]
|
||||
|
||||
for trail_run in trail_runs:
|
||||
statement = select(TrailStep).where(TrailStep.trailrun_id == trail_run.id)
|
||||
trail_steps = db_session.exec(statement).all()
|
||||
|
||||
trail_steps = [TrailStep(**trail_step.__dict__) for trail_step in trail_steps]
|
||||
trail_run.steps = trail_steps
|
||||
|
||||
for trail_step in trail_steps:
|
||||
statement = select(Course).where(Course.id == trail_step.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
trail_step.data = dict(course=course)
|
||||
|
||||
trail_read = TrailRead(
|
||||
**trail.dict(),
|
||||
runs=trail_runs,
|
||||
)
|
||||
|
||||
return trail_read
|
||||
|
||||
|
||||
async def remove_course_from_trail(
|
||||
request: Request,
|
||||
user: PublicUser,
|
||||
course_id: str,
|
||||
db_session: Session,
|
||||
) -> TrailRead:
|
||||
statement = select(Course).where(Course.id == course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Course not found"
|
||||
)
|
||||
|
||||
statement = select(Trail).where(
|
||||
Trail.org_id == course.org_id, Trail.user_id == user.id
|
||||
)
|
||||
trail = db_session.exec(statement).first()
|
||||
|
||||
if not trail:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Trail not found"
|
||||
)
|
||||
|
||||
statement = select(TrailRun).where(
|
||||
TrailRun.trail_id == trail.id, TrailRun.course_id == course.id
|
||||
)
|
||||
trail_run = db_session.exec(statement).first()
|
||||
|
||||
if trail_run:
|
||||
db_session.delete(trail_run)
|
||||
db_session.commit()
|
||||
|
||||
statement = select(TrailRun).where(TrailRun.trail_id == trail.id)
|
||||
trail_runs = db_session.exec(statement).all()
|
||||
|
||||
trail_runs = [
|
||||
TrailRunRead(**trail_run.__dict__, steps=[]) for trail_run in trail_runs
|
||||
]
|
||||
for trail_run in trail_runs:
|
||||
statement = select(TrailStep).where(TrailStep.trailrun_id == trail_run.id)
|
||||
trail_steps = db_session.exec(statement).all()
|
||||
|
||||
trail_steps = [TrailStep(**trail_step.__dict__) for trail_step in trail_steps]
|
||||
trail_run.steps = trail_steps
|
||||
|
||||
for trail_step in trail_steps:
|
||||
statement = select(Course).where(Course.id == trail_step.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
trail_step.data = dict(course=course)
|
||||
|
||||
trail_read = TrailRead(
|
||||
**trail.dict(),
|
||||
runs=trail_runs,
|
||||
)
|
||||
|
||||
return trail_read
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue