Skip to content

Commit 300178b

Browse files
authored
feat: stop experiment on dashboard (#108)
* fix #107 * fix #36
1 parent 71630bb commit 300178b

File tree

6 files changed

+107
-6
lines changed

6 files changed

+107
-6
lines changed

swanlab/server/api/experiment.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
# from ...utils import create_time
1919
from urllib.parse import quote, unquote # 转码路径参数
2020
from typing import List, Dict
21-
from ...utils import get_a_lock
21+
from ...utils import get_a_lock, create_time
2222
from ...log import swanlog as swl
2323

2424
router = APIRouter()
@@ -329,3 +329,28 @@ async def get_experimet_charts(experiment_id: int):
329329
chart_path: str = os.path.join(swc.root, __find_experiment(experiment_id)["name"], "chart.json")
330330
chart = __get_charts(chart_path)
331331
return SUCCESS_200(chart)
332+
333+
334+
@router.get("/{experiment_id}/stop")
335+
async def get_stop_charts(experiment_id: int):
336+
"""停止实验
337+
338+
Parameters
339+
----------
340+
experiment_id : int
341+
实验唯一ID
342+
"""
343+
config_path: str = os.path.join(swc.root, "project.json")
344+
with open(config_path, mode="r", encoding="utf-8") as f:
345+
config = ujson.load(f)
346+
# 获取需要停止的实验在配置中的索引
347+
index = next((index for index, d in enumerate(config["experiments"]) if d["experiment_id"] == experiment_id), None)
348+
# 修改对应实验的状态
349+
if not config["experiments"][index]["status"] == 0:
350+
# 不在运行中的状态不予修改
351+
return Exception("Experiment status is not running")
352+
config["experiments"][index]["status"] = -1
353+
config["experiments"][index]["update_time"] = create_time()
354+
with get_a_lock(config_path, "w") as f:
355+
ujson.dump(config, f, ensure_ascii=False, indent=4)
356+
return SUCCESS_200({"update_time": create_time()})

vue/src/components/SLStatusLabel.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ const handleClick = () => {
6767

6868
<style lang="scss" scoped>
6969
.sl-status-label {
70-
@apply px-3 py-1 rounded-full text-sm;
70+
@apply px-3 py-1 rounded-full text-sm text-center flex items-center;
7171
}
7272
.stoped {
7373
@apply bg-negative-dimmest text-negative-default;

vue/src/i18n/en-US/experiment.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@
3434
"cpu": "CPU Count: {value}",
3535
"gpu": "GPU Count: {value}",
3636
"type": "GPU Type: {value}"
37+
},
38+
"stop": {
39+
"button": "Confirm",
40+
"modal": {
41+
"title": "Attention!",
42+
"text": "Should the experiment be stopped as it is irreversible and may result in unpredictable consequences?"
43+
}
3744
}
3845
},
3946
"config": {

vue/src/store/modules/experiment.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ export const useExperimentStroe = defineStore('charts', () => {
2626
const setStatus = (status) => {
2727
experiment.value.status = status
2828
}
29+
// 修改更新时间
30+
const setUpateTIme = (time) => {
31+
experiment.value.update_time = time
32+
}
2933

3034
return {
3135
// state
@@ -39,6 +43,7 @@ export const useExperimentStroe = defineStore('charts', () => {
3943
defaultColor,
4044
isRunning,
4145
// action
42-
setStatus
46+
setStatus,
47+
setUpateTIme
4348
}
4449
})

vue/src/views/experiment/pages/index/components/ExperimentHeader.vue

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
<div class="flex pb-4">
2424
<div class="min-w-[150px]">{{ $t(`experiment.index.header.experiment_infos.status`) }}</div>
2525
<SLStatusLabel :name="experiment.name" :id="experiment.id" :status="experiment.status" />
26+
<!-- 停止按钮 -->
27+
<StopButton />
2628
</div>
2729
<div v-for="item in experiment_infos" :key="item.title">
2830
<div class="flex pb-4" v-if="item.value">
@@ -56,6 +58,7 @@ import { formatTime } from '@swanlab-vue/utils/time'
5658
import { t } from '@swanlab-vue/i18n'
5759
import { useExperimentStroe } from '@swanlab-vue/store'
5860
import { ref } from 'vue'
61+
import StopButton from './StopButton.vue'
5962
6063
const experiment = ref(useExperimentStroe().experiment)
6164
@@ -69,7 +72,7 @@ const experiment_infos = computed(() => {
6972
},
7073
{
7174
title: 'last_time',
72-
value: duration()
75+
value: duration.value
7376
},
7477
{
7578
title: 'version',
@@ -122,7 +125,7 @@ const experiment_device = computed(() => {
122125
/**
123126
* 计算实验的持续时间
124127
*/
125-
const duration = () => {
128+
const duration = computed(() => {
126129
const time1 = new Date(experiment.value.create_time)
127130
const currentTime = new Date()
128131
const time2 =
@@ -161,7 +164,7 @@ const duration = () => {
161164
}
162165
163166
return formattedTime.join('')
164-
}
167+
})
165168
</script>
166169
167170
<style lang="scss" scoped>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<template>
2+
<button
3+
class="p-2 rounded-full border-2 transition-all duration-200 ml-2"
4+
:class="showColor ? 'border-red-500' : 'cursor-not-allowed'"
5+
@mouseover="() => (hover = true)"
6+
@mouseout="() => (hover = false)"
7+
@click="() => (showWarning = status === 0 && true)"
8+
>
9+
<div class="w-4 h-4 bg-gray-200 transition-all duration-200" :class="showColor ? 'bg-red-500' : ''"></div>
10+
</button>
11+
<SLModal class="px-10 pt-10 pb-5 overflow-hidden" maxW="500" v-model="showWarning">
12+
<div class="w-full">
13+
<h1 class="text-lg font-semibold text-negative-default">{{ $t('experiment.index.header.stop.modal.title') }}</h1>
14+
<p class="py-4">{{ $t('experiment.index.header.stop.modal.text') }}</p>
15+
<div class="flex justify-end">
16+
<button class="px-2 py-1 border transition-all rounded-lg hover:bg-red-500 hover:text-white" @click="confirm">
17+
{{ $t('experiment.index.header.stop.button') }}
18+
</button>
19+
</div>
20+
</div>
21+
</SLModal>
22+
</template>
23+
24+
<script setup>
25+
/**
26+
* @description: 停止实验按钮
27+
* @file: StopButton.vue
28+
* @since: 2023-12-30 20:30:30
29+
**/
30+
import { useExperimentStroe, useProjectStore } from '@swanlab-vue/store'
31+
import { ref } from 'vue'
32+
import { computed } from 'vue'
33+
import SLModal from '@swanlab-vue/components/SLModal.vue'
34+
import http from '@swanlab-vue/api/http'
35+
36+
// ---------------------------------- 弹窗相关 ----------------------------------
37+
38+
const experiment = useExperimentStroe()
39+
const id = experiment.id
40+
const status = experiment.status
41+
42+
const hover = ref(false) // 是否hover
43+
// 展示hover时可点击样式
44+
const showColor = computed(() => {
45+
return status === 0 && hover.value
46+
})
47+
48+
const showWarning = ref(false) // 弹窗状态
49+
50+
// ---------------------------------- 确认删除 ----------------------------------
51+
52+
const confirm = async () => {
53+
const { data } = await http.get(`/experiment/${id}/stop`)
54+
if (!data) return
55+
experiment.setUpateTIme(data.update_time)
56+
useProjectStore().setExperimentStatus(id, -1)
57+
showWarning.value = false
58+
}
59+
</script>
60+
61+
<style lang="scss" scoped></style>

0 commit comments

Comments
 (0)