feat: add document pdf activity type

This commit is contained in:
swve 2023-04-26 21:43:43 +02:00
parent be57196fb7
commit 9dbd47c36f
9 changed files with 263 additions and 8 deletions

View file

@ -13,6 +13,7 @@ import { Check } from "lucide-react";
import { swrFetcher } from "@services/utils/ts/requests";
import { markActivityAsComplete } from "@services/courses/activity";
import ToolTip from "@components/UI/Tooltip/Tooltip";
import DocumentPdfActivity from "@components/Pages/Activities/DocumentPdf/DocumentPdf";
function ActivityPage(params: any) {
const activityid = params.params.activityid;
@ -77,6 +78,8 @@ function ActivityPage(params: any) {
{/* todo : use apis & streams instead of this */}
{activity.type == "video" && <VideoActivity course={course} activity={activity} />}
{activity.type == "documentpdf" && <DocumentPdfActivity course={course} activity={activity} />}
<ActivityMarkerWrapper>
{course.trail.activities_marked_complete &&

View file

@ -2,10 +2,12 @@ import React, { useState } from "react";
import { ArrowLeftIcon, Cross1Icon } from "@radix-ui/react-icons";
import DynamicPageActivityImage from "public/activities_types/dynamic-page-activity.png";
import VideoPageActivityImage from "public//activities_types/video-page-activity.png";
import DocumentPdfPageActivityImage from "public//activities_types/documentpdf-page-activity.png";
import { styled, keyframes } from '@stitches/react';
import DynamicCanvaModal from "./NewActivityModal/DynamicCanva";
import VideoModal from "./NewActivityModal/Video";
import Image from "next/image";
import DocumentPdfModal from "./NewActivityModal/DocumentPdf";
function NewActivityModal({ closeModal, submitActivity, submitFileActivity, chapterId }: any) {
const [selectedView, setSelectedView] = useState("home");
@ -27,11 +29,11 @@ function NewActivityModal({ closeModal, submitActivity, submitFileActivity, chap
</ActivityTypeImage>
<ActivityTypeTitle>Video Page</ActivityTypeTitle>
</ActivityOption>
<ActivityOption onClick={() => { setSelectedView("video") }}>
<ActivityOption onClick={() => { setSelectedView("documentpdf") }}>
<ActivityTypeImage>
<Image alt="Video Page" src={VideoPageActivityImage}></Image>
<Image alt="Document PDF Page" src={DocumentPdfPageActivityImage}></Image>
</ActivityTypeImage>
<ActivityTypeTitle>Video Page</ActivityTypeTitle>
<ActivityTypeTitle>PDF Document Page</ActivityTypeTitle>
</ActivityOption>
</ActivityChooserWrapper>
)}
@ -43,6 +45,10 @@ function NewActivityModal({ closeModal, submitActivity, submitFileActivity, chap
{selectedView === "video" && (
<VideoModal submitFileActivity={submitFileActivity} chapterId={chapterId} />
)}
{selectedView === "documentpdf" && (
<DocumentPdfModal submitFileActivity={submitFileActivity} chapterId={chapterId} />
)}
</div>
);
}

View file

@ -0,0 +1,58 @@
import FormLayout, { ButtonBlack, Flex, FormField, FormLabel, FormMessage, Input, Textarea } from "@components/UI/Form/Form";
import React, { useState } from "react";
import * as Form from '@radix-ui/react-form';
import BarLoader from "react-spinners/BarLoader";
function DocumentPdfModal({ submitFileActivity, chapterId }: any) {
const [documentpdf, setDocumentPdf] = React.useState(null) as any;
const [isSubmitting, setIsSubmitting] = useState(false);
const [name, setName] = React.useState("");
const handleDocumentPdfChange = (event: React.ChangeEvent<any>) => {
setDocumentPdf(event.target.files[0]);
};
const handleNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setName(event.target.value);
};
const handleSubmit = async (e: any) => {
e.preventDefault();
setIsSubmitting(true);
let status = await submitFileActivity(documentpdf, "documentpdf", { name, type: "documentpdf" }, chapterId);
setIsSubmitting(false);
};
return (
<FormLayout onSubmit={handleSubmit}>
<FormField name="documentpdf-activity-name">
<Flex css={{ alignItems: 'baseline', justifyContent: 'space-between' }}>
<FormLabel>PDF Document name</FormLabel>
<FormMessage match="valueMissing">Please provide a name for your PDF Document activity</FormMessage>
</Flex>
<Form.Control asChild>
<Input onChange={handleNameChange} type="text" required />
</Form.Control>
</FormField>
<FormField name="documentpdf-activity-file">
<Flex css={{ alignItems: 'baseline', justifyContent: 'space-between' }}>
<FormLabel>PDF Document file</FormLabel>
<FormMessage match="valueMissing">Please provide a PDF Document for your activity</FormMessage>
</Flex>
<Form.Control asChild>
<input type="file" onChange={handleDocumentPdfChange} required />
</Form.Control>
</FormField>
<Flex css={{ marginTop: 25, justifyContent: 'flex-end' }}>
<Form.Submit asChild>
<ButtonBlack type="submit" css={{ marginTop: 10 }}>
{isSubmitting ? <BarLoader cssOverride={{borderRadius:60,}} width={60} color="#ffffff" /> : "Create activity"}
</ButtonBlack>
</Form.Submit>
</Flex>
</FormLayout>
);
}
export default DocumentPdfModal;

View file

@ -0,0 +1,75 @@
import { getBackendUrl } from "@services/config/config";
import React from "react";
import styled from "styled-components";
function DocumentPdfActivity({ activity, course }: { activity: any; course: any }) {
function getChapterName() {
let chapterName = "";
let chapterId = activity.chapter_id;
course.chapters.forEach((chapter: any) => {
if (chapter.chapter_id === chapterId) {
chapterName = chapter.name;
}
});
return chapterName;
}
return (
<DocumentPdfActivityLayout>
<DocumentPdfTitle>
<p>Chapter : {getChapterName()}</p>
{activity.name}
</DocumentPdfTitle>
<DocumentPdfPlayerWrapper>
<iframe
src={`${getBackendUrl()}content/uploads/documents/documentpdf/${activity.content.documentpdf.activity_id}/${activity.content.documentpdf.filename}`}
/>
</DocumentPdfPlayerWrapper>
</DocumentPdfActivityLayout>
);
}
export default DocumentPdfActivity;
const DocumentPdfActivityLayout = styled.div`
display: flex;
flex-direction: column;
margin-top: 10px;
background: #141414;
min-width: 100%;
min-height: 1200px;
`;
const DocumentPdfTitle = styled.div`
display: flex;
width: 1300px;
margin: 0 auto;
padding-top: 20px;
font-size: 24px;
font-weight: 700;
color: #fff;
flex-direction: column;
p {
font-size: 14px;
padding: 0;
margin: 0;
color: #ffffffaa;
}
`;
const DocumentPdfPlayerWrapper = styled.div`
display: flex;
width: 1300px;
margin: 0 auto;
justify-content: center;
padding-top: 20px;
iframe {
width: 1300px;
height: 500px;
border-radius: 7px;
background-color: black;
border: none;
}
`;

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

View file

@ -17,13 +17,18 @@ export async function createFileActivity(file: File, type: string, data: any, ch
formData.append("coursechapter_id", chapter_id);
let org_id = "test";
let endpoint = `${getAPIUrl()}activities/video?org_id=${org_id}`;
let endpoint = "";
if (type === "video") {
formData.append("name", data.name);
formData.append("video_file", file);
endpoint = endpoint;
endpoint = `${getAPIUrl()}activities/video?org_id=${org_id}`;
} else if (type === "documentpdf") {
formData.append("pdf_file", file);
formData.append("name", data.name);
endpoint = `${getAPIUrl()}activities/documentpdf?org_id=${org_id}`;
} else {
// Handle other file types here
}
const result: any = await fetch(endpoint, RequestBodyForm("POST", formData));

View file

@ -1,6 +1,7 @@
from fastapi import APIRouter, Depends, UploadFile, Form, Request
from src.services.courses.activities.activities import *
from src.security.auth import get_current_user
from src.services.courses.activities.pdf import create_documentpdf_activity
from src.services.courses.activities.video import create_video_activity
router = APIRouter()
@ -45,7 +46,7 @@ async def api_delete_activity(request: Request, activity_id: str, org_id: str,
"""
return await delete_activity(request, activity_id, current_user)
# Video play
# Video activity
@router.post("/video")
@ -54,3 +55,10 @@ async def api_create_video_activity(request: Request, org_id: str, name: str =
Create new activity
"""
return await create_video_activity(request, name, coursechapter_id, current_user, video_file)
@router.post("/documentpdf")
async def api_create_documentpdf_activity(request: Request, org_id: str, name: str = Form(), coursechapter_id: str = Form(), current_user: PublicUser = Depends(get_current_user), pdf_file: UploadFile | None = None):
"""
Create new activity
"""
return await create_documentpdf_activity(request, name, coursechapter_id, current_user, pdf_file)

View file

@ -0,0 +1,77 @@
from pydantic import BaseModel
from src.security.security import verify_user_rights_with_roles
from src.services.courses.activities.uploads.pdfs import upload_pdf
from src.services.users.users import PublicUser
from src.services.courses.activities.activities import ActivityInDB
from fastapi import HTTPException, status, UploadFile, Request
from uuid import uuid4
from datetime import datetime
async def create_documentpdf_activity(request: Request, name: str, coursechapter_id: str, current_user: PublicUser, pdf_file: UploadFile | None = None):
activities = request.app.db["activities"]
courses = request.app.db["courses"]
# generate activity_id
activity_id = str(f"activity_{uuid4()}")
# get org_id from course
coursechapter = await courses.find_one(
{"chapters_content.coursechapter_id": coursechapter_id})
org_id = coursechapter["org_id"]
# check if pdf_file is not None
if not pdf_file:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Pdf : No pdf file provided")
if pdf_file.content_type not in ["application/pdf"]:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Pdf : Wrong pdf format")
# get pdf format
if pdf_file.filename:
pdf_format = pdf_file.filename.split(".")[-1]
else:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Pdf : No pdf file provided")
activity_object = ActivityInDB(
org_id=org_id,
activity_id=activity_id,
coursechapter_id=coursechapter_id,
name=name,
type="documentpdf",
content={
"documentpdf": {
"filename": "documentpdf."+pdf_format,
"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, activity_id, element_org_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())
await activities.insert_one(activity.dict())
# upload pdf
if pdf_file:
# get pdffile format
await upload_pdf(pdf_file, activity_id)
# todo : choose whether to update the chapter or not
# update chapter
await courses.update_one({"chapters_content.coursechapter_id": coursechapter_id}, {
"$addToSet": {"chapters_content.$.activities": activity_id}})
return activity

View file

@ -0,0 +1,23 @@
import os
async def upload_pdf(pdf_file, activity_id):
contents = pdf_file.file.read()
pdf_format = pdf_file.filename.split(".")[-1]
if not os.path.exists("content/uploads/documents/documentpdf"):
# create folder
os.makedirs("content/uploads/documents/documentpdf")
# create folder
os.mkdir(f"content/uploads/documents/documentpdf/{activity_id}")
try:
with open(f"content/uploads/documents/documentpdf/{activity_id}/documentpdf.{pdf_format}", 'wb') as f:
f.write(contents)
f.close()
except Exception as e:
return {"message": "There was an error uploading the file"}
finally:
pdf_file.file.close()