feat: rename lectures to activities across the app

This commit is contained in:
swve 2023-03-23 22:57:36 +01:00
parent bc2def6178
commit 7b63eb573c
51 changed files with 570 additions and 570 deletions

View file

@ -4,7 +4,7 @@ import { default as React, useEffect, useRef } from "react";
import { useRouter } from "next/navigation";
import { getLecture } from "@services/courses/lectures";
import { getActivity } from "@services/courses/activities";
import AuthProvider from "@components/Security/AuthProvider";
import EditorWrapper from "@components/Editor/EditorWrapper";
import useSWR, { mutate } from "swr";
@ -12,21 +12,21 @@ import { getAPIUrl } from "@services/config/config";
import { swrFetcher } from "@services/utils/requests";
function EditLecture(params: any) {
function EditActivity(params: any) {
const router = useRouter();
const lectureid = params.params.lectureid;
const activityid = params.params.activityid;
const courseid = params.params.courseid;
const { data: courseInfo, error: error_course } = useSWR(`${getAPIUrl()}courses/meta/course_${courseid}`, swrFetcher);
const { data: lecture, error: error_lecture } = useSWR(`${getAPIUrl()}lectures/lecture_${lectureid}`, swrFetcher);
const { data: activity, error: error_activity } = useSWR(`${getAPIUrl()}activities/activity_${activityid}`, swrFetcher);
return (
<AuthProvider>
{!courseInfo || !lecture ? <div>Loading...</div> : <EditorWrapper course={courseInfo} lecture={lecture} content={lecture.content}></EditorWrapper>}
{!courseInfo || !activity ? <div>Loading...</div> : <EditorWrapper course={courseInfo} activity={activity} content={activity.content}></EditorWrapper>}
</AuthProvider>
);
}
export default EditLecture;
export default EditActivity;

View file

@ -3,61 +3,61 @@ import { useRouter } from "next/navigation";
import Link from "next/link";
import React, { useMemo } from "react";
import Layout from "@components/UI/Layout";
import { getLecture } from "@services/courses/lectures";
import { getActivity } from "@services/courses/activities";
import { getAPIUrl, getBackendUrl, getUriWithOrg } from "@services/config/config";
import Canva from "@components/LectureViews/DynamicCanva/DynamicCanva";
import Canva from "@components/ActivityViews/DynamicCanva/DynamicCanva";
import styled from "styled-components";
import { getCourse } from "@services/courses/courses";
import VideoLecture from "@components/LectureViews/Video/Video";
import VideoActivity from "@components/ActivityViews/Video/Video";
import useSWR, { mutate } from "swr";
import { Check } from "lucide-react";
import { maskLectureAsComplete } from "@services/courses/activity";
import { maskActivityAsComplete } from "@services/courses/activity";
import { swrFetcher } from "@services/utils/requests";
function LecturePage(params: any) {
const lectureid = params.params.lectureid;
function ActivityPage(params: any) {
const activityid = params.params.activityid;
const courseid = params.params.courseid;
const orgslug = params.params.orgslug;
const { data: course, error: error_course } = useSWR(`${getAPIUrl()}courses/meta/course_${courseid}`, swrFetcher);
const { data: lecture, error: error_lecture } = useSWR(`${getAPIUrl()}lectures/lecture_${lectureid}`, swrFetcher);
const { data: activity, error: error_activity } = useSWR(`${getAPIUrl()}activities/activity_${activityid}`, swrFetcher);
console.log(course, lecture);
console.log(course, activity);
async function markLectureAsCompleteFront() {
const activity = await maskLectureAsComplete("" + lectureid, courseid, lecture.lecture_id.replace("lecture_", ""));
mutate(`${getAPIUrl()}lectures/lecture_${lectureid}`);
async function markActivityAsCompleteFront() {
const activity = await maskActivityAsComplete("" + activityid, courseid, activity.activity_id.replace("activity_", ""));
mutate(`${getAPIUrl()}activities/activity_${activityid}`);
mutate(`${getAPIUrl()}courses/meta/course_${courseid}`);
}
return (
<>
{error_course && <p>Failed to load</p>}
{!course || !lecture ? (
{!course || !activity ? (
<div>Loading...</div>
) : (
<LectureLayout>
<LectureTopWrapper>
<LectureThumbnail>
<ActivityLayout>
<ActivityTopWrapper>
<ActivityThumbnail>
<Link href={getUriWithOrg(orgslug,"") +`/course/${courseid}`}>
<img src={`${getBackendUrl()}content/uploads/img/${course.course.thumbnail}`} alt="" />
</Link>
</LectureThumbnail>
<LectureInfo>
</ActivityThumbnail>
<ActivityInfo>
<p>Course</p>
<h1>{course.course.name}</h1>
</LectureInfo>
</LectureTopWrapper>
</ActivityInfo>
</ActivityTopWrapper>
<ChaptersWrapper>
{course.chapters.map((chapter: any) => {
return (
<>
<div style={{ display: "flex", flexDirection: "row" }} key={chapter.chapter_id}>
{chapter.lectures.map((lecture: any) => {
{chapter.activities.map((activity: any) => {
return (
<>
<Link href={getUriWithOrg(orgslug,"") +`/course/${courseid}/lecture/${lecture.id.replace("lecture_", "")}`}>
<ChapterIndicator key={lecture.id} />
<Link href={getUriWithOrg(orgslug,"") +`/course/${courseid}/activity/${activity.id.replace("activity_", "")}`}>
<ChapterIndicator key={activity.id} />
</Link>{" "}
</>
);
@ -69,15 +69,15 @@ function LecturePage(params: any) {
})}
</ChaptersWrapper>
{lecture ? (
{activity ? (
<CourseContent>
{lecture.type == "dynamic" && <Canva content={lecture.content} lecture={lecture} />}
{activity.type == "dynamic" && <Canva content={activity.content} activity={activity} />}
{/* todo : use apis & streams instead of this */}
{lecture.type == "video" && <VideoLecture course={course} lecture={lecture} />}
{activity.type == "video" && <VideoActivity course={course} activity={activity} />}
<ActivityMarkerWrapper>
{course.activity.lectures_marked_complete &&
course.activity.lectures_marked_complete.includes("lecture_" + lectureid) &&
{course.activity.activities_marked_complete &&
course.activity.activities_marked_complete.includes("activity_" + activityid) &&
course.activity.status == "ongoing" ? (
<button style={{ backgroundColor: "green" }}>
<i>
@ -86,7 +86,7 @@ function LecturePage(params: any) {
Already completed
</button>
) : (
<button onClick={markLectureAsCompleteFront}>
<button onClick={markActivityAsCompleteFront}>
{" "}
<i>
<Check size={20}></Check>
@ -99,15 +99,15 @@ function LecturePage(params: any) {
) : (
<div>Loading...</div>
)}
</LectureLayout>
</ActivityLayout>
)}
</>
);
}
const LectureLayout = styled.div``;
const ActivityLayout = styled.div``;
const LectureThumbnail = styled.div`
const ActivityThumbnail = styled.div`
padding-right: 30px;
justify-self: center;
img {
@ -117,7 +117,7 @@ const LectureThumbnail = styled.div`
height: 57px;
}
`;
const LectureInfo = styled.div`
const ActivityInfo = styled.div`
h1 {
margin-top: 0px;
}
@ -158,7 +158,7 @@ const ChapterIndicator = styled.div`
}
`;
const LectureTopWrapper = styled.div`
const ActivityTopWrapper = styled.div`
width: 1300px;
padding-top: 50px;
margin: 0 auto;
@ -215,4 +215,4 @@ const ActivityMarkerWrapper = styled.div`
}
`;
export default LecturePage;
export default ActivityPage;

View file

@ -11,8 +11,8 @@ import Chapter from "@components/Drags/Chapter";
import { createChapter, deleteChapter, getCourseChaptersMetadata, updateChaptersMetadata } from "@services/courses/chapters";
import { useRouter } from "next/navigation";
import NewChapterModal from "@components/Modals/CourseEdit/NewChapter";
import NewLectureModal from "@components/Modals/CourseEdit/NewLecture";
import { createLecture, createFileLecture } from "@services/courses/lectures";
import NewActivityModal from "@components/Modals/CourseEdit/NewActivity";
import { createActivity, createFileActivity } from "@services/courses/activities";
import { getOrganizationContextInfo } from "@services/organizations/orgs";
function CourseEdit(params: any) {
@ -23,9 +23,9 @@ function CourseEdit(params: any) {
// New Chapter Modal State
const [newChapterModal, setNewChapterModal] = useState(false) as any;
// New Lecture Modal State
const [newLectureModal, setNewLectureModal] = useState(false) as any;
const [newLectureModalData, setNewLectureModalData] = useState("") as any;
// New Activity Modal State
const [newActivityModal, setNewActivityModal] = useState(false) as any;
const [newActivityModalData, setNewActivityModalData] = useState("") as any;
// Check window availability
const [winReady, setwinReady] = useState(false);
@ -53,16 +53,16 @@ function CourseEdit(params: any) {
const chapterOrder = data.chapterOrder ? data.chapterOrder : [];
return chapterOrder.map((chapterId: any) => {
const chapter = data.chapters[chapterId];
let lectures = [];
if (data.lectures) {
lectures = chapter.lectureIds.map((lectureId: any) => data.lectures[lectureId])
? chapter.lectureIds.map((lectureId: any) => data.lectures[lectureId])
let activities = [];
if (data.activities) {
activities = chapter.activityIds.map((activityId: any) => data.activities[activityId])
? chapter.activityIds.map((activityId: any) => data.activities[activityId])
: [];
}
return {
list: {
chapter: chapter,
lectures: lectures,
activities: activities,
},
};
});
@ -75,23 +75,23 @@ function CourseEdit(params: any) {
setNewChapterModal(false);
};
// Submit new lecture
const submitLecture = async (lecture: any) => {
console.log("submitLecture", lecture);
// Submit new activity
const submitActivity = async (activity: any) => {
console.log("submitActivity", activity);
let org = await getOrganizationContextInfo(orgslug);
await updateChaptersMetadata(courseid, data);
await createLecture(lecture, lecture.chapterId, org.org_id);
await createActivity(activity, activity.chapterId, org.org_id);
await getCourseChapters();
setNewLectureModal(false);
setNewActivityModal(false);
};
// Submit File Upload
const submitFileLecture = async (file: any, type: any, lecture: any, chapterId: string) => {
console.log("submitFileLecture", file);
const submitFileActivity = async (file: any, type: any, activity: any, chapterId: string) => {
console.log("submitFileActivity", file);
await updateChaptersMetadata(courseid, data);
await createFileLecture(file, type, lecture, chapterId);
await createFileActivity(file, type, activity, chapterId);
await getCourseChapters();
setNewLectureModal(false);
setNewActivityModal(false);
};
const deleteChapterUI = async (chapterId: any) => {
@ -111,10 +111,10 @@ function CourseEdit(params: any) {
*/
const openNewLectureModal = async (chapterId: any) => {
console.log("openNewLectureModal", chapterId);
setNewLectureModal(true);
setNewLectureModalData(chapterId);
const openNewActivityModal = async (chapterId: any) => {
console.log("openNewActivityModal", chapterId);
setNewActivityModal(true);
setNewActivityModalData(chapterId);
};
// Close new chapter modal
@ -122,8 +122,8 @@ function CourseEdit(params: any) {
setNewChapterModal(false);
};
const closeNewLectureModal = () => {
setNewLectureModal(false);
const closeNewActivityModal = () => {
setNewActivityModal(false);
};
/*
@ -134,12 +134,12 @@ function CourseEdit(params: any) {
const { destination, source, draggableId, type } = result;
console.log(result);
// check if the lecture is dropped outside the droppable area
// check if the activity is dropped outside the droppable area
if (!destination) {
return;
}
// check if the lecture is dropped in the same place
// check if the activity is dropped in the same place
if (destination.droppableId === source.droppableId && destination.index === source.index) {
return;
}
@ -159,26 +159,26 @@ function CourseEdit(params: any) {
return;
}
//////////////////////// LECTURES IN SAME CHAPTERS ////////////////////////////
// check if the lecture is dropped in the same chapter
//////////////////////// ACTIVITIES IN SAME CHAPTERS ////////////////////////////
// check if the activity is dropped in the same chapter
const start = data.chapters[source.droppableId];
const finish = data.chapters[destination.droppableId];
// check if the lecture is dropped in the same chapter
// check if the activity is dropped in the same chapter
if (start === finish) {
// create new arrays for chapters and lectures
// create new arrays for chapters and activities
const chapter = data.chapters[source.droppableId];
const newLectureIds = Array.from(chapter.lectureIds);
const newActivityIds = Array.from(chapter.activityIds);
// remove the lecture from the old position
newLectureIds.splice(source.index, 1);
// remove the activity from the old position
newActivityIds.splice(source.index, 1);
// add the lecture to the new position
newLectureIds.splice(destination.index, 0, draggableId);
// add the activity to the new position
newActivityIds.splice(destination.index, 0, draggableId);
const newChapter = {
...chapter,
lectureIds: newLectureIds,
activityIds: newActivityIds,
};
const newState = {
@ -193,25 +193,25 @@ function CourseEdit(params: any) {
return;
}
//////////////////////// LECTURES IN DIFF CHAPTERS ////////////////////////////
// check if the lecture is dropped in a different chapter
//////////////////////// ACTIVITIES IN DIFF CHAPTERS ////////////////////////////
// check if the activity is dropped in a different chapter
if (start !== finish) {
// create new arrays for chapters and lectures
const startChapterLectureIds = Array.from(start.lectureIds);
// create new arrays for chapters and activities
const startChapterActivityIds = Array.from(start.activityIds);
// remove the lecture from the old position
startChapterLectureIds.splice(source.index, 1);
// remove the activity from the old position
startChapterActivityIds.splice(source.index, 1);
const newStart = {
...start,
lectureIds: startChapterLectureIds,
activityIds: startChapterActivityIds,
};
// add the lecture to the new position within the chapter
const finishChapterLectureIds = Array.from(finish.lectureIds);
finishChapterLectureIds.splice(destination.index, 0, draggableId);
// add the activity to the new position within the chapter
const finishChapterActivityIds = Array.from(finish.activityIds);
finishChapterActivityIds.splice(destination.index, 0, draggableId);
const newFinish = {
...finish,
lectureIds: finishChapterLectureIds,
activityIds: finishChapterActivityIds,
};
const newState = {
@ -249,13 +249,13 @@ function CourseEdit(params: any) {
</button>
</Title>
{newChapterModal && <NewChapterModal closeModal={closeNewChapterModal} submitChapter={submitChapter}></NewChapterModal>}
{newLectureModal && (
<NewLectureModal
closeModal={closeNewLectureModal}
submitFileLecture={submitFileLecture}
submitLecture={submitLecture}
chapterId={newLectureModalData}
></NewLectureModal>
{newActivityModal && (
<NewActivityModal
closeModal={closeNewActivityModal}
submitFileActivity={submitFileActivity}
submitActivity={submitActivity}
chapterId={newActivityModalData}
></NewActivityModal>
)}
<br />
@ -271,7 +271,7 @@ function CourseEdit(params: any) {
<Chapter
orgslug={orgslug}
courseid={courseid}
openNewLectureModal={openNewLectureModal}
openNewActivityModal={openNewActivityModal}
deleteChapter={deleteChapterUI}
key={index}
info={info}

View file

@ -53,10 +53,10 @@ const CourseIdPage = (params: any) => {
{course.chapters.map((chapter: any) => {
return (
<>
{chapter.lectures.map((lecture: any) => {
{chapter.activities.map((activity: any) => {
return (
<>
<Link href={getUriWithOrg(orgslug,"") +`/course/${courseid}/lecture/${lecture.id.replace("lecture_", "")}`}>
<Link href={getUriWithOrg(orgslug,"") +`/course/${courseid}/activity/${activity.id.replace("activity_", "")}`}>
<ChapterIndicator />
</Link>{" "}
</>
@ -92,12 +92,12 @@ const CourseIdPage = (params: any) => {
return (
<>
<h3>Chapter : {chapter.name}</h3>
{chapter.lectures.map((lecture: any) => {
{chapter.activities.map((activity: any) => {
return (
<>
<p>
Lecture {lecture.name}
<Link href={getUriWithOrg(orgslug,"") +`/course/${courseid}/lecture/${lecture.id.replace("lecture_", "")}`} rel="noopener noreferrer">
Activity {activity.name}
<Link href={getUriWithOrg(orgslug,"") +`/course/${courseid}/activity/${activity.id.replace("activity_", "")}`} rel="noopener noreferrer">
<EyeOpenIcon />
</Link>{" "}
</p>

View file

@ -13,7 +13,7 @@ import PDFBlock from "@components/Editor/Extensions/PDF/PDFBlock";
interface Editor {
content: string;
lecture: any;
activity: any;
//course: any;
}
@ -32,19 +32,19 @@ function Canva(props: Editor) {
}),
ImageBlock.configure({
editable: isEditable,
lecture: props.lecture,
activity: props.activity,
}),
VideoBlock.configure({
editable: true,
lecture: props.lecture,
activity: props.activity,
}),
MathEquationBlock.configure({
editable: false,
lecture: props.lecture,
activity: props.activity,
}),
PDFBlock.configure({
editable: true,
lecture: props.lecture,
activity: props.activity,
}),
Youtube.configure({
controls: true,

View file

@ -2,10 +2,10 @@ import { getBackendUrl } from "@services/config/config";
import React from "react";
import styled from "styled-components";
function VideoLecture({ lecture, course }: { lecture: any; course: any }) {
function VideoActivity({ activity, course }: { activity: any; course: any }) {
function getChapterName() {
let chapterName = "";
let chapterId = lecture.chapter_id;
let chapterId = activity.chapter_id;
course.chapters.forEach((chapter: any) => {
if (chapter.chapter_id === chapterId) {
chapterName = chapter.name;
@ -15,21 +15,21 @@ function VideoLecture({ lecture, course }: { lecture: any; course: any }) {
}
return (
<VideoLectureLayout>
<VideoActivityLayout>
<VideoTitle>
<p>Chapter : {getChapterName()}</p>
{lecture.name}
{activity.name}
</VideoTitle>
<VideoPlayerWrapper>
<video controls src={`${getBackendUrl()}content/uploads/video/${lecture.content.video.lecture_id}/${lecture.content.video.filename}`}></video>
<video controls src={`${getBackendUrl()}content/uploads/video/${activity.content.video.activity_id}/${activity.content.video.filename}`}></video>
</VideoPlayerWrapper>
</VideoLectureLayout>
</VideoActivityLayout>
);
}
export default VideoLecture;
export default VideoActivity;
const VideoLectureLayout = styled.div`
const VideoActivityLayout = styled.div`
display: flex;
flex-direction: column;
margin-top: 10px;

View file

@ -5,31 +5,31 @@ import { EyeOpenIcon, Pencil2Icon } from '@radix-ui/react-icons'
import styled from "styled-components";
import { getUriWithOrg } from "@services/config/config";
function Lecture(props: any) {
function Activity(props: any) {
return (
<Draggable key={props.lecture.id} draggableId={props.lecture.id} index={props.index}>
<Draggable key={props.activity.id} draggableId={props.activity.id} index={props.index}>
{(provided) => (
<LectureWrapper key={props.lecture.id} {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef}>
<p>{props.lecture.name} </p>
<ActivityWrapper key={props.activity.id} {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef}>
<p>{props.activity.name} </p>
<Link
href={getUriWithOrg(props.orgslug,"")+`/course/${props.courseid}/lecture/${props.lecture.id.replace("lecture_", "")}`}
href={getUriWithOrg(props.orgslug,"")+`/course/${props.courseid}/activity/${props.activity.id.replace("activity_", "")}`}
rel="noopener noreferrer">
&nbsp; <EyeOpenIcon/>
</Link>
<Link
href={getUriWithOrg(props.orgslug,"") +`/course/${props.courseid}/lecture/${props.lecture.id.replace("lecture_", "")}/edit`}
href={getUriWithOrg(props.orgslug,"") +`/course/${props.courseid}/activity/${props.activity.id.replace("activity_", "")}/edit`}
rel="noopener noreferrer">
&nbsp; <Pencil2Icon/>
</Link>
</LectureWrapper>
</ActivityWrapper>
)}
</Draggable>
);
}
export const LectureWrapper = styled.div`
export const ActivityWrapper = styled.div`
padding: 2px;
padding-left: 17px;
list-style: none;
@ -44,4 +44,4 @@ export const LectureWrapper = styled.div`
}
`;
export default Lecture;
export default Activity;

View file

@ -1,7 +1,7 @@
import React from "react";
import styled from "styled-components";
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
import Lecture, { LectureWrapper } from "./Lecture";
import Activity, { ActivityWrapper } from "./Activity";
function Chapter(props: any) {
return (
@ -18,10 +18,10 @@ function Chapter(props: any) {
{props.info.list.chapter.name}{" "}
<button
onClick={() => {
props.openNewLectureModal(props.info.list.chapter.id);
props.openNewActivityModal(props.info.list.chapter.id);
}}
>
Create Lecture
Create Activity
</button>
<button
onClick={() => {
@ -31,14 +31,14 @@ function Chapter(props: any) {
X
</button>
</h3>
<Droppable key={props.info.list.chapter.id} droppableId={props.info.list.chapter.id} type="lecture">
<Droppable key={props.info.list.chapter.id} droppableId={props.info.list.chapter.id} type="activity">
{(provided) => (
<LecturesList {...provided.droppableProps} ref={provided.innerRef}>
{props.info.list.lectures.map((lecture: any, index: any) => (
<Lecture orgslug={props.orgslug} courseid={props.courseid} key={lecture.id} lecture={lecture} index={index}></Lecture>
<ActivitiesList {...provided.droppableProps} ref={provided.innerRef}>
{props.info.list.activities.map((activity: any, index: any) => (
<Activity orgslug={props.orgslug} courseid={props.courseid} key={activity.id} activity={activity} index={index}></Activity>
))}
{provided.placeholder}
</LecturesList>
</ActivitiesList>
)}
</Droppable>
</ChapterWrapper>
@ -66,7 +66,7 @@ const ChapterWrapper = styled.div`
}
`;
const LecturesList = styled.div`
const ActivitiesList = styled.div`
padding: 10px;
`;

View file

@ -1,15 +1,15 @@
export const initialData = {
lectures: {
"lecture-1": { id: "lecture-1", content: "First lecture" },
"lecture-2": { id: "lecture-2", content: "Second lecture" },
"lecture-3": { id: "lecture-3", content: "Third lecture" },
"lecture-4": { id: "lecture-4", content: "Fourth lecture" },
"lecture-5": { id: "lecture-5", content: "Fifth lecture" },
activities: {
"activity-1": { id: "activity-1", content: "First activity" },
"activity-2": { id: "activity-2", content: "Second activity" },
"activity-3": { id: "activity-3", content: "Third activity" },
"activity-4": { id: "activity-4", content: "Fourth activity" },
"activity-5": { id: "activity-5", content: "Fifth activity" },
},
chapters: {
"chapter-1": { id: "chapter-1", name: "Chapter 1", lectureIds: ["lecture-1", "lecture-2", "lecture-3"] },
"chapter-2": { id: "chapter-2", name: "Chapter 2", lectureIds: ["lecture-4"] },
"chapter-3": { id: "chapter-3", name: "Chapter 3", lectureIds: ["lecture-5"] },
"chapter-1": { id: "chapter-1", name: "Chapter 1", activityIds: ["activity-1", "activity-2", "activity-3"] },
"chapter-2": { id: "chapter-2", name: "Chapter 2", activityIds: ["activity-4"] },
"chapter-3": { id: "chapter-3", name: "Chapter 3", activityIds: ["activity-5"] },
},
chapterOrder: ["chapter-1", "chapter-2", "chapter-3"],

View file

@ -28,7 +28,7 @@ interface Editor {
content: string;
ydoc: any;
provider: any;
lecture: any;
activity: any;
course: any;
setContent: (content: string) => void;
}
@ -52,23 +52,23 @@ function Editor(props: Editor) {
}),
ImageBlock.configure({
editable: true,
lecture: props.lecture,
activity: props.activity,
}),
VideoBlock.configure({
editable: true,
lecture: props.lecture,
activity: props.activity,
}),
MathEquationBlock.configure({
editable: true,
lecture: props.lecture,
activity: props.activity,
}),
PDFBlock.configure({
editable: true,
lecture: props.lecture,
activity: props.activity,
}),
QuizBlock.configure({
editable: true,
lecture: props.lecture,
activity: props.activity,
}),
Youtube.configure({
controls: true,
@ -112,7 +112,7 @@ function Editor(props: Editor) {
<EditorInfoThumbnail src={`${getBackendUrl()}content/uploads/img/${props.course.course.thumbnail}`} alt=""></EditorInfoThumbnail>
<EditorInfoDocName>
{" "}
<b>{props.course.course.name}</b> <SlashIcon /> {props.lecture.name}{" "}
<b>{props.course.course.name}</b> <SlashIcon /> {props.activity.name}{" "}
</EditorInfoDocName>
<EditorSaveButton onClick={() => props.setContent(editor.getJSON())}>
Save <Save size={12} />

View file

@ -2,11 +2,11 @@ import { default as React, } from "react";
import * as Y from "yjs";
import { WebrtcProvider } from "y-webrtc";
import Editor from "./Editor";
import { updateLecture } from "../../services/courses/lectures";
import { updateActivity } from "@services/courses/activities";
interface EditorWrapperProps {
content: string;
lecture: any;
activity: any;
course:any
}
@ -18,16 +18,16 @@ function EditorWrapper(props: EditorWrapperProps) : JSX.Element {
const [isLoading, setIsLoading] = React.useState(true);
function createRTCProvider() {
// const provider = new WebrtcProvider(props.lecture.lecture_id, ydoc);
// const provider = new WebrtcProvider(props.activity.activity_id, ydoc);
// setYdocState(ydoc);
// setProviderState(provider);
setIsLoading(false);
}
async function setContent(content: any) {
let lecture = props.lecture;
lecture.content = content;
const res = await updateLecture(lecture, lecture.lecture_id);
let activity = props.activity;
activity.content = content;
const res = await updateActivity(activity, activity.activity_id);
alert(JSON.stringify(res));
}
@ -35,7 +35,7 @@ function EditorWrapper(props: EditorWrapperProps) : JSX.Element {
createRTCProvider();
return <div>Loading...</div>;
} else {
return <Editor course={props.course} lecture={props.lecture} content={props.content} setContent={setContent} provider={providerState} ydoc={ydocState}></Editor>;
return <Editor course={props.course} activity={props.activity} content={props.content} setContent={setContent} provider={providerState} ydoc={ydocState}></Editor>;
}
}

View file

@ -17,7 +17,7 @@ function ImageBlockComponent(props: any) {
const handleSubmit = async (e: any) => {
e.preventDefault();
setIsLoading(true);
let object = await uploadNewImageFile(image, props.extension.options.lecture.lecture_id);
let object = await uploadNewImageFile(image, props.extension.options.activity.activity_id);
setIsLoading(false);
setblockObject(object);
props.updateAttributes({
@ -41,7 +41,7 @@ function ImageBlockComponent(props: any) {
{blockObject && (
<BlockImage>
<img
src={`${getBackendUrl()}content/uploads/files/lectures/${props.extension.options.lecture.lecture_id}/blocks/imageBlock/${blockObject.block_id}/${blockObject.block_data.file_id}.${
src={`${getBackendUrl()}content/uploads/files/activities/${props.extension.options.activity.activity_id}/blocks/imageBlock/${blockObject.block_id}/${blockObject.block_data.file_id}.${
blockObject.block_data.file_format
}`}
alt=""

View file

@ -17,7 +17,7 @@ function PDFBlockComponent(props: any) {
const handleSubmit = async (e: any) => {
e.preventDefault();
setIsLoading(true);
let object = await uploadNewPDFFile(pdf, props.extension.options.lecture.lecture_id);
let object = await uploadNewPDFFile(pdf, props.extension.options.activity.activity_id);
setIsLoading(false);
setblockObject(object);
props.updateAttributes({
@ -41,7 +41,7 @@ function PDFBlockComponent(props: any) {
{blockObject && (
<BlockPDF>
<iframe
src={`${getBackendUrl()}content/uploads/files/lectures/${props.extension.options.lecture.lecture_id}/blocks/pdfBlock/${blockObject.block_id}/${blockObject.block_data.file_id}.${
src={`${getBackendUrl()}content/uploads/files/activities/${props.extension.options.activity.activity_id}/blocks/pdfBlock/${blockObject.block_id}/${blockObject.block_data.file_id}.${
blockObject.block_data.file_format
}`}
/>

View file

@ -88,7 +88,7 @@ function ImageBlockComponent(props: any) {
console.log(questions);
console.log(answers);
try {
let res = await submitQuizBlock(props.extension.options.lecture.lecture_id, {questions : questions , answers : answers})
let res = await submitQuizBlock(props.extension.options.activity.activity_id, {questions : questions , answers : answers})
console.log(res.block_id);
props.updateAttributes({
quizId: {

View file

@ -17,7 +17,7 @@ function VideoBlockComponents(props: any) {
const handleSubmit = async (e: any) => {
e.preventDefault();
setIsLoading(true);
let object = await uploadNewVideoFile(video, props.extension.options.lecture.lecture_id);
let object = await uploadNewVideoFile(video, props.extension.options.activity.activity_id);
setIsLoading(false);
setblockObject(object);
props.updateAttributes({
@ -42,7 +42,7 @@ function VideoBlockComponents(props: any) {
<BlockVideo>
<video
controls
src={`${getBackendUrl()}content/uploads/files/lectures/${props.extension.options.lecture.lecture_id}/blocks/videoBlock/${blockObject.block_id}/${blockObject.block_data.file_id}.${
src={`${getBackendUrl()}content/uploads/files/activities/${props.extension.options.activity.activity_id}/blocks/videoBlock/${blockObject.block_id}/${blockObject.block_data.file_id}.${
blockObject.block_data.file_format
}`}
></video>

View file

@ -2,10 +2,10 @@ import React, { useState } from "react";
import { ArrowLeftIcon, Cross1Icon } from "@radix-ui/react-icons";
import Modal from "../Modal";
import styled from "styled-components";
import DynamicCanvaModal from "./NewLectureModal/DynamicCanva";
import VideoModal from "./NewLectureModal/Video";
import DynamicCanvaModal from "./NewActivityModal/DynamicCanva";
import VideoModal from "./NewActivityModal/Video";
function NewLectureModal({ closeModal, submitLecture, submitFileLecture, chapterId }: any) {
function NewActivityModal({ closeModal, submitActivity, submitFileActivity, chapterId }: any) {
const [selectedView, setSelectedView] = useState("home");
return (
@ -16,29 +16,29 @@ function NewLectureModal({ closeModal, submitLecture, submitFileLecture, chapter
<button onClick={closeModal}>
<Cross1Icon />
</button>
<h1>Add New Lecture</h1>
<h1>Add New Activity</h1>
<br />
{selectedView === "home" && (
<LectureChooserWrapper>
<LectureButton onClick={() => {setSelectedView("dynamic")}}>📄</LectureButton>
<LectureButton onClick={() => {setSelectedView("video")}}>📹</LectureButton>
</LectureChooserWrapper>
<ActivityChooserWrapper>
<ActivityButton onClick={() => {setSelectedView("dynamic")}}>📄</ActivityButton>
<ActivityButton onClick={() => {setSelectedView("video")}}>📹</ActivityButton>
</ActivityChooserWrapper>
)}
{selectedView === "dynamic" && (
<DynamicCanvaModal submitLecture={submitLecture} chapterId={chapterId} />
<DynamicCanvaModal submitActivity={submitActivity} chapterId={chapterId} />
)}
{selectedView === "video" && (
<VideoModal submitFileLecture={submitFileLecture} chapterId={chapterId} />
<VideoModal submitFileActivity={submitFileActivity} chapterId={chapterId} />
)}
</Modal>
);
}
const LectureChooserWrapper = styled.div`
const ActivityChooserWrapper = styled.div`
display: flex;
flex-direction: row;
align-items: center;
@ -46,7 +46,7 @@ const LectureChooserWrapper = styled.div`
gap: 20px;
`;
const LectureButton = styled.button`
const ActivityButton = styled.button`
padding: 40px;
border-radius: 10px !important;
border: none;
@ -59,4 +59,4 @@ const LectureButton = styled.button`
}
`;
export default NewLectureModal;
export default NewActivityModal;

View file

@ -0,0 +1,36 @@
import React, { useState } from "react";
function DynamicCanvaModal({ submitActivity, chapterId }: any) {
const [activityName, setActivityName] = useState("");
const [activityDescription, setActivityDescription] = useState("");
const handleActivityNameChange = (e: any) => {
setActivityName(e.target.value);
};
const handleActivityDescriptionChange = (e: any) => {
setActivityDescription(e.target.value);
};
const handleSubmit = async (e: any) => {
e.preventDefault();
console.log({ activityName, activityDescription, chapterId });
submitActivity({
name: activityName,
chapterId: chapterId,
type: "dynamic",
});
};
return (
<div>
<div>
<input type="text" onChange={handleActivityNameChange} placeholder="Activity Name" /> <br />
<input type="text" onChange={handleActivityDescriptionChange} placeholder="Activity Description" />
<br />
<button onClick={handleSubmit}>Add Activity</button>
</div>
</div>
);
}
export default DynamicCanvaModal;

View file

@ -1,6 +1,6 @@
import React from "react";
function VideoModal({ submitFileLecture, chapterId }: any) {
function VideoModal({ submitFileActivity, chapterId }: any) {
const [video, setVideo] = React.useState(null) as any;
const [name, setName] = React.useState("");
@ -14,11 +14,11 @@ function VideoModal({ submitFileLecture, chapterId }: any) {
const handleSubmit = async (e: any) => {
e.preventDefault();
let status = await submitFileLecture(video, "video", { name, type: "video" }, chapterId);
let status = await submitFileActivity(video, "video", { name, type: "video" }, chapterId);
};
/* TODO : implement some sort of progress bar for file uploads, it is not possible yet because i'm not using axios.
and the actual upload isn't happening here anyway, it's in the submitFileLecture function */
and the actual upload isn't happening here anyway, it's in the submitFileActivity function */
return (
<div>

View file

@ -16,7 +16,7 @@ function NewChapterModal({ submitChapter , closeModal }: any) {
const handleSubmit = async (e: any) => {
e.preventDefault();
console.log({ chapterName, chapterDescription });
submitChapter({ name : chapterName, description : chapterDescription , lectures : [] });
submitChapter({ name : chapterName, description : chapterDescription , activities : [] });
};
return (

View file

@ -1,36 +0,0 @@
import React, { useState } from "react";
function DynamicCanvaModal({ submitLecture, chapterId }: any) {
const [lectureName, setLectureName] = useState("");
const [lectureDescription, setLectureDescription] = useState("");
const handleLectureNameChange = (e: any) => {
setLectureName(e.target.value);
};
const handleLectureDescriptionChange = (e: any) => {
setLectureDescription(e.target.value);
};
const handleSubmit = async (e: any) => {
e.preventDefault();
console.log({ lectureName, lectureDescription, chapterId });
submitLecture({
name: lectureName,
chapterId: chapterId,
type: "dynamic",
});
};
return (
<div>
<div>
<input type="text" onChange={handleLectureNameChange} placeholder="Lecture Name" /> <br />
<input type="text" onChange={handleLectureDescriptionChange} placeholder="Lecture Description" />
<br />
<button onClick={handleSubmit}>Add Lecture</button>
</div>
</div>
);
}
export default DynamicCanvaModal;

View file

@ -30,7 +30,7 @@ export default function middleware(req: NextRequest) {
: hostname.replace(`.localhost:3000`, "");
/* Editor route */
if (url.pathname.match(/^\/course\/[^/]+\/lecture\/[^/]+\/edit$/)) {
if (url.pathname.match(/^\/course\/[^/]+\/activity\/[^/]+\/edit$/)) {
url.pathname = `/_editor${url.pathname}`;
console.log("editor route", url.pathname);

View file

@ -1,11 +1,11 @@
import { getAPIUrl } from "@services/config/config";
import { RequestBody, RequestBodyForm } from "@services/utils/requests";
export async function uploadNewImageFile(file: any, lecture_id: string) {
export async function uploadNewImageFile(file: any, activity_id: string) {
// Send file thumbnail as form data
const formData = new FormData();
formData.append("file_object", file);
formData.append("lecture_id", lecture_id);
formData.append("activity_id", activity_id);
return fetch(`${getAPIUrl()}blocks/image`, RequestBodyForm("POST", formData))
.then((result) => result.json())

View file

@ -1,11 +1,11 @@
import { getAPIUrl } from "@services/config/config";
import { RequestBody, RequestBodyForm } from "@services/utils/requests";
export async function uploadNewPDFFile(file: any, lecture_id: string) {
export async function uploadNewPDFFile(file: any, activity_id: string) {
// Send file thumbnail as form data
const formData = new FormData();
formData.append("file_object", file);
formData.append("lecture_id", lecture_id);
formData.append("activity_id", activity_id);
return fetch(`${getAPIUrl()}blocks/pdf`, RequestBodyForm("POST", formData))
.then((result) => result.json())

View file

@ -2,8 +2,8 @@ import { getAPIUrl } from "@services/config/config";
import { RequestBody, RequestBodyForm } from "@services/utils/requests";
export async function submitQuizBlock(lecture_id: string, data: any) {
const result: any = await fetch(`${getAPIUrl()}blocks/quiz/${lecture_id}"`, RequestBody("POST", data))
export async function submitQuizBlock(activity_id: string, data: any) {
const result: any = await fetch(`${getAPIUrl()}blocks/quiz/${activity_id}"`, RequestBody("POST", data))
.then((result) => result.json())
.catch((error) => console.log("error", error));
return result;

View file

@ -1,11 +1,11 @@
import { getAPIUrl } from "@services/config/config";
import { RequestBody, RequestBodyForm } from "@services/utils/requests";
export async function uploadNewVideoFile(file: any, lecture_id: string) {
export async function uploadNewVideoFile(file: any, activity_id: string) {
// Send file thumbnail as form data
const formData = new FormData();
formData.append("file_object", file);
formData.append("lecture_id", lecture_id);
formData.append("activity_id", activity_id);
return fetch(`${getAPIUrl()}blocks/video`, RequestBodyForm("POST", formData))
.then((result) => result.json())

View file

@ -1,13 +1,13 @@
import { getAPIUrl } from "@services/config/config";
import { RequestBody, RequestBodyForm } from "@services/utils/requests";
export async function createLecture(data: any, chapter_id: any, org_id: any) {
export async function createActivity(data: any, chapter_id: any, org_id: any) {
data.content = {};
// remove chapter_id from data
delete data.chapterId;
const result: any = await fetch(`${getAPIUrl()}lectures/?coursechapter_id=${chapter_id}&org_id=${org_id}`, RequestBody("POST", data))
const result: any = await fetch(`${getAPIUrl()}activities/?coursechapter_id=${chapter_id}&org_id=${org_id}`, RequestBody("POST", data))
.then((result) => result.json())
.catch((error) => console.log("error", error));
@ -16,17 +16,17 @@ export async function createLecture(data: any, chapter_id: any, org_id: any) {
return result;
}
export async function createFileLecture(file: File, type: string, data: any, chapter_id: any) {
export async function createFileActivity(file: File, type: string, data: any, chapter_id: any) {
// Send file thumbnail as form data
const formData = new FormData();
formData.append("coursechapter_id", chapter_id);
let endpoint = `${getAPIUrl()}lectures/video`;
let endpoint = `${getAPIUrl()}activities/video`;
if (type === "video") {
formData.append("name", data.name);
formData.append("video_file", file);
endpoint = `${getAPIUrl()}lectures/video`;
endpoint = `${getAPIUrl()}activities/video`;
}
const result: any = await fetch(endpoint, RequestBodyForm("POST", formData))
@ -38,16 +38,16 @@ export async function createFileLecture(file: File, type: string, data: any, cha
return result;
}
export async function getLecture(lecture_id: any) {
const result: any = await fetch(`${getAPIUrl()}lectures/${lecture_id}`, RequestBody("GET", null))
export async function getActivity(activity_id: any) {
const result: any = await fetch(`${getAPIUrl()}activities/${activity_id}`, RequestBody("GET", null))
.then((result) => result.json())
.catch((error) => console.log("error", error));
return result;
}
export async function updateLecture(data: any, lecture_id: any) {
const result: any = await fetch(`${getAPIUrl()}lectures/${lecture_id}`, RequestBody("PUT", data))
export async function updateActivity(data: any, activity_id: any) {
const result: any = await fetch(`${getAPIUrl()}activities/${activity_id}`, RequestBody("PUT", data))
.then((result) => result.json())
.catch((error) => console.log("error", error));

View file

@ -23,8 +23,8 @@ export async function closeActivity(org_id: string, activity_id: string) {
return result;
}
export async function maskLectureAsComplete(org_id: string, course_id: string, lecture_id: string) {
const result: any = await fetch(`${getAPIUrl()}activity/${org_id}/add_lecture/${course_id}/${lecture_id}`, RequestBody("POST", null))
export async function maskActivityAsComplete(org_id: string, course_id: string, activity_id: string) {
const result: any = await fetch(`${getAPIUrl()}activity/${org_id}/add_activity/${course_id}/${activity_id}`, RequestBody("POST", null))
.then((result) => result.json())
.catch((error) => console.log("error", error));
return result;

View file

@ -1,6 +1,6 @@
from fastapi import APIRouter
from src.routers import activity, blocks, users, auth, houses, orgs, roles
from src.routers.courses import chapters, collections, courses,lectures
from src.routers.courses import chapters, collections, courses,activities
global_router = APIRouter(prefix="/api")
@ -15,7 +15,7 @@ global_router.include_router(roles.router, prefix="/roles", tags=["roles"])
global_router.include_router(blocks.router, prefix="/blocks", tags=["blocks"])
global_router.include_router(courses.router, prefix="/courses", tags=["courses"])
global_router.include_router(chapters.router, prefix="/chapters", tags=["chapters"])
global_router.include_router(lectures.router, prefix="/lectures", tags=["lectures"])
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"])

View file

@ -1,6 +1,6 @@
from fastapi import APIRouter, Depends, Request
from src.dependencies.auth import get_current_user
from src.services.activity import Activity, add_lecture_to_activity, close_activity, create_activity, get_user_activities, get_user_activities_orgslug
from src.services.activity import Activity, add_activity_to_activity, close_activity, create_activity, get_user_activities, get_user_activities_orgslug
router = APIRouter()
@ -31,12 +31,12 @@ async def api_get_activity_by_orgslug(request: Request, org_slug: str, user=Depe
return await get_user_activities_orgslug(request, user, org_slug=org_slug)
@router.post("/{org_id}/add_lecture/{course_id}/{lecture_id}")
async def api_add_lecture_to_activity(request: Request, org_id: str, course_id: str, lecture_id: str, user=Depends(get_current_user)):
@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 lecture to activity
Add activity to activity
"""
return await add_lecture_to_activity(request, user, org_id, course_id, lecture_id)
return await add_activity_to_activity(request, user, org_id, course_id, activity_id)
@router.patch("/{org_id}/close_activity/{activity_id}")

View file

@ -14,11 +14,11 @@ router = APIRouter()
####################
@router.post("/image")
async def api_create_image_file_block(request: Request, file_object: UploadFile, lecture_id: str = Form(), current_user: PublicUser = Depends(get_current_user)):
async def api_create_image_file_block(request: Request, file_object: UploadFile, activity_id: str = Form(), current_user: PublicUser = Depends(get_current_user)):
"""
Create new image file
"""
return await create_image_block(request, file_object, lecture_id)
return await create_image_block(request, file_object, activity_id)
@router.get("/image")
@ -33,11 +33,11 @@ async def api_get_image_file_block(request: Request, file_id: str, current_user:
####################
@router.post("/video")
async def api_create_video_file_block(request: Request, file_object: UploadFile, lecture_id: str = Form(), current_user: PublicUser = Depends(get_current_user)):
async def api_create_video_file_block(request: Request, file_object: UploadFile, activity_id: str = Form(), current_user: PublicUser = Depends(get_current_user)):
"""
Create new video file
"""
return await create_video_block(request, file_object, lecture_id)
return await create_video_block(request, file_object, activity_id)
@router.get("/video")
@ -52,11 +52,11 @@ async def api_get_video_file_block(request: Request, file_id: str, current_user:
####################
@router.post("/pdf")
async def api_create_pdf_file_block(request: Request, file_object: UploadFile, lecture_id: str = Form(), current_user: PublicUser = Depends(get_current_user)):
async def api_create_pdf_file_block(request: Request, file_object: UploadFile, activity_id: str = Form(), current_user: PublicUser = Depends(get_current_user)):
"""
Create new pdf file
"""
return await create_pdf_block(request, file_object, lecture_id)
return await create_pdf_block(request, file_object, activity_id)
@router.get("/pdf")
@ -71,12 +71,12 @@ async def api_get_pdf_file_block(request: Request, file_id: str, current_user: P
# Quiz Block
####################
@router.post("/quiz/{lecture_id}")
async def api_create_quiz_block(request: Request, quiz_block: quizBlock, lecture_id: str, current_user: PublicUser = Depends(get_current_user)):
@router.post("/quiz/{activity_id}")
async def api_create_quiz_block(request: Request, quiz_block: quizBlock, activity_id: str, current_user: PublicUser = Depends(get_current_user)):
"""
Create new document file
"""
return await create_quiz_block(request, quiz_block, lecture_id, current_user)
return await create_quiz_block(request, quiz_block, activity_id, current_user)
@router.get("/quiz/options")

View file

@ -0,0 +1,56 @@
from fastapi import APIRouter, Depends, UploadFile, Form, Request
from src.services.courses.activities.activities import *
from src.dependencies.auth import get_current_user
from src.services.courses.activities.video import create_video_activity
router = APIRouter()
@router.post("/")
async def api_create_activity(request: Request, activity_object: Activity, org_id: str, coursechapter_id: str, current_user: PublicUser = Depends(get_current_user)):
"""
Create new activity
"""
return await create_activity(request, activity_object, org_id, coursechapter_id, current_user)
@router.get("/{activity_id}")
async def api_get_activity(request: Request, activity_id: str, current_user: PublicUser = Depends(get_current_user)):
"""
Get single activity by activity_id
"""
return await get_activity(request, activity_id, current_user=current_user)
@router.get("/coursechapter/{coursechapter_id}")
async def api_get_activities(request: Request, coursechapter_id: str, current_user: PublicUser = Depends(get_current_user)):
"""
Get CourseChapter activities
"""
return await get_activities(request, coursechapter_id, current_user)
@router.put("/{activity_id}")
async def api_update_activity(request: Request, activity_object: Activity, activity_id: str, current_user: PublicUser = Depends(get_current_user)):
"""
Update activity by activity_id
"""
return await update_activity(request, activity_object, activity_id, current_user)
@router.delete("/{activity_id}")
async def api_delete_activity(request: Request, activity_id: str, org_id: str, current_user: PublicUser = Depends(get_current_user)):
"""
Delete activity by activity_id
"""
return await delete_activity(request, activity_id, current_user)
# Video play
@router.post("/video")
async def api_create_video_activity(request: Request, org_id: str, name: str = Form(), coursechapter_id: str = Form(), current_user: PublicUser = Depends(get_current_user), video_file: UploadFile | None = None):
"""
Create new activity
"""
return await create_video_activity(request, name, coursechapter_id, current_user, video_file)

View file

@ -37,7 +37,7 @@ async def api_get_course(request: Request, course_id: str, current_user: Public
@router.get("/meta/{course_id}")
async def api_get_course_meta(request: Request, course_id: str, current_user: PublicUser = Depends(get_current_user)):
"""
Get single Course Metadata (chapters, lectures) by course_id
Get single Course Metadata (chapters, activities) by course_id
"""
return await get_course_meta(request, course_id, current_user=current_user)

View file

@ -1,56 +0,0 @@
from fastapi import APIRouter, Depends, UploadFile, Form, Request
from src.services.courses.lectures.lectures import *
from src.dependencies.auth import get_current_user
from src.services.courses.lectures.video import create_video_lecture
router = APIRouter()
@router.post("/")
async def api_create_lecture(request: Request, lecture_object: Lecture, org_id: str, coursechapter_id: str, current_user: PublicUser = Depends(get_current_user)):
"""
Create new lecture
"""
return await create_lecture(request, lecture_object, org_id, coursechapter_id, current_user)
@router.get("/{lecture_id}")
async def api_get_lecture(request: Request, lecture_id: str, current_user: PublicUser = Depends(get_current_user)):
"""
Get single lecture by lecture_id
"""
return await get_lecture(request, lecture_id, current_user=current_user)
@router.get("/coursechapter/{coursechapter_id}")
async def api_get_lectures(request: Request, coursechapter_id: str, current_user: PublicUser = Depends(get_current_user)):
"""
Get CourseChapter lectures
"""
return await get_lectures(request, coursechapter_id, current_user)
@router.put("/{lecture_id}")
async def api_update_lecture(request: Request, lecture_object: Lecture, lecture_id: str, current_user: PublicUser = Depends(get_current_user)):
"""
Update lecture by lecture_id
"""
return await update_lecture(request, lecture_object, lecture_id, current_user)
@router.delete("/{lecture_id}")
async def api_delete_lecture(request: Request, lecture_id: str, org_id: str, current_user: PublicUser = Depends(get_current_user)):
"""
Delete lecture by lecture_id
"""
return await delete_lecture(request, lecture_id, current_user)
# Video play
@router.post("/video")
async def api_create_video_lecture(request: Request, org_id: str, name: str = Form(), coursechapter_id: str = Form(), current_user: PublicUser = Depends(get_current_user), video_file: UploadFile | None = None):
"""
Create new lecture
"""
return await create_video_lecture(request, name, coursechapter_id, current_user, video_file)

View file

@ -16,8 +16,8 @@ class Activity(BaseModel):
course_id: str
status: Optional[Literal['ongoing', 'done', 'closed']] = 'ongoing'
masked: Optional[bool] = False
lectures_marked_complete: Optional[List[str]] = []
lectures_data: Optional[List[dict]] = []
activities_marked_complete: Optional[List[str]] = []
activities_data: Optional[List[dict]] = []
class ActivityInDB(Activity):
@ -74,12 +74,12 @@ async def get_user_activities(request: Request, user: PublicUser, org_id: str):
status_code=status.HTTP_409_CONFLICT, detail="No activities found")
for activity in await user_activities.to_list(length=100):
# get number of lectures in the course
# get number of activities in the course
coursechapters = await get_coursechapters_meta(request, activity['course_id'], user)
# calculate progression using the number of lectures marked complete and the total number of lectures
# calculate progression using the number of activities marked complete and the total number of activities
progression = round(
len(activity['lectures_marked_complete']) / len(coursechapters['lectures']) * 100, 2)
len(activity['activities_marked_complete']) / len(coursechapters['activities']) * 100, 2)
course = await courses.find_one({"course_id": activity['course_id']}, {'_id': 0})
@ -104,12 +104,12 @@ async def get_user_activities_orgslug(request: Request, user: PublicUser, org_sl
status_code=status.HTTP_409_CONFLICT, detail="No activities found")
for activity in await user_activities.to_list(length=100):
# get number of lectures in the course
# get number of activities in the course
coursechapters = await get_coursechapters_meta(request, activity['course_id'], user)
# calculate progression using the number of lectures marked complete and the total number of lectures
# calculate progression using the number of activities marked complete and the total number of activities
progression = round(
len(activity['lectures_marked_complete']) / len(coursechapters['lectures']) * 100, 2)
len(activity['activities_marked_complete']) / len(coursechapters['activities']) * 100, 2)
course = await courses.find_one({"course_id": activity['course_id']}, {'_id': 0})
@ -120,18 +120,18 @@ async def get_user_activities_orgslug(request: Request, user: PublicUser, org_sl
return activities_metadata
async def add_lecture_to_activity(request: Request, user: PublicUser, org_id: str, course_id: str, lecture_id: str):
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}"
lecture_id = f"lecture_{lecture_id}"
activity_id = f"activity_{activity_id}"
activity = await activities.find_one(
{"course_id": course_id,
"user_id": user.user_id
}, {'_id': 0})
if lecture_id not in activity['lectures_marked_complete']:
activity['lectures_marked_complete'].append(str(lecture_id))
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
@ -142,7 +142,7 @@ async def add_lecture_to_activity(request: Request, user: PublicUser, org_id: st
else:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Lecture already marked complete")
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,):

View file

@ -9,23 +9,23 @@ from src.services.blocks.utils.upload_files import upload_file_and_return_file_o
from src.services.users.users import PublicUser
async def create_image_block(request: Request, image_file: UploadFile, lecture_id: str):
async def create_image_block(request: Request, image_file: UploadFile, activity_id: str):
blocks = request.app.db["blocks"]
lecture = request.app.db["lectures"]
activity = request.app.db["activities"]
block_type = "imageBlock"
# get org_id from lecture
lecture = await lecture.find_one({"lecture_id": lecture_id}, {"_id": 0, "org_id": 1})
org_id = lecture["org_id"]
# get org_id from activity
activity = await activity.find_one({"activity_id": activity_id}, {"_id": 0, "org_id": 1})
org_id = activity["org_id"]
# get block id
block_id = str(f"block_{uuid4()}")
block_data = await upload_file_and_return_file_object(request, image_file, lecture_id, block_id, ["jpg", "jpeg", "png", "gif"], block_type)
block_data = await upload_file_and_return_file_object(request, image_file, activity_id, block_id, ["jpg", "jpeg", "png", "gif"], block_type)
# create block
block = Block(block_id=block_id, lecture_id=lecture_id,
block = Block(block_id=block_id, activity_id=activity_id,
block_type=block_type, block_data=block_data, org_id=org_id)
# insert block

View file

@ -9,23 +9,23 @@ from src.services.blocks.utils.upload_files import upload_file_and_return_file_o
from src.services.users.users import PublicUser
async def create_pdf_block(request: Request, pdf_file: UploadFile, lecture_id: str):
async def create_pdf_block(request: Request, pdf_file: UploadFile, activity_id: str):
blocks = request.app.db["blocks"]
lecture = request.app.db["lectures"]
activity = request.app.db["activities"]
block_type = "pdfBlock"
# get org_id from lecture
lecture = await lecture.find_one({"lecture_id": lecture_id}, {"_id": 0, "org_id": 1})
org_id = lecture["org_id"]
# get org_id from activity
activity = await activity.find_one({"activity_id": activity_id}, {"_id": 0, "org_id": 1})
org_id = activity["org_id"]
# get block id
block_id = str(f"block_{uuid4()}")
block_data = await upload_file_and_return_file_object(request, pdf_file, lecture_id, block_id, ["pdf"], block_type)
block_data = await upload_file_and_return_file_object(request, pdf_file, activity_id, block_id, ["pdf"], block_type)
# create block
block = Block(block_id=block_id, lecture_id=lecture_id,
block = Block(block_id=block_id, activity_id=activity_id,
block_type=block_type, block_data=block_data, org_id=org_id)
# insert block

View file

@ -28,18 +28,18 @@ class quizBlock(BaseModel):
answers: List[answer]
async def create_quiz_block(request: Request, quizBlock: quizBlock, lecture_id: str, user: PublicUser):
async def create_quiz_block(request: Request, quizBlock: quizBlock, activity_id: str, user: PublicUser):
blocks = request.app.db["blocks"]
lectures = request.app.db["lectures"]
activities = request.app.db["activities"]
# Get org_id from lecture
lecture = await lectures.find_one({"lecture_id": lecture_id}, {"_id": 0, "org_id": 1})
org_id = lecture["org_id"]
# Get org_id from activity
activity = await activities.find_one({"activity_id": activity_id}, {"_id": 0, "org_id": 1})
org_id = activity["org_id"]
block_id = str(f"block_{uuid4()}")
# create block
block = Block(block_id=block_id, lecture_id=lecture_id,
block = Block(block_id=block_id, activity_id=activity_id,
block_type="quizBlock", block_data=quizBlock, org_id=org_id)
# insert block

View file

@ -9,23 +9,23 @@ from src.services.blocks.utils.upload_files import upload_file_and_return_file_o
from src.services.users.users import PublicUser
async def create_video_block(request: Request, video_file: UploadFile, lecture_id: str):
async def create_video_block(request: Request, video_file: UploadFile, activity_id: str):
blocks = request.app.db["blocks"]
lecture = request.app.db["lectures"]
activity = request.app.db["activities"]
block_type = "videoBlock"
# get org_id from lecture
lecture = await lecture.find_one({"lecture_id": lecture_id}, {"_id": 0, "org_id": 1})
org_id = lecture["org_id"]
# get org_id from activity
activity = await activity.find_one({"activity_id": activity_id}, {"_id": 0, "org_id": 1})
org_id = activity["org_id"]
# get block id
block_id = str(f"block_{uuid4()}")
block_data = await upload_file_and_return_file_object(request, video_file, lecture_id, block_id, ["mp4", "webm", "ogg"], block_type)
block_data = await upload_file_and_return_file_object(request, video_file, activity_id, block_id, ["mp4", "webm", "ogg"], block_type)
# create block
block = Block(block_id=block_id, lecture_id=lecture_id,
block = Block(block_id=block_id, activity_id=activity_id,
block_type=block_type, block_data=block_data, org_id=org_id)
# insert block

View file

@ -7,7 +7,7 @@ from pydantic import BaseModel
class Block(BaseModel):
block_id: str
lecture_id: str
activity_id: str
org_id: str
block_type: Literal["quizBlock", "videoBlock", "pdfBlock", "imageBlock"]
block_data: Any

View file

@ -7,4 +7,4 @@ class BlockFile(BaseModel):
file_name: str
file_size: int
file_type: str
lecture_id: str
activity_id: str

View file

@ -6,7 +6,7 @@ from src.services.blocks.schemas.files import BlockFile
from src.services.users.schemas.users import PublicUser
async def upload_file_and_return_file_object(request: Request, file: UploadFile, lecture_id: str, block_id: str, list_of_allowed_file_formats: list, type_of_block: str):
async def upload_file_and_return_file_object(request: Request, file: UploadFile, activity_id: str, block_id: str, list_of_allowed_file_formats: list, type_of_block: str):
# get file id
file_id = str(uuid.uuid4())
@ -37,16 +37,16 @@ async def upload_file_and_return_file_object(request: Request, file: UploadFile,
file_name=file_name,
file_size=file_size,
file_type=file_type,
lecture_id=lecture_id
activity_id=activity_id
)
# create folder for lecture
if not os.path.exists(f"content/uploads/files/lectures/{lecture_id}/blocks/{type_of_block}/{block_id}"):
# create folder for lecture
os.makedirs(f"content/uploads/files/lectures/{lecture_id}/blocks/{type_of_block}/{block_id}")
# create folder for activity
if not os.path.exists(f"content/uploads/files/activities/{activity_id}/blocks/{type_of_block}/{block_id}"):
# create folder for activity
os.makedirs(f"content/uploads/files/activities/{activity_id}/blocks/{type_of_block}/{block_id}")
# upload file to server
with open(f"content/uploads/files/lectures/{lecture_id}/blocks/{type_of_block}/{block_id}/{file_id}.{file_format}", 'wb') as f:
with open(f"content/uploads/files/activities/{activity_id}/blocks/{type_of_block}/{block_id}/{file_id}.{file_format}", 'wb') as f:
f.write(file_binary)
f.close()

View file

@ -0,0 +1,145 @@
from pydantic import BaseModel
from src.services.security import verify_user_rights_with_roles
from src.services.users.schemas.users import PublicUser, User
from fastapi import FastAPI, HTTPException, status, Request, Response, BackgroundTasks, UploadFile, File
from uuid import uuid4
from datetime import datetime
#### Classes ####################################################
class Activity(BaseModel):
name: str
type: str
content: object
class ActivityInDB(Activity):
activity_id: str
coursechapter_id: str
org_id: str
creationDate: str
updateDate: str
#### Classes ####################################################
####################################################
# CRUD
####################################################
async def create_activity(request: Request, activity_object: Activity, org_id: str, coursechapter_id: str, current_user: PublicUser):
activities = request.app.db["activities"]
coursechapters = request.app.db["coursechapters"]
# generate activity_id
activity_id = str(f"activity_{uuid4()}")
hasRoleRights = await verify_user_rights_with_roles(request, "create", current_user.user_id, activity_id, org_id)
if not hasRoleRights:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Roles : Insufficient rights to perform this action")
# create activity
activity = ActivityInDB(**activity_object.dict(), creationDate=str(
datetime.now()), coursechapter_id=coursechapter_id, updateDate=str(datetime.now()), activity_id=activity_id, org_id=org_id)
await activities.insert_one(activity.dict())
# update chapter
await coursechapters.update_one({"coursechapter_id": coursechapter_id}, {
"$addToSet": {"activities": activity_id}})
return activity
async def get_activity(request: Request, activity_id: str, current_user: PublicUser):
activities = request.app.db["activities"]
activity = await activities.find_one({"activity_id": activity_id})
# verify course rights
hasRoleRights = await verify_user_rights_with_roles(request, "read", current_user.user_id, activity_id, element_org_id=activity["org_id"])
if not hasRoleRights:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Roles : Insufficient rights to perform this action")
if not activity:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist")
activity = ActivityInDB(**activity)
return activity
async def update_activity(request: Request, activity_object: Activity, activity_id: str, current_user: PublicUser):
activities = request.app.db["activities"]
activity = await activities.find_one({"activity_id": activity_id})
# verify course rights
await verify_user_rights_with_roles(request, "update", current_user.user_id, activity_id, element_org_id=activity["org_id"])
if activity:
creationDate = activity["creationDate"]
# get today's date
datetime_object = datetime.now()
updated_course = ActivityInDB(
activity_id=activity_id, coursechapter_id=activity["coursechapter_id"], creationDate=creationDate, updateDate=str(datetime_object), org_id=activity["org_id"], **activity_object.dict())
await activities.update_one({"activity_id": activity_id}, {
"$set": updated_course.dict()})
return ActivityInDB(**updated_course.dict())
else:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="activity does not exist")
async def delete_activity(request: Request, activity_id: str, current_user: PublicUser):
activities = request.app.db["activities"]
activity = await activities.find_one({"activity_id": activity_id})
# verify course rights
await verify_user_rights_with_roles(request, "delete", current_user.user_id, activity_id, element_org_id=activity["org_id"])
if not activity:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="activity does not exist")
isDeleted = await activities.delete_one({"activity_id": activity_id})
if isDeleted:
return {"detail": "activity deleted"}
else:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database")
####################################################
# Misc
####################################################
async def get_activities(request: Request, coursechapter_id: str, current_user: PublicUser):
activities = request.app.db["activities"]
# TODO : TERRIBLE SECURITY ISSUE HERE, NEED TO FIX ASAP
# TODO : TERRIBLE SECURITY ISSUE HERE, NEED TO FIX ASAP
# TODO : TERRIBLE SECURITY ISSUE HERE, NEED TO FIX ASAP
activities = activities.find({"coursechapter_id": coursechapter_id})
if not activities:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist")
activities = [ActivityInDB(**activity) for activity in await activities.to_list(length=100)]
return activities

View file

@ -1,14 +1,14 @@
import os
async def upload_video(video_file, lecture_id):
async def upload_video(video_file, activity_id):
contents = video_file.file.read()
video_format = video_file.filename.split(".")[-1]
# create folder
os.mkdir(f"content/uploads/video/{lecture_id}")
os.mkdir(f"content/uploads/video/{activity_id}")
try:
with open(f"content/uploads/video/{lecture_id}/video.{video_format}", 'wb') as f:
with open(f"content/uploads/video/{activity_id}/video.{video_format}", 'wb') as f:
f.write(contents)
f.close()

View file

@ -1,19 +1,19 @@
from pydantic import BaseModel
from src.services.security import verify_user_rights_with_roles
from src.services.courses.lectures.uploads.videos import upload_video
from src.services.courses.activities.uploads.videos import upload_video
from src.services.users.users import PublicUser
from src.services.courses.lectures.lectures import LectureInDB
from src.services.courses.activities.activities import ActivityInDB
from fastapi import HTTPException, status, UploadFile, Request
from uuid import uuid4
from datetime import datetime
async def create_video_lecture(request: Request,name: str, coursechapter_id: str, current_user: PublicUser, video_file: UploadFile | None = None):
lectures = request.app.db["lectures"]
async def create_video_activity(request: Request,name: str, coursechapter_id: str, current_user: PublicUser, video_file: UploadFile | None = None):
activities = request.app.db["activities"]
coursechapters = request.app.db["coursechapters"]
# generate lecture_id
lecture_id = str(f"lecture_{uuid4()}")
# generate activity_id
activity_id = str(f"activity_{uuid4()}")
# check if video_file is not None
if not video_file:
@ -21,39 +21,39 @@ async def create_video_lecture(request: Request,name: str, coursechapter_id: st
status_code=status.HTTP_409_CONFLICT, detail="Video : No video file provided")
video_format = video_file.filename.split(".")[-1]
lecture_object = LectureInDB(
lecture_id=lecture_id,
activity_object = ActivityInDB(
activity_id=activity_id,
coursechapter_id=coursechapter_id,
name=name,
type="video",
content={
"video": {
"filename": "video."+video_format,
"lecture_id": lecture_id,
"activity_id": activity_id,
}
},
creationDate=str(datetime.now()),
updateDate=str(datetime.now()),
)
hasRoleRights = await verify_user_rights_with_roles(request,"create", current_user.user_id, lecture_id)
hasRoleRights = await verify_user_rights_with_roles(request,"create", current_user.user_id, activity_id)
if not hasRoleRights:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Roles : Insufficient rights to perform this action")
# create lecture
lecture = LectureInDB(**lecture_object.dict())
await lectures.insert_one(lecture.dict())
# create activity
activity = ActivityInDB(**activity_object.dict())
await activities.insert_one(activity.dict())
# upload video
if video_file:
# get videofile format
await upload_video(video_file, lecture_id)
await upload_video(video_file, activity_id)
# todo : choose whether to update the chapter or not
# update chapter
await coursechapters.update_one({"coursechapter_id": coursechapter_id}, {
"$addToSet": {"lectures": lecture_id}})
"$addToSet": {"activities": activity_id}})
return lecture
return activity

View file

@ -4,7 +4,7 @@ from typing import List
from uuid import uuid4
from pydantic import BaseModel
from src.services.courses.courses import Course, CourseInDB
from src.services.courses.lectures.lectures import Lecture, LectureInDB
from src.services.courses.activities.activities import Activity, ActivityInDB
from src.services.security import verify_user_rights_with_roles
from src.services.users.users import PublicUser
from fastapi import HTTPException, status, Request, Response, BackgroundTasks, UploadFile, File
@ -13,7 +13,7 @@ from fastapi import HTTPException, status, Request, Response, BackgroundTasks, U
class CourseChapter(BaseModel):
name: str
description: str
lectures: list
activities: list
class CourseChapterInDB(CourseChapter):
@ -27,7 +27,7 @@ class CourseChapterInDB(CourseChapter):
class CourseChapterMetaData(BaseModel):
chapterOrder: List[str]
chapters: dict
lectures: object
activities: object
#### Classes ####################################################
@ -158,7 +158,7 @@ async def get_coursechapters(request: Request,course_id: str, page: int = 1, lim
async def get_coursechapters_meta(request: Request, course_id: str, current_user: PublicUser):
coursechapters = request.app.db["coursechapters"]
courses = request.app.db["courses"]
lectures = request.app.db["lectures"]
activities = request.app.db["activities"]
coursechapters = coursechapters.find(
{"course_id": course_id}).sort("name", 1)
@ -166,35 +166,35 @@ async def get_coursechapters_meta(request: Request, course_id: str, current_user
course = await courses.find_one({"course_id": course_id})
course = Course(**course) # type: ignore
# lectures
coursechapter_lectureIds_global = []
# activities
coursechapter_activityIds_global = []
# chapters
chapters = {}
for coursechapter in await coursechapters.to_list(length=100):
coursechapter = CourseChapterInDB(**coursechapter)
coursechapter_lectureIds = []
coursechapter_activityIds = []
for lecture in coursechapter.lectures:
coursechapter_lectureIds.append(lecture)
coursechapter_lectureIds_global.append(lecture)
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, "lectureIds": coursechapter_lectureIds
"id": coursechapter.coursechapter_id, "name": coursechapter.name, "activityIds": coursechapter_activityIds
}
# lectures
lectures_list = {}
for lecture in await lectures.find({"lecture_id": {"$in": coursechapter_lectureIds_global}}).to_list(length=100):
lecture = LectureInDB(**lecture)
lectures_list[lecture.lecture_id] = {
"id": lecture.lecture_id, "name": lecture.name, "type": lecture.type, "content": lecture.content
# activities
activities_list = {}
for activity in await activities.find({"activity_id": {"$in": coursechapter_activityIds_global}}).to_list(length=100):
activity = ActivityInDB(**activity)
activities_list[activity.activity_id] = {
"id": activity.activity_id, "name": activity.name, "type": activity.type, "content": activity.content
}
final = {
"chapters": chapters,
"chapterOrder": course.chapters,
"lectures": lectures_list
"activities": activities_list
}
return final
@ -211,7 +211,7 @@ async def update_coursechapters_meta(request: Request,course_id: str, coursechap
if coursechapters_metadata.chapters is not None:
for coursechapter_id, chapter_metadata in coursechapters_metadata.chapters.items():
filter_query = {"coursechapter_id": coursechapter_id}
update_query = {"$set": {"lectures": chapter_metadata["lectureIds"]}}
update_query = {"$set": {"activities": chapter_metadata["activityIds"]}}
result = await coursechapters.update_one(filter_query, update_query)
if result.matched_count == 0:
# handle error when no documents are matched by the filter query

View file

@ -2,7 +2,7 @@ import json
from typing import List
from uuid import uuid4
from pydantic import BaseModel
from src.services.courses.lectures.lectures import LectureInDB
from src.services.courses.activities.activities import ActivityInDB
from src.services.courses.thumbnails import upload_thumbnail
from src.services.users.users import PublicUser
from src.services.security import *
@ -35,7 +35,7 @@ class CourseInDB(Course):
class CourseChapter(BaseModel):
name: str
description: str
lectures: list
activities: list
class CourseChapterInDB(CourseChapter):
@ -75,7 +75,7 @@ async def get_course_meta(request: Request, course_id: str, current_user: Public
activities = request.app.db["activities"]
course = await courses.find_one({"course_id": course_id})
lectures = request.app.db["lectures"]
activities = request.app.db["activities"]
# verify course rights
await verify_rights(request, course_id, current_user, "read")
@ -87,35 +87,35 @@ async def get_course_meta(request: Request, course_id: str, current_user: Public
coursechapters = coursechapters.find(
{"course_id": course_id}).sort("name", 1)
# lectures
coursechapter_lectureIds_global = []
# activities
coursechapter_activityIds_global = []
# chapters
chapters = {}
for coursechapter in await coursechapters.to_list(length=100):
coursechapter = CourseChapterInDB(**coursechapter)
coursechapter_lectureIds = []
coursechapter_activityIds = []
for lecture in coursechapter.lectures:
coursechapter_lectureIds.append(lecture)
coursechapter_lectureIds_global.append(lecture)
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, "lectureIds": coursechapter_lectureIds
"id": coursechapter.coursechapter_id, "name": coursechapter.name, "activityIds": coursechapter_activityIds
}
# lectures
lectures_list = {}
for lecture in await lectures.find({"lecture_id": {"$in": coursechapter_lectureIds_global}}).to_list(length=100):
lecture = LectureInDB(**lecture)
lectures_list[lecture.lecture_id] = {
"id": lecture.lecture_id, "name": lecture.name, "type": lecture.type, "content": lecture.content
# 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_lectures = []
chapters_list_with_activities = []
for chapter in chapters:
chapters_list_with_lectures.append(
{"id": chapters[chapter]["id"], "name": chapters[chapter]["name"], "lectures": [lectures_list[lecture] for lecture in chapters[chapter]["lectureIds"]]})
chapters_list_with_activities.append(
{"id": chapters[chapter]["id"], "name": chapters[chapter]["name"], "activities": [activities_list[activity] for activity in chapters[chapter]["activityIds"]]})
course = Course(**course)
# Get activity by user
@ -128,7 +128,7 @@ async def get_course_meta(request: Request, course_id: str, current_user: Public
return {
"course": course,
"chapters": chapters_list_with_lectures,
"chapters": chapters_list_with_activities,
"activity": activity
}

View file

@ -1,145 +0,0 @@
from pydantic import BaseModel
from src.services.security import verify_user_rights_with_roles
from src.services.users.schemas.users import PublicUser, User
from fastapi import FastAPI, HTTPException, status, Request, Response, BackgroundTasks, UploadFile, File
from uuid import uuid4
from datetime import datetime
#### Classes ####################################################
class Lecture(BaseModel):
name: str
type: str
content: object
class LectureInDB(Lecture):
lecture_id: str
coursechapter_id: str
org_id: str
creationDate: str
updateDate: str
#### Classes ####################################################
####################################################
# CRUD
####################################################
async def create_lecture(request: Request, lecture_object: Lecture, org_id: str, coursechapter_id: str, current_user: PublicUser):
lectures = request.app.db["lectures"]
coursechapters = request.app.db["coursechapters"]
# generate lecture_id
lecture_id = str(f"lecture_{uuid4()}")
hasRoleRights = await verify_user_rights_with_roles(request, "create", current_user.user_id, lecture_id, org_id)
if not hasRoleRights:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Roles : Insufficient rights to perform this action")
# create lecture
lecture = LectureInDB(**lecture_object.dict(), creationDate=str(
datetime.now()), coursechapter_id=coursechapter_id, updateDate=str(datetime.now()), lecture_id=lecture_id, org_id=org_id)
await lectures.insert_one(lecture.dict())
# update chapter
await coursechapters.update_one({"coursechapter_id": coursechapter_id}, {
"$addToSet": {"lectures": lecture_id}})
return lecture
async def get_lecture(request: Request, lecture_id: str, current_user: PublicUser):
lectures = request.app.db["lectures"]
lecture = await lectures.find_one({"lecture_id": lecture_id})
# verify course rights
hasRoleRights = await verify_user_rights_with_roles(request, "read", current_user.user_id, lecture_id, element_org_id=lecture["org_id"])
if not hasRoleRights:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Roles : Insufficient rights to perform this action")
if not lecture:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist")
lecture = LectureInDB(**lecture)
return lecture
async def update_lecture(request: Request, lecture_object: Lecture, lecture_id: str, current_user: PublicUser):
lectures = request.app.db["lectures"]
lecture = await lectures.find_one({"lecture_id": lecture_id})
# verify course rights
await verify_user_rights_with_roles(request, "update", current_user.user_id, lecture_id, element_org_id=lecture["org_id"])
if lecture:
creationDate = lecture["creationDate"]
# get today's date
datetime_object = datetime.now()
updated_course = LectureInDB(
lecture_id=lecture_id, coursechapter_id=lecture["coursechapter_id"], creationDate=creationDate, updateDate=str(datetime_object), org_id=lecture["org_id"], **lecture_object.dict())
await lectures.update_one({"lecture_id": lecture_id}, {
"$set": updated_course.dict()})
return LectureInDB(**updated_course.dict())
else:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="lecture does not exist")
async def delete_lecture(request: Request, lecture_id: str, current_user: PublicUser):
lectures = request.app.db["lectures"]
lecture = await lectures.find_one({"lecture_id": lecture_id})
# verify course rights
await verify_user_rights_with_roles(request, "delete", current_user.user_id, lecture_id, element_org_id=lecture["org_id"])
if not lecture:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="lecture does not exist")
isDeleted = await lectures.delete_one({"lecture_id": lecture_id})
if isDeleted:
return {"detail": "lecture deleted"}
else:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database")
####################################################
# Misc
####################################################
async def get_lectures(request: Request, coursechapter_id: str, current_user: PublicUser):
lectures = request.app.db["lectures"]
# TODO : TERRIBLE SECURITY ISSUE HERE, NEED TO FIX ASAP
# TODO : TERRIBLE SECURITY ISSUE HERE, NEED TO FIX ASAP
# TODO : TERRIBLE SECURITY ISSUE HERE, NEED TO FIX ASAP
lectures = lectures.find({"coursechapter_id": coursechapter_id})
if not lectures:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist")
lectures = [LectureInDB(**lecture) for lecture in await lectures.to_list(length=100)]
return lectures

View file

@ -5,7 +5,7 @@
# from uuid import uuid4
# from fastapi import File, UploadFile, Request
# from src.services.courses.chapters import CourseChapter, create_coursechapter
# from src.services.courses.lectures.lectures import Lecture, create_lecture
# from src.services.courses.activities.activities import Activity, create_activity
# from src.services.courses.thumbnails import upload_thumbnail
# from src.services.users.users import PublicUser, User, UserInDB, UserWithPassword
@ -107,7 +107,7 @@
# collections=["*"],
# organizations=["*"],
# coursechapters=["*"],
# lectures=["*"],
# activities=["*"],
# ))],
# linked_users=[admin_user.user_id],
# )
@ -171,16 +171,16 @@
# coursechapter = CourseChapter(
# name=fake_multilang.unique.sentence(),
# description=fake_multilang.unique.text(),
# lectures=[],
# activities=[],
# )
# coursechapter = await create_coursechapter(request,coursechapter, course_id, current_user)
# pprint(coursechapter)
# if coursechapter:
# # create lectures
# # create activities
# for i in range(0, 5):
# lecture = Lecture(
# activity = Activity(
# name=fake_multilang.unique.sentence(),
# type="dynamic",
# content={},
# )
# lecture = await create_lecture(request,lecture, coursechapter['coursechapter_id'], current_user)
# activity = await create_activity(request,activity, coursechapter['coursechapter_id'], current_user)

View file

@ -21,7 +21,7 @@ class Elements(BaseModel):
collections: Permission
organizations: Permission
coursechapters: Permission
lectures: Permission
activities: Permission
def __getitem__(self, item):
return getattr(self, item)

View file

@ -90,8 +90,8 @@ async def check_element_type(element_id):
return "coursechapters"
elif element_id.startswith("collection_"):
return "collections"
elif element_id.startswith("lecture_"):
return "lectures"
elif element_id.startswith("activity_"):
return "activities"
else:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Issue verifying element nature")