|
|
|
@ -55,52 +55,47 @@ function fmtDuration(start?: string, end?: string) {
|
|
|
|
const pad = (n: number) => String(n).padStart(2, "0");
|
|
|
|
const pad = (n: number) => String(n).padStart(2, "0");
|
|
|
|
return `${h}:${pad(m)}:${pad(sec)}`;
|
|
|
|
return `${h}:${pad(m)}:${pad(sec)}`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const displayNo = (i: number) => {
|
|
|
|
// ✅ 모든 Runs 페이징 수집 후 테이블에 주입
|
|
|
|
const start = (data.value.params.pageNum - 1) * data.value.params.pageSize;
|
|
|
|
|
|
|
|
return data.value.totalDataLength - (start + i);
|
|
|
|
|
|
|
|
};
|
|
|
|
async function loadRunsAll() {
|
|
|
|
async function loadRunsAll() {
|
|
|
|
runsLoading.value = true;
|
|
|
|
runsLoading.value = true;
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
const all: any[] = [];
|
|
|
|
const all: any[] = [];
|
|
|
|
const seen = new Set<string>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 첫 페이지부터 page_size 명시 (SDK 케이스 둘 다 지원)
|
|
|
|
// 1페이지
|
|
|
|
let first = await ExecutionsService.search({ pageSize: 79 });
|
|
|
|
let resp = await ExecutionsService.search({
|
|
|
|
|
|
|
|
page_size: 500,
|
|
|
|
|
|
|
|
} as any);
|
|
|
|
|
|
|
|
all.push(...(resp?.data?.runs ?? []));
|
|
|
|
|
|
|
|
|
|
|
|
all.push(...(first?.data?.runs ?? []));
|
|
|
|
// 다음 토큰 추출(스네이크/카멜 모두 대비)
|
|
|
|
let token: string | undefined =
|
|
|
|
let token: string | undefined =
|
|
|
|
first?.data?.next_page_token ?? first?.data?.nextPageToken;
|
|
|
|
resp?.data?.next_page_token ?? resp?.data?.nextPageToken;
|
|
|
|
|
|
|
|
|
|
|
|
// 다음 페이지들
|
|
|
|
// 다음 페이지들
|
|
|
|
let guard = 0;
|
|
|
|
const seen = new Set<string>();
|
|
|
|
while (token && !seen.has(token) && guard < 1000) {
|
|
|
|
while (token && !seen.has(token)) {
|
|
|
|
seen.add(token);
|
|
|
|
seen.add(token);
|
|
|
|
|
|
|
|
|
|
|
|
// snake_case 우선 호출
|
|
|
|
// ✅ 무조건 token을 넣어서 호출 (snake/camel 둘 다 넣기)
|
|
|
|
let page = await ExecutionsService.search({ pageSize: 79 });
|
|
|
|
resp = await ExecutionsService.search({
|
|
|
|
|
|
|
|
page_token: token,
|
|
|
|
// camelCase만 받는 SDK 대비 폴백
|
|
|
|
pageToken: token,
|
|
|
|
if (
|
|
|
|
page_size: 500,
|
|
|
|
(!Array.isArray(page?.data?.runs) || page?.data?.runs.length === 0) &&
|
|
|
|
} as any);
|
|
|
|
(page?.data?.next_page_token === token ||
|
|
|
|
|
|
|
|
page?.data?.next_page_token === undefined)
|
|
|
|
all.push(...(resp?.data?.runs ?? []));
|
|
|
|
) {
|
|
|
|
token = resp?.data?.next_page_token ?? resp?.data?.nextPageToken;
|
|
|
|
page = await ExecutionsService.search({
|
|
|
|
|
|
|
|
pageToken: token,
|
|
|
|
|
|
|
|
pageSize: 500,
|
|
|
|
|
|
|
|
} as any);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
all.push(...(page?.data?.runs ?? []));
|
|
|
|
|
|
|
|
token = page?.data?.next_page_token ?? page?.data?.nextPageToken;
|
|
|
|
|
|
|
|
guard += 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 중복 방지(안전)
|
|
|
|
// 중복 방지
|
|
|
|
const dedup = Array.from(
|
|
|
|
const dedup = Array.from(
|
|
|
|
new Map(all.map((r: any) => [r?.run_id ?? r?.id ?? r?.name, r])).values(),
|
|
|
|
new Map(all.map((r: any) => [r?.run_id ?? r?.id ?? r?.name, r])).values(),
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// 👉 테이블 데이터로 매핑해서 그대로 꽂기
|
|
|
|
// 테이블 행으로 매핑
|
|
|
|
data.value.results = dedup.map((r: any, idx: number) => ({
|
|
|
|
data.value.results = dedup.map((r: any, idx: number) => ({
|
|
|
|
no: idx + 1,
|
|
|
|
no: idx + 1,
|
|
|
|
name: r?.display_name ?? r?.name ?? r?.run_id ?? "(no name)",
|
|
|
|
name: r?.display_name ?? r?.name ?? r?.run_id ?? "(no name)",
|
|
|
|
@ -112,38 +107,27 @@ async function loadRunsAll() {
|
|
|
|
r?.pipeline_version_reference?.pipeline_version_id ??
|
|
|
|
r?.pipeline_version_reference?.pipeline_version_id ??
|
|
|
|
"-",
|
|
|
|
"-",
|
|
|
|
startTime: fmtStart(r?.created_at),
|
|
|
|
startTime: fmtStart(r?.created_at),
|
|
|
|
registryStatus: r?.storage_state ?? "-", // AVAILABLE 등
|
|
|
|
registryStatus: r?.storage_state ?? "-",
|
|
|
|
// 필요 시 원본 활용
|
|
|
|
|
|
|
|
run_id: r?.run_id,
|
|
|
|
run_id: r?.run_id,
|
|
|
|
raw: r,
|
|
|
|
raw: r,
|
|
|
|
}));
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
// 카운트/페이지 길이 갱신
|
|
|
|
experimentOptions.value = Array.from(
|
|
|
|
|
|
|
|
new Set(data.value.results.map((r: any) => String(r.experiment || "-"))),
|
|
|
|
|
|
|
|
).filter((v) => v && v !== "-");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
workflowOptions.value = Array.from(
|
|
|
|
|
|
|
|
new Set(data.value.results.map((r: any) => String(r.workflow || "-"))),
|
|
|
|
|
|
|
|
).filter((v) => v && v !== "-");
|
|
|
|
data.value.totalDataLength = data.value.results.length;
|
|
|
|
data.value.totalDataLength = data.value.results.length;
|
|
|
|
setPaginationLength();
|
|
|
|
setPaginationLength();
|
|
|
|
|
|
|
|
|
|
|
|
console.log(
|
|
|
|
|
|
|
|
"[Runs] total_size(from API):",
|
|
|
|
|
|
|
|
first?.data?.total_size ?? first?.data?.totalSize,
|
|
|
|
|
|
|
|
"fetched:",
|
|
|
|
|
|
|
|
data.value.results.length,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
console.table(
|
|
|
|
|
|
|
|
data.value.results.slice(0, 50).map((r: any) => ({
|
|
|
|
|
|
|
|
no: r.no,
|
|
|
|
|
|
|
|
run_id: r.run_id,
|
|
|
|
|
|
|
|
name: r.name,
|
|
|
|
|
|
|
|
status: r.status,
|
|
|
|
|
|
|
|
startTime: r.startTime,
|
|
|
|
|
|
|
|
duration: r.duration,
|
|
|
|
|
|
|
|
})),
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
} catch (e: any) {
|
|
|
|
} catch (e: any) {
|
|
|
|
console.error("[Runs] 호출 실패:", e?.response?.data ?? e);
|
|
|
|
console.error("[Runs] 호출 실패:", e?.response?.data ?? e);
|
|
|
|
} finally {
|
|
|
|
} finally {
|
|
|
|
runsLoading.value = false;
|
|
|
|
runsLoading.value = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const pagedResults = computed(() => {
|
|
|
|
const pagedResults = computed(() => {
|
|
|
|
const { pageNum, pageSize } = data.value.params;
|
|
|
|
const { pageNum, pageSize } = data.value.params;
|
|
|
|
const start = (pageNum - 1) * pageSize;
|
|
|
|
const start = (pageNum - 1) * pageSize;
|
|
|
|
@ -171,6 +155,8 @@ const searchOptions = [
|
|
|
|
{ searchType: "Registry Status", searchText: "registryStatus" },
|
|
|
|
{ searchType: "Registry Status", searchText: "registryStatus" },
|
|
|
|
];
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const experimentOptions = ref<string[]>([]);
|
|
|
|
|
|
|
|
const workflowOptions = ref<string[]>([]);
|
|
|
|
const execDialogOpen = ref(false);
|
|
|
|
const execDialogOpen = ref(false);
|
|
|
|
const execMode = ref<"create" | "edit" | "clone">("create");
|
|
|
|
const execMode = ref<"create" | "edit" | "clone">("create");
|
|
|
|
const execSelected = ref<any>(null);
|
|
|
|
const execSelected = ref<any>(null);
|
|
|
|
@ -192,6 +178,8 @@ const data = ref({
|
|
|
|
pageSize: 10,
|
|
|
|
pageSize: 10,
|
|
|
|
searchType: "",
|
|
|
|
searchType: "",
|
|
|
|
searchText: "",
|
|
|
|
searchText: "",
|
|
|
|
|
|
|
|
experimentFilter: "",
|
|
|
|
|
|
|
|
workflowFilter: "",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
results: [],
|
|
|
|
results: [],
|
|
|
|
totalDataLength: 0,
|
|
|
|
totalDataLength: 0,
|
|
|
|
@ -205,96 +193,52 @@ const data = ref({
|
|
|
|
userOption: [],
|
|
|
|
userOption: [],
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const getCodeList = () => {
|
|
|
|
const filteredResults = computed(() => {
|
|
|
|
// UserService.search(data.value.params).then((d) => {
|
|
|
|
const { searchType, searchText, experimentFilter, workflowFilter } =
|
|
|
|
// if (d.status === 200) {
|
|
|
|
data.value.params;
|
|
|
|
// data.value.userOption = d.data.userList;
|
|
|
|
|
|
|
|
// }
|
|
|
|
let list = data.value.results;
|
|
|
|
// });
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const getData = () => {
|
|
|
|
// 실드롭다운 필터
|
|
|
|
const params = { ...data.value.params };
|
|
|
|
if (experimentFilter) {
|
|
|
|
if (params.searchType === "" || params.searchText === "") {
|
|
|
|
list = list.filter((r) => String(r.experiment).includes(experimentFilter));
|
|
|
|
delete params.searchType;
|
|
|
|
}
|
|
|
|
delete params.searchText;
|
|
|
|
if (workflowFilter) {
|
|
|
|
|
|
|
|
list = list.filter((r) => String(r.workflow).includes(workflowFilter));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
data.value.results = [
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
no: 11,
|
|
|
|
|
|
|
|
name: "run-batch32-lr0.001",
|
|
|
|
|
|
|
|
status: "Succeeded",
|
|
|
|
|
|
|
|
duration: "0:00:21",
|
|
|
|
|
|
|
|
experiment: "Baseline Model Training",
|
|
|
|
|
|
|
|
workflow: "baseline_train_pipeline",
|
|
|
|
|
|
|
|
startTime: "2025-05-20 10:12",
|
|
|
|
|
|
|
|
registryStatus: "Registered",
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
no: 10,
|
|
|
|
|
|
|
|
name: "run-batch64-lr0.001",
|
|
|
|
|
|
|
|
status: "Failed",
|
|
|
|
|
|
|
|
duration: "0:00:20",
|
|
|
|
|
|
|
|
experiment: "Baseline Model Training",
|
|
|
|
|
|
|
|
workflow: "baseline_train_pipeline",
|
|
|
|
|
|
|
|
startTime: "2025-05-20 09:10",
|
|
|
|
|
|
|
|
registryStatus: "Not Registered",
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
no: 9,
|
|
|
|
|
|
|
|
name: "run-batch32-lr0.0005",
|
|
|
|
|
|
|
|
status: "Succeeded",
|
|
|
|
|
|
|
|
duration: "0:00:21",
|
|
|
|
|
|
|
|
experiment: "Baseline Model Training",
|
|
|
|
|
|
|
|
workflow: "baseline_train_pipeline",
|
|
|
|
|
|
|
|
startTime: "2025-05-19 10:12",
|
|
|
|
|
|
|
|
registryStatus: "Registered",
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
no: 8,
|
|
|
|
|
|
|
|
name: "run-batch64-lr0.0005",
|
|
|
|
|
|
|
|
status: "Running",
|
|
|
|
|
|
|
|
duration: "0:00:20",
|
|
|
|
|
|
|
|
experiment: "Baseline Model Training",
|
|
|
|
|
|
|
|
workflow: "baseline_train_pipeline",
|
|
|
|
|
|
|
|
startTime: "2025-05-18 11:50",
|
|
|
|
|
|
|
|
registryStatus: "Not Registered",
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
no: 7,
|
|
|
|
|
|
|
|
name: "run-augmented-data",
|
|
|
|
|
|
|
|
status: "Succeeded",
|
|
|
|
|
|
|
|
duration: "0:00:20",
|
|
|
|
|
|
|
|
experiment: "Baseline Model Training",
|
|
|
|
|
|
|
|
workflow: "baseline_train_pipeline",
|
|
|
|
|
|
|
|
startTime: "2025-05-17 09:12",
|
|
|
|
|
|
|
|
registryStatus: "Registered",
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
no: 6,
|
|
|
|
|
|
|
|
name: "run-augmented-data",
|
|
|
|
|
|
|
|
status: "Succeeded",
|
|
|
|
|
|
|
|
duration: "0:00:20",
|
|
|
|
|
|
|
|
experiment: "Baseline Model Training",
|
|
|
|
|
|
|
|
workflow: "baseline_train_pipeline",
|
|
|
|
|
|
|
|
startTime: "2025-05-17 09:12",
|
|
|
|
|
|
|
|
registryStatus: "Registered",
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
no: 5,
|
|
|
|
|
|
|
|
name: "run-augmented-data",
|
|
|
|
|
|
|
|
status: "Succeeded",
|
|
|
|
|
|
|
|
duration: "0:00:20",
|
|
|
|
|
|
|
|
experiment: "Baseline Model Training",
|
|
|
|
|
|
|
|
workflow: "baseline_train_pipeline",
|
|
|
|
|
|
|
|
startTime: "2025-05-17 09:12",
|
|
|
|
|
|
|
|
registryStatus: "Registered",
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
data.value.totalDataLength = data.value.results.length;
|
|
|
|
|
|
|
|
setPaginationLength();
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 텍스트 검색
|
|
|
|
|
|
|
|
const q = (searchText || "").trim().toLowerCase();
|
|
|
|
|
|
|
|
if (q) {
|
|
|
|
|
|
|
|
if (!searchType) {
|
|
|
|
|
|
|
|
// All: 여러 필드 OR 매칭
|
|
|
|
|
|
|
|
list = list.filter((r) => {
|
|
|
|
|
|
|
|
const pool = [
|
|
|
|
|
|
|
|
r.name,
|
|
|
|
|
|
|
|
r.status,
|
|
|
|
|
|
|
|
r.duration,
|
|
|
|
|
|
|
|
r.experiment,
|
|
|
|
|
|
|
|
r.workflow,
|
|
|
|
|
|
|
|
r.registryStatus,
|
|
|
|
|
|
|
|
r.startTime,
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
return pool.some((v) =>
|
|
|
|
|
|
|
|
String(v ?? "")
|
|
|
|
|
|
|
|
.toLowerCase()
|
|
|
|
|
|
|
|
.includes(q),
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
list = list.filter((r) =>
|
|
|
|
|
|
|
|
String(r[searchType] ?? "")
|
|
|
|
|
|
|
|
.toLowerCase()
|
|
|
|
|
|
|
|
.includes(q),
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return list;
|
|
|
|
|
|
|
|
});
|
|
|
|
const setPaginationLength = () => {
|
|
|
|
const setPaginationLength = () => {
|
|
|
|
if (data.value.totalDataLength % data.value.params.pageSize === 0) {
|
|
|
|
if (data.value.totalDataLength % data.value.params.pageSize === 0) {
|
|
|
|
data.value.pageLength =
|
|
|
|
data.value.pageLength =
|
|
|
|
@ -318,7 +262,6 @@ const handleClone = () => {
|
|
|
|
|
|
|
|
|
|
|
|
const changePageNum = (page) => {
|
|
|
|
const changePageNum = (page) => {
|
|
|
|
data.value.params.pageNum = page;
|
|
|
|
data.value.params.pageNum = page;
|
|
|
|
getData();
|
|
|
|
|
|
|
|
};
|
|
|
|
};
|
|
|
|
const openCreateExecution = () => {
|
|
|
|
const openCreateExecution = () => {
|
|
|
|
execMode.value = "create";
|
|
|
|
execMode.value = "create";
|
|
|
|
@ -364,7 +307,6 @@ const getSelectedAllData = () => {
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
onMounted(() => {
|
|
|
|
loadRunsAll();
|
|
|
|
loadRunsAll();
|
|
|
|
getCodeList();
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
</script>
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
@ -542,8 +484,8 @@ onMounted(() => {
|
|
|
|
</thead>
|
|
|
|
</thead>
|
|
|
|
<tbody class="text-body-2">
|
|
|
|
<tbody class="text-body-2">
|
|
|
|
<tr
|
|
|
|
<tr
|
|
|
|
v-for="item in data.results"
|
|
|
|
v-for="(item, i) in pagedResults"
|
|
|
|
:key="item.no"
|
|
|
|
:key="item.run_id || item.no || i"
|
|
|
|
class="text-center"
|
|
|
|
class="text-center"
|
|
|
|
>
|
|
|
|
>
|
|
|
|
<td>
|
|
|
|
<td>
|
|
|
|
@ -554,7 +496,8 @@ onMounted(() => {
|
|
|
|
style="min-width: 36px"
|
|
|
|
style="min-width: 36px"
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
</td>
|
|
|
|
</td>
|
|
|
|
<td>{{ item.no }}</td>
|
|
|
|
|
|
|
|
|
|
|
|
<td>{{ displayNo(i) }}</td>
|
|
|
|
<td>{{ item.name }}</td>
|
|
|
|
<td>{{ item.name }}</td>
|
|
|
|
<td>
|
|
|
|
<td>
|
|
|
|
<v-icon v-if="item.status === 'Succeeded'" color="green"
|
|
|
|
<v-icon v-if="item.status === 'Succeeded'" color="green"
|
|
|
|
@ -586,7 +529,7 @@ onMounted(() => {
|
|
|
|
:total-visible="10"
|
|
|
|
:total-visible="10"
|
|
|
|
color="primary"
|
|
|
|
color="primary"
|
|
|
|
rounded="circle"
|
|
|
|
rounded="circle"
|
|
|
|
@update:model-value="getData"
|
|
|
|
@update:model-value="changePageNum"
|
|
|
|
></v-pagination>
|
|
|
|
></v-pagination>
|
|
|
|
</v-card-actions>
|
|
|
|
</v-card-actions>
|
|
|
|
</v-col>
|
|
|
|
</v-col>
|
|
|
|
|