diff --git a/backend/src/submission/router.py b/backend/src/submission/router.py index 99a48d6b..683001cb 100644 --- a/backend/src/submission/router.py +++ b/backend/src/submission/router.py @@ -2,7 +2,7 @@ from typing import Sequence from fastapi import APIRouter, Depends, BackgroundTasks -from fastapi.responses import FileResponse +from fastapi.responses import FileResponse, StreamingResponse from sqlalchemy.ext.asyncio import AsyncSession from src.dependencies import get_async_db @@ -15,7 +15,7 @@ ) from src.submission.exceptions import FileNotFound from src.submission.exceptions import FilesNotFound -from src.submission.utils import upload_files, remove_files +from src.submission.utils import upload_files, remove_files, zip_stream from src.user.dependencies import admin_user_validation, get_authenticated_user from src.user.schemas import User from . import service @@ -94,6 +94,12 @@ async def get_file(path: str, submission: Submission = Depends(retrieve_submissi return FileResponse(path=path) +@router.get("/{submission_id}/zip", response_class=StreamingResponse) +async def get_all_files(submission: Submission = Depends(retrieve_submission)): + path = submission_path(submission.files_uuid, "") + return StreamingResponse(zip_stream(path, submission.group_id), media_type="application/zip") + + @router.get("/{submission_id}/artifacts", response_model=list[File]) async def get_artifacts(submission: Submission = Depends(retrieve_submission)): if submission.status == Status.InProgress: diff --git a/backend/src/submission/utils.py b/backend/src/submission/utils.py index bc741ea6..7e0afe1d 100644 --- a/backend/src/submission/utils.py +++ b/backend/src/submission/utils.py @@ -1,6 +1,8 @@ import os import shutil import zipfile +import pathlib +import io import fnmatch from uuid import uuid4 @@ -45,5 +47,16 @@ def upload_files(files: list[UploadFile], project: Project) -> str: return uuid +def zip_stream(path, group_id: int): + base_path = pathlib.Path(path) + data = io.BytesIO() + with zipfile.ZipFile(data, mode='w') as z: + for f_name in base_path.iterdir(): + name = f"group_{group_id}/{str(f_name).replace(path, "")}" + z.write(f_name, arcname=name) + data.seek(0) + yield from data + + def remove_files(uuid: str): shutil.rmtree(submissions_path(uuid)) diff --git a/frontend/src/components/submission/SubmissionCard.vue b/frontend/src/components/submission/SubmissionCard.vue index 41c4248a..8244dd63 100644 --- a/frontend/src/components/submission/SubmissionCard.vue +++ b/frontend/src/components/submission/SubmissionCard.vue @@ -63,6 +63,11 @@ + + + {{ $t("submission.download_all_files") }} + + @@ -91,6 +96,13 @@ const downloadFile = (index: number) => { const file = files.value![index]; download_file(`/api/submissions/${submission.value!.id}/files/${file.filename}`, file.filename); }; + +const downloadAll = () => { + download_file( + `/api/submissions/${submission.value!.id}/zip`, + `submission_group_${submission.value?.group_id}` + ); +};