Skip to content

Commit

Permalink
refactor(ui-launcher): minor improvements
Browse files Browse the repository at this point in the history
hdinia committed May 21, 2024
1 parent b63a111 commit bd98824
Showing 3 changed files with 62 additions and 48 deletions.
4 changes: 2 additions & 2 deletions antarest/core/config.py
Original file line number Diff line number Diff line change
@@ -391,8 +391,8 @@ def get_time_limit(self, launcher: str) -> int:
launcher_config = config_map.get(launcher)
if launcher_config is None:
raise InvalidConfigurationError(launcher)
# The default time limit is not available for the local launcher
return getattr(launcher_config, "default_time_limit", 3600)
# Default to 172800 (48 hours) for local launcher.
return getattr(launcher_config, "default_time_limit", 172800)


@dataclass(frozen=True)
93 changes: 53 additions & 40 deletions webapp/src/components/App/Studies/LauncherDialog.tsx
Original file line number Diff line number Diff line change
@@ -42,7 +42,8 @@ import SwitchFE from "../../common/fieldEditors/SwitchFE";
import moment from "moment";

const DEFAULT_NB_CPU = 22;
const DEFAULT_TIME_LIMIT = 240 * 3600; // 240 hours in seconds
const MIN_TIME_LIMIT = 1 * 3600; // 1 hour in seconds.
const MAX_TIME_LIMIT = 240 * 3600; // 240 hours in seconds.

interface Props {
open: boolean;
@@ -58,7 +59,7 @@ function LauncherDialog(props: Readonly<Props>) {
const [options, setOptions] = useState<LaunchOptions>({
nb_cpu: DEFAULT_NB_CPU,
auto_unzip: true,
time_limit: undefined,
time_limit: MIN_TIME_LIMIT,
});
const [solverVersion, setSolverVersion] = useState<string>();
const [isLaunching, setIsLaunching] = useState(false);
@@ -68,7 +69,7 @@ function LauncherDialog(props: Readonly<Props>) {
shallowEqual,
);

const res = usePromiseWithSnackbarError(
const launcherCores = usePromiseWithSnackbarError(
() =>
getLauncherCores().then((cores) => {
setOptions((prevOptions) => {
@@ -84,18 +85,22 @@ function LauncherDialog(props: Readonly<Props>) {
},
);

const { data: launcherTimeLimit } = usePromiseWithSnackbarError(
async () => {
return await getLauncherTimeLimit();
},
const launcherTimeLimit = usePromiseWithSnackbarError(
() =>
getLauncherTimeLimit().then((timeLimit) => {
setOptions((prevOptions) => {
return {
...prevOptions,
time_limit: timeLimit,
};
});
return timeLimit;
}),
{
errorMessage: t("study.error.launcherTimeLimit"),
},
);

const minSeconds = 3600;
const maxSeconds = launcherTimeLimit ?? DEFAULT_TIME_LIMIT;

const { data: outputList } = usePromiseWithSnackbarError(
() => Promise.all(studyIds.map((sid) => getStudyOutputs(sid))),
{ errorMessage: t("study.error.listOutputs"), deps: [studyIds] },
@@ -110,7 +115,7 @@ function LauncherDialog(props: Readonly<Props>) {
// Event Handlers
////////////////////////////////////////////////////////////////

const handleLaunchClick = async () => {
const handleLaunchClick = () => {
if (studyIds.length > 0) {
setIsLaunching(true);
Promise.all(
@@ -195,7 +200,7 @@ function LauncherDialog(props: Readonly<Props>) {
*/
const parseHoursToSeconds = (hourString: string): number => {
const seconds = moment.duration(hourString, "hours").asSeconds();
return Math.max(minSeconds, Math.min(seconds, maxSeconds));
return Math.max(MIN_TIME_LIMIT, Math.min(seconds, MAX_TIME_LIMIT));
};

////////////////////////////////////////////////////////////////
@@ -218,7 +223,7 @@ function LauncherDialog(props: Readonly<Props>) {
sx={{ mx: 2 }}
color="primary"
variant="contained"
disabled={isLaunching || !res.isResolved}
disabled={isLaunching || !launcherCores.isResolved}
onClick={handleLaunchClick}
>
{t("global.launch")}
@@ -279,34 +284,42 @@ function LauncherDialog(props: Readonly<Props>) {
width: "50%",
}}
/>
<TextField
id="launcher-option-time-limit"
label={t("study.timeLimit")}
type="number"
variant="outlined"
// Convert from seconds to hours the displayed value
value={
options.time_limit === undefined
? maxSeconds / 3600
: options.time_limit / 3600
}
onChange={(e) =>
handleChange("time_limit", parseHoursToSeconds(e.target.value))
}
InputLabelProps={{
shrink: true,
}}
inputProps={{
min: minSeconds / 3600,
max: maxSeconds / 3600,
step: 1,
}}
sx={{
minWidth: "125px",
}}

<UsePromiseCond
response={launcherTimeLimit}
ifResolved={(timeLimit) => (
<TextField
id="launcher-option-time-limit"
label={t("study.timeLimit")}
type="number"
variant="outlined"
required
value={(options.time_limit ?? timeLimit) / 3600} // Convert seconds to hours for display.
onChange={(e) => {
handleChange(
"time_limit",
parseHoursToSeconds(e.target.value),
);
}}
InputLabelProps={{
shrink: true,
}}
inputProps={{
min: MIN_TIME_LIMIT,
max: MAX_TIME_LIMIT,
step: 1,
}}
sx={{
minWidth: "125px",
}}
/>
)}
ifPending={() => <Skeleton width={125} height={60} />}
ifRejected={() => <Skeleton width={125} height={60} />}
/>

<UsePromiseCond
response={res}
response={launcherCores}
ifResolved={(cores) => (
<TextField
id="nb-cpu"
@@ -477,7 +490,7 @@ function LauncherDialog(props: Readonly<Props>) {
name: o.name,
}))}
disabled={!!options.xpansion_r_version || !options.xpansion}
data={options.xpansion?.output_id ?? ""}
data={options.xpansion?.output_id || ""}
setValue={(data: string) =>
handleObjectChange("xpansion", {
output_id: data,
13 changes: 7 additions & 6 deletions webapp/src/services/api/study.ts
Original file line number Diff line number Diff line change
@@ -206,7 +206,7 @@ export const exportStudy = async (

export const getExportUrl = (sid: string, skipOutputs = false): string =>
`${
getConfig().downloadHostUrl ??
getConfig().downloadHostUrl ||
getConfig().baseUrl + getConfig().restEndpoint
}/v1/studies/${sid}/export?no_output=${skipOutputs}`;

@@ -226,7 +226,7 @@ export const importStudy = async (
if (onProgress) {
options.onUploadProgress = (progressEvent): void => {
const percentCompleted = Math.round(
(progressEvent.loaded * 100) / (progressEvent.total ?? 1),
(progressEvent.loaded * 100) / (progressEvent.total || 1),
);
onProgress(percentCompleted);
};
@@ -253,7 +253,7 @@ export const importFile = async (
if (onProgress) {
options.onUploadProgress = (progressEvent): void => {
const percentCompleted = Math.round(
(progressEvent.loaded * 100) / (progressEvent.total ?? 1),
(progressEvent.loaded * 100) / (progressEvent.total || 1),
);
onProgress(percentCompleted);
};
@@ -305,9 +305,10 @@ export const getLauncherCores = async (): Promise<Record<string, number>> => {
};

/**
* Time limit for SLURM jobs (in seconds).
* Time limit for SLURM jobs.
* If a jobs exceed this time limit, SLURM kills the job and it is considered failed.
* Often used value: 172800 (48 hours)
*
* @returns The time limit in seconds, Often used value: 172800 (48 hours).
*/
export const getLauncherTimeLimit = async (): Promise<number> => {
const res = await client.get("/v1/launcher/time-limit");
@@ -336,7 +337,7 @@ export const mapLaunchJobDTO = (j: LaunchJobDTO): LaunchJob => ({
exitCode: j.exit_code,
});

export const getStudyJobs = async (
export const getStudyJobs = (
studyId?: string,
filterOrphans = true,
latest = false,

0 comments on commit bd98824

Please sign in to comment.