fix : 프로젝트 선택 버그 수정 및 Execution 데이터 추가

main
jschoi 9 months ago
parent f9800c23d1
commit ac1f47a72a

@ -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>

@ -122,16 +122,13 @@ const router = createRouter({
routes, routes,
}); });
router.beforeEach((to) => { router.beforeEach((to, from) => {
const authed = !!( const authed = !!(
typeof storage.getToken === "function" && storage.getToken() typeof storage.getToken === "function" && storage.getToken()
); );
const isLogin = to.name === "login";
const isLogin = to.name === "login" || to.path === "/login"; const isSignup = to.name === "signup";
const isSignup = to.name === "signup" || to.path === "/signup"; const isSelect = to.name === "select";
const isSelect = to.name === "select" || to.path === "/select";
const bootDone = sessionStorage.getItem("initialRedirectDone") === "1";
if (!authed) { if (!authed) {
if (!isLogin && !isSignup) { if (!isLogin && !isSignup) {
@ -140,32 +137,26 @@ router.beforeEach((to) => {
return true; return true;
} }
if (!bootDone && !isSelect && !isLogin && !isSignup) { const hasProject = !!localStorage.getItem("projectId"); // ✅ 프로젝트 선택 여부
sessionStorage.setItem("initialRedirectDone", "1"); const bootDone = sessionStorage.getItem("initialRedirectDone") === "1";
return { name: "select", replace: true, query: { redirect: to.fullPath } };
}
if (to.matched.some((r) => r.meta?.requiresAdmin)) {
try {
const raw =
typeof storage?.getAuth === "function"
? storage.getAuth()
: JSON.parse(localStorage.getItem("autoflow-auth") || "null");
const roles = raw?.userInfo?.roles ?? raw?.roles ?? [];
const authCd = raw?.userInfo?.authCd ?? raw?.authCd ?? raw?.auth;
const isAdmin = // 이미 프로젝트 선택됨 → 어떤 화면이든 통과
(Array.isArray(roles) if (hasProject) return true;
? roles.includes("ROLE_ADMIN")
: roles === "ROLE_ADMIN") || authCd === "ADMIN";
if (!isAdmin) { // 아직 프로젝트 미선택
return { name: "home", replace: true }; if (!bootDone) {
} // ✅ 선택 화면에 "들어온 순간"을 부트 완료로 간주 (여기서 한 번만 세팅)
} catch { if (isSelect) {
return { name: "home", replace: true }; sessionStorage.setItem("initialRedirectDone", "1");
return true;
} }
// ✅ select 로 1회만 보냄
return { name: "select", replace: true, query: { redirect: to.fullPath } };
}
// 부트 완료인데 여전히 프로젝트 미선택이면, select만 허용
if (!isSelect) {
return { name: "select", replace: true, query: { redirect: to.fullPath } };
} }
return true; return true;

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, onBeforeUnmount, ref, computed } from "vue"; import { onMounted, onBeforeUnmount, ref, computed } from "vue";
import { useRouter } from "vue-router"; import { useRoute, useRouter } from "vue-router";
import { useAutoflowStore } from "@/stores/autoflowStore"; import { useAutoflowStore } from "@/stores/autoflowStore";
import type { import type {
@ -21,6 +21,7 @@ const DEFAULT_PERMISSIONS: Permission[] = [
/** ===== 라우터 & 스토어 ===== */ /** ===== 라우터 & 스토어 ===== */
const router = useRouter(); const router = useRouter();
const route = useRoute();
const autoflowStore = useAutoflowStore(); const autoflowStore = useAutoflowStore();
/** ===== 상태 ===== */ /** ===== 상태 ===== */
@ -265,11 +266,20 @@ const closeDialog = () => {
const selectProject = (index: number) => { const selectProject = (index: number) => {
const selected = pagedProjects.value[index]; const selected = pagedProjects.value[index];
if (!selected) return;
// 1)
localStorage.setItem("projectId", String(selected.id));
localStorage.setItem("projectName", selected.title);
// ( )
autoflowStore.setProjectId(selected.id); autoflowStore.setProjectId(selected.id);
autoflowStore.setProjectName(selected.title); autoflowStore.setProjectName(selected.title);
router.push("/home");
};
// 2) redirect , /home
const dest = (route.query.redirect as string) || "/home";
router.replace(dest);
};
const grantDefaultPermissions = async ( const grantDefaultPermissions = async (
projectId: number, projectId: number,
usernames: string[], usernames: string[],

Loading…
Cancel
Save