Skip to content

Commit

Permalink
Merge pull request hotosm#94 from hotosm/bug/validation_model
Browse files Browse the repository at this point in the history
FIX : Bug in validation
  • Loading branch information
kshitijrajsharma authored Apr 10, 2023
2 parents 389823a + 20750c5 commit 180da83
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 34 deletions.
8 changes: 8 additions & 0 deletions backend/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ def create(self, validated_data):
existing_trainings = Training.objects.filter(model_id=model_id).exclude(
status__in=["FINISHED", "FAILED"]
)
model = get_object_or_404(Model, id=model_id)
if not Label.objects.filter(
aoi__in=AOI.objects.filter(dataset=model.dataset)
).exists():
raise ValidationError(
"Error: No labels associated with the model, Create AOI & Labels for Dataset"
)

if existing_trainings.exists():
raise ValidationError(
"Another training is already running or submitted for this model."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { useNavigate, useParams } from "react-router-dom";
import { modelStatus } from "../../../../utils";
import axios from "../../../../axios";
import { useMutation, useQuery } from "react-query";
import Popup from "./Popup";

import OSMUser from "../../../Shared/OSMUser";
import SaveIcon from "@material-ui/icons/Save";
import { Checkbox, FormControlLabel } from "@mui/material";
Expand All @@ -25,6 +27,8 @@ const AIModelEditor = (props) => {
const [error, setError] = useState(null);
const [epochs, setEpochs] = useState(20);
const [zoomLevel, setZoomLevel] = useState([19]);
const [popupOpen, setPopupOpen] = useState(false);
const [popupRowData, setPopupRowData] = useState(null);

const [random, setRandom] = useState(Math.random());
const [batchSize, setBatchSize] = useState(8);
Expand All @@ -45,6 +49,20 @@ const AIModelEditor = (props) => {
} finally {
}
};
const openPopupWithTrainingId = async (trainingId) => {
try {
const response = await axios.get(`/training/${trainingId}/`);
setPopupRowData(response.data);
setPopupOpen(true);
} catch (error) {
console.error("Error fetching training information:", error);
}
};
const handlePopupOpen = (row) => {
setPopupRowData(row);
setPopupOpen(true);
};

const { data, isLoading, refetch } = useQuery("getModelById", getModelById, {
refetchInterval: 60000,
});
Expand Down Expand Up @@ -92,23 +110,43 @@ const AIModelEditor = (props) => {
<Typography variant="h6" component="div">
Model ID: {data.id}
</Typography>
<Typography variant="h6" component="div">
Status: {modelStatus(data.status)}
{data.published_training &&
", training: " + data.published_training + ", "}
<Typography
variant="h6"
component="div"
style={{ display: "flex", justifyContent: "space-between" }}
>
<div>
Status: <b>{modelStatus(data.status)}</b>
{data.published_training && (
<>
,
<Link
href="#"
onClick={(e) => {
e.preventDefault();
openPopupWithTrainingId(data.published_training);
}}
color="inherit"
>
<b>Training: {data.published_training}</b>
</Link>
</>
)}
</div>
{data.status === 0 && (
<Link
href="#"
<Button
variant="contained"
color="primary"
onClick={(e) => {
e.preventDefault();
navigate("/start-mapping/" + data.id);
}}
color="inherit"
>
Start mapping
</Link>
</Button>
)}
</Typography>

<Typography variant="h6" component="div">
<Link
href="#"
Expand Down Expand Up @@ -257,6 +295,13 @@ const AIModelEditor = (props) => {
</Grid>
</Grid>
)}
{popupRowData && (
<Popup
open={popupOpen}
handleClose={() => setPopupOpen(false)}
row={popupRowData}
/>
)}
</>
);
};
Expand Down
69 changes: 47 additions & 22 deletions frontend/src/components/Layout/AIModels/AIModelEditor/Popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import {
DialogContent,
DialogActions,
Button,
CircularProgress,
} from "@mui/material";
import axios from "../../../../axios";

const Popup = ({ open, handleClose, row }) => {
const [error, setError] = useState(null);
const [traceback, setTraceback] = useState(null);
const [imageUrl, setImageUrl] = useState(null);
const [loading, setLoading] = useState(false);

const getTrainingStatus = async (taskId) => {
try {
Expand Down Expand Up @@ -60,10 +62,13 @@ const Popup = ({ open, handleClose, row }) => {
};

useEffect(() => {
setLoading(true);
if (row.status === "FAILED" || row.status === "RUNNING") {
getTrainingStatus(row.task_id);
getTrainingStatus(row.task_id).finally(() => setLoading(false));
} else if (row.status === "FINISHED") {
getDatasetId(row.model);
getDatasetId(row.model).finally(() => setLoading(false));
} else {
setLoading(false);
}
}, [row.status, row.task_id, row.model]);

Expand Down Expand Up @@ -95,32 +100,52 @@ const Popup = ({ open, handleClose, row }) => {
<p>
<b>Accuracy:</b> {row.accuracy}
</p>
<p>
<b>Status:</b> {row.status}
</p>
{(row.status === "FAILED" || row.status === "RUNNING") && (
<>
<p>
<b>Status:</b> {row.status}
</p>
{traceback && (
<div
style={{
backgroundColor: "black",
color: "white",
padding: "10px",
fontSize: "12px",
whiteSpace: "pre-wrap",
fontFamily: "monospace",
overflow: "auto",
}}
>
{renderTraceback()}
{loading ? (
<div style={{ display: "flex", justifyContent: "center" }}>
<CircularProgress />
</div>
) : (
traceback && (
<div
style={{
backgroundColor: "black",
color: "white",
padding: "10px",
fontSize: "12px",
whiteSpace: "pre-wrap",
fontFamily: "monospace",
overflow: "auto",
}}
>
{renderTraceback()}
</div>
)
)}
</>
)}
{row.status === "FINISHED" && imageUrl && (
<div style={{ display: "flex", justifyContent: "center" }}>
<img src={imageUrl} alt="training graph" style={{ width: "98%" }} />
</div>
{row.status === "FINISHED" && (
<>
{loading ? (
<div style={{ display: "flex", justifyContent: "center" }}>
<CircularProgress />
</div>
) : (
imageUrl && (
<div style={{ display: "flex", justifyContent: "center" }}>
<img
src={imageUrl}
alt="training graph"
style={{ width: "98%" }}
/>
</div>
)
)}
</>
)}
</DialogContent>

Expand Down
28 changes: 25 additions & 3 deletions frontend/src/components/Layout/AIModels/AIModelEditor/Trainings.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import React, { useContext, useEffect, useState } from "react";
import { DataGrid } from "@mui/x-data-grid";
import { Grid, IconButton, Tooltip, Typography } from "@mui/material";
import {
Grid,
IconButton,
Tooltip,
Typography,
CircularProgress,
} from "@mui/material";

import { useMutation, useQuery } from "react-query";
import axios from "../../../../axios";
import Alert from "@material-ui/lab/Alert";
Expand All @@ -23,6 +30,8 @@ const DEFAULT_FILTER = {
const AIModelsList = (props) => {
const [error, setError] = useState(null);
const [popupOpen, setPopupOpen] = useState(false);
const [publishing, setPublishing] = useState(false);

const [popupRow, setPopupRow] = useState(null);

const handlePopupOpen = (row) => {
Expand Down Expand Up @@ -61,6 +70,13 @@ const AIModelsList = (props) => {
field: "description",
headerName: "Description",
flex: 1,
renderCell: (params) => {
return (
<Tooltip title={params.value} placement="right">
<span>{params.value}</span>
</Tooltip>
);
},
},
{
field: "epochs",
Expand Down Expand Up @@ -172,13 +188,16 @@ const AIModelsList = (props) => {
mutate(params.row.id);
}}
>
<PublishIcon />
{publishing ? (
<CircularProgress size={24} />
) : (
<PublishIcon />
)}
</IconButton>
</Tooltip>
)}
</>
);
// <p>{`${params.row.status} and id ${params.row.id}`}</p>;
},
},
];
Expand All @@ -191,6 +210,7 @@ const AIModelsList = (props) => {

const { accessToken } = useContext(AuthContext);
const publishModel = async (trainingId) => {
setPublishing(true);
try {
const headers = {
"access-token": accessToken,
Expand All @@ -213,8 +233,10 @@ const AIModelsList = (props) => {
console.log("isError");
setError(JSON.stringify(e));
} finally {
setPublishing(false);
}
};

const { mutate } = useMutation(publishModel);
return (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ const AIModelsList = (props) => {
{
field: "name",
headerName: "Name",
width: 250,
renderCell: (params) => {
return (
<Tooltip title={params.value} placement="right">
<span>{params.value}</span>
</Tooltip>
);
},
},
{
field: "created_at",
Expand Down Expand Up @@ -79,6 +87,7 @@ const AIModelsList = (props) => {
{
field: "dataset",
headerName: "Dataset ID",
width: 150,

renderCell: (params) => {
return <span> {params.value}</span>;
Expand Down
13 changes: 12 additions & 1 deletion frontend/src/components/Layout/Start/ModelsMap/ModelsMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,18 @@ const PublishedAIModelsList = (props) => {
);
},
},
{ field: "name", headerName: "Name" },
{
field: "name",
headerName: "Name",
width: 250,
renderCell: (params) => {
return (
<Tooltip title={params.value} placement="right">
<span>{params.value}</span>
</Tooltip>
);
},
},
{
field: "last_modified",
headerName: "Last Modified",
Expand Down
18 changes: 18 additions & 0 deletions frontend/src/components/Layout/Start/Prediction/Prediction.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { GeoJSON } from "react-leaflet";
const Prediction = () => {
const { id } = useParams();
const [error, setError] = useState(false);
const [apiCallInProgress, setApiCallInProgress] = useState(false);

const [map, setMap] = useState(null);
const [zoom, setZoom] = useState(0);
const [responseTime, setResponseTime] = useState(0);
Expand All @@ -28,6 +30,20 @@ const Prediction = () => {
]);
const [josmEnabled, setJosmEnabled] = useState(false);

useEffect(() => {
if (!apiCallInProgress) {
return;
}

const timer = setInterval(() => {
setResponseTime((prevResponseTime) => prevResponseTime + 1);
}, 1000);

return () => {
clearInterval(timer);
};
}, [apiCallInProgress]);

useEffect(() => {
getModel();
}, []);
Expand Down Expand Up @@ -80,6 +96,7 @@ const Prediction = () => {
data: predictions,
isLoading: predictionLoading,
} = useMutation(async () => {
setApiCallInProgress(true);
const headers = {
"access-token": accessToken,
};
Expand All @@ -98,6 +115,7 @@ const Prediction = () => {
const res = await axios.post(`/prediction/`, body, { headers });
const endTime = new Date().getTime(); // measure end time
setResponseTime((endTime - startTime) / 1000); // calculate and store response time in seconds
setApiCallInProgress(false);
if (res.error) {
setError(
`${res.error.response.statusText}, ${JSON.stringify(
Expand Down
Loading

0 comments on commit 180da83

Please sign in to comment.