From e14ba02f97582f0cb2bc3cf2662cf617aca0c365 Mon Sep 17 00:00:00 2001 From: swve Date: Sun, 26 Mar 2023 18:46:03 +0200 Subject: [PATCH 1/2] feat: introduce trail api and depreciate old api --- .../(withmenu)/course/[courseid]/page.tsx | 22 +- .../app/_orgs/[orgslug]/(withmenu)/layout.tsx | 2 +- .../(withmenu)/{progress => trail}/page.tsx | 53 ++--- front/components/UI/Elements/Menu.tsx | 7 +- front/services/config/config.ts | 3 +- front/services/courses/activity.ts | 12 +- src/main.py | 4 +- src/routers/activity.py | 47 ---- src/routers/trail.py | 47 ++++ src/services/activity.py | 164 -------------- src/services/courses/courses.py | 17 +- src/services/orgs.py | 3 + src/services/trail.py | 205 ++++++++++++++++++ 13 files changed, 315 insertions(+), 271 deletions(-) rename front/app/_orgs/[orgslug]/(withmenu)/{progress => trail}/page.tsx (60%) delete mode 100644 src/routers/activity.py create mode 100644 src/routers/trail.py delete mode 100644 src/services/activity.py create mode 100644 src/services/trail.py diff --git a/front/app/_orgs/[orgslug]/(withmenu)/course/[courseid]/page.tsx b/front/app/_orgs/[orgslug]/(withmenu)/course/[courseid]/page.tsx index b0d6e311..41103208 100644 --- a/front/app/_orgs/[orgslug]/(withmenu)/course/[courseid]/page.tsx +++ b/front/app/_orgs/[orgslug]/(withmenu)/course/[courseid]/page.tsx @@ -1,6 +1,6 @@ "use client"; import { EyeOpenIcon, Pencil2Icon } from "@radix-ui/react-icons"; -import { closeActivity, createActivity } from "@services/courses/activity"; +import { removeCourse, startCourse } from "@services/courses/activity"; import Link from "next/link"; import React from "react"; import styled from "styled-components"; @@ -13,21 +13,19 @@ const CourseIdPage = (params: any) => { const orgslug = params.params.orgslug; const { data: course, error: error } = useSWR(`${getAPIUrl()}courses/meta/course_${courseid}`, swrFetcher); - async function startActivity() { + async function startCourseUI() { // Create activity - await createActivity("course_" + courseid); + await startCourse("course_" + courseid, orgslug); // Mutate course mutate(`${getAPIUrl()}courses/meta/course_${courseid}`); } - async function quitActivity() { - // Get activity id and org id - let activity_id = course.activity.activity_id; - let org_id = course.activity.org_id; + async function quitCourse() { + // Close activity - let activity = await closeActivity(activity_id, org_id); + let activity = await removeCourse("course_" + courseid, orgslug); console.log(activity); // Mutate course @@ -111,12 +109,12 @@ const CourseIdPage = (params: any) => { - {course.activity.status == "ongoing" ? ( - ) : ( - + )} diff --git a/front/app/_orgs/[orgslug]/(withmenu)/layout.tsx b/front/app/_orgs/[orgslug]/(withmenu)/layout.tsx index 4269ad70..a74dcc9c 100644 --- a/front/app/_orgs/[orgslug]/(withmenu)/layout.tsx +++ b/front/app/_orgs/[orgslug]/(withmenu)/layout.tsx @@ -7,7 +7,7 @@ export default function RootLayout({ children, params }: { children: React.React return ( <> - + {children} diff --git a/front/app/_orgs/[orgslug]/(withmenu)/progress/page.tsx b/front/app/_orgs/[orgslug]/(withmenu)/trail/page.tsx similarity index 60% rename from front/app/_orgs/[orgslug]/(withmenu)/progress/page.tsx rename to front/app/_orgs/[orgslug]/(withmenu)/trail/page.tsx index 83561c96..2d86dacb 100644 --- a/front/app/_orgs/[orgslug]/(withmenu)/progress/page.tsx +++ b/front/app/_orgs/[orgslug]/(withmenu)/trail/page.tsx @@ -5,42 +5,43 @@ import React from "react"; import { styled } from "styled-components"; import useSWR from "swr"; -function Activity(params: any) { +function Trail(params: any) { let orgslug = params.params.orgslug; - const { data: activities, error: error } = useSWR(`${getAPIUrl()}activity/org_slug/${orgslug}/activities`, swrFetcher); - + const { data: trail, error: error } = useSWR(`${getAPIUrl()}trail/org_slug/${orgslug}/trail`, swrFetcher); + + return ( - -

Activity

+ +

Trail


{error &&

Failed to load

} - {!activities ? ( + {!trail ? (
Loading...
) : (
- {activities.map((activity: any) => ( - - - - - - + {trail.courses.map((course: any) => ( + + + + + +

Course

-

{activity.course.name}

-
-
- -
+

{course.course_object.name}

+ + + + ))}
)} -
+ ); } -export default Activity; +export default Trail; -const ActivityLayout = styled.div` +const TrailLayout = styled.div` display: flex; margin: 0 auto; width: 1300px; @@ -48,13 +49,13 @@ const ActivityLayout = styled.div` flex-direction: column; `; -const ActivityMetadata = styled.div` +const TrailMetadata = styled.div` display: flex; flex-direction: row; width: 100%; height: 100%; `; -const ActivityBox = styled.div` +const TrailBox = styled.div` display: flex; flex-direction: column; width: 100%; @@ -67,7 +68,7 @@ const ActivityBox = styled.div` background: #ffffff; `; -const ActivityThumbnail = styled.div` +const TrailThumbnail = styled.div` padding-right: 30px; height: 100%; border-radius: 7px 0px 0px 7px; @@ -78,7 +79,7 @@ const ActivityThumbnail = styled.div` } `; -const ActivityInfo = styled.div` +const TrailInfo = styled.div` width: 100%; height: 100%; background: #ffffff; @@ -99,7 +100,7 @@ const ActivityInfo = styled.div` } `; -const ActivityProgress = styled.div` +const TrailProgress = styled.div` margin-top: 10px; border-radius: 20px; height: 10px; diff --git a/front/components/UI/Elements/Menu.tsx b/front/components/UI/Elements/Menu.tsx index 2a39f59f..722b20d1 100644 --- a/front/components/UI/Elements/Menu.tsx +++ b/front/components/UI/Elements/Menu.tsx @@ -10,10 +10,11 @@ import { useRouter, useSearchParams, usePathname } from "next/navigation"; import { headers } from "next/headers"; import { getOrgFromUri, getUriWithOrg } from "@services/config/config"; -export const Menu = (params : any) => { +export const Menu = (props : any ) => { const router = useRouter(); const pathname = usePathname(); - const orgslug = getOrgFromUri(); + const orgslug = props.orgslug; + return ( @@ -41,7 +42,7 @@ export const Menu = (params : any) => {
  • {" "} - Activity + Trail
  • More
  • diff --git a/front/services/config/config.ts b/front/services/config/config.ts index b8352c0d..8e9a63a6 100644 --- a/front/services/config/config.ts +++ b/front/services/config/config.ts @@ -21,7 +21,8 @@ export const getOrgFromUri = () => { } else { if (typeof window !== "undefined") { const hostname = window.location.hostname; - return hostname.replace(".localhost:3000", ""); + + return hostname.replace(".localhost", ""); } } }; diff --git a/front/services/courses/activity.ts b/front/services/courses/activity.ts index 9bd6c481..ea3802d1 100644 --- a/front/services/courses/activity.ts +++ b/front/services/courses/activity.ts @@ -6,18 +6,15 @@ import { getAPIUrl } from "@services/config/config"; GET requests are called from the frontend using SWR (https://swr.vercel.app/) */ -export async function createActivity(course_id: string) { - let data = { - course_id: course_id, - }; - const result: any = await fetch(`${getAPIUrl()}activity/start`, RequestBody("POST", data)) +export async function startCourse(course_id: string, org_slug: string) { + const result: any = await fetch(`${getAPIUrl()}trail/${org_slug}/add_course/${course_id}`, RequestBody("POST", null)) .then((result) => result.json()) .catch((error) => console.log("error", error)); return result; } -export async function closeActivity(org_id: string, activity_id: string) { - const result: any = await fetch(`${getAPIUrl()}activity/${org_id}/close_activity/${activity_id}"`, RequestBody("PATCH", null)) +export async function removeCourse(course_id: string, org_slug: string) { + const result: any = await fetch(`${getAPIUrl()}trail/${org_slug}/remove_course/${course_id}`, RequestBody("POST", null)) .then((result) => result.json()) .catch((error) => console.log("error", error)); return result; @@ -29,4 +26,3 @@ export async function maskActivityAsComplete(org_id: string, course_id: string, .catch((error) => console.log("error", error)); return result; } - diff --git a/src/main.py b/src/main.py index f85d6cbb..add8842f 100644 --- a/src/main.py +++ b/src/main.py @@ -1,5 +1,5 @@ from fastapi import APIRouter -from src.routers import activity, blocks, users, auth, orgs, roles +from src.routers import blocks, trail, users, auth, orgs, roles from src.routers.courses import chapters, collections, courses,activities @@ -16,6 +16,6 @@ global_router.include_router(courses.router, prefix="/courses", tags=["courses"] global_router.include_router(chapters.router, prefix="/chapters", tags=["chapters"]) global_router.include_router(activities.router, prefix="/activities", tags=["activities"]) global_router.include_router(collections.router, prefix="/collections", tags=["collections"]) -global_router.include_router(activity.router, prefix="/activity", tags=["activity"]) +global_router.include_router(trail.router, prefix="/trail", tags=["trail"]) diff --git a/src/routers/activity.py b/src/routers/activity.py deleted file mode 100644 index 6ea6e04c..00000000 --- a/src/routers/activity.py +++ /dev/null @@ -1,47 +0,0 @@ -from fastapi import APIRouter, Depends, Request -from src.security.auth import get_current_user -from src.services.activity import Activity, add_activity_to_activity, close_activity, create_activity, get_user_activities, get_user_activities_orgslug - - -router = APIRouter() - - -@router.post("/start") -async def api_start_activity(request: Request, activity_object: Activity, user=Depends(get_current_user)): - """ - Start activity - """ - return await create_activity(request, user, activity_object) - -# TODO : get activity by user_is and org_id and course_id - - -@router.get("/org_id/{org_id}/activities") -async def api_get_activity_by_orgid(request: Request, org_id: str, user=Depends(get_current_user)): - """ - Get a user activities - """ - return await get_user_activities(request, user, org_id=org_id) - -@router.get("/org_slug/{org_slug}/activities") -async def api_get_activity_by_orgslug(request: Request, org_slug: str, user=Depends(get_current_user)): - """ - Get a user activities using org slug - """ - return await get_user_activities_orgslug(request, user, org_slug=org_slug) - - -@router.post("/{org_id}/add_activity/{course_id}/{activity_id}") -async def api_add_activity_to_activity(request: Request, org_id: str, course_id: str, activity_id: str, user=Depends(get_current_user)): - """ - Add activity to activity - """ - return await add_activity_to_activity(request, user, org_id, course_id, activity_id) - - -@router.patch("/{org_id}/close_activity/{activity_id}") -async def api_close_activity(request: Request, org_id: str, activity_id: str, user=Depends(get_current_user)): - """ - Close activity - """ - return await close_activity(request, user, org_id, activity_id) diff --git a/src/routers/trail.py b/src/routers/trail.py new file mode 100644 index 00000000..ef345895 --- /dev/null +++ b/src/routers/trail.py @@ -0,0 +1,47 @@ +from typing import Optional +from fastapi import APIRouter, Depends, Request +from src.security.auth import get_current_user +from src.services.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 + + +router = APIRouter() + + +@router.post("/start") +async def api_start_trail(request: Request, trail_object: Trail, org_id: str, user=Depends(get_current_user)) -> Trail: + """ + Start trail + """ + return await create_trail(request, user, org_id, trail_object) + + +@router.get("/org_id/{org_id}/trail") +async def api_get_trail_by_orgid(request: Request, org_slug: str, user=Depends(get_current_user)): + """ + Get a user trails + """ + return await get_user_trail(request, user=user, org_slug=org_slug) + + +@router.get("/org_slug/{org_slug}/trail") +async def api_get_trail_by_orgslug(request: Request, org_slug: str, user=Depends(get_current_user)): + """ + Get a user trails using org slug + """ + return await get_user_trail_with_orgslug(request, user, org_slug=org_slug) + + +@router.post("/{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)): + """ + Add Course to trail + """ + return await add_course_to_trail(request, user, org_slug, course_id) + + +@router.post("/{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)): + """ + Remove Course from trail + """ + return await remove_course_from_trail(request, user, org_slug, course_id) diff --git a/src/services/activity.py b/src/services/activity.py deleted file mode 100644 index f5894e19..00000000 --- a/src/services/activity.py +++ /dev/null @@ -1,164 +0,0 @@ -from cmath import log -from datetime import datetime -import json -from typing import List, Literal, Optional -from uuid import uuid4 -from fastapi import HTTPException, Request, status -from pydantic import BaseModel -from src.services.courses.chapters import get_coursechapters_meta - -from src.services.users.users import PublicUser - -#### Classes #################################################### - - -class Activity(BaseModel): - course_id: str - status: Optional[Literal['ongoing', 'done', 'closed']] = 'ongoing' - masked: Optional[bool] = False - activities_marked_complete: Optional[List[str]] = [] - activities_data: Optional[List[dict]] = [] - - -class ActivityInDB(Activity): - activity_id: str - user_id: str - org_id: str - creationDate: str = datetime.now().isoformat() - updateDate: str = datetime.now().isoformat() - - -#### Classes #################################################### - - -async def create_activity(request: Request, user: PublicUser, activity_object: Activity): - activities = request.app.db["activities"] - - # find if the user has already started the course - isActivityAlreadCreated = await activities.find_one( - {"course_id": activity_object.course_id, "user_id": user.user_id}) - - if isActivityAlreadCreated: - if isActivityAlreadCreated['status'] == 'closed': - activity_object.status = 'ongoing' - await activities.update_one( - {"activity_id": isActivityAlreadCreated['activity_id']}, {"$set": activity_object.dict()}) - return activity_object - else: - raise HTTPException( - status_code=status.HTTP_409_CONFLICT, detail="Activity already created") - # create activity id - activity_id = f"activity_{uuid4()}" - - # create activity - activity = ActivityInDB(**activity_object.dict(),activity_id=activity_id, - user_id=user.user_id, org_id=activity_object.course_id) - - await activities.insert_one(activity.dict()) - - return activity - - -async def get_user_activities(request: Request, user: PublicUser, org_id: str): - activities = request.app.db["activities"] - courses = request.app.db["courses"] - coursechapters = request.app.db["coursechapters"] - - activities_metadata = [] - - user_activities = activities.find( - {"user_id": user.user_id}, {'_id': 0}) - - if not user_activities: - raise HTTPException( - status_code=status.HTTP_409_CONFLICT, detail="No activities found") - - for activity in await user_activities.to_list(length=100): - # get number of activities in the course - coursechapters = await get_coursechapters_meta(request, activity['course_id'], user) - - # calculate progression using the number of activities marked complete and the total number of activities - progression = round( - len(activity['activities_marked_complete']) / len(coursechapters['activities']) * 100, 2) - - course = await courses.find_one({"course_id": activity['course_id']}, {'_id': 0}) - - # add progression to the activity - one_activity = {"course": course, "activitydata": activity, "progression": progression} - activities_metadata.append(one_activity) - - return activities_metadata - -async def get_user_activities_orgslug(request: Request, user: PublicUser, org_slug: str): - activities = request.app.db["activities"] - courses = request.app.db["courses"] - coursechapters = request.app.db["coursechapters"] - - activities_metadata = [] - - user_activities = activities.find( - {"user_id": user.user_id}, {'_id': 0}) - - if not user_activities: - raise HTTPException( - status_code=status.HTTP_409_CONFLICT, detail="No activities found") - - for activity in await user_activities.to_list(length=100): - # get number of activities in the course - coursechapters = await get_coursechapters_meta(request, activity['course_id'], user) - - # calculate progression using the number of activities marked complete and the total number of activities - progression = round( - len(activity['activities_marked_complete']) / len(coursechapters['activities']) * 100, 2) - - course = await courses.find_one({"course_id": activity['course_id']}, {'_id': 0}) - - # add progression to the activity - one_activity = {"course": course, "activitydata": activity, "progression": progression} - activities_metadata.append(one_activity) - - return activities_metadata - - -async def add_activity_to_activity(request: Request, user: PublicUser, org_id: str, course_id: str, activity_id: str): - activities = request.app.db["activities"] - course_id = f"course_{course_id}" - activity_id = f"activity_{activity_id}" - - activity = await activities.find_one( - {"course_id": course_id, - "user_id": user.user_id - }, {'_id': 0}) - - if activity_id not in activity['activities_marked_complete']: - activity['activities_marked_complete'].append(str(activity_id)) - await activities.update_one( - {"activity_id": activity['activity_id']}, {"$set": activity}) - return activity - - if not activity: - raise HTTPException( - status_code=status.HTTP_409_CONFLICT, detail="Activity not found") - - else: - raise HTTPException( - status_code=status.HTTP_409_CONFLICT, detail="Activity already marked complete") - - -async def close_activity(request: Request, user: PublicUser, activity_id: str, org_id: str,): - activities = request.app.db["activities"] - activity = await activities.find_one( - {"activity_id": activity_id, "user_id": user.user_id}) - - if not activity: - raise HTTPException( - status_code=status.HTTP_409_CONFLICT, detail="Activity not found") - - activity['status'] = 'closed' - - await activities.update_one( - {"activity_id": activity['activity_id']}, {"$set": activity}) - - activity = ActivityInDB(**activity) - - return activity diff --git a/src/services/courses/courses.py b/src/services/courses/courses.py index 5e568904..d0682379 100644 --- a/src/services/courses/courses.py +++ b/src/services/courses/courses.py @@ -72,7 +72,7 @@ async def get_course(request: Request, course_id: str, current_user: PublicUser) async def get_course_meta(request: Request, course_id: str, current_user: PublicUser): courses = request.app.db["courses"] coursechapters = request.app.db["coursechapters"] - activities = request.app.db["activities"] + trails = request.app.db["trails"] course = await courses.find_one({"course_id": course_id}) activities = request.app.db["activities"] @@ -119,17 +119,20 @@ async def get_course_meta(request: Request, course_id: str, current_user: Public course = Course(**course) # Get activity by user - activity = await activities.find_one( - {"course_id": course_id, "user_id": current_user.user_id}) - if activity: - activity = json.loads(json.dumps(activity, default=str)) + trail = await trails.find_one( + {"courses.course_id": course_id, "user_id": current_user.user_id}) + print(trail) + 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: - activity = "" + trail_course = "" return { "course": course, "chapters": chapters_list_with_activities, - "activity": activity + "trail": trail_course } diff --git a/src/services/orgs.py b/src/services/orgs.py index 28fd642a..a0de2f47 100644 --- a/src/services/orgs.py +++ b/src/services/orgs.py @@ -30,6 +30,9 @@ class PublicOrganization(Organization): slug: str org_id: str + def __getitem__(self, item): + return getattr(self, item) + #### Classes #################################################### diff --git a/src/services/trail.py b/src/services/trail.py new file mode 100644 index 00000000..69d74b34 --- /dev/null +++ b/src/services/trail.py @@ -0,0 +1,205 @@ +from cmath import log +from datetime import datetime +import json +from typing import List, Literal, Optional +from uuid import uuid4 +from fastapi import HTTPException, Request, status +from pydantic import BaseModel +from src.services.courses.chapters import get_coursechapters_meta +from src.services.orgs import PublicOrganization + +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) -> Trail: + trails = request.app.db["trails"] + + # 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) + + # create trail id + trail_id = 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()) + + 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}) + 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 + + return Trail(**trail) + + +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"]}) + if not trail: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Trail not found") + for courses in trail["courses"]: + course_id = courses["course_id"] + + chapters_meta = await get_coursechapters_meta(request, course_id, user) + activities = chapters_meta["activities"] + + # get course object without _id + course_object = await courses_mongo.find_one({"course_id": course_id}, {"_id": 0}) + + courses["course_object"] = course_object + num_activities = len(activities) + + num_completed_activities = len( + courses.get("activities_marked_complete", [])) + courses["progress"] = round( + (num_completed_activities / num_activities) * 100, 2) if num_activities > 0 else 0 + + return Trail(**trail) + + +async def add_activity_to_trail(request: Request, user: PublicUser, trail_id: str, course_id: str, activity_id: str) -> Trail: + trails = request.app.db["trails"] + trail = await trails.find_one({"trail_id": trail_id, "user_id": user.user_id}) + if not trail: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Trail not found") + for element in trail["courses"]: + if element["course_id"] == course_id: + if activity_id not in element["activities_marked_complete"]: + element["activities_marked_complete"].append(activity_id) + break + await trails.replace_one({"trail_id": 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"] + + + 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: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Trail not found") + + # 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"] + + + org = await orgs.find_one({"slug": orgslug}) + + org = PublicOrganization(**org) + + print(org) + + trail = await trails.find_one( + {"user_id": user.user_id, "org_id": org["org_id"]}) + + if not trail: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Trail not found") + + # check if course is already present in the trail + + for element in trail["courses"]: + if element["course_id"] == course_id: + trail["courses"].remove(element) + break + + await trails.replace_one({"trail_id": trail['trail_id']}, trail) + return Trail(**trail) \ No newline at end of file From 83dbdfdc05623f75ec8bba04b2e669af22ff992c Mon Sep 17 00:00:00 2001 From: swve Date: Sun, 26 Mar 2023 19:26:17 +0200 Subject: [PATCH 2/2] fix: improve trail api --- .../[courseid]/activity/[activityid]/page.tsx | 14 +++---- .../(withmenu)/course/[courseid]/page.tsx | 1 - front/services/courses/activity.ts | 8 ++-- src/routers/trail.py | 14 ++++++- src/services/trail.py | 38 +++++++++++++++---- 5 files changed, 53 insertions(+), 22 deletions(-) diff --git a/front/app/_orgs/[orgslug]/(withmenu)/course/[courseid]/activity/[activityid]/page.tsx b/front/app/_orgs/[orgslug]/(withmenu)/course/[courseid]/activity/[activityid]/page.tsx index 937cfd27..1ba17cb3 100644 --- a/front/app/_orgs/[orgslug]/(withmenu)/course/[courseid]/activity/[activityid]/page.tsx +++ b/front/app/_orgs/[orgslug]/(withmenu)/course/[courseid]/activity/[activityid]/page.tsx @@ -11,8 +11,8 @@ import { getCourse } from "@services/courses/courses"; import VideoActivity from "@components/ActivityViews/Video/Video"; import useSWR, { mutate } from "swr"; import { Check } from "lucide-react"; -import { maskActivityAsComplete } from "@services/courses/activity"; import { swrFetcher } from "@services/utils/requests"; +import { markActivityAsComplete } from "@services/courses/activity"; function ActivityPage(params: any) { const activityid = params.params.activityid; @@ -20,12 +20,11 @@ function ActivityPage(params: any) { const orgslug = params.params.orgslug; const { data: course, error: error_course } = useSWR(`${getAPIUrl()}courses/meta/course_${courseid}`, swrFetcher); - const { data: activity, error: error_activity } = useSWR(`${getAPIUrl()}activities/activity_${activityid}`, swrFetcher); + const { data: activity, error: error_activity } = useSWR(`${getAPIUrl()}trail/org_slug/${orgslug}/trail`, swrFetcher); - console.log(course, activity); async function markActivityAsCompleteFront() { - const activity = await maskActivityAsComplete("" + activityid, courseid, activity.activity_id.replace("activity_", "")); + const trail = await markActivityAsComplete(orgslug, courseid, activityid); mutate(`${getAPIUrl()}activities/activity_${activityid}`); mutate(`${getAPIUrl()}courses/meta/course_${courseid}`); } @@ -76,9 +75,10 @@ function ActivityPage(params: any) { {activity.type == "video" && } - {course.activity.activities_marked_complete && - course.activity.activities_marked_complete.includes("activity_" + activityid) && - course.activity.status == "ongoing" ? ( + + {course.trail.activities_marked_complete && + course.trail.activities_marked_complete.includes("activity_" + activityid) && + course.trail.status == "ongoing" ? (