Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance : Feedback Implented New Models & Styles #145

Merged
merged 15 commits into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,5 @@ RUN pip install setuptools --upgrade
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt


WORKDIR /app
COPY . /app
41 changes: 31 additions & 10 deletions backend/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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):
Expand Down Expand Up @@ -89,19 +88,41 @@ 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)
created_at = models.DateTimeField(auto_now_add=True)
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)
39 changes: 33 additions & 6 deletions backend/core/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions backend/core/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
AOIViewSet,
APIStatus,
DatasetViewSet,
FeedbackAOIViewset,
FeedbackLabelViewset,
FeedbackView,
FeedbackViewset,
GenerateGpxView,
Expand All @@ -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 = [
Expand Down
46 changes: 40 additions & 6 deletions backend/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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),
Expand All @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ services:
volumes:
- ./backend:/app
- ${RAMP_HOME}:/RAMP_HOME
- ${TRAINING_WORKSPACE}:/TRAINING_WORKSPACE
# - ${TRAINING_WORKSPACE}:/TRAINING_WORKSPACE
depends_on:
- redis
- postgres
Expand All @@ -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
Expand Down
Binary file added frontend/public/rapid-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 60 additions & 0 deletions frontend/src/components/Layout/AIModels/AIModelEditor/Feedback.js
Original file line number Diff line number Diff line change
@@ -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 && (
<Tooltip
title={`Total number of feedback on this training is ${feedbackData.features.length}`}
placement="left"
>
<IconButton aria-label="popup">
<FeedbackIcon size="small" />
</IconButton>
</Tooltip>
)}
{isLoading && (
<IconButton aria-label="popup">
<FindReplaceIcon size="small" />
</IconButton>
)}
</>
);
};
export default Feedback;
27 changes: 17 additions & 10 deletions frontend/src/components/Layout/AIModels/AIModelEditor/Trainings.js
Original file line number Diff line number Diff line change
Expand Up @@ -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" }],
Expand Down Expand Up @@ -151,16 +152,22 @@ const TrainingsList = (props) => {
},
{
field: "popup",
headerName: "Info",
width: 10,
renderCell: (params) => (
<IconButton
onClick={() => handlePopupOpen(params.row)}
aria-label="popup"
>
<InfoIcon size="small" />
</IconButton>
),
headerName: "Info/Feedback",
width: 100,
renderCell: (params) => {
// console.log("params in info row", params);
return (
<div>
<IconButton
onClick={() => handlePopupOpen(params.row)}
aria-label="popup"
>
<InfoIcon size="small" />
</IconButton>
<Feedback trainingId={params.row.id}></Feedback>
</div>
);
},
},
{
field: "accuracy",
Expand Down
Loading
Loading