mirror of
https://github.com/wassname/optuna-dashboard.git
synced 2026-09-21 13:10:53 +08:00
Add saving state
This commit is contained in:
@@ -282,20 +282,23 @@ def create_app(storage: BaseStorage) -> Bottle:
|
||||
],
|
||||
}
|
||||
|
||||
@app.post("/api/studies/<study_id:int>/note")
|
||||
@app.put("/api/studies/<study_id:int>/note")
|
||||
@handle_json_api_exception
|
||||
def save_note(study_id: int) -> BottleViewReturn:
|
||||
response.content_type = "application/json"
|
||||
|
||||
system_attrs = storage.get_study_system_attrs(study_id)
|
||||
req_note_ver = request.json.get("version", None)
|
||||
req_note_body = request.json.get("body", None)
|
||||
if req_note_ver is None or req_note_body is None:
|
||||
response.status = 400 # Bad request
|
||||
return {"reason": "Invalid request."}
|
||||
|
||||
system_attrs = storage.get_study_system_attrs(study_id)
|
||||
if not note.version_is_incremented(system_attrs, req_note_ver):
|
||||
response.status = 400 # Bad request
|
||||
return {"reason": "The text you are editing has changed. Please copy your edits and refresh the page."}
|
||||
response.status = 409 # Conflict
|
||||
return {
|
||||
"reason": "The text you are editing has changed. Please copy your edits and refresh the page.",
|
||||
}
|
||||
|
||||
note.save_note(storage, study_id, req_note_ver, req_note_body)
|
||||
response.status = 204 # No content
|
||||
|
||||
@@ -96,8 +96,8 @@ export const actionCreator = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const saveNote = (studyId: number, note: Note) => {
|
||||
saveNoteAPI(studyId, note)
|
||||
const saveNote = (studyId: number, note: Note): Promise<void> => {
|
||||
return saveNoteAPI(studyId, note)
|
||||
.then(() => {
|
||||
const newStudy = Object.assign({}, studyDetails[studyId])
|
||||
newStudy.note = note
|
||||
@@ -113,6 +113,7 @@ export const actionCreator = () => {
|
||||
enqueueSnackbar(`Failed: ${reason}`, {
|
||||
variant: "error",
|
||||
})
|
||||
throw err
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -189,7 +189,7 @@ export const saveNoteAPI = (
|
||||
note: { version: number; body: string }
|
||||
): Promise<void> => {
|
||||
return axiosInstance
|
||||
.post<void>(`/api/studies/${studyId}/note`, note)
|
||||
.put<void>(`/api/studies/${studyId}/note`, note)
|
||||
.then((res) => {
|
||||
return
|
||||
})
|
||||
|
||||
@@ -10,6 +10,7 @@ export const Note: FC<{
|
||||
latestNote: Note
|
||||
}> = ({ studyId, latestNote }) => {
|
||||
const theme = useTheme()
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [disable, setDisable] = useState(true)
|
||||
const [curNote, setCurNote] = useState({ version: 0, body: "" })
|
||||
const textAreaRef = createRef<HTMLTextAreaElement>()
|
||||
@@ -20,12 +21,20 @@ export const Note: FC<{
|
||||
setCurNote(latestNote)
|
||||
}, [])
|
||||
const handleSave = () => {
|
||||
const nextVersion = curNote.version + 1
|
||||
const newNote = {
|
||||
version: curNote.version + 1,
|
||||
body: textAreaRef.current ? textAreaRef.current.value : ""
|
||||
version: nextVersion,
|
||||
body: textAreaRef.current ? textAreaRef.current.value : "",
|
||||
}
|
||||
setCurNote(newNote)
|
||||
action.saveNote(studyId, newNote)
|
||||
setSaving(true)
|
||||
action
|
||||
.saveNote(studyId, newNote)
|
||||
.then(() => {
|
||||
setCurNote(newNote)
|
||||
})
|
||||
.finally(() => {
|
||||
setSaving(false)
|
||||
})
|
||||
}
|
||||
const handleRefresh = () => {
|
||||
if (!textAreaRef.current) {
|
||||
@@ -39,6 +48,7 @@ export const Note: FC<{
|
||||
return (
|
||||
<>
|
||||
<TextField
|
||||
disabled={saving}
|
||||
minRows={10}
|
||||
multiline={true}
|
||||
placeholder="Take a note (The note is saved to study's system_attrs)"
|
||||
@@ -47,11 +57,11 @@ export const Note: FC<{
|
||||
defaultValue={curNote.body}
|
||||
onChange={() => {
|
||||
const cur = textAreaRef.current ? textAreaRef.current.value : ""
|
||||
setDisable(cur === latestNote.body)
|
||||
setDisable(cur === curNote.body)
|
||||
}}
|
||||
/>
|
||||
<Box sx={{ display: "flex", flexDirection: "row", alignItems: "center" }}>
|
||||
{notLatest && (
|
||||
{notLatest && !saving && (
|
||||
<>
|
||||
<Typography
|
||||
sx={{
|
||||
@@ -77,7 +87,7 @@ export const Note: FC<{
|
||||
<Box sx={{ flexGrow: 1 }} />
|
||||
<LoadingButton
|
||||
onClick={handleSave}
|
||||
loading={false}
|
||||
loading={saving}
|
||||
loadingPosition="start"
|
||||
startIcon={<SaveIcon />}
|
||||
variant="contained"
|
||||
|
||||
@@ -385,7 +385,12 @@ export const StudyDetail: FC<{
|
||||
</Card>
|
||||
<Card sx={{ margin: theme.spacing(2) }}>
|
||||
<CardContent>
|
||||
<Typography variant="h6" sx={{fontSize: "1.25rem", fontWeight: 600}}>Note</Typography>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{ fontSize: "1.25rem", fontWeight: 600 }}
|
||||
>
|
||||
Note
|
||||
</Typography>
|
||||
{studyDetail !== null && (
|
||||
<Note studyId={studyIdNumber} latestNote={studyDetail.note} />
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user