Skip to content
Snippets Groups Projects
Commit 4f7c69693731 authored by Fabien Amarger's avatar Fabien Amarger
Browse files

feat(frontend): Add download buttons for dataset and logs on import process table

parent 62685fc2b6d4
No related branches found
No related tags found
1 merge request!16Log import_data to S3 storage
Pipeline #226883 passed
......@@ -317,7 +317,14 @@
),
params,
);
// TODO fetch files for all import processes
// See https://forge.extranet.logilab.fr/cubicweb/RQL/-/merge_requests/99
const dataLogQuery = transaction.push(
"Any X, DATA.download_url(), LOG.download_url() WHERE X eid %(eid)s, " +
"X has_output_dataset DATA?, X import_report LOG?",
{ eid: importProcessListQuery.ref().row(0).column(0) },
);
const result = await client.executeTransaction(transaction);
const importProcessListResult = result.resolveQuery(
importProcessListQuery.ref(),
);
......@@ -320,7 +327,15 @@
const result = await client.executeTransaction(transaction);
const importProcessListResult = result.resolveQuery(
importProcessListQuery.ref(),
);
const dataLogListResult = result.resolveQuery(dataLogQuery.ref());
for (const result of importProcessListResult) {
const dataLog = dataLogListResult.find((e) => e[0] === result[0]);
if (dataLog != undefined) {
result[5] = dataLog[1];
result[6] = dataLog[2];
}
}
return importProcessListResultSetToObject(importProcessListResult);
} catch (e) {
if (e && typeof e === "object" && "title" in e) {
......@@ -335,16 +350,17 @@
function importProcessListResultSetToObject(
resultSet: ResultSet,
): ImportProcess[] {
return resultSet.map(
(r) =>
({
eid: r[0],
date: r[1],
recipe: r[2],
project: r[3],
state: r[4],
}) as ImportProcess,
);
return resultSet.map((r) => {
return {
eid: r[0],
date: r[1],
recipe: r[2],
project: r[3],
state: r[4],
dataset_url: r[5],
log_url: r[6],
} as ImportProcess;
});
}
function projectListResultSetToObject(resultSet: ResultSet) {
......
import { Box, Tooltip, TooltipProps } from "@mui/material";
export function ButtonTooltip({ children, ...rest }: TooltipProps) {
return (
<Tooltip {...rest}>
{/* This box is necessary for the tooltip to show when button is disabled */}
<Box>{children}</Box>
</Tooltip>
);
}
import { Box, Button, ButtonGroup, Tooltip } from "@mui/material";
import { Button, ButtonGroup } from "@mui/material";
import FileUploadIcon from "@mui/icons-material/FileUpload";
import Visibility from "@mui/icons-material/Visibility";
import { ChangeEventHandler, useRef } from "react";
import { CWFile } from "@/types";
......@@ -2,7 +2,9 @@
import FileUploadIcon from "@mui/icons-material/FileUpload";
import Visibility from "@mui/icons-material/Visibility";
import { ChangeEventHandler, useRef } from "react";
import { CWFile } from "@/types";
import { downloadFile, openFile } from "@/utils";
import { ButtonTooltip } from "./ButtonTooltip";
interface FileFieldProps {
label: string;
......@@ -23,8 +25,5 @@
let fileToOpen = value?.data;
// The file is available on the server, fetch it
if (!fileToOpen && value?.downloadUrl) {
const response = await fetch(value.downloadUrl, {
credentials: "include",
});
fileToOpen = await response.blob();
fileToOpen = await downloadFile(value.downloadUrl);
}
......@@ -30,10 +29,5 @@
}
if (fileToOpen) {
const url = URL.createObjectURL(fileToOpen);
window.open(url);
} else {
console.warn("No file available either locally or on the server");
}
openFile(fileToOpen);
};
const chooseFile = () => {
......@@ -75,8 +69,8 @@
>
{label}: {filePresent ? "Modifier le fichier" : "Choisir un fichier"}
</Button>
<Tooltip
<ButtonTooltip
title={
filePresent ? "Télécharger le fichier" : "Aucun fichier en ligne"
}
>
......@@ -79,18 +73,15 @@
title={
filePresent ? "Télécharger le fichier" : "Aucun fichier en ligne"
}
>
{/* This box is necessary for the tooltip to show */}
<Box>
<Button
startIcon={<Visibility />}
disabled={disabled || !filePresent}
onClick={onPressOpen}
>
Voir
</Button>
</Box>
</Tooltip>
<Button
startIcon={<Visibility />}
disabled={disabled || !filePresent}
onClick={onPressOpen}
>
Voir
</Button>
</ButtonTooltip>
</ButtonGroup>
</>
);
......
......@@ -8,7 +8,8 @@
TableBody,
CircularProgress,
Typography,
IconButton,
} from "@mui/material";
import CheckIcon from "@mui/icons-material/Check";
import CloseIcon from "@mui/icons-material/Close";
import ScheduleIcon from "@mui/icons-material/Schedule";
......@@ -11,8 +12,10 @@
} from "@mui/material";
import CheckIcon from "@mui/icons-material/Check";
import CloseIcon from "@mui/icons-material/Close";
import ScheduleIcon from "@mui/icons-material/Schedule";
import Visibility from "@mui/icons-material/Visibility";
import VisibilityOff from "@mui/icons-material/VisibilityOff";
import { ImportProcess } from "@/types";
import { useGetImportProcesses } from "@/hooks/useGetImportProcesses";
import { ErrorScreen } from "./ErrorScreen";
import { LoadingScreen } from "./LoadingScreen";
......@@ -15,7 +18,9 @@
import { ImportProcess } from "@/types";
import { useGetImportProcesses } from "@/hooks/useGetImportProcesses";
import { ErrorScreen } from "./ErrorScreen";
import { LoadingScreen } from "./LoadingScreen";
import { downloadFile, openFile } from "@/utils";
import { ButtonTooltip } from "./ButtonTooltip";
export interface ImprotProcessTableProps {
dataServiceEid?: number;
......@@ -23,7 +28,13 @@
projectEid?: number;
}
// TODO add logs link
async function showFile(url?: string) {
if (url) {
const fileToOpen = await downloadFile(url);
openFile(fileToOpen);
}
}
export function ImportProcessTable(props: ImprotProcessTableProps) {
const {
data: importProcessList,
......@@ -60,6 +71,8 @@
<TableCell>Recette</TableCell>
<TableCell>État</TableCell>
<TableCell>Projet</TableCell>
<TableCell width={50}>Dataset</TableCell>
<TableCell width={50}>Log</TableCell>
</TableRow>
</TableHead>
<TableBody>
......@@ -63,18 +76,8 @@
</TableRow>
</TableHead>
<TableBody>
{importProcessList.map((row) => (
<TableRow key={row.eid}>
<TableCell component="th" scope="row">
{row.eid}
</TableCell>
<TableCell>{row.date}</TableCell>
<TableCell>{row.recipe}</TableCell>
<TableCell>
<StateIcon state={row.state} />
</TableCell>
<TableCell>{row.project}</TableCell>
</TableRow>
{importProcessList.map((row, i) => (
<ImportProcessTableRow key={i} importProcess={row} />
))}
</TableBody>
</Table>
......@@ -82,6 +85,59 @@
);
}
function ImportProcessTableRow({
importProcess,
}: {
importProcess: ImportProcess;
}) {
const hasDataset = importProcess.dataset_url != null;
const hasLog = importProcess.log_url != null;
return (
<TableRow key={importProcess.eid}>
<TableCell component="th" scope="row">
{importProcess.eid}
</TableCell>
<TableCell>{importProcess.date}</TableCell>
<TableCell>{importProcess.recipe}</TableCell>
<TableCell>
<StateIcon state={importProcess.state} />
</TableCell>
<TableCell>{importProcess.project}</TableCell>
<TableCell>
<ButtonTooltip
title={
hasDataset
? "Voir le fichier de Dataset"
: "Aucun Dataset disponible"
}
>
<IconButton
color="primary"
disabled={!hasDataset}
onClick={() => showFile(importProcess.dataset_url)}
>
{hasDataset ? <Visibility /> : <VisibilityOff />}
</IconButton>
</ButtonTooltip>
</TableCell>
<TableCell>
<ButtonTooltip
title={hasLog ? "Voir le fichier de log" : "Aucun log disponible"}
>
<IconButton
color="primary"
disabled={!hasLog}
onClick={() => showFile(importProcess.log_url)}
>
{hasLog ? <Visibility /> : <VisibilityOff />}
</IconButton>
</ButtonTooltip>
</TableCell>
</TableRow>
);
}
function StateIcon({ state }: { state: ImportProcess["state"] }) {
if (state === "successful") {
return <CheckIcon />;
......
......@@ -36,4 +36,6 @@
recipe: string;
project: string;
state: "waiting" | "ongoing" | "error" | "successful";
dataset_url?: string;
log_url?: string;
};
export function openFile(file?: Blob) {
if (file) {
const url = URL.createObjectURL(file);
window.open(url);
} else {
console.warn("No file available either locally or on the server");
}
}
export async function downloadFile(url: string) {
const response = await fetch(url, {
credentials: "include",
});
return await response.blob();
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment