Skip to content

Commit

Permalink
chore: added validation to training settings input
Browse files Browse the repository at this point in the history
  • Loading branch information
jeafreezy committed Nov 16, 2024
1 parent 0428f40 commit fc46437
Show file tree
Hide file tree
Showing 11 changed files with 199 additions and 87 deletions.
59 changes: 55 additions & 4 deletions frontend/src/app/providers/models-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useSessionStorage } from "@/hooks/use-storage";
import {
APPLICATION_ROUTES,
HOT_FAIR_MODEL_CREATION_LOCAL_STORAGE_KEY,
MODEL_CREATION_CONTENT,
showErrorToast,
showSuccessToast,
TMS_URL_REGEX_PATTERN,
Expand Down Expand Up @@ -51,6 +52,9 @@ export enum MODEL_CREATION_FORM_NAME {
OAM_TIME_NAME = "oamTileName",
OAM_BOUNDS = "oamBounds",
TRAINING_AREAS = "trainingAreas",
TRAINING_REQUEST_SUCCESS = 'trainingRequestIsSuccessful',
TRAINING_REQUEST_MESSAGE = 'trainingRequestMessage',
TRAINING_SETTINGS_IS_VALID = 'trainingSettingsIsValid'
}

export const FORM_VALIDATION_CONFIG = {
Expand Down Expand Up @@ -150,6 +154,9 @@ type FormData = {
batchSize: number;
boundaryWidth: number;
zoomLevels: number[];
trainingRequestIsSuccessful: boolean
trainingRequestMessage: string
trainingSettingsIsValid: boolean
};

const initialFormState: FormData = {
Expand Down Expand Up @@ -177,6 +184,10 @@ const initialFormState: FormData = {
batchSize: 8,
boundaryWidth: 3,
zoomLevels: [19, 20, 21],
trainingSettingsIsValid: true,
// Training requests response
trainingRequestIsSuccessful: true,
trainingRequestMessage: ""
};

const ModelsContext = createContext<{
Expand Down Expand Up @@ -206,6 +217,7 @@ const ModelsContext = createContext<{
>;
hasLabeledTrainingAreas: boolean;
hasAOIsWithGeometry: boolean;
resetState: () => void
}>({
formData: initialFormState,
setFormData: () => { },
Expand All @@ -224,6 +236,7 @@ const ModelsContext = createContext<{
>,
hasLabeledTrainingAreas: false,
hasAOIsWithGeometry: false,
resetState: () => { }
});

export const ModelsProvider: React.FC<{
Expand Down Expand Up @@ -265,13 +278,47 @@ export const ModelsProvider: React.FC<{
mutationConfig: {
onSuccess: () => {
showSuccessToast(TOAST_NOTIFICATIONS.trainingRequestSubmittedSuccess);
handleChange(
MODEL_CREATION_FORM_NAME.TRAINING_REQUEST_SUCCESS,
true
);
handleChange(
MODEL_CREATION_FORM_NAME.TRAINING_REQUEST_MESSAGE,
MODEL_CREATION_CONTENT.confirmation.trainingRequestSuccess
);
// delay for a few seconds before resetting the state
timeOutRef.current = setTimeout(() => {
setFormData(initialFormState);
}, 3000);
setFormData((prevFormData) => ({
...initialFormState,
// Preserve the training requests information because it's needed in the confirmation page.
trainingRequestMessage: prevFormData.trainingRequestMessage,
trainingRequestIsSuccessful: prevFormData.trainingRequestIsSuccessful,
}));
}, 2000);
},

onError: (error) => {
showErrorToast(error);
// delay for a few seconds before resetting the state, but keep the data that will be needed for submitting training
// request incase the user wants to do that.
timeOutRef.current = setTimeout(() => {
setFormData((prevFormData) => ({
...initialFormState,
// Preserve the training requests information because it's needed in the confirmation page.
trainingRequestMessage: prevFormData.trainingRequestMessage,
trainingRequestIsSuccessful: prevFormData.trainingRequestIsSuccessful,
}));
}, 2000);

handleChange(
MODEL_CREATION_FORM_NAME.TRAINING_REQUEST_SUCCESS,
false
);
handleChange(
MODEL_CREATION_FORM_NAME.TRAINING_REQUEST_MESSAGE,
// @ts-expect-error bad type definition
`Your created model could not be trained because ${String(error?.response?.data[0]).toLocaleLowerCase()}. Click on the enhance button below to retrain your model.`
);
},
},
});
Expand Down Expand Up @@ -341,7 +388,9 @@ export const ModelsProvider: React.FC<{
.length === 0
);
}, [formData]);

const resetState = () => {
setFormData(initialFormState)
}
const memoizedValues = useMemo(
() => ({
setFormData,
Expand All @@ -350,7 +399,8 @@ export const ModelsProvider: React.FC<{
createNewModelMutation,
hasLabeledTrainingAreas,
hasAOIsWithGeometry,
formData
formData,
resetState
}),
[
setFormData,
Expand All @@ -360,6 +410,7 @@ export const ModelsProvider: React.FC<{
createNewModelMutation,
hasLabeledTrainingAreas,
hasAOIsWithGeometry,
resetState
],
);

Expand Down
76 changes: 43 additions & 33 deletions frontend/src/app/routes/models/confirmation.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,59 @@
import { useModelsContext } from "@/app/providers/models-provider";
import ModelFormConfirmation from "@/assets/images/model_creation_success.png";
import { Button } from "@/components/ui/button";
import { Image } from "@/components/ui/image";
import { Link } from "@/components/ui/link";
import ModelEnhancementDialog from "@/features/models/components/dialogs/model-enhancement-dialog";
import { useDialog } from "@/hooks/use-dialog";
import { APPLICATION_ROUTES, MODEL_CREATION_CONTENT } from "@/utils";
import ConfettiExplosion from "react-confetti-explosion";
import { useSearchParams } from "react-router-dom";
import { useNavigate, useSearchParams } from "react-router-dom";

export const ModelConfirmationPage = () => {
const [searchParams] = useSearchParams();

const modelId = searchParams.get("id");
const { formData } = useModelsContext();
const { isOpened, openDialog, closeDialog } = useDialog();
const navigate = useNavigate();

const handleClick = () => {
if (formData.trainingRequestIsSuccessful) {
navigate(`${APPLICATION_ROUTES.MODELS}/${modelId}`)
} else {
openDialog()
}
}
return (
<div className={"col-start-3 col-span-8 flex flex-col gap-y-10"}>
<div className="flex items-center justify-center w-full h-full flex-col gap-y-10 text-center">
<ConfettiExplosion
force={0.2}
duration={5000}
particleCount={250}
height={10000}
/>
<Image src={ModelFormConfirmation} alt="Model Creation Success Icon" />
<p className="text-title-2">Model {modelId} is Created!</p>
<p className="text-gray">
{MODEL_CREATION_CONTENT.confirmation.description}
</p>
<div className="flex items-center justify-between gap-x-4">
<Link
href={`${APPLICATION_ROUTES.MODELS}/${modelId}`}
title={MODEL_CREATION_CONTENT.confirmation.buttons.goToModel}
nativeAnchor={false}
>
<Button>
{MODEL_CREATION_CONTENT.confirmation.buttons.goToModel}
</Button>
</Link>
<Link
href={`${APPLICATION_ROUTES.MODELS}`}
title={MODEL_CREATION_CONTENT.confirmation.buttons.exploreModels}
>
<Button variant="dark">
{MODEL_CREATION_CONTENT.confirmation.buttons.exploreModels}
<>
<ModelEnhancementDialog isOpened={isOpened} closeDialog={closeDialog} modelId={modelId as string} />
<div className={"col-start-3 col-span-8 flex flex-col gap-y-10"}>
<div className="flex items-center justify-center w-full h-full flex-col gap-y-10 text-center">
<ConfettiExplosion
force={0.2}
duration={5000}
particleCount={250}
height={10000}
/>
<Image src={ModelFormConfirmation} alt="Model Creation Success Icon" />
<p className="text-title-2">Model {modelId} is Created!</p>
<p className="text-gray">
{formData.trainingRequestMessage}
</p>
<div className="flex items-center justify-between gap-x-4">
<Button onClick={handleClick}>
{formData.trainingRequestIsSuccessful ? MODEL_CREATION_CONTENT.confirmation.buttons.goToModel : MODEL_CREATION_CONTENT.confirmation.buttons.enhanceModel}
</Button>
</Link>
<Link
href={!formData.trainingRequestIsSuccessful ? `${APPLICATION_ROUTES.MODELS}/${modelId}` : `${APPLICATION_ROUTES.MODELS}`}
title={!formData.trainingRequestIsSuccessful ? MODEL_CREATION_CONTENT.confirmation.buttons.goToModel : MODEL_CREATION_CONTENT.confirmation.buttons.exploreModels}
>
<Button variant="dark">
{!formData.trainingRequestIsSuccessful ? MODEL_CREATION_CONTENT.confirmation.buttons.goToModel : MODEL_CREATION_CONTENT.confirmation.buttons.exploreModels}
</Button>
</Link>
</div>
</div>
</div>
</div>
</>
);
};
1 change: 1 addition & 0 deletions frontend/src/app/routes/models/model-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export const ModelDetailsPage = () => {
<ModelEnhancementDialog
isOpened={isModelEnhancementDialogOpened}
closeDialog={closeModelEnhancementDialog}
modelId={data?.id as string}
/>
<Head title={`${data?.name} Model`} />
<ModelFilesDialog
Expand Down
14 changes: 10 additions & 4 deletions frontend/src/app/routes/models/models-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import useDebounce from "@/hooks/use-debounce";
import { useDialog } from "@/hooks/use-dialog";
import { MobileModelFiltersDialog } from "@/features/models/components/dialogs";
import { Head } from "@/components/seo";
import { ModelsProvider } from "@/app/providers/models-provider";

export enum LayoutView {
LIST = "list",
Expand Down Expand Up @@ -58,9 +59,9 @@ const ClearFilters = ({
}) => {
const canClearAllFilters = Boolean(
query[SEARCH_PARAMS.searchQuery] ||
query[SEARCH_PARAMS.startDate] ||
query[SEARCH_PARAMS.endDate] ||
query[SEARCH_PARAMS.id],
query[SEARCH_PARAMS.startDate] ||
query[SEARCH_PARAMS.endDate] ||
query[SEARCH_PARAMS.id],
);

return (
Expand Down Expand Up @@ -334,7 +335,12 @@ export const ModelsPage = () => {
disabled={isPending}
/>
<section className="my-10 min-h-screen">
<PageHeader />
{/*
Providing access to the models context, so that the 'create model' button can reset the store before going to the model creation form.
*/}
<ModelsProvider>
<PageHeader />
</ModelsProvider>
<div className="flex flex-col gap-y-4">
<div className=" flex items-center justify-between w-full ">
<div className="flex items-center justify-between w-full md:gap-x-4 gap-y-2 md:gap-y-0 md:w-auto">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,11 @@ const ProgressButtons: React.FC<ProgressButtonsProps> = ({
case APPLICATION_ROUTES.CREATE_NEW_MODEL:
return (
formData.modelName.length >=
FORM_VALIDATION_CONFIG[MODEL_CREATION_FORM_NAME.MODEL_NAME]
.minLength &&
FORM_VALIDATION_CONFIG[MODEL_CREATION_FORM_NAME.MODEL_NAME]
.minLength &&
formData.modelDescription.length >=
FORM_VALIDATION_CONFIG[MODEL_CREATION_FORM_NAME.MODEL_DESCRIPTION]
.minLength
FORM_VALIDATION_CONFIG[MODEL_CREATION_FORM_NAME.MODEL_DESCRIPTION]
.minLength
);

case APPLICATION_ROUTES.CREATE_NEW_MODEL_TRAINING_DATASET:
Expand All @@ -126,8 +126,8 @@ const ProgressButtons: React.FC<ProgressButtonsProps> = ({
return (
formData.tmsURLValidation.valid &&
formData.datasetName.length >=
FORM_VALIDATION_CONFIG[MODEL_CREATION_FORM_NAME.DATASET_NAME]
.minLength
FORM_VALIDATION_CONFIG[MODEL_CREATION_FORM_NAME.DATASET_NAME]
.minLength
);
} else if (
formData.trainingDatasetOption === TrainingDatasetOption.USE_EXISTING
Expand All @@ -139,7 +139,7 @@ const ProgressButtons: React.FC<ProgressButtonsProps> = ({
}
case APPLICATION_ROUTES.CREATE_NEW_MODEL_TRAINING_SETTINGS:
// confirm that the user has selected at least an option
return formData.zoomLevels.length > 0;
return formData.zoomLevels.length > 0 && formData.trainingSettingsIsValid;
case APPLICATION_ROUTES.CREATE_NEW_MODEL_TRAINING_AREA:
return (
hasLabeledTrainingAreas && hasAOIsWithGeometry && formData.oamBounds
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const trainingTypes = [
const TrainingSettingsForm = () => {
const [showAdvancedSettings, setShowAdvancedSettings] =
useState<boolean>(false);

const [validationMessage, setValidationMessage] = useState('Hellow')
const { formData, handleChange } = useModelsContext();

const advancedSettings = [
Expand Down Expand Up @@ -182,7 +182,7 @@ const TrainingSettingsForm = () => {
/>
</button>
</div>
{showAdvancedSettings && (
{showAdvancedSettings && (<>
<div className="flex items-center justify-between gap-4 flex-wrap lg:flex-nowrap">
{advancedSettings.filter(setting => setting.enabled).map((setting, id) => (
<div key={`training-settings-${id}`} className="w-full">
Expand All @@ -202,15 +202,38 @@ const TrainingSettingsForm = () => {
// @ts-expect-error bad type definition
FORM_VALIDATION_CONFIG[formData.baseModel][setting.value].max
}
handleInput={(e) =>
handleChange(setting.value, Number(e.target.value))
}
handleInput={(e) => {
const inputValue = Number(e.target.value);

const min =
// @ts-expect-error bad type definition
FORM_VALIDATION_CONFIG[formData.baseModel][setting.value].min;
const max =
// @ts-expect-error bad type definition
FORM_VALIDATION_CONFIG[formData.baseModel][setting.value].max;
handleChange(setting.value, inputValue);
if (inputValue < min || inputValue > max) {
// Set validation message for out-of-range values
setValidationMessage(
`${setting.label} must be between ${min} and ${max}.`
);
handleChange(MODEL_CREATION_FORM_NAME.TRAINING_SETTINGS_IS_VALID, false)
} else {
// Clear the validation message if the value is valid
setValidationMessage("");
handleChange(setting.value, inputValue);
handleChange(MODEL_CREATION_FORM_NAME.TRAINING_SETTINGS_IS_VALID, true)
}
}}
toolTipContent={setting.toolTip}
/>
</div>
))}
</div>
<p>{validationMessage}</p>
</>
)}

</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import ModelTrainingSettingsDialog from "./training-settings-dialog";
type ModelEnhancementDialogProps = {
isOpened: boolean;
closeDialog: () => void;
modelId: string
};
const ModelEnhancementDialog: React.FC<ModelEnhancementDialogProps> = ({
isOpened,
closeDialog,
closeDialog, modelId
}) => {
const { isOpened: isTrainingSettingsDialogOpened, openDialog, closeDialog: closeTrainingSettingsDialog } = useDialog();

Expand Down Expand Up @@ -43,7 +44,7 @@ const ModelEnhancementDialog: React.FC<ModelEnhancementDialogProps> = ({
closeDialog={closeDialog}
label={APP_CONTENT.models.modelsDetailsCard.modelUpdate.dialogHeading}
>
<ModelTrainingSettingsDialog isOpened={isTrainingSettingsDialogOpened} closeDialog={handleClose} />
<ModelTrainingSettingsDialog isOpened={isTrainingSettingsDialogOpened} closeDialog={handleClose} modelId={modelId} />
<ul className="flex flex-col gap-y-4 w-full">
{options.map((option, id) => (
<li
Expand Down
Loading

0 comments on commit fc46437

Please sign in to comment.