diff --git a/backend/Dockerfile b/backend/Dockerfile index b7e40872..2f67c2be 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -28,6 +28,5 @@ RUN pip install setuptools --upgrade COPY requirements.txt requirements.txt RUN pip install -r requirements.txt - WORKDIR /app COPY . /app \ No newline at end of file diff --git a/backend/core/models.py b/backend/core/models.py index b4ed7435..b1d67be1 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -31,8 +31,8 @@ class DownloadStatus(models.IntegerChoices): dataset = models.ForeignKey(Dataset, to_field="id", on_delete=models.CASCADE) geom = geomodels.PolygonField(srid=4326) - download_status = models.IntegerField(default=-1, choices=DownloadStatus.choices) - last_fetched_date = models.DateTimeField(null=True, blank=True) + label_status = models.IntegerField(default=-1, choices=DownloadStatus.choices) + label_fetched = models.DateTimeField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) last_modified = models.DateTimeField(auto_now=True) @@ -42,7 +42,6 @@ class Label(models.Model): geom = geomodels.GeometryField(srid=4326) osm_id = models.BigIntegerField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) - last_modified = models.DateTimeField(auto_now=True) class Model(models.Model): @@ -89,11 +88,11 @@ class Training(models.Model): class Feedback(models.Model): - ACTION_TYPE = ( - ("CREATE", "CREATE"), - ("MODIFY", "MODIFY"), - ("ACCEPT", "ACCEPT"), - ("INITIAL", "INITIAL"), + FEEDBACK_TYPE = ( + ("TP", "True Positive"), + ("TN", "True Negative"), + ("FP", "False Positive"), + ("FN", "False Negative"), ) geom = geomodels.GeometryField(srid=4326) training = models.ForeignKey(Training, to_field="id", on_delete=models.CASCADE) @@ -101,7 +100,29 @@ class Feedback(models.Model): zoom_level = models.PositiveIntegerField( validators=[MinValueValidator(18), MaxValueValidator(23)] ) - action = models.CharField(choices=ACTION_TYPE, max_length=10) + feedback_type = models.CharField(choices=FEEDBACK_TYPE, max_length=10) + comments = models.TextField(max_length=100,null=True,blank=True) + user = models.ForeignKey(OsmUser, to_field="osm_id", on_delete=models.CASCADE) + source_imagery = models.URLField() + + +class FeedbackAOI(models.Model): + class DownloadStatus(models.IntegerChoices): + DOWNLOADED = 1 + NOT_DOWNLOADED = -1 + RUNNING = 0 + training = models.ForeignKey(Training, to_field="id", on_delete=models.CASCADE) + geom = geomodels.PolygonField(srid=4326) + label_status = models.IntegerField(default=-1, choices=DownloadStatus.choices) + label_fetched = models.DateTimeField(null=True, blank=True) + created_at = models.DateTimeField(auto_now_add=True) last_modified = models.DateTimeField(auto_now=True) + source_imagery = models.URLField() user = models.ForeignKey(OsmUser, to_field="osm_id", on_delete=models.CASCADE) - validated = models.BooleanField(default=False) + + +class FeedbackLabel(models.Model): + osm_id = models.BigIntegerField(null=True, blank=True) + feedback_aoi = models.ForeignKey(FeedbackAOI, to_field="id", on_delete=models.CASCADE) + geom = geomodels.PolygonField(srid=4326) + created_at = models.DateTimeField(auto_now_add=True) diff --git a/backend/core/serializers.py b/backend/core/serializers.py index 35cf792e..2381ce3e 100644 --- a/backend/core/serializers.py +++ b/backend/core/serializers.py @@ -57,11 +57,32 @@ class Meta: read_only_fields = ( "created_at", "last_modified", - "last_fetched_date", - "download_status", + "label_fetched", + "label_status", ) +class FeedbackAOISerializer(GeoFeatureModelSerializer): + class Meta: + model = FeedbackAOI + geo_field = "geom" + fields = "__all__" + partial = True + + read_only_fields = ( + "created_at", + "last_modified", + "label_fetched", + "label_status", + "user", + ) + + def create(self, validated_data): + user = self.context["request"].user + validated_data["user"] = user + return super().create(validated_data) + + class FeedbackSerializer(GeoFeatureModelSerializer): class Meta: model = Feedback @@ -90,10 +111,15 @@ class Meta: # auto_bbox = True fields = "__all__" # defining all the fields to be included in curd for now , we can restrict few if we want - read_only_fields = ( - "created_at", - "last_modified", - ) + read_only_fields = ("created_at", "osm_id") + + +class FeedbackLabelSerializer(GeoFeatureModelSerializer): + class Meta: + model = FeedbackLabel + geo_field = "geom" + fields = "__all__" + read_only_fields = ("created_at", "osm_id") class LabelFileSerializer( @@ -136,6 +162,7 @@ class FeedbackParamSerializer(serializers.Serializer): batch_size = serializers.IntegerField(required=False) freeze_layers = serializers.BooleanField(required=False) + class PredictionParamSerializer(serializers.Serializer): bbox = serializers.ListField(child=serializers.FloatField(), required=True) model_id = serializers.IntegerField(required=True) diff --git a/backend/core/urls.py b/backend/core/urls.py index 8abbea60..8c395cdd 100644 --- a/backend/core/urls.py +++ b/backend/core/urls.py @@ -7,6 +7,8 @@ AOIViewSet, APIStatus, DatasetViewSet, + FeedbackAOIViewset, + FeedbackLabelViewset, FeedbackView, FeedbackViewset, GenerateGpxView, @@ -31,6 +33,8 @@ router.register(r"training", TrainingViewSet) router.register(r"model", ModelViewSet) router.register(r"feedback", FeedbackViewset) +router.register(r"feedback-aoi", FeedbackAOIViewset) +router.register(r"feedback-label", FeedbackLabelViewset) urlpatterns = [ diff --git a/backend/core/views.py b/backend/core/views.py index b6c9e5bb..ddbf8357 100644 --- a/backend/core/views.py +++ b/backend/core/views.py @@ -38,11 +38,22 @@ from rest_framework.views import APIView from rest_framework_gis.filters import InBBoxFilter, TMSTileFilter -from .models import AOI, Dataset, Feedback, Label, Model, Training +from .models import ( + AOI, + Dataset, + Feedback, + FeedbackAOI, + FeedbackLabel, + Label, + Model, + Training, +) from .serializers import ( AOISerializer, DatasetSerializer, + FeedbackAOISerializer, FeedbackFileSerializer, + FeedbackLabelSerializer, FeedbackParamSerializer, FeedbackSerializer, LabelSerializer, @@ -150,7 +161,30 @@ class FeedbackViewset(viewsets.ModelViewSet): queryset = Feedback.objects.all() http_method_names = ["get", "post", "patch", "delete"] serializer_class = FeedbackSerializer # connecting serializer - filterset_fields = ["training", "user", "action", "validated"] + filterset_fields = ["training", "user", "feedback_type"] + + +class FeedbackAOIViewset(viewsets.ModelViewSet): + authentication_classes = [OsmAuthentication] + permission_classes = [IsOsmAuthenticated] + permission_allowed_methods = ["GET"] + queryset = FeedbackAOI.objects.all() + http_method_names = ["get", "post", "patch", "delete"] + serializer_class = FeedbackAOISerializer + filterset_fields = [ + "training", + "user", + ] + + +class FeedbackLabelViewset(viewsets.ModelViewSet): + authentication_classes = [OsmAuthentication] + permission_classes = [IsOsmAuthenticated] + permission_allowed_methods = ["GET"] + queryset = FeedbackLabel.objects.all() + http_method_names = ["get", "post", "patch", "delete"] + serializer_class = FeedbackLabelSerializer + filterset_fields = ["feedback_aoi"] class ModelViewSet( @@ -208,7 +242,7 @@ def post(self, request, aoi_id, *args, **kwargs): """ obj = get_object_or_404(AOI, id=aoi_id) try: - obj.download_status = 0 + obj.label_status = 0 obj.save() raw_data_params = { "geometry": json.loads(obj.geom.geojson), @@ -218,12 +252,12 @@ def post(self, request, aoi_id, *args, **kwargs): result = request_rawdata(raw_data_params) file_download_url = result["download_url"] process_rawdata(file_download_url, aoi_id) - obj.download_status = 1 - obj.last_fetched_date = datetime.utcnow() + obj.label_status = 1 + obj.label_fetched = datetime.utcnow() obj.save() return Response("Success", status=status.HTTP_201_CREATED) except Exception as ex: - obj.download_status = -1 + obj.label_status = -1 obj.save() # raise ex return Response("OSM Fetch Failed", status=500) diff --git a/docker-compose.yml b/docker-compose.yml index 263647eb..80dfc78e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -35,7 +35,7 @@ services: volumes: - ./backend:/app - ${RAMP_HOME}:/RAMP_HOME - - ${TRAINING_WORKSPACE}:/TRAINING_WORKSPACE + # - ${TRAINING_WORKSPACE}:/TRAINING_WORKSPACE depends_on: - redis - postgres @@ -55,7 +55,7 @@ services: volumes: - ./backend:/app - ${RAMP_HOME}:/RAMP_HOME - - ${TRAINING_WORKSPACE}:/TRAINING_WORKSPACE + # - ${TRAINING_WORKSPACE}:/TRAINING_WORKSPACE depends_on: - backend-api - redis diff --git a/frontend/public/rapid-logo.png b/frontend/public/rapid-logo.png new file mode 100644 index 00000000..1d6f3b6d Binary files /dev/null and b/frontend/public/rapid-logo.png differ diff --git a/frontend/src/components/Layout/AIModels/AIModelEditor/Feedback.js b/frontend/src/components/Layout/AIModels/AIModelEditor/Feedback.js new file mode 100644 index 00000000..709bd4a9 --- /dev/null +++ b/frontend/src/components/Layout/AIModels/AIModelEditor/Feedback.js @@ -0,0 +1,60 @@ +import { IconButton, Tooltip } from "@mui/material"; +import React, { useContext } from "react"; +import InfoIcon from "@mui/icons-material/Info"; +import FeedbackIcon from "@mui/icons-material/Feedback"; +import { useQuery } from "react-query"; +import axios from "../../../../axios"; +import AuthContext from "../../../../Context/AuthContext"; +import FindReplaceIcon from "@mui/icons-material/FindReplace"; +const Feedback = ({ trainingId }) => { + const { accessToken } = useContext(AuthContext); + const getFeedback = async () => { + try { + const headers = { + "access-token": accessToken, + }; + const res = await axios.get(`/feedback/?training=${trainingId}`, null, { + headers, + }); + + if (res.error) { + } else { + // console.log(`/feedback/?training=${trainingId}`, res.data); + return res.data; + } + } catch (e) { + console.log("isError", e); + } finally { + } + }; + const { data: feedbackData, isLoading } = useQuery( + "getFeedback" + trainingId, + getFeedback, + { + refetchInterval: 10000, + } + ); + + return ( + <> + {feedbackData && + feedbackData.features && + feedbackData.features.length > 0 && ( + + + + + + )} + {isLoading && ( + + + + )} + + ); +}; +export default Feedback; diff --git a/frontend/src/components/Layout/AIModels/AIModelEditor/Trainings.js b/frontend/src/components/Layout/AIModels/AIModelEditor/Trainings.js index f6526afb..4c160786 100644 --- a/frontend/src/components/Layout/AIModels/AIModelEditor/Trainings.js +++ b/frontend/src/components/Layout/AIModels/AIModelEditor/Trainings.js @@ -20,6 +20,7 @@ import Popup from "./Popup"; import InfoIcon from "@mui/icons-material/Info"; import AuthContext from "../../../../Context/AuthContext"; +import Feedback from "./Feedback"; const DEFAULT_FILTER = { items: [{ columnField: "created_date", id: 8537, operatorValue: "contains" }], @@ -151,16 +152,22 @@ const TrainingsList = (props) => { }, { field: "popup", - headerName: "Info", - width: 10, - renderCell: (params) => ( - handlePopupOpen(params.row)} - aria-label="popup" - > - - - ), + headerName: "Info/Feedback", + width: 100, + renderCell: (params) => { + // console.log("params in info row", params); + return ( +
+ handlePopupOpen(params.row)} + aria-label="popup" + > + + + +
+ ); + }, }, { field: "accuracy", diff --git a/frontend/src/components/Layout/Start/Prediction/EditableGeoJSON.js b/frontend/src/components/Layout/Start/Prediction/EditableGeoJSON.js index b6eac26f..bfc1e18f 100644 --- a/frontend/src/components/Layout/Start/Prediction/EditableGeoJSON.js +++ b/frontend/src/components/Layout/Start/Prediction/EditableGeoJSON.js @@ -1,10 +1,14 @@ -import React, { useEffect, useState } from "react"; +import React, { useContext, useEffect, useState } from "react"; import L from "leaflet"; import { GeoJSON } from "react-leaflet"; import "@geoman-io/leaflet-geoman-free"; import "@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css"; - +import { useMutation } from "react-query"; +// import L from "leaflet"; +import AuthContext from "../../../../Context/AuthContext"; +import axios from "../../../../axios"; +import { converToGeoPolygon } from "../../../../utils"; function deg2tile(lat_deg, lon_deg, zoom) { const lat_rad = (Math.PI / 180) * lat_deg; const n = Math.pow(2, zoom); @@ -34,32 +38,32 @@ function tile2boundingbox(xtile, ytile, zoom) { return L.latLngBounds(cornerNW, cornerSE); } -function addTileBoundaryLayer( - mapref, - addedTiles, - tileX, - tileY, - zoom, - setAddedTiles, - tileBoundaryLayer -) { - const key = `${tileX}_${tileY}_${zoom}`; +// function addTileBoundaryLayer( +// mapref, +// addedTiles, +// tileX, +// tileY, +// zoom, +// setAddedTiles, +// tileBoundaryLayer +// ) { +// const key = `${tileX}_${tileY}_${zoom}`; - if (!addedTiles.has(key)) { - console.log("Key doesn't present in map"); - const bounds = tile2boundingbox(tileX, tileY, zoom); - tileBoundaryLayer = L.rectangle(bounds, { - color: "yellow", - fill: false, - pmIgnore: true, - }); - tileBoundaryLayer.name = "Tile Box"; - mapref.addLayer(tileBoundaryLayer); - mapref.fitBounds(tileBoundaryLayer.getBounds()); - addedTiles.add(key); - setAddedTiles(addedTiles); - } -} +// if (!addedTiles.has(key)) { +// console.log("Key doesn't present in map"); +// const bounds = tile2boundingbox(tileX, tileY, zoom); +// tileBoundaryLayer = L.rectangle(bounds, { +// color: "yellow", +// fill: false, +// pmIgnore: true, +// }); +// tileBoundaryLayer.name = "Tile Box"; +// mapref.addLayer(tileBoundaryLayer); +// mapref.fitBounds(tileBoundaryLayer.getBounds()); +// addedTiles.add(key); +// setAddedTiles(addedTiles); +// } +// } function getFeatureStyle(feature) { let color = "red"; @@ -84,6 +88,10 @@ const EditableGeoJSON = ({ setModifiedCount, setDeletedCount, tileBoundaryLayer, + modelId, + trainingId, + sourceImagery, + refestchFeeedback, }) => { const onPMCreate = (event) => { console.log("Created"); @@ -109,86 +117,131 @@ const EditableGeoJSON = ({ corner.lng, predictionZoomlevel ); - addTileBoundaryLayer( - mapref, - addedTiles, - tileX, - tileY, - predictionZoomlevel, - setAddedTiles - ); + // addTileBoundaryLayer( + // mapref, + // addedTiles, + // tileX, + // tileY, + // predictionZoomlevel, + // setAddedTiles + // ); } mapref.removeLayer(createdLayer); }; + const { accessToken } = useContext(AuthContext); + + const submitFeedback = async (layer) => { + try { + // console.log("layer", layer); + const newAOI = { + id: Math.random(), + latlngs: layer.getLatLngs()[0], + }; + const points = JSON.stringify( + converToGeoPolygon([newAOI])[0][0].reduce( + (p, c, i) => p + c[1] + " " + c[0] + ",", + "" + ) + ).slice(1, -2); + + const polygon = "SRID=4326;POLYGON((" + points + "))"; + + let body = { + geom: polygon, + zoom_level: predictionZoomlevel, + feedback_type: "TN", + source_imagery: sourceImagery, + training: trainingId, + comments: "comments is not support yet", + }; + + const headers = { + "access-token": accessToken, + }; + const res = await axios.post(`/feedback/`, body, { headers }); + console.log("res ", res); + refestchFeeedback(); + } catch (error) { + console.log("Error in submitting feedback", error); + } finally { + } + }; + const { mutate: mutateSubmitFeedback } = useMutation(submitFeedback); + const onEachFeature = (feature, layer) => { - layer.on({ - "pm:update": (event) => { - const bounds = event.layer.getBounds(); - const corners = [bounds.getSouthWest(), bounds.getNorthEast()]; + // layer.on({ + // "pm:update": (event) => { + // const bounds = event.layer.getBounds(); + // const corners = [bounds.getSouthWest(), bounds.getNorthEast()]; - for (const corner of corners) { - const [tileX, tileY] = deg2tile( - corner.lat, - corner.lng, - predictionZoomlevel - ); - addTileBoundaryLayer( - mapref, - addedTiles, - tileX, - tileY, - predictionZoomlevel, - setAddedTiles - ); - } + // for (const corner of corners) { + // const [tileX, tileY] = deg2tile( + // corner.lat, + // corner.lng, + // predictionZoomlevel + // ); + // // addTileBoundaryLayer( + // // mapref, + // // addedTiles, + // // tileX, + // // tileY, + // // predictionZoomlevel, + // // setAddedTiles + // // ); + // } - const editedLayer = event.target; - const editedData = editedLayer.toGeoJSON(); - const editedFeatureIndex = data.features.findIndex( - (feature) => feature.id === editedData.id - ); - const newData = { ...data }; - newData.features[editedFeatureIndex] = editedData; - setPredictions(newData); - if (feature.properties.action !== "MODIFY") { - feature.properties.action = "MODIFY"; - setModifiedCount((prevCount) => prevCount + 1); - } - }, - "pm:remove": (event) => { - const bounds = event.layer.getBounds(); - const corners = [bounds.getSouthWest(), bounds.getNorthEast()]; + // const editedLayer = event.target; + // const editedData = editedLayer.toGeoJSON(); + // const editedFeatureIndex = data.features.findIndex( + // (feature) => feature.id === editedData.id + // ); + // const newData = { ...data }; + // newData.features[editedFeatureIndex] = editedData; + // setPredictions(newData); + // if (feature.properties.action !== "MODIFY") { + // feature.properties.action = "MODIFY"; + // setModifiedCount((prevCount) => prevCount + 1); + // } + // }, + // "pm:remove": (event) => { + // const bounds = event.layer.getBounds(); + // const corners = [bounds.getSouthWest(), bounds.getNorthEast()]; - for (const corner of corners) { - const [tileX, tileY] = deg2tile( - corner.lat, - corner.lng, - predictionZoomlevel - ); - addTileBoundaryLayer( - mapref, - addedTiles, - tileX, - tileY, - predictionZoomlevel, - setAddedTiles - ); - } - const deletedLayer = event.layer; - const newFeatures = data.features.filter( - (feature) => - feature.properties.id !== deletedLayer.feature.properties.id - ); - setPredictions({ ...data, features: newFeatures }); - setDeletedCount((prevCount) => prevCount + 1); - }, - }); + // for (const corner of corners) { + // const [tileX, tileY] = deg2tile( + // corner.lat, + // corner.lng, + // predictionZoomlevel + // ); + // // addTileBoundaryLayer( + // // mapref, + // // addedTiles, + // // tileX, + // // tileY, + // // predictionZoomlevel, + // // setAddedTiles + // // ); + // } + // const deletedLayer = event.layer; + // const newFeatures = data.features.filter( + // (feature) => + // feature.properties.id !== deletedLayer.feature.properties.id + // ); + // setPredictions({ ...data, features: newFeatures }); + // setDeletedCount((prevCount) => prevCount + 1); + // }, + // }); layer.on("click", (e) => { console.log(e); if (feature.properties.action === "INITIAL") { const popupContent = `
- +

+ This feedback will be presented on the model (id: ${modelId}, training id: ${trainingId}) for improvements +

+ Comments: +
+
`; const popup = L.popup() @@ -200,23 +253,10 @@ const EditableGeoJSON = ({ .querySelector("#rightButton") .addEventListener("click", () => { feature.properties.action = "ACCEPT"; - const bounds = layer.getBounds(); - const corners = [bounds.getSouthWest(), bounds.getNorthEast()]; - for (const corner of corners) { - const [tileX, tileY] = deg2tile( - corner.lat, - corner.lng, - predictionZoomlevel - ); - addTileBoundaryLayer( - mapref, - addedTiles, - tileX, - tileY, - predictionZoomlevel, - setAddedTiles - ); - } + console.log("popup layer ", layer); + // handle submitting feedback + mutateSubmitFeedback(layer); + popup.close(); }); } }); diff --git a/frontend/src/components/Layout/Start/Prediction/Prediction.js b/frontend/src/components/Layout/Start/Prediction/Prediction.js index 36e7aa7b..cd98a351 100644 --- a/frontend/src/components/Layout/Start/Prediction/Prediction.js +++ b/frontend/src/components/Layout/Start/Prediction/Prediction.js @@ -16,7 +16,7 @@ import { Link, Select, } from "@mui/material"; - +import L from "leaflet"; import React, { useContext, useEffect, useRef, useState } from "react"; import { FeatureGroup, @@ -25,7 +25,6 @@ import { TileLayer, useMapEvents, } from "react-leaflet"; -import L from "leaflet"; import { useMutation, useQuery } from "react-query"; import { useNavigate, useParams } from "react-router-dom"; import axios from "../../../../axios"; @@ -56,9 +55,9 @@ const Prediction = () => { const [apiCallInProgress, setApiCallInProgress] = useState(false); const [confidence, setConfidence] = useState(90); const [totalPredictionsCount, settotalPredictionsCount] = useState(0); - const [DeletedCount, setDeletedCount] = useState(0); - const [CreatedCount, setCreatedCount] = useState(0); - const [ModifiedCount, setModifiedCount] = useState(0); + // const [DeletedCount, setDeletedCount] = useState(0); + // const [CreatedCount, setCreatedCount] = useState(0); + // const [ModifiedCount, setModifiedCount] = useState(0); const [map, setMap] = useState(null); const [zoom, setZoom] = useState(15); const [responseTime, setResponseTime] = useState(0); @@ -232,9 +231,7 @@ const Prediction = () => { const updatedPredictions = addIdsToPredictions(res.data); setPredictions(updatedPredictions); settotalPredictionsCount(updatedPredictions.features.length); - setCreatedCount(0); - setModifiedCount(0); - setDeletedCount(0); + if (addedTiles.size > 0) { console.log("Map has tileboundarylayer"); } @@ -359,7 +356,37 @@ const Prediction = () => { return { ...predictions, features }; } const navigate = useNavigate(); + const getFeedback = async (trainingId) => { + if (!modelInfo || !modelInfo.trainingId) return; + try { + const headers = { + "access-token": accessToken, + }; + const res = await axios.get( + `/feedback/?training=${modelInfo.trainingId}`, + null, + { + headers, + } + ); + if (res.error) { + } else { + console.log("getFeedback ", res.data); + return res.data; + } + } catch (e) { + console.log("isError", e); + } finally { + } + }; + const { data: feedbackData, refetch: refetchFeedback } = useQuery( + "getfeedback" + (modelInfo && modelInfo.trainingId), + getFeedback, + { + refetchInterval: 120000, + } + ); return ( <> @@ -386,18 +413,24 @@ const Prediction = () => { )} - {predictions && ( + {predictions && dataset && ( { + refetchFeedback(); + }} /> )} @@ -453,48 +486,22 @@ const Prediction = () => { Response: {responseTime} sec - {predictions && ( - - - Feedback - + + + + Feedback + + + Initial Predictions: + {totalPredictionsCount} + + {feedbackData && feedbackData.features && ( - Initial Predictions: - {totalPredictionsCount} + Total feedbacks count:{feedbackData.features.length} - {CreatedCount > 0 && ( - - Total Created: - {CreatedCount} - - )} - {ModifiedCount > 0 && ( - - Total Modified: - {ModifiedCount} - - )} - {DeletedCount > 0 && ( - - Total Deleted: - {DeletedCount} - - )} - {CreatedCount + ModifiedCount + DeletedCount > 1 && - !feedbackSubmitted && ( - - Submit my feedback - - )} - - )} + )} + + {loading ? ( @@ -505,17 +512,19 @@ const Prediction = () => { Loaded Model - - { - e.preventDefault(); - navigate("/ai-models/" + modelInfo.id); - }} - color="inherit" - > - ID: {modelInfo.id} - + + + { + e.preventDefault(); + navigate("/ai-models/" + modelInfo.id); + }} + color="inherit" + > + ID: {modelInfo.id} + + Name: {modelInfo.name} diff --git a/frontend/src/components/Layout/TrainingDS/DatasetEditor/AOI.js b/frontend/src/components/Layout/TrainingDS/DatasetEditor/AOI.js index 7ca4bc55..b5b7b911 100644 --- a/frontend/src/components/Layout/TrainingDS/DatasetEditor/AOI.js +++ b/frontend/src/components/Layout/TrainingDS/DatasetEditor/AOI.js @@ -150,7 +150,7 @@ const AOI = (props) => { // mutateFetch(layer.aoiId); // console.log("Open in Editor") window.open( - `https://mapwith.ai/rapid#background=${ + `https://rapideditor.org/rapid#background=${ props.oamImagery ? "custom:" + props.oamImagery.url : "Bing" @@ -166,7 +166,7 @@ const AOI = (props) => { RapiD logo diff --git a/frontend/src/components/Layout/TrainingDS/DatasetEditor/AOIDetails.js b/frontend/src/components/Layout/TrainingDS/DatasetEditor/AOIDetails.js index 37add50d..3d558995 100644 --- a/frontend/src/components/Layout/TrainingDS/DatasetEditor/AOIDetails.js +++ b/frontend/src/components/Layout/TrainingDS/DatasetEditor/AOIDetails.js @@ -1,47 +1,50 @@ -import { Typography } from '@material-ui/core'; -import React from 'react' -import { useQuery } from 'react-query'; +import { Typography } from "@material-ui/core"; +import React from "react"; +import { useQuery } from "react-query"; -import axios from '../../../../axios' +import axios from "../../../../axios"; -import {timeSince,aoiStatusText} from '../../../../utils' -const AOIDetails = props => -{ +import { timeSince, aoiStatusText } from "../../../../utils"; +const AOIDetails = (props) => { + // console.log("rendering AOIDetails",props) + const fetchAOI = async () => { + try { + const res = await axios.get(`/aoi/${props.aoiId}/`); - // console.log("rendering AOIDetails",props) - const fetchAOI = async () => { + if (res.error) { + // setMapError(res.error.response.statusText); + console.log(res.error.response.statusText); + } else { + // success full fetch + // console.log("API details, ",props.aoiId,res.data); + return res.data; + } + } catch (e) { + console.log("isError", e); + } finally { + } + }; + const { data } = useQuery("fetchAOI" + props.aoiId, fetchAOI, { + refetchInterval: 5000, + }); - try { - - - const res = await axios.get(`/aoi/${props.aoiId}/`); - - if (res.error){ - // setMapError(res.error.response.statusText); - console.log(res.error.response.statusText); - } - else - { - - // success full fetch - // console.log("API details, ",props.aoiId,res.data); - return res.data; - } - - } catch (e) { - console.log("isError",e); - - } finally { - - } - }; - const { data } = useQuery("fetchAOI" + props.aoiId,fetchAOI,{refetchInterval:5000}); - - return <> - {data && - {aoiStatusText(data.properties.download_status)} {data.properties.last_fetched_date && timeSince(new Date(data.properties.last_fetched_date),new Date()) } - } + return ( + <> + {data && ( + + {aoiStatusText(data.properties.label_status)}{" "} + {data.properties.label_fetched && + timeSince(new Date(data.properties.label_fetched), new Date())} + + )} -} + ); +}; -export default AOIDetails \ No newline at end of file +export default AOIDetails; diff --git a/frontend/src/components/Layout/TrainingDS/DatasetEditor/DatasetMap.js b/frontend/src/components/Layout/TrainingDS/DatasetEditor/DatasetMap.js index db432b65..11ce4017 100644 --- a/frontend/src/components/Layout/TrainingDS/DatasetEditor/DatasetMap.js +++ b/frontend/src/components/Layout/TrainingDS/DatasetEditor/DatasetMap.js @@ -23,7 +23,11 @@ import { multiPolygon } from "@turf/helpers"; import axios from "../../../../axios"; -import { approximateGeom } from "../../../../utils"; +import { + approximateGeom, + converToGeoPolygon, + converToPolygon, +} from "../../../../utils"; import DatasetEditorHeader from "./DatasetEditorHeader"; import AuthContext from "../../../../Context/AuthContext"; const DatasetMap = (props) => { @@ -415,29 +419,6 @@ const DatasetMap = (props) => { const _onEditStop = (e) => { setIsEditing(false); }; - const converToPolygon = (layer) => { - const allPoly = []; - layer.forEach((element) => { - const x = element.latlngs.map((e) => [e.lat, e.lng]); - allPoly.push([x]); - }); - return allPoly; - }; - - const converToGeoPolygon = (layer) => { - if (layer.length === 0) return []; - const allPoly = converToPolygon(layer); - - // console.log("converToGeoPolygon",allPoly) - - const newAll = []; - allPoly.forEach((element) => { - const x = [...element[0], element[0][0]]; - - newAll.push([x]); - }); - return newAll; - }; const blueOptions = { color: "#03002e", width: 10, opacity: 1 }; diff --git a/frontend/src/utils.js b/frontend/src/utils.js index 281356a0..f9a2b5c3 100644 --- a/frontend/src/utils.js +++ b/frontend/src/utils.js @@ -3,7 +3,7 @@ export const timeSince = (when, then) => { const seconds = (then.getTime() - when.getTime()) / 1000; if (seconds > 86400 * 30) - return " +" + ((seconds / (86400 * 30))).toFixed() + " month(s) ago"; + return " +" + (seconds / (86400 * 30)).toFixed() + " month(s) ago"; if (seconds > 604800) return " +" + (seconds / 604800).toFixed() + " week(s) ago"; if (seconds > 86400) @@ -164,4 +164,28 @@ export const timeSpan = (start, end) => { const delta = e - s; return (Math.abs(delta) / 36e5).toFixed(2); } else return ""; -}; \ No newline at end of file +}; + +export const converToPolygon = (layer) => { + const allPoly = []; + layer.forEach((element) => { + const x = element.latlngs.map((e) => [e.lat, e.lng]); + allPoly.push([x]); + }); + return allPoly; +}; + +export const converToGeoPolygon = (layer) => { + if (layer.length === 0) return []; + const allPoly = converToPolygon(layer); + + // console.log("converToGeoPolygon",allPoly) + + const newAll = []; + allPoly.forEach((element) => { + const x = [...element[0], element[0][0]]; + + newAll.push([x]); + }); + return newAll; +};