Skip to content

Commit

Permalink
Merge pull request #154 from hotosm/feature/feedback_v1
Browse files Browse the repository at this point in the history
Feature/feedback v1
  • Loading branch information
kshitijrajsharma authored Aug 21, 2023
2 parents c13cbb6 + 050d20f commit 39a597b
Show file tree
Hide file tree
Showing 50 changed files with 1,594 additions and 384 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ frontend/.pnp.js

# testing
frontend/coverage
postgres-data/*

# production
frontend/build
Expand Down
18 changes: 14 additions & 4 deletions backend/core/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@


@admin.register(Dataset)
class DatasetAdmin(geoadmin.GeoModelAdmin):
class DatasetAdmin(geoadmin.OSMGeoAdmin):
list_display = ["name", "created_by"]


@admin.register(Model)
class ModelAdmin(geoadmin.GeoModelAdmin):
list_display = ["get_dataset_id", "name", "status", "created_at"]
class ModelAdmin(geoadmin.OSMGeoAdmin):
list_display = ["get_dataset_id", "name", "status", "created_at", "created_by"]

def get_dataset_id(self, obj):
return obj.dataset.id
Expand All @@ -22,7 +22,7 @@ def get_dataset_id(self, obj):


@admin.register(Training)
class TrainingAdmin(geoadmin.GeoModelAdmin):
class TrainingAdmin(geoadmin.OSMGeoAdmin):
list_display = [
"get_model_id",
"description",
Expand All @@ -37,3 +37,13 @@ def get_model_id(self, obj):
return obj.model.id

get_model_id.short_description = "Model"


@admin.register(FeedbackAOI)
class FeedbackAOIAdmin(geoadmin.OSMGeoAdmin):
list_display = ["training", "user"]


@admin.register(Feedback)
class FeedbackAdmin(geoadmin.OSMGeoAdmin):
list_display = ["feedback_type", "training", "user", "created_at"]
77 changes: 65 additions & 12 deletions backend/core/serializers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.conf import settings
from login.models import OsmUser
from rest_framework import serializers
from rest_framework_gis.serializers import (
Expand Down Expand Up @@ -102,32 +103,36 @@ def to_representation(self, instance):
return ret


class LabelSerializer(
GeoFeatureModelSerializer
): # serializers are used to translate models objects to api
class LabelSerializer(GeoFeatureModelSerializer):
class Meta:
model = Label
geo_field = "geom" # this will be used as geometry in order to create geojson api , geofeatureserializer will let you create api in geojson
geo_field = "geom"
# auto_bbox = True
fields = "__all__" # defining all the fields to be included in curd for now , we can restrict few if we want
fields = "__all__"

read_only_fields = ("created_at", "osm_id")
# 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")
# read_only_fields = ("created_at", "osm_id")


class LabelFileSerializer(
GeoFeatureModelSerializer
): # serializers are used to translate models objects to api
class LabelFileSerializer(GeoFeatureModelSerializer):
class Meta:
model = Label
geo_field = "geom" # this will be used as geometry in order to create geojson api , geofeatureserializer will let you create api in geojson
geo_field = "geom"
# auto_bbox = True
fields = ("osm_id",)


class FeedbackLabelFileSerializer(GeoFeatureModelSerializer):
class Meta:
model = FeedbackLabel
geo_field = "geom"
# auto_bbox = True
fields = ("osm_id",)

Expand Down Expand Up @@ -160,7 +165,55 @@ class FeedbackParamSerializer(serializers.Serializer):
training_id = serializers.IntegerField(required=True)
epochs = serializers.IntegerField(required=False)
batch_size = serializers.IntegerField(required=False)
freeze_layers = serializers.BooleanField(required=False)
zoom_level = serializers.ListField(child=serializers.IntegerField(), required=False)

def validate_training_id(self, value):
try:
Training.objects.get(id=value)
except Training.DoesNotExist:
raise serializers.ValidationError("Training doesn't exist")

return value

def validate(self, data):
training_id = data.get("training_id")

try:
fd_aois = FeedbackAOI.objects.filter(training=training_id)
except FeedbackAOI.DoesNotExist:
raise serializers.ValidationError(
"No feedback AOI is associated with Training"
)

if fd_aois.filter(
label_status=FeedbackAOI.DownloadStatus.NOT_DOWNLOADED
).exists():
raise serializers.ValidationError(
"Not all AOIs have their labels downloaded"
)

if "epochs" in data and (
data["epochs"] > settings.EPOCHS_LIMIT or data["epochs"] <= 0
):
raise serializers.ValidationError(
f"Epochs should be 1 - {settings.EPOCHS_LIMIT} on this server"
)

if "batch_size" in data and (
data["batch_size"] > settings.BATCH_SIZE_LIMIT or data["batch_size"] <= 0
):
raise serializers.ValidationError(
f"Batch size should be 1 - {settings.BATCH_SIZE_LIMIT} on this server"
)

if "zoom_level" in data:
for zoom in data["zoom_level"]:
if zoom < 19 or zoom > 21:
raise serializers.ValidationError(
"Zoom level must be between 19 and 21"
)

return data


class PredictionParamSerializer(serializers.Serializer):
Expand Down
94 changes: 32 additions & 62 deletions backend/core/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@
import ramp.utils
import tensorflow as tf
from celery import shared_task
from core.models import AOI, Feedback, Label, Training
from core.serializers import FeedbackFileSerializer, LabelFileSerializer
from core.models import AOI, Feedback, FeedbackAOI, FeedbackLabel, Label, Training
from core.serializers import (
FeedbackFileSerializer,
FeedbackLabelFileSerializer,
LabelFileSerializer,
)
from core.utils import bbox, download_imagery, get_start_end_download_coords
from django.conf import settings
from django.contrib.gis.db.models.aggregates import Extent
Expand Down Expand Up @@ -65,34 +69,32 @@ def train_model(
shutil.rmtree(training_input_image_source)
os.makedirs(training_input_image_source)
if feedback:
feedback_objects = Feedback.objects.filter(
training__id=feedback,
validated=True,
)
bbox_feedback = feedback_objects.aggregate(Extent("geom"))[
"geom__extent"
]
bbox_geo = GEOSGeometry(
f"POLYGON(({bbox_feedback[0]} {bbox_feedback[1]},{bbox_feedback[2]} {bbox_feedback[1]},{bbox_feedback[2]} {bbox_feedback[3]},{bbox_feedback[0]} {bbox_feedback[3]},{bbox_feedback[0]} {bbox_feedback[1]}))"
)
print(training_input_image_source)
print(bbox_feedback)
with open(
os.path.join(training_input_image_source, "labels_bbox.geojson"),
"w",
encoding="utf-8",
) as f:
f.write(bbox_geo.geojson)
try:
aois = FeedbackAOI.objects.filter(training=feedback)
except FeedbackAOI.DoesNotExist:
raise ValueError(
f"No Feedback AOI is attached with supplied training id:{dataset_id}, Create AOI first",
)

else:
try:
aois = AOI.objects.filter(dataset=dataset_id)
except AOI.DoesNotExist:
raise ValueError(
f"No AOI is attached with supplied dataset id:{dataset_id}, Create AOI first",
)

for obj in aois:
bbox_coords = bbox(obj.geom.coords[0])
for z in zoom_level:
zm_level = z
print(
f"""Running Download process for
feedback {training_id} - dataset : {dataset_id} , zoom : {zm_level}"""
aoi : {obj.id} - dataset : {dataset_id} , zoom : {zm_level}"""
)
try:
tile_size = DEFAULT_TILE_SIZE # by default
bbox_coords = list(bbox_feedback)

start, end = get_start_end_download_coords(
bbox_coords, zm_level, tile_size
)
Expand All @@ -107,49 +109,17 @@ def train_model(
except Exception as ex:
raise ex

else:
try:
aois = AOI.objects.filter(dataset=dataset_id)
except AOI.DoesNotExist:
raise ValueError(
f"No AOI is attached with supplied dataset id:{dataset_id}, Create AOI first",
)

for obj in aois:
bbox_coords = bbox(obj.geom.coords[0])
for z in zoom_level:
zm_level = z
print(
f"""Running Download process for
aoi : {obj.id} - dataset : {dataset_id} , zoom : {zm_level}"""
)
try:
tile_size = DEFAULT_TILE_SIZE # by default

start, end = get_start_end_download_coords(
bbox_coords, zm_level, tile_size
)
# start downloading
download_imagery(
start,
end,
zm_level,
base_path=training_input_image_source,
source=source_imagery,
)
except Exception as ex:
raise ex

## -----------LABEL GENERATOR---------
logging.debug("Label Generator started")
logging.info("Label Generator started")
aoi_list = [r.id for r in aois]
logging.info(aoi_list)

if feedback:
feedback_objects = Feedback.objects.filter(
training__id=feedback,
validated=True,
)
serialized_field = FeedbackFileSerializer(feedback_objects, many=True)
label = FeedbackLabel.objects.filter(feedback_aoi__in=aoi_list)
logging.info(label)

serialized_field = FeedbackLabelFileSerializer(label, many=True)
else:
aoi_list = [r.id for r in aois]
label = Label.objects.filter(aoi__in=aoi_list)
serialized_field = LabelFileSerializer(label, many=True)

Expand Down
15 changes: 12 additions & 3 deletions backend/core/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
FeedbackLabelViewset,
FeedbackView,
FeedbackViewset,
GenerateFeedbackAOIGpxView,
GenerateGpxView,
LabelViewSet,
ModelViewSet,
PredictionView,
RawdataApiView,
RawdataApiAOIView,
RawdataApiFeedbackView,
TrainingViewSet,
TrainingWorkspaceDownloadView,
TrainingWorkspaceView,
Expand All @@ -39,15 +41,22 @@

urlpatterns = [
path("", include(router.urls)),
path("label/osm/fetch/<int:aoi_id>/", RawdataApiView.as_view()),
path("label/osm/fetch/<int:aoi_id>/", RawdataApiAOIView.as_view()),
path(
"label/feedback/osm/fetch/<int:feedbackaoi_id>/",
RawdataApiFeedbackView.as_view(),
),
# path("download/<int:dataset_id>/", download_training_data),
path("training/status/<str:run_id>/", run_task_status),
path("training/publish/<int:training_id>/", publish_training),
path("prediction/", PredictionView.as_view()),
path("apply/feedback/", FeedbackView.as_view()),
path("feedback/training/submit/", FeedbackView.as_view()),
path("status/", APIStatus.as_view()),
path("geojson2osm/", geojson2osmconverter, name="geojson2osmconverter"),
path("aoi/gpx/<int:aoi_id>/", GenerateGpxView.as_view()),
path(
"feedback-aoi/gpx/<int:feedback_aoi_id>/", GenerateFeedbackAOIGpxView.as_view()
),
path("workspace/", TrainingWorkspaceView.as_view()),
path(
"workspace/download/<path:lookup_dir>/", TrainingWorkspaceDownloadView.as_view()
Expand Down
Loading

0 comments on commit 39a597b

Please sign in to comment.