From 0b86bac19b58b81e8180a37dad07871e91433f53 Mon Sep 17 00:00:00 2001 From: Ryota Nakano <71750787+unkosan@users.noreply.github.com> Date: Thu, 27 Mar 2025 13:42:01 +0900 Subject: [PATCH 1/4] feat: fixed order of child job on fetching --- frontend/services/route/train.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/frontend/services/route/train.ts b/frontend/services/route/train.ts index d8f70b79..a1e4b64c 100644 --- a/frontend/services/route/train.ts +++ b/frontend/services/route/train.ts @@ -92,7 +92,7 @@ export const responsePostSearchJobs = z.array( item_epochs_current: z.number().int().min(0), }), ]) - ), + ).transform((x) => x.sort((a, b) => a.item_id - b.item_id)), }) ); @@ -114,6 +114,15 @@ export const responseGetItem = z.object({ ), epochs_finished: z.array(z.number().int().min(0)), minimum_NLLs: z.array(z.union([z.null(), z.number().min(0)])), + }).transform((x) => { + const newIndices = Array.from({ length: x.indices.length }, (_, i) => i) + const reorderedIndices = newIndices.map((_, i) => x.indices.indexOf(i)) + return { + indices: newIndices, + statuses: x.statuses.map((_, i) => x.statuses[reorderedIndices[i]]), + epochs_finished: x.epochs_finished.map((_, i) => x.epochs_finished[reorderedIndices[i]]), + minimum_NLLs: x.minimum_NLLs.map((_, i) => x.minimum_NLLs[reorderedIndices[i]]), + } }), }); From b96be2dd56e0cb64ad2437f5da92bec5e6c87452 Mon Sep 17 00:00:00 2001 From: Ryota Nakano <71750787+unkosan@users.noreply.github.com> Date: Thu, 27 Mar 2025 15:01:05 +0900 Subject: [PATCH 2/4] fix: vae id shown on renaming vae name --- frontend/components/viewer/model-picker/vae-picker.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/components/viewer/model-picker/vae-picker.tsx b/frontend/components/viewer/model-picker/vae-picker.tsx index 97701051..5955b599 100644 --- a/frontend/components/viewer/model-picker/vae-picker.tsx +++ b/frontend/components/viewer/model-picker/vae-picker.tsx @@ -127,7 +127,7 @@ const VAEPicker: React.FC<{ Date: Thu, 27 Mar 2025 15:01:42 +0900 Subject: [PATCH 3/4] feat: custom format for duration on job-cards --- .../hooks/__tests__/use-job-card.test.ts | 52 ++++++++++--------- .../gmm-jobs-list/hooks/use-job-card.ts | 46 +++++++++++----- .../gmm-home/gmm-jobs-list/job-card.tsx | 7 +-- .../vae-jobs-list/child-job-card.tsx | 3 -- .../vae-jobs-list/hooks/use-child-job-card.ts | 30 ++++++++--- 5 files changed, 87 insertions(+), 51 deletions(-) diff --git a/frontend/components/gmm-home/gmm-jobs-list/hooks/__tests__/use-job-card.test.ts b/frontend/components/gmm-home/gmm-jobs-list/hooks/__tests__/use-job-card.test.ts index 7f977d8b..a829b039 100644 --- a/frontend/components/gmm-home/gmm-jobs-list/hooks/__tests__/use-job-card.test.ts +++ b/frontend/components/gmm-home/gmm-jobs-list/hooks/__tests__/use-job-card.test.ts @@ -9,7 +9,7 @@ jest.mock("next/router", () => ({ describe("useJobCard", () => { const mockUuid = "test-uuid"; - const mockDuration = 3600; // 1 hour in seconds + const mockDuration = 3_600_000; // 1 hour in milliseconds const mockRouter = { query: { experiment: "current-uuid", @@ -23,17 +23,13 @@ describe("useJobCard", () => { }); it("should return the current UUID from router query", () => { - const { result } = renderHook(() => - useJobCard({ uuid: mockUuid, duration: mockDuration }) - ); + const { result } = renderHook(() => useJobCard({ uuid: mockUuid })); expect(result.current.currentUUID).toBe("current-uuid"); }); it("should navigate to the job details when handleClick is called", () => { - const { result } = renderHook(() => - useJobCard({ uuid: mockUuid, duration: mockDuration }) - ); + const { result } = renderHook(() => useJobCard({ uuid: mockUuid })); act(() => { result.current.handleClick(); @@ -49,9 +45,7 @@ describe("useJobCard", () => { }); it("should return different styles based on selection state", () => { - const { result } = renderHook(() => - useJobCard({ uuid: mockUuid, duration: mockDuration }) - ); + const { result } = renderHook(() => useJobCard({ uuid: mockUuid })); const selectedStyle = result.current.getCardStyle(true); const unselectedStyle = result.current.getCardStyle(false); @@ -61,22 +55,21 @@ describe("useJobCard", () => { }); it("should format job duration correctly", () => { - const { result } = renderHook(() => - useJobCard({ uuid: mockUuid, duration: mockDuration }) - ); + const { result } = renderHook(() => useJobCard({ uuid: mockUuid })); - const formattedDuration = result.current.formatJobDuration(mockDuration); + const formattedDuration = result.current.formatDurationText( + "progress", + mockDuration + ); // The exact format might vary based on date-fns version and locale, // so we'll just check that it contains the expected parts expect(formattedDuration).toContain("Running for"); - expect(formattedDuration).toContain("hour"); + expect(formattedDuration).toContain("1h"); }); it("should correctly identify progress and suspend statuses", () => { - const { result } = renderHook(() => - useJobCard({ uuid: mockUuid, duration: mockDuration }) - ); + const { result } = renderHook(() => useJobCard({ uuid: mockUuid })); expect(result.current.isProgressOrSuspend("progress")).toBe(true); expect(result.current.isProgressOrSuspend("suspend")).toBe(true); @@ -88,23 +81,32 @@ describe("useJobCard", () => { it("should handle different durations correctly", () => { // Test with 30 minutes const { result: result1 } = renderHook(() => - useJobCard({ uuid: mockUuid, duration: 1800 }) + useJobCard({ uuid: mockUuid }) + ); + const formattedDuration1 = result1.current.formatDurationText( + "progress", + 1800 ); - const formattedDuration1 = result1.current.formatJobDuration(1800); expect(formattedDuration1).toContain("Running for"); // Test with 2 days const { result: result2 } = renderHook(() => - useJobCard({ uuid: mockUuid, duration: 172800 }) + useJobCard({ uuid: mockUuid }) + ); + const formattedDuration2 = result2.current.formatDurationText( + "progress", + 172800 ); - const formattedDuration2 = result2.current.formatJobDuration(172800); expect(formattedDuration2).toContain("Running for"); // Test with 0 seconds const { result: result3 } = renderHook(() => - useJobCard({ uuid: mockUuid, duration: 0 }) + useJobCard({ uuid: mockUuid }) + ); + const formattedDuration3 = result3.current.formatDurationText( + "progress", + 0 ); - const formattedDuration3 = result3.current.formatJobDuration(0); - expect(formattedDuration3).toContain("Running for"); + expect(formattedDuration3).toBe(""); }); }); diff --git a/frontend/components/gmm-home/gmm-jobs-list/hooks/use-job-card.ts b/frontend/components/gmm-home/gmm-jobs-list/hooks/use-job-card.ts index 54c78e2a..f51dddbb 100644 --- a/frontend/components/gmm-home/gmm-jobs-list/hooks/use-job-card.ts +++ b/frontend/components/gmm-home/gmm-jobs-list/hooks/use-job-card.ts @@ -10,24 +10,20 @@ export type JobStatus = export interface UseJobCardProps { uuid: string; - duration: number; } export interface UseJobCardReturn { currentUUID: string | string[] | undefined; handleClick: () => void; getCardStyle: (isSelected: boolean) => React.CSSProperties; - formatJobDuration: (duration: number) => string; + formatDurationText: (status: JobStatus, duration?: number) => string; isProgressOrSuspend: (status: JobStatus) => boolean; } /** * Custom hook for managing JobCard state and interactions */ -export function useJobCard({ - uuid, - duration, -}: UseJobCardProps): UseJobCardReturn { +export function useJobCard({ uuid }: UseJobCardProps): UseJobCardReturn { const router = useRouter(); const currentUUID = router.query.experiment; @@ -60,11 +56,37 @@ export function useJobCard({ /** * Format duration text */ - const formatJobDuration = (duration: number): string => { - return ( - "Running for " + - formatDuration(intervalToDuration({ start: 0, end: duration * 1000 })) - ); + const formatDurationText = (status: JobStatus, duration?: number): string => { + if (status !== "progress" || !duration) { + return ""; + } + + const durationObj = intervalToDuration({ + start: 0, + end: duration, + }); + + // Convert days to hours and add to existing hours + const totalHours = (durationObj.days || 0) * 24 + (durationObj.hours || 0); + + // Create abbreviated format: "xxh xxm xxs" + const parts: string[] = []; + + if (totalHours > 0) { + parts.push(`${totalHours}h`); + } + + if (durationObj.minutes) { + parts.push(`${durationObj.minutes}m`); + } + + if (durationObj.seconds) { + parts.push(`${durationObj.seconds}s`); + } + + const str = parts.join(" "); + + return `Running for ${str}`; }; /** @@ -78,7 +100,7 @@ export function useJobCard({ currentUUID, handleClick, getCardStyle, - formatJobDuration, + formatDurationText, isProgressOrSuspend, }; } diff --git a/frontend/components/gmm-home/gmm-jobs-list/job-card.tsx b/frontend/components/gmm-home/gmm-jobs-list/job-card.tsx index 42a2ea38..c5f81a2c 100644 --- a/frontend/components/gmm-home/gmm-jobs-list/job-card.tsx +++ b/frontend/components/gmm-home/gmm-jobs-list/job-card.tsx @@ -21,11 +21,10 @@ const JobCard: React.FC = (props) => { currentUUID, handleClick, getCardStyle, - formatJobDuration, + formatDurationText, isProgressOrSuspend, } = useJobCard({ uuid, - duration, }); /** @@ -37,7 +36,9 @@ const JobCard: React.FC = (props) => { {name}
{status === "progress" && ( - {formatJobDuration(duration)} + + {formatDurationText(status, duration)} + )} {status === "success" && ( diff --git a/frontend/components/trainer-home/vae-jobs-list/child-job-card.tsx b/frontend/components/trainer-home/vae-jobs-list/child-job-card.tsx index e4b7030d..bb87ebdd 100644 --- a/frontend/components/trainer-home/vae-jobs-list/child-job-card.tsx +++ b/frontend/components/trainer-home/vae-jobs-list/child-job-card.tsx @@ -32,9 +32,6 @@ const ChildJobCard: React.FC = (props) => { : undefined; const { formatDurationText, handleClick, getCardStyle } = useChildJobCard({ - status, - duration, - isSelected, onClick: props.onClick, }); diff --git a/frontend/components/trainer-home/vae-jobs-list/hooks/use-child-job-card.ts b/frontend/components/trainer-home/vae-jobs-list/hooks/use-child-job-card.ts index af769fad..4275b0c7 100644 --- a/frontend/components/trainer-home/vae-jobs-list/hooks/use-child-job-card.ts +++ b/frontend/components/trainer-home/vae-jobs-list/hooks/use-child-job-card.ts @@ -1,10 +1,7 @@ -import { formatDuration, intervalToDuration } from "date-fns"; +import { intervalToDuration } from "date-fns"; import { JobStatus } from "./use-job-card"; export interface UseChildJobCardProps { - status: JobStatus; - duration?: number; - isSelected?: boolean; onClick?: (event: React.MouseEvent) => void; } @@ -18,9 +15,6 @@ export interface UseChildJobCardReturn { * Custom hook for managing ChildJobCard state and interactions */ export function useChildJobCard({ - status, - duration, - isSelected, onClick, }: UseChildJobCardProps): UseChildJobCardReturn { /** @@ -36,7 +30,27 @@ export function useChildJobCard({ end: duration, }); - return `Running for ${formatDuration(durationObj)}`; + // Convert days to hours and add to existing hours + const totalHours = (durationObj.days || 0) * 24 + (durationObj.hours || 0); + + // Create abbreviated format: "xxh xxm xxs" + const parts: string[] = []; + + if (totalHours > 0) { + parts.push(`${totalHours}h`); + } + + if (durationObj.minutes) { + parts.push(`${durationObj.minutes}m`); + } + + if (durationObj.seconds) { + parts.push(`${durationObj.seconds}s`); + } + + const str = parts.join(" "); + + return `Running for ${str}`; }; /** From 13e95c4eaa8d45f905524c55563a3bd310ca67f5 Mon Sep 17 00:00:00 2001 From: Ryota Nakano <71750787+unkosan@users.noreply.github.com> Date: Thu, 27 Mar 2025 15:18:11 +0900 Subject: [PATCH 4/4] feat: removed 'experiment' tag --- frontend/pages/gmm/index.tsx | 2 +- frontend/pages/trainer/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/pages/gmm/index.tsx b/frontend/pages/gmm/index.tsx index 7302c3df..2ee11ad5 100644 --- a/frontend/pages/gmm/index.tsx +++ b/frontend/pages/gmm/index.tsx @@ -113,7 +113,7 @@ const DetailPane: React.FC = () => { return (
-

Experiment: {jobItem.name}

+

{jobItem.name}