diff --git a/optuna_dashboard/ts/components/Artifact/TrialArtifactCards.tsx b/optuna_dashboard/ts/components/Artifact/ArtifactCards.tsx
similarity index 63%
rename from optuna_dashboard/ts/components/Artifact/TrialArtifactCards.tsx
rename to optuna_dashboard/ts/components/Artifact/ArtifactCards.tsx
index 7bd29c307..4f3127c5c 100644
--- a/optuna_dashboard/ts/components/Artifact/TrialArtifactCards.tsx
+++ b/optuna_dashboard/ts/components/Artifact/ArtifactCards.tsx
@@ -19,32 +19,44 @@ import React, {
useRef,
useState,
} from "react"
-
-import { Trial } from "ts/types/optuna"
import { actionCreator } from "../../action"
+import { StudyDetail, Trial } from "../../types/optuna"
import { ArtifactCardMedia } from "./ArtifactCardMedia"
-import { useDeleteTrialArtifactDialog } from "./DeleteArtifactDialog"
+import { useDeleteArtifactDialog } from "./DeleteArtifactDialog"
import { isTableArtifact, useTableArtifactModal } from "./TableArtifactViewer"
import {
isThreejsArtifact,
useThreejsArtifactModal,
} from "./ThreejsArtifactViewer"
-export const TrialArtifactCards: FC<{ trial: Trial }> = ({ trial }) => {
+type StudyOrTrial =
+ | {
+ type: "study"
+ study: StudyDetail
+ }
+ | {
+ type: "trial"
+ trial: Trial
+ }
+export const ArtifactCards: FC<{
+ studyOrTrial: StudyOrTrial
+ isArtifactModifiable?: boolean
+}> = ({ studyOrTrial, isArtifactModifiable = true }) => {
const theme = useTheme()
const [openDeleteArtifactDialog, renderDeleteArtifactDialog] =
- useDeleteTrialArtifactDialog()
+ useDeleteArtifactDialog()
const [openThreejsArtifactModal, renderThreejsArtifactModal] =
useThreejsArtifactModal()
const [openTableArtifactModal, renderTableArtifactModal] =
useTableArtifactModal()
- const isArtifactModifiable = (trial: Trial) => {
- return trial.state === "Running" || trial.state === "Waiting"
- }
const width = "200px"
const height = "150px"
- const artifacts = [...trial.artifacts].sort((a, b) => {
+ const sortedArtifacts = [
+ ...(studyOrTrial.type === "study"
+ ? studyOrTrial.study.artifacts
+ : studyOrTrial.trial.artifacts),
+ ].sort((a, b) => {
if (a.filename < b.filename) {
return -1
} else if (a.filename > b.filename) {
@@ -56,18 +68,15 @@ export const TrialArtifactCards: FC<{ trial: Trial }> = ({ trial }) => {
return (
<>
-
- Artifacts
-
- {artifacts.map((artifact) => {
- const urlPath = `/artifacts/${trial.study_id}/${trial.trial_id}/${artifact.artifact_id}`
+ {sortedArtifacts.map((artifact) => {
+ const urlPath =
+ studyOrTrial.type === "study"
+ ? `/artifacts/${studyOrTrial.study.id}/${artifact.artifact_id}`
+ : `/artifacts/${studyOrTrial.trial.study_id}/${studyOrTrial.trial.trial_id}/${artifact.artifact_id}`
return (
= ({ trial }) => {
marginBottom: theme.spacing(2),
width: width,
margin: theme.spacing(0, 1, 1, 0),
- display: "flex",
- flexDirection: "column",
- alignItems: "center",
+ display: studyOrTrial.type === "trial" ? "flex" : undefined,
+ flexDirection:
+ studyOrTrial.type === "trial" ? "column" : undefined,
+ alignItems:
+ studyOrTrial.type === "trial" ? "center" : undefined,
+ border:
+ studyOrTrial.type === "study"
+ ? `1px solid ${theme.palette.divider}`
+ : undefined,
}}
>
= ({ trial }) => {
sx={{
p: theme.spacing(0.5, 0),
flexGrow: 1,
- wordBreak: "break-all",
- maxWidth: `calc(100% - ${theme.spacing(
- 4 +
- (isThreejsArtifact(artifact) ? 4 : 0) +
- (isArtifactModifiable(trial) ? 4 : 0)
- )})`,
+ wordBreak:
+ studyOrTrial.type === "trial" ? "break-all" : undefined,
+ maxWidth:
+ studyOrTrial.type === "trial"
+ ? `calc(100% - ${theme.spacing(
+ 4 +
+ (isThreejsArtifact(artifact) ? 4 : 0) +
+ (isArtifactModifiable ? 4 : 0)
+ )})`
+ : `calc(100% - ${
+ isThreejsArtifact(artifact)
+ ? theme.spacing(12)
+ : theme.spacing(8)
+ })`,
+ wordWrap:
+ studyOrTrial.type === "study" ? "break-word" : undefined,
}}
>
{artifact.filename}
@@ -132,7 +157,7 @@ export const TrialArtifactCards: FC<{ trial: Trial }> = ({ trial }) => {
) : null}
- {isArtifactModifiable(trial) ? (
+ {isArtifactModifiable && (
= ({ trial }) => {
sx={{ margin: "auto 0" }}
onClick={() => {
openDeleteArtifactDialog(
- trial.study_id,
- trial.trial_id,
- artifact
+ studyOrTrial.type === "study"
+ ? {
+ type: "study",
+ studyId: studyOrTrial.study.id,
+ artifact,
+ }
+ : {
+ type: "trial",
+ studyId: studyOrTrial.trial.study_id,
+ trialId: studyOrTrial.trial.trial_id,
+ artifact,
+ }
)
}}
>
- ) : null}
+ )}
= ({ trial }) => {
)
})}
- {isArtifactModifiable(trial) ? (
-
- ) : null}
+ {isArtifactModifiable && (
+
+ )}
{renderDeleteArtifactDialog()}
{renderThreejsArtifactModal()}
@@ -174,14 +212,14 @@ export const TrialArtifactCards: FC<{ trial: Trial }> = ({ trial }) => {
)
}
-const TrialArtifactUploader: FC<{
- trial: Trial
+const ArtifactUploader: FC<{
+ studyOrTrial: StudyOrTrial
width: string
height: string
-}> = ({ trial, width, height }) => {
+}> = ({ studyOrTrial, width, height }) => {
const theme = useTheme()
const action = actionCreator()
- const [dragOver, setDragOver] = useState(false)
+ const [dragOver, setDragOver] = useState(false)
const inputRef = useRef(null)
const handleClick: MouseEventHandler = () => {
@@ -190,34 +228,55 @@ const TrialArtifactUploader: FC<{
}
inputRef.current.click()
}
+
const handleOnChange: ChangeEventHandler = (e) => {
const files = e.target.files
if (files === null) {
return
}
- action.uploadTrialArtifact(trial.study_id, trial.trial_id, files[0])
- }
- const handleDrop: DragEventHandler = (e) => {
- e.stopPropagation()
- e.preventDefault()
- const files = e.dataTransfer.files
- setDragOver(false)
- for (let i = 0; i < files.length; i++) {
- action.uploadTrialArtifact(trial.study_id, trial.trial_id, files[i])
+ if (studyOrTrial.type === "study") {
+ action.uploadStudyArtifact(studyOrTrial.study.id, files[0])
+ } else if (studyOrTrial.type === "trial") {
+ action.uploadTrialArtifact(
+ studyOrTrial.trial.study_id,
+ studyOrTrial.trial.trial_id,
+ files[0]
+ )
}
}
+
const handleDragOver: DragEventHandler = (e) => {
e.stopPropagation()
e.preventDefault()
e.dataTransfer.dropEffect = "copy"
setDragOver(true)
}
+
const handleDragLeave: DragEventHandler = (e) => {
e.stopPropagation()
e.preventDefault()
e.dataTransfer.dropEffect = "copy"
setDragOver(false)
}
+
+ const handleDrop: DragEventHandler = (e) => {
+ e.stopPropagation()
+ e.preventDefault()
+ const files = e.dataTransfer.files
+ setDragOver(false)
+ for (let i = 0; i < files.length; i++) {
+ if (studyOrTrial.type === "study") {
+ action.uploadStudyArtifact(studyOrTrial.study.id, files[i])
+ } else if (studyOrTrial.type === "trial") {
+ action.uploadTrialArtifact(
+ studyOrTrial.trial.study_id,
+ studyOrTrial.trial.trial_id,
+ files[i]
+ )
+ }
+ }
+ }
+
return (
void,
- () => ReactNode,
-] => {
- const action = actionCreator()
-
- const [openDeleteArtifactDialog, setOpenDeleteArtifactDialog] =
- useState(false)
- const [target, setTarget] = useState<[number, number, Artifact | null]>([
- -1,
- -1,
- null,
- ])
-
- const handleCloseDeleteArtifactDialog = () => {
- setOpenDeleteArtifactDialog(false)
- setTarget([-1, -1, null])
- }
-
- const handleDeleteArtifact = () => {
- const [studyId, trialId, artifact] = target
- if (artifact === null) {
- return
+type Target =
+ | {
+ type: "study"
+ studyId: number
+ artifact: Artifact
}
- action.deleteTrialArtifact(studyId, trialId, artifact.artifact_id)
- setOpenDeleteArtifactDialog(false)
- setTarget([-1, -1, null])
- }
-
- const openDialog = (studyId: number, trialId: number, artifact: Artifact) => {
- setTarget([studyId, trialId, artifact])
- setOpenDeleteArtifactDialog(true)
- }
-
- const renderDeleteArtifactDialog = () => {
- return (
-
- )
- }
- return [openDialog, renderDeleteArtifactDialog]
-}
-
-export const useDeleteStudyArtifactDialog = (): [
- (studyId: number, artifact: Artifact) => void,
+ | {
+ type: "trial"
+ studyId: number
+ trialId: number
+ artifact: Artifact
+ }
+export const useDeleteArtifactDialog = (): [
+ (target: Target) => void,
() => ReactNode,
] => {
const action = actionCreator()
-
const [openDeleteArtifactDialog, setOpenDeleteArtifactDialog] =
useState(false)
- const [target, setTarget] = useState<[number, Artifact | null]>([-1, null])
+ const [target, setTarget] = useState(null)
const handleCloseDeleteArtifactDialog = () => {
setOpenDeleteArtifactDialog(false)
- setTarget([-1, null])
+ setTarget(null)
}
const handleDeleteArtifact = () => {
- const [studyId, artifact] = target
- if (artifact === null) {
- return
+ if (target === null) return
+ if (target.type === "study") {
+ action.deleteStudyArtifact(target.studyId, target.artifact.artifact_id)
+ } else if (target.type === "trial") {
+ action.deleteTrialArtifact(
+ target.studyId,
+ target.trialId,
+ target.artifact.artifact_id
+ )
}
- action.deleteStudyArtifact(studyId, artifact.artifact_id)
setOpenDeleteArtifactDialog(false)
- setTarget([-1, null])
+ setTarget(null)
}
- const openDialog = (studyId: number, artifact: Artifact) => {
- setTarget([studyId, artifact])
+ const openDialog = (target: Target) => {
+ setTarget(target)
setOpenDeleteArtifactDialog(true)
}
@@ -94,7 +63,7 @@ export const useDeleteStudyArtifactDialog = (): [
)
diff --git a/optuna_dashboard/ts/components/Artifact/SelectedTrialArtifactCards.tsx b/optuna_dashboard/ts/components/Artifact/SelectedTrialArtifactCards.tsx
index 7b7bdeb82..e1c9edfa9 100644
--- a/optuna_dashboard/ts/components/Artifact/SelectedTrialArtifactCards.tsx
+++ b/optuna_dashboard/ts/components/Artifact/SelectedTrialArtifactCards.tsx
@@ -20,7 +20,7 @@ import React, { FC, useMemo, useState } from "react"
import { StudyDirection } from "@optuna/types"
import { StudyDetail, Trial } from "ts/types/optuna"
import { ArtifactCardMedia } from "./ArtifactCardMedia"
-import { useDeleteTrialArtifactDialog } from "./DeleteArtifactDialog"
+import { useDeleteArtifactDialog } from "./DeleteArtifactDialog"
import { isTableArtifact, useTableArtifactModal } from "./TableArtifactViewer"
import {
isThreejsArtifact,
@@ -33,7 +33,7 @@ export const SelectedTrialArtifactCards: FC<{
}> = ({ study, selectedTrials }) => {
const theme = useTheme()
const [openDeleteArtifactDialog, renderDeleteArtifactDialog] =
- useDeleteTrialArtifactDialog()
+ useDeleteArtifactDialog()
const [openThreejsArtifactModal, renderThreejsArtifactModal] =
useThreejsArtifactModal()
const [openTableArtifactModal, renderTableArtifactModal] =
@@ -199,11 +199,12 @@ export const SelectedTrialArtifactCards: FC<{
color="inherit"
sx={{ margin: "auto 0" }}
onClick={() => {
- openDeleteArtifactDialog(
- trial.study_id,
- trial.trial_id,
- artifact
- )
+ openDeleteArtifactDialog({
+ type: "trial",
+ studyId: trial.study_id,
+ trialId: trial.trial_id,
+ artifact,
+ })
}}
>
diff --git a/optuna_dashboard/ts/components/Artifact/StudyArtifactCards.tsx b/optuna_dashboard/ts/components/Artifact/StudyArtifactCards.tsx
deleted file mode 100644
index b24995f28..000000000
--- a/optuna_dashboard/ts/components/Artifact/StudyArtifactCards.tsx
+++ /dev/null
@@ -1,256 +0,0 @@
-import DeleteIcon from "@mui/icons-material/Delete"
-import DownloadIcon from "@mui/icons-material/Download"
-import FullscreenIcon from "@mui/icons-material/Fullscreen"
-import UploadFileIcon from "@mui/icons-material/UploadFile"
-import {
- Box,
- Card,
- CardActionArea,
- CardContent,
- IconButton,
- Typography,
- useTheme,
-} from "@mui/material"
-import React, {
- ChangeEventHandler,
- DragEventHandler,
- FC,
- MouseEventHandler,
- useRef,
- useState,
-} from "react"
-
-import { StudyDetail } from "ts/types/optuna"
-import { actionCreator } from "../../action"
-import { ArtifactCardMedia } from "./ArtifactCardMedia"
-import { useDeleteStudyArtifactDialog } from "./DeleteArtifactDialog"
-import { isTableArtifact, useTableArtifactModal } from "./TableArtifactViewer"
-import {
- isThreejsArtifact,
- useThreejsArtifactModal,
-} from "./ThreejsArtifactViewer"
-
-export const StudyArtifactCards: FC<{ study: StudyDetail }> = ({ study }) => {
- const theme = useTheme()
- const [openDeleteArtifactDialog, renderDeleteArtifactDialog] =
- useDeleteStudyArtifactDialog()
- const [openThreejsArtifactModal, renderThreejsArtifactModal] =
- useThreejsArtifactModal()
- const [openTableArtifactModal, renderTableArtifactModal] =
- useTableArtifactModal()
-
- const width = "200px"
- const height = "150px"
- const artifacts = [...study.artifacts].sort((a, b) => {
- if (a.filename < b.filename) {
- return -1
- } else if (a.filename > b.filename) {
- return 1
- } else {
- return 0
- }
- })
-
- return (
- <>
-
- {artifacts.map((artifact) => {
- const urlPath = `/artifacts/${study.id}/${artifact.artifact_id}`
- return (
-
-
-
-
- {artifact.filename}
-
- {isThreejsArtifact(artifact) ? (
- {
- openThreejsArtifactModal(urlPath, artifact)
- }}
- >
-
-
- ) : null}
- {isTableArtifact(artifact) ? (
- {
- openTableArtifactModal(urlPath, artifact)
- }}
- >
-
-
- ) : null}
- {
- openDeleteArtifactDialog(study.id, artifact)
- }}
- >
-
-
-
-
-
-
-
- )
- })}
-
-
- {renderDeleteArtifactDialog()}
- {renderThreejsArtifactModal()}
- {renderTableArtifactModal()}
- >
- )
-}
-
-const StudyArtifactUploader: FC<{
- study: StudyDetail
- width: string
- height: string
-}> = ({ study, width, height }) => {
- const theme = useTheme()
- const [dragOver, setDragOver] = useState(false)
- const action = actionCreator()
-
- const inputRef = useRef(null)
- const handleClick: MouseEventHandler = () => {
- if (!inputRef || !inputRef.current) {
- return
- }
- inputRef.current.click()
- }
-
- const handleOnChange: ChangeEventHandler = (e) => {
- const files = e.target.files
- if (files === null) {
- return
- }
- action.uploadStudyArtifact(study.id, files[0])
- }
-
- const handleDragOver: DragEventHandler = (e) => {
- e.stopPropagation()
- e.preventDefault()
- e.dataTransfer.dropEffect = "copy"
- setDragOver(true)
- }
-
- const handleDragLeave: DragEventHandler = (e) => {
- e.stopPropagation()
- e.preventDefault()
- e.dataTransfer.dropEffect = "copy"
- setDragOver(false)
- }
-
- const handleDrop: DragEventHandler = (e) => {
- e.stopPropagation()
- e.preventDefault()
- const files = e.dataTransfer.files
- setDragOver(false)
- for (let i = 0; i < files.length; i++) {
- action.uploadStudyArtifact(study.id, files[i])
- }
- }
-
- return (
-
-
-
-
-
- Upload a New File
-
- Drag your file here or click to browse.
-
-
-
-
- )
-}
diff --git a/optuna_dashboard/ts/components/StudyHistory.tsx b/optuna_dashboard/ts/components/StudyHistory.tsx
index bcf161a4d..532aad615 100644
--- a/optuna_dashboard/ts/components/StudyHistory.tsx
+++ b/optuna_dashboard/ts/components/StudyHistory.tsx
@@ -20,7 +20,7 @@ import {
useStudySummaryValue,
} from "../state"
import { artifactIsAvailable } from "../state"
-import { StudyArtifactCards } from "./Artifact/StudyArtifactCards"
+import { ArtifactCards } from "./Artifact/ArtifactCards"
import { BestTrialsCard } from "./BestTrialsCard"
import { GraphHistory } from "./GraphHistory"
import { GraphHyperparameterImportance } from "./GraphHyperparameterImportances"
@@ -201,7 +201,9 @@ export const StudyHistory: FC<{ studyId: number }> = ({ studyId }) => {
>
Study Artifacts
-
+
diff --git a/optuna_dashboard/ts/components/TrialList.tsx b/optuna_dashboard/ts/components/TrialList.tsx
index 2c59c4edb..a94c2ac28 100644
--- a/optuna_dashboard/ts/components/TrialList.tsx
+++ b/optuna_dashboard/ts/components/TrialList.tsx
@@ -30,7 +30,7 @@ import { actionCreator } from "../action"
import { useConstants } from "../constantsProvider"
import { artifactIsAvailable } from "../state"
import { useQuery } from "../urlQuery"
-import { TrialArtifactCards } from "./Artifact/TrialArtifactCards"
+import { ArtifactCards } from "./Artifact/ArtifactCards"
import { TrialNote } from "./Note"
import { TrialFormWidgets } from "./TrialFormWidgets"
@@ -305,7 +305,25 @@ export const TrialListDetail: FC<{
value !== null ? renderInfo(key, value) : null
)}
- {artifactEnabled && }
+ {artifactEnabled && (
+ <>
+
+ Artifacts
+
+
+ >
+ )}
)
}