mirror of
https://github.com/wassname/optuna-dashboard.git
synced 2026-09-11 12:30:25 +08:00
Merge pull request #832 from c-bata/remove-recoil-standalone-app
[standalone_app] Use `React.Context` instead of `Recoil`
This commit is contained in:
+1
-1
@@ -5,7 +5,7 @@
|
||||
"description": "Dashboard for Optuna",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"fmt": "biome format --write .",
|
||||
"fmt": "biome format --write . && biome check standalone_app --apply",
|
||||
"lint": "npm run lint:eslint && npm run lint:biome",
|
||||
"lint:eslint": "eslint . --ext .ts,.tsx --max-warnings 0",
|
||||
"lint:biome": "biome format . && biome ci standalone_app",
|
||||
|
||||
Generated
+25
-63
@@ -20,8 +20,7 @@
|
||||
"plotly.js-dist-min": "^2.30.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.22.2",
|
||||
"recoil": "^0.7.7"
|
||||
"react-router-dom": "^6.22.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/plotly.js": "^2.29.2",
|
||||
@@ -1325,9 +1324,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.2.tgz",
|
||||
"integrity": "sha512-+Rnav+CaoTE5QJc4Jcwh5toUpnVLKYbpU6Ys0zqbakqbaLQHeglLVHPfxOiQqdNmUy5C2lXz5dwC6tQNX2JW2Q==",
|
||||
"version": "1.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz",
|
||||
"integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
@@ -2577,11 +2576,6 @@
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/hamt_plus": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz",
|
||||
"integrity": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA=="
|
||||
},
|
||||
"node_modules/has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
@@ -3165,11 +3159,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "6.22.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.2.tgz",
|
||||
"integrity": "sha512-YD3Dzprzpcq+tBMHBS822tCjnWD3iIZbTeSXMY9LPSG541EfoBGyZ3bS25KEnaZjLcmQpw2AVLkFyfgXY8uvcw==",
|
||||
"version": "6.22.3",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz",
|
||||
"integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.15.2"
|
||||
"@remix-run/router": "1.15.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -3179,12 +3173,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router-dom": {
|
||||
"version": "6.22.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.2.tgz",
|
||||
"integrity": "sha512-WgqxD2qySEIBPZ3w0sHH+PUAiamDeszls9tzqMPBDA1YYVucTBXLU7+gtRfcSnhe92A3glPnvSxK2dhNoAVOIQ==",
|
||||
"version": "6.22.3",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz",
|
||||
"integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.15.2",
|
||||
"react-router": "6.22.2"
|
||||
"@remix-run/router": "1.15.3",
|
||||
"react-router": "6.22.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -3221,25 +3215,6 @@
|
||||
"node": ">= 10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/recoil": {
|
||||
"version": "0.7.7",
|
||||
"resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.7.tgz",
|
||||
"integrity": "sha512-8Og5KPQW9LwC577Vc7Ug2P0vQshkv1y3zG3tSSkWMqkWSwHmE+by06L8JtnGocjW6gcCvfwB3YtrJG6/tWivNQ==",
|
||||
"dependencies": {
|
||||
"hamt_plus": "1.0.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.13.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"react-native": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/regenerator-runtime": {
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||
@@ -4638,9 +4613,9 @@
|
||||
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
|
||||
},
|
||||
"@remix-run/router": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.2.tgz",
|
||||
"integrity": "sha512-+Rnav+CaoTE5QJc4Jcwh5toUpnVLKYbpU6Ys0zqbakqbaLQHeglLVHPfxOiQqdNmUy5C2lXz5dwC6tQNX2JW2Q=="
|
||||
"version": "1.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz",
|
||||
"integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w=="
|
||||
},
|
||||
"@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.12.1",
|
||||
@@ -5586,11 +5561,6 @@
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||
"dev": true
|
||||
},
|
||||
"hamt_plus": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz",
|
||||
"integrity": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA=="
|
||||
},
|
||||
"has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
@@ -6012,20 +5982,20 @@
|
||||
"dev": true
|
||||
},
|
||||
"react-router": {
|
||||
"version": "6.22.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.2.tgz",
|
||||
"integrity": "sha512-YD3Dzprzpcq+tBMHBS822tCjnWD3iIZbTeSXMY9LPSG541EfoBGyZ3bS25KEnaZjLcmQpw2AVLkFyfgXY8uvcw==",
|
||||
"version": "6.22.3",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz",
|
||||
"integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==",
|
||||
"requires": {
|
||||
"@remix-run/router": "1.15.2"
|
||||
"@remix-run/router": "1.15.3"
|
||||
}
|
||||
},
|
||||
"react-router-dom": {
|
||||
"version": "6.22.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.2.tgz",
|
||||
"integrity": "sha512-WgqxD2qySEIBPZ3w0sHH+PUAiamDeszls9tzqMPBDA1YYVucTBXLU7+gtRfcSnhe92A3glPnvSxK2dhNoAVOIQ==",
|
||||
"version": "6.22.3",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz",
|
||||
"integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==",
|
||||
"requires": {
|
||||
"@remix-run/router": "1.15.2",
|
||||
"react-router": "6.22.2"
|
||||
"@remix-run/router": "1.15.3",
|
||||
"react-router": "6.22.3"
|
||||
}
|
||||
},
|
||||
"react-transition-group": {
|
||||
@@ -6048,14 +6018,6 @@
|
||||
"resolve": "^1.20.0"
|
||||
}
|
||||
},
|
||||
"recoil": {
|
||||
"version": "0.7.7",
|
||||
"resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.7.tgz",
|
||||
"integrity": "sha512-8Og5KPQW9LwC577Vc7Ug2P0vQshkv1y3zG3tSSkWMqkWSwHmE+by06L8JtnGocjW6gcCvfwB3YtrJG6/tWivNQ==",
|
||||
"requires": {
|
||||
"hamt_plus": "1.0.2"
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
"plotly.js-dist-min": "^2.30.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.22.2",
|
||||
"recoil": "^0.7.7"
|
||||
"react-router-dom": "^6.22.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React from "react"
|
||||
import ReactDOM from "react-dom/client"
|
||||
import { RecoilRoot } from "recoil"
|
||||
import { App } from "./components/App"
|
||||
import { StorageProvider } from "./components/StorageProvider"
|
||||
import "./index.css"
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
<RecoilRoot>
|
||||
<StorageProvider>
|
||||
<App />
|
||||
</RecoilRoot>
|
||||
</StorageProvider>
|
||||
</React.StrictMode>
|
||||
)
|
||||
|
||||
@@ -13,17 +13,15 @@ import React, {
|
||||
MouseEventHandler,
|
||||
useRef,
|
||||
useState,
|
||||
useContext,
|
||||
} from "react"
|
||||
import { useSetRecoilState } from "recoil"
|
||||
import { loadJournalStorage } from "../journalStorage"
|
||||
import { loadSQLite3Storage } from "../sqlite3"
|
||||
import { studiesState } from "../state"
|
||||
import { StorageContext, getStorage } from "./StorageProvider"
|
||||
|
||||
export const StorageLoader: FC = () => {
|
||||
const theme = useTheme()
|
||||
const [dragOver, setDragOver] = useState<boolean>(false)
|
||||
const { setStorage } = useContext(StorageContext)
|
||||
|
||||
const setStudies = useSetRecoilState<Study[]>(studiesState)
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
const loadStorageFromFile = (file: File): void => {
|
||||
@@ -31,13 +29,8 @@ export const StorageLoader: FC = () => {
|
||||
r.addEventListener("load", () => {
|
||||
const arrayBuffer = r.result as ArrayBuffer | null
|
||||
if (arrayBuffer !== null) {
|
||||
const header = new Uint8Array(arrayBuffer, 0, 16)
|
||||
const headerString = new TextDecoder().decode(header)
|
||||
if (headerString === "SQLite format 3\u0000") {
|
||||
loadSQLite3Storage(arrayBuffer, setStudies)
|
||||
} else {
|
||||
loadJournalStorage(arrayBuffer, setStudies)
|
||||
}
|
||||
const s = getStorage(arrayBuffer)
|
||||
setStorage(s)
|
||||
}
|
||||
})
|
||||
r.readAsArrayBuffer(file)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import React, { FC, createContext, useState } from "react"
|
||||
import { JournalFileStorage } from "../journalStorage"
|
||||
import { SQLite3Storage } from "../sqlite3"
|
||||
|
||||
export const StorageContext = createContext<{
|
||||
storage: OptunaStorage | null
|
||||
setStorage: (storage: OptunaStorage) => void
|
||||
}>({
|
||||
storage: null,
|
||||
setStorage: () => {},
|
||||
})
|
||||
|
||||
export const getStorage = (arrayBuffer: ArrayBuffer): OptunaStorage => {
|
||||
const header = new Uint8Array(arrayBuffer, 0, 16)
|
||||
const headerString = new TextDecoder().decode(header)
|
||||
if (headerString === "SQLite format 3\u0000") {
|
||||
return new SQLite3Storage(arrayBuffer)
|
||||
}
|
||||
return new JournalFileStorage(arrayBuffer)
|
||||
}
|
||||
|
||||
export const StorageProvider: FC<{
|
||||
children: React.ReactNode
|
||||
}> = ({ children }) => {
|
||||
const [storage, setStorage] = useState<OptunaStorage | null>(null)
|
||||
return (
|
||||
<StorageContext.Provider value={{ storage, setStorage }}>
|
||||
{children}
|
||||
</StorageContext.Provider>
|
||||
)
|
||||
}
|
||||
@@ -13,30 +13,36 @@ import {
|
||||
useTheme,
|
||||
} from "@mui/material"
|
||||
import Grid2 from "@mui/material/Unstable_Grid2"
|
||||
import React, { FC } from "react"
|
||||
import React, { FC, useContext, useState, useEffect } from "react"
|
||||
import { Link, useParams } from "react-router-dom"
|
||||
import { useRecoilValue } from "recoil"
|
||||
import { studiesState } from "../state"
|
||||
import { PlotHistory } from "./PlotHistory"
|
||||
import { PlotImportance } from "./PlotImportance"
|
||||
import { PlotIntermediateValues } from "./PlotIntermediateValues"
|
||||
import { StorageContext } from "./StorageProvider"
|
||||
import { TrialTable } from "./TrialTable"
|
||||
|
||||
const useStudyValue = (idx: number): Study | null => {
|
||||
const studies = useRecoilValue<Study[]>(studiesState)
|
||||
return studies[idx] || null
|
||||
}
|
||||
|
||||
export const StudyDetail: FC<{
|
||||
toggleColorMode: () => void
|
||||
}> = ({ toggleColorMode }) => {
|
||||
const theme = useTheme()
|
||||
const { idx } = useParams<{ idx: string }>()
|
||||
const idxNumber = parseInt(idx || "", 10)
|
||||
const study = useStudyValue(idxNumber)
|
||||
|
||||
const { storage } = useContext(StorageContext)
|
||||
const [study, setStudy] = useState<Study | null>(null)
|
||||
useEffect(() => {
|
||||
const fetchStudy = async () => {
|
||||
if (storage === null) {
|
||||
return
|
||||
}
|
||||
const study = await storage.getStudy(idxNumber)
|
||||
setStudy(study)
|
||||
}
|
||||
fetchStudy()
|
||||
}, [storage, idxNumber])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
<AppBar position="static">
|
||||
<Container
|
||||
sx={{
|
||||
@@ -133,6 +139,6 @@ export const StudyDetail: FC<{
|
||||
</Card>
|
||||
</>
|
||||
</Container>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -19,23 +19,40 @@ import {
|
||||
useTheme,
|
||||
} from "@mui/material"
|
||||
import { styled } from "@mui/system"
|
||||
import React, { FC, useState, useMemo, useDeferredValue } from "react"
|
||||
import React, {
|
||||
FC,
|
||||
useEffect,
|
||||
useContext,
|
||||
useState,
|
||||
useMemo,
|
||||
useDeferredValue,
|
||||
} from "react"
|
||||
import { Link } from "react-router-dom"
|
||||
import { useRecoilValue } from "recoil"
|
||||
import { studiesState } from "../state"
|
||||
import { StorageLoader } from "./StorageLoader"
|
||||
import { StorageContext } from "./StorageProvider"
|
||||
|
||||
export const StudyList: FC<{
|
||||
toggleColorMode: () => void
|
||||
}> = ({ toggleColorMode }) => {
|
||||
const theme = useTheme()
|
||||
const studies = useRecoilValue<Study[]>(studiesState)
|
||||
const { storage } = useContext(StorageContext)
|
||||
const [studies, setStudies] = useState<StudySummary[]>([])
|
||||
|
||||
const [_studyFilterText, setStudyFilterText] = useState<string>("")
|
||||
const [sortBy, setSortBy] = useState<"id-asc" | "id-desc">("id-asc")
|
||||
const studyFilterText = useDeferredValue(_studyFilterText)
|
||||
useEffect(() => {
|
||||
const fetchStudies = async () => {
|
||||
if (storage === null) {
|
||||
return
|
||||
}
|
||||
const studies = await storage.getStudies()
|
||||
setStudies(studies)
|
||||
}
|
||||
fetchStudies()
|
||||
}, [storage])
|
||||
const filteredStudies = useMemo(() => {
|
||||
const studyFilter = (row: Study): boolean => {
|
||||
const studyFilter = (row: StudySummary): boolean => {
|
||||
const keywords = studyFilterText.split(" ")
|
||||
return !keywords.every((k) => {
|
||||
if (k === "") {
|
||||
@@ -44,7 +61,7 @@ export const StudyList: FC<{
|
||||
return row.study_name.indexOf(k) >= 0
|
||||
})
|
||||
}
|
||||
let filteredStudies: Study[] = studies.filter((s) => !studyFilter(s))
|
||||
let filteredStudies: StudySummary[] = studies.filter((s) => !studyFilter(s))
|
||||
if (sortBy === "id-desc") {
|
||||
filteredStudies = filteredStudies.reverse()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { SetterOrUpdater } from "recoil"
|
||||
|
||||
// JournalStorage
|
||||
enum JournalOperation {
|
||||
CREATE_STUDY = 0,
|
||||
@@ -329,10 +327,20 @@ class JournalStorage {
|
||||
}
|
||||
}
|
||||
|
||||
export const loadJournalStorage = (
|
||||
arrayBuffer: ArrayBuffer,
|
||||
setter: SetterOrUpdater<Study[]>
|
||||
): void => {
|
||||
export class JournalFileStorage implements OptunaStorage {
|
||||
studies: Study[]
|
||||
constructor(arrayBuffer: ArrayBuffer) {
|
||||
this.studies = loadJournalStorage(arrayBuffer)
|
||||
}
|
||||
getStudies = async (): Promise<StudySummary[]> => {
|
||||
return this.studies
|
||||
}
|
||||
getStudy = async (idx: number): Promise<Study | null> => {
|
||||
return this.studies[idx] || null
|
||||
}
|
||||
}
|
||||
|
||||
export const loadJournalStorage = (arrayBuffer: ArrayBuffer): Study[] => {
|
||||
const decoder = new TextDecoder("utf-8")
|
||||
const logs = decoder.decode(arrayBuffer).split("\n")
|
||||
|
||||
@@ -383,6 +391,5 @@ export const loadJournalStorage = (
|
||||
}
|
||||
}
|
||||
|
||||
const studies = journalStorage.getStudies()
|
||||
setter((prev) => [...prev, ...studies])
|
||||
return journalStorage.getStudies()
|
||||
}
|
||||
|
||||
+109
-88
@@ -1,6 +1,5 @@
|
||||
// @ts-ignore
|
||||
import sqlite3InitModule from "@sqlite.org/sqlite-wasm"
|
||||
import { SetterOrUpdater } from "recoil"
|
||||
|
||||
type SQLite3DB = {
|
||||
exec(options: {
|
||||
@@ -10,44 +9,57 @@ type SQLite3DB = {
|
||||
}): void
|
||||
}
|
||||
|
||||
export const loadSQLite3Storage = (
|
||||
arrayBuffer: ArrayBuffer,
|
||||
setter: SetterOrUpdater<Study[]>
|
||||
): void => {
|
||||
sqlite3InitModule({
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
print: (...args: any): void => {
|
||||
console.log(args)
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
printErr: (...args: any): void => {
|
||||
console.log(args)
|
||||
},
|
||||
// @ts-ignore
|
||||
}).then((sqlite3) => {
|
||||
const p = sqlite3.wasm.allocFromTypedArray(arrayBuffer)
|
||||
const db = new sqlite3.oo1.DB()
|
||||
const rc = sqlite3.capi.sqlite3_deserialize(
|
||||
export class SQLite3Storage implements OptunaStorage {
|
||||
db: Promise<SQLite3DB>
|
||||
summaries_cache: StudySummary[] | null
|
||||
constructor(arrayBuffer: ArrayBuffer) {
|
||||
this.db = this.initDB(arrayBuffer)
|
||||
this.summaries_cache = null
|
||||
}
|
||||
|
||||
async initDB(arrayBuffer: ArrayBuffer): Promise<SQLite3DB> {
|
||||
return sqlite3InitModule({
|
||||
print: console.log,
|
||||
printErr: console.log,
|
||||
// @ts-ignore
|
||||
db.pointer,
|
||||
"main",
|
||||
p,
|
||||
arrayBuffer.byteLength,
|
||||
arrayBuffer.byteLength,
|
||||
sqlite3.capi.SQLITE_DESERIALIZE_FREEONCLOSE
|
||||
)
|
||||
db.checkRc(rc)
|
||||
try {
|
||||
const schemaVersion = getSchemaVersion(db)
|
||||
if (!isSupportedSchema(schemaVersion)) {
|
||||
return
|
||||
}
|
||||
const studies = getStudies(db, schemaVersion)
|
||||
setter((prev) => [...prev, ...studies])
|
||||
} finally {
|
||||
db.close()
|
||||
}).then((sqlite3) => {
|
||||
const p = sqlite3.wasm.allocFromTypedArray(arrayBuffer)
|
||||
const db = new sqlite3.oo1.DB()
|
||||
const rc = sqlite3.capi.sqlite3_deserialize(
|
||||
// @ts-ignore
|
||||
db.pointer,
|
||||
"main",
|
||||
p,
|
||||
arrayBuffer.byteLength,
|
||||
arrayBuffer.byteLength,
|
||||
sqlite3.capi.SQLITE_DESERIALIZE_FREEONCLOSE
|
||||
)
|
||||
db.checkRc(rc)
|
||||
return db
|
||||
})
|
||||
}
|
||||
|
||||
getStudies = async (): Promise<StudySummary[]> => {
|
||||
const db = await this.db
|
||||
this.summaries_cache = getStudySummaries(db)
|
||||
return this.summaries_cache
|
||||
}
|
||||
|
||||
getStudy = async (idx: number): Promise<Study | null> => {
|
||||
const db = await this.db
|
||||
const schemaVersion = getSchemaVersion(db)
|
||||
if (!isSupportedSchema(schemaVersion)) {
|
||||
return null
|
||||
}
|
||||
})
|
||||
if (this.summaries_cache === null) {
|
||||
this.summaries_cache = getStudySummaries(db)
|
||||
}
|
||||
const summary = this.summaries_cache[idx]
|
||||
if (summary === undefined) {
|
||||
return null
|
||||
}
|
||||
return getStudy(db, schemaVersion, summary)
|
||||
}
|
||||
}
|
||||
|
||||
const getSchemaVersion = (db: SQLite3DB): string => {
|
||||
@@ -84,8 +96,8 @@ const isGreaterSchemaVersion = (
|
||||
return left > right
|
||||
}
|
||||
|
||||
const getStudies = (db: SQLite3DB, schemaVersion: string): Study[] => {
|
||||
const studies: Study[] = []
|
||||
const getStudySummaries = (db: SQLite3DB): StudySummary[] => {
|
||||
const summaries: StudySummary[] = []
|
||||
db.exec({
|
||||
sql:
|
||||
"SELECT s.study_id, s.study_name, sd.direction, sd.objective" +
|
||||
@@ -99,64 +111,73 @@ const getStudies = (db: SQLite3DB, schemaVersion: string): Study[] => {
|
||||
vals[2] === "MINIMIZE" ? "minimize" : "maximize"
|
||||
const objective = vals[3]
|
||||
|
||||
const trials = getTrials(db, studyId, schemaVersion)
|
||||
const union_search_space: SearchSpaceItem[] = []
|
||||
const union_user_attrs: AttributeSpec[] = []
|
||||
let intersection_search_space: Set<SearchSpaceItem> = new Set()
|
||||
|
||||
for (const trial of trials) {
|
||||
const userAttrs = getTrialUserAttributes(db, trial.trial_id)
|
||||
for (const attr of userAttrs) {
|
||||
if (union_user_attrs.findIndex((s) => s.key === attr.key) === -1) {
|
||||
union_user_attrs.push({ key: attr.key, sortable: false })
|
||||
}
|
||||
}
|
||||
|
||||
const params = getTrialParams(db, trial.trial_id)
|
||||
const param_names = new Set<string>()
|
||||
for (const param of params) {
|
||||
param_names.add(param.name)
|
||||
if (
|
||||
union_search_space.findIndex((s) => s.name === param.name) === -1
|
||||
) {
|
||||
union_search_space.push({ name: param.name })
|
||||
}
|
||||
}
|
||||
if (intersection_search_space.size === 0) {
|
||||
// biome-ignore lint/complexity/noForEach: <explanation>
|
||||
param_names.forEach((s) => {
|
||||
intersection_search_space.add({
|
||||
name: s,
|
||||
})
|
||||
})
|
||||
} else {
|
||||
intersection_search_space = new Set(
|
||||
Array.from(intersection_search_space).filter((s) =>
|
||||
param_names.has(s.name)
|
||||
)
|
||||
)
|
||||
}
|
||||
trial.params = params
|
||||
trial.user_attrs = userAttrs
|
||||
}
|
||||
|
||||
if (objective === 0) {
|
||||
studies.push({
|
||||
summaries.push({
|
||||
study_id: studyId,
|
||||
study_name: studyName,
|
||||
directions: [direction],
|
||||
union_search_space: union_search_space,
|
||||
intersection_search_space: Array.from(intersection_search_space),
|
||||
union_user_attrs: union_user_attrs,
|
||||
trials: trials,
|
||||
})
|
||||
return
|
||||
}
|
||||
const index = studies.findIndex((s) => s.study_id === studyId)
|
||||
studies[index].directions.push(direction)
|
||||
const index = summaries.findIndex((s) => s.study_id === studyId)
|
||||
summaries[index].directions.push(direction)
|
||||
},
|
||||
})
|
||||
return studies
|
||||
return summaries
|
||||
}
|
||||
|
||||
const getStudy = (
|
||||
db: SQLite3DB,
|
||||
schemaVersion: string,
|
||||
summary: StudySummary
|
||||
): Study => {
|
||||
const study: Study = {
|
||||
study_id: summary.study_id,
|
||||
study_name: summary.study_name,
|
||||
directions: summary.directions,
|
||||
union_search_space: [],
|
||||
intersection_search_space: [],
|
||||
union_user_attrs: [],
|
||||
trials: [],
|
||||
}
|
||||
|
||||
let intersection_search_space: Set<SearchSpaceItem> = new Set()
|
||||
study.trials = getTrials(db, summary.study_id, schemaVersion)
|
||||
for (const trial of study.trials) {
|
||||
const userAttrs = getTrialUserAttributes(db, trial.trial_id)
|
||||
for (const attr of userAttrs) {
|
||||
if (study.union_user_attrs.findIndex((s) => s.key === attr.key) === -1) {
|
||||
study.union_user_attrs.push({ key: attr.key, sortable: false })
|
||||
}
|
||||
}
|
||||
|
||||
const params = getTrialParams(db, trial.trial_id)
|
||||
const param_names = new Set<string>()
|
||||
for (const param of params) {
|
||||
param_names.add(param.name)
|
||||
if (
|
||||
study.union_search_space.findIndex((s) => s.name === param.name) === -1
|
||||
) {
|
||||
study.union_search_space.push({ name: param.name })
|
||||
}
|
||||
}
|
||||
if (intersection_search_space.size === 0) {
|
||||
// biome-ignore lint/complexity/noForEach: <explanation>
|
||||
param_names.forEach((s) => {
|
||||
intersection_search_space.add({ name: s })
|
||||
})
|
||||
} else {
|
||||
intersection_search_space = new Set(
|
||||
Array.from(intersection_search_space).filter((s) =>
|
||||
param_names.has(s.name)
|
||||
)
|
||||
)
|
||||
}
|
||||
trial.params = params
|
||||
trial.user_attrs = userAttrs
|
||||
}
|
||||
study.intersection_search_space = Array.from(intersection_search_space)
|
||||
return study
|
||||
}
|
||||
|
||||
const getTrials = (
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import { atom } from "recoil"
|
||||
|
||||
export const studiesState = atom<Study[]>({
|
||||
key: "studies",
|
||||
default: [],
|
||||
})
|
||||
Vendored
+11
@@ -4,6 +4,11 @@ type TrialState = "Running" | "Complete" | "Pruned" | "Fail" | "Waiting"
|
||||
type TrialStateFinished = "Complete" | "Fail" | "Pruned"
|
||||
type StudyDirection = "maximize" | "minimize" | "not_set"
|
||||
|
||||
type OptunaStorage = {
|
||||
getStudies: () => Promise<StudySummary[]>
|
||||
getStudy: (idx: number) => Promise<Study | null>
|
||||
}
|
||||
|
||||
type FloatDistribution = {
|
||||
type: "FloatDistribution"
|
||||
low: number
|
||||
@@ -46,6 +51,12 @@ type AttributeSpec = {
|
||||
sortable: boolean
|
||||
}
|
||||
|
||||
type StudySummary = {
|
||||
study_id: number
|
||||
study_name: string
|
||||
directions: StudyDirection[]
|
||||
}
|
||||
|
||||
type Study = {
|
||||
study_id: number
|
||||
study_name: string
|
||||
|
||||
@@ -1,22 +1,15 @@
|
||||
import React, { FC, useEffect } from "react"
|
||||
import React, { FC, useEffect, useContext } from "react"
|
||||
import ReactDOM from "react-dom/client"
|
||||
import { RecoilRoot, SetterOrUpdater, useSetRecoilState } from "recoil"
|
||||
import { App } from "./components/App"
|
||||
import {
|
||||
StorageContext,
|
||||
StorageProvider,
|
||||
getStorage,
|
||||
} from "./components/StorageProvider"
|
||||
import "./index.css"
|
||||
import { loadJournalStorage } from "./journalStorage"
|
||||
import { loadSQLite3Storage } from "./sqlite3"
|
||||
import { studiesState } from "./state"
|
||||
|
||||
export const AppWrapper: FC = () => {
|
||||
const setStudies = useSetRecoilState<Study[]>(studiesState)
|
||||
|
||||
// TODO(c-bata): Fix the type annotation
|
||||
const onceSetStudies: SetterOrUpdater<Study[]> = (
|
||||
setter: (currVal: Study[]) => Study[]
|
||||
): void => {
|
||||
const studies = setter([])
|
||||
setStudies(studies)
|
||||
}
|
||||
const { setStorage } = useContext(StorageContext)
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
|
||||
useEffect(() => {
|
||||
@@ -27,8 +20,6 @@ export const AppWrapper: FC = () => {
|
||||
let len: number
|
||||
let bytes: Uint8Array
|
||||
let arrayBuffer: ArrayBuffer
|
||||
let header: Uint8Array
|
||||
let headerString: string
|
||||
|
||||
switch (message.type) {
|
||||
case "optunaStorage":
|
||||
@@ -40,13 +31,7 @@ export const AppWrapper: FC = () => {
|
||||
bytes[i] = binaryString.charCodeAt(i)
|
||||
}
|
||||
arrayBuffer = bytes.buffer
|
||||
header = new Uint8Array(arrayBuffer, 0, 16)
|
||||
headerString = new TextDecoder().decode(header)
|
||||
if (headerString === "SQLite format 3\u0000") {
|
||||
loadSQLite3Storage(arrayBuffer, onceSetStudies)
|
||||
} else {
|
||||
loadJournalStorage(arrayBuffer, onceSetStudies)
|
||||
}
|
||||
setStorage(getStorage(arrayBuffer))
|
||||
break
|
||||
}
|
||||
})
|
||||
@@ -56,8 +41,8 @@ export const AppWrapper: FC = () => {
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
<RecoilRoot>
|
||||
<StorageProvider>
|
||||
<AppWrapper />
|
||||
</RecoilRoot>
|
||||
</StorageProvider>
|
||||
</React.StrictMode>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user