From 77c0cf941938f7f9dd3066257d0792d8dde924ff Mon Sep 17 00:00:00 2001 From: hatim dinia Date: Wed, 18 Dec 2024 14:22:24 +0100 Subject: [PATCH] feat(ui-debug): add download of files in their original format --- .../Singlestudy/explore/Debug/Data/Json.tsx | 37 ++++++++++-------- .../Singlestudy/explore/Debug/Data/Text.tsx | 29 ++++++++------ .../explore/Debug/Data/Unsupported.tsx | 23 ++++------- webapp/src/services/api/studies/raw/index.ts | 38 +++++++++++++++++++ webapp/src/services/api/studies/raw/types.ts | 5 +++ 5 files changed, 90 insertions(+), 42 deletions(-) diff --git a/webapp/src/components/App/Singlestudy/explore/Debug/Data/Json.tsx b/webapp/src/components/App/Singlestudy/explore/Debug/Data/Json.tsx index f014626bf5..ed564d2d07 100644 --- a/webapp/src/components/App/Singlestudy/explore/Debug/Data/Json.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Debug/Data/Json.tsx @@ -24,13 +24,14 @@ import { downloadFile } from "../../../../../../utils/fileUtils"; import { useEffect, useState } from "react"; import { Filename, Flex, Menubar } from "./styles"; import UploadFileButton from "../../../../../common/buttons/UploadFileButton"; +import { getRawFile } from "@/services/api/studies/raw"; function Json({ filePath, filename, studyId, canEdit }: DataCompProps) { const [t] = useTranslation(); const { enqueueSnackbar } = useSnackbar(); const [currentJson, setCurrentJson] = useState(); - const res = usePromiseWithSnackbarError( + const fileRes = usePromiseWithSnackbarError( () => getStudyData(studyId, filePath, -1), { errorMessage: t("studies.error.retrieveData"), @@ -38,9 +39,24 @@ function Json({ filePath, filename, studyId, canEdit }: DataCompProps) { }, ); + const rawFileRes = usePromiseWithSnackbarError( + () => getRawFile(studyId, filePath), + { + errorMessage: t("studies.error.retrieveData"), + deps: [studyId, filePath], + }, + ); + useEffect(() => { - setCurrentJson(res.data); - }, [res.data]); + setCurrentJson(fileRes.data); + }, [fileRes.data]); + + const handleDownload = () => { + if (rawFileRes.data) { + const { data, filename } = rawFileRes.data; + downloadFile(data, filename); + } + }; //////////////////////////////////////////////////////////////// // Event Handlers @@ -58,17 +74,8 @@ function Json({ filePath, filename, studyId, canEdit }: DataCompProps) { }); }; - const handleDownload = () => { - if (currentJson !== undefined) { - downloadFile( - JSON.stringify(currentJson, null, 2), - filename.endsWith(".json") ? filename : `${filename}.json`, - ); - } - }; - const handleUploadSuccessful = () => { - res.reload(); + fileRes.reload(); }; //////////////////////////////////////////////////////////////// @@ -77,7 +84,7 @@ function Json({ filePath, filename, studyId, canEdit }: DataCompProps) { return ( ( @@ -93,7 +100,7 @@ function Json({ filePath, filename, studyId, canEdit }: DataCompProps) { getStudyData(studyId, filePath).then((text) => parseContent(text, { filePath, fileType }), @@ -86,21 +87,27 @@ function Text({ }, ); - //////////////////////////////////////////////////////////////// - // Event Handlers - //////////////////////////////////////////////////////////////// + const rawFileRes = usePromiseWithSnackbarError( + () => getRawFile(studyId, filePath), + { + errorMessage: t("studies.error.retrieveData"), + deps: [studyId, filePath], + }, + ); const handleDownload = () => { - if (res.data) { - downloadFile( - res.data, - filename.endsWith(".txt") ? filename : `${filename}.txt`, - ); + if (rawFileRes.data) { + const { data, filename } = rawFileRes.data; + downloadFile(data, filename); } }; + //////////////////////////////////////////////////////////////// + // Event Handlers + //////////////////////////////////////////////////////////////// + const handleUploadSuccessful = () => { - res.reload(); + fileRes.reload(); }; //////////////////////////////////////////////////////////////// @@ -109,7 +116,7 @@ function Text({ return ( ( diff --git a/webapp/src/components/App/Singlestudy/explore/Debug/Data/Unsupported.tsx b/webapp/src/components/App/Singlestudy/explore/Debug/Data/Unsupported.tsx index e4bcfccc5c..aaf91400bf 100644 --- a/webapp/src/components/App/Singlestudy/explore/Debug/Data/Unsupported.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Debug/Data/Unsupported.tsx @@ -20,14 +20,14 @@ import type { DataCompProps } from "../utils"; import DownloadButton from "@/components/common/buttons/DownloadButton"; import UploadFileButton from "@/components/common/buttons/UploadFileButton"; import usePromiseWithSnackbarError from "@/hooks/usePromiseWithSnackbarError"; -import { getStudyData } from "@/services/api/study"; import { downloadFile } from "@/utils/fileUtils"; +import { getRawFile } from "@/services/api/studies/raw"; function Unsupported({ studyId, filePath, filename, canEdit }: DataCompProps) { const { t } = useTranslation(); - const res = usePromiseWithSnackbarError( - () => getStudyData(studyId, filePath), + const rawFileRes = usePromiseWithSnackbarError( + () => getRawFile(studyId, filePath), { errorMessage: t("studies.error.retrieveData"), deps: [studyId, filePath], @@ -39,15 +39,12 @@ function Unsupported({ studyId, filePath, filename, canEdit }: DataCompProps) { //////////////////////////////////////////////////////////////// const handleDownload = () => { - if (res.data) { - downloadFile(res.data, filename); + if (rawFileRes.data) { + const { data, filename } = rawFileRes.data; + downloadFile(data, filename); } }; - const handleUploadSuccessful = () => { - res.reload(); - }; - //////////////////////////////////////////////////////////////// // JSX //////////////////////////////////////////////////////////////// @@ -56,13 +53,7 @@ function Unsupported({ studyId, filePath, filename, canEdit }: DataCompProps) { {filename} - {canEdit && ( - - )} + {canEdit && } diff --git a/webapp/src/services/api/studies/raw/index.ts b/webapp/src/services/api/studies/raw/index.ts index 85524560d5..b334611562 100644 --- a/webapp/src/services/api/studies/raw/index.ts +++ b/webapp/src/services/api/studies/raw/index.ts @@ -17,6 +17,7 @@ import type { DeleteFileParams, DownloadMatrixParams, ImportFileParams, + RawFile, } from "./types"; export async function downloadMatrix(params: DownloadMatrixParams) { @@ -51,3 +52,40 @@ export async function deleteFile(params: DeleteFileParams) { await client.delete(url, { params: { path } }); } + +/** + * Reads an original raw file from a study with its metadata. + * + * @param studyId - Unique identifier of the study + * @param filePath - Path to the file within the study + * @returns Promise containing the file data and metadata + */ +export async function getRawFile( + studyId: string, + filePath: string, +): Promise { + const response = await client.get( + `/v1/studies/${studyId}/raw/original-file`, + { + params: { + path: filePath, + }, + responseType: "blob", + }, + ); + + const contentDisposition = response.headers["content-disposition"]; + let filename = filePath.split("/").pop() || "file"; // fallback filename + + if (contentDisposition) { + const matches = /filename=([^;]+)/.exec(contentDisposition); + if (matches?.[1]) { + filename = matches[1].replace(/"/g, "").trim(); + } + } + + return { + data: response.data, + filename: filename, + }; +} diff --git a/webapp/src/services/api/studies/raw/types.ts b/webapp/src/services/api/studies/raw/types.ts index 937fd84119..43ff274183 100644 --- a/webapp/src/services/api/studies/raw/types.ts +++ b/webapp/src/services/api/studies/raw/types.ts @@ -39,3 +39,8 @@ export interface DeleteFileParams { studyId: StudyMetadata["id"]; path: string; } + +export interface RawFile { + data: Blob; + filename: string; +}