diff --git a/backend/src/routers/category.py b/backend/src/routers/category.py index 3d680eb..3a9c427 100755 --- a/backend/src/routers/category.py +++ b/backend/src/routers/category.py @@ -1,9 +1,14 @@ +import datetime +import os +import zipfile from typing import List from fastapi import APIRouter, BackgroundTasks, Depends, status +from fastapi.responses import FileResponse, JSONResponse from fastapi_jwt_auth import AuthJWT from sqlalchemy.ext.asyncio import AsyncSession +import src.settings as settings from src.database.database import get_session from src.exceptions.base_exception import BaseException from src.exceptions.login_exception import LoginException @@ -11,6 +16,7 @@ from src.models.auth import User from src.models.category import (Category, CategoryOut, CategoryPost, CategoryPut) from src.models.sign import Sign, SignOut +from src.models.signvideo import SignVideo router = APIRouter(prefix="/categories") @@ -95,7 +101,6 @@ async def delete_category(category_id: int, Authorize: AuthJWT = Depends(), sess raise LoginException("User not found") c = await Category.get_by_id(id=category_id, session=session) - print(c) if not c: raise BaseException(message="Category not found", status_code=status.HTTP_404_NOT_FOUND) @@ -104,3 +109,44 @@ async def delete_category(category_id: int, Authorize: AuthJWT = Depends(), sess await c.delete(session=session) return {"message": "Category deleted successfully"} + +@router.get("/{category_id}/download", status_code=status.HTTP_200_OK) +async def download_all(category_id: int, background_tasks: BackgroundTasks, Authorize: AuthJWT = Depends(), session: AsyncSession = Depends(get_session)): + Authorize.jwt_required() + + user = Authorize.get_jwt_subject() + user = await User.get_by_id(id=user, session=session) + + if not user: + raise LoginException("User not found") + + c = await Category.get_by_id(id=category_id, session=session) + if not c: + raise BaseException(message="Category not found", status_code=status.HTTP_404_NOT_FOUND) + + # get all the sign videos of the category + category_signs = c.signs + + # get all the paths of the sign videos + paths = [] + for sign in category_signs: + for video in sign.sign_videos: + paths.append(video.path) + + zip_path = f"/tmp/{datetime.datetime.now().timestamp()}.zip" + + # create the zip file + with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED, False) as zip_file: + for path in paths: + zip_file.write(f"{settings.DATA_PATH}/{path}", os.path.basename(path)) + + background_tasks.add_task(delete_zip_file, zip_path) + # serve the zip file as the response + response = FileResponse(zip_path, media_type="application/zip", filename=f"{c.name}_signs.zip") + + return response + + +# define a function to delete the zip file +async def delete_zip_file(zip_path): + os.remove(zip_path) \ No newline at end of file diff --git a/frontend/src/components/SignsPage.tsx b/frontend/src/components/SignsPage.tsx index 8a3af0b..6b0d2f5 100644 --- a/frontend/src/components/SignsPage.tsx +++ b/frontend/src/components/SignsPage.tsx @@ -29,7 +29,7 @@ const SignsPage: React.FC = () => { }; const handleDownloadData = async () => { - downloadSigns().then((blob: Blob) => { + downloadSigns(id!).then((blob: Blob) => { const url = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; diff --git a/frontend/src/services/signs.ts b/frontend/src/services/signs.ts index 8e6ad9a..96fcc99 100644 --- a/frontend/src/services/signs.ts +++ b/frontend/src/services/signs.ts @@ -51,11 +51,11 @@ const getSign = async (id: number) => { return response.json(); }; -const downloadSigns = async () => { +const downloadSigns = async (category: string) => { // get access token from local storage const token = localStorage.getItem('accessToken'); // make request to download signs - const response = await fetch(`${process.env.REACT_APP_API_URL}/signs/download/all`, { + const response = await fetch(`${process.env.REACT_APP_API_URL}/categories/${category}/download`, { headers: { Authorization: `Bearer ${token}` }