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

Feature/feedback v1 #154

Merged
merged 24 commits into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
dca22f0
in dataset and mapping pages, show tile separaters
omranlm Aug 10, 2023
2c953af
Feedback page based on training Id and initial map for feedback AOIs
omranlm Aug 10, 2023
9e11ebc
Added api for feedback labela and osm fetch
kshitijrajsharma Aug 11, 2023
0ae085d
Merge pull request #149 from hotosm/feature/feedback_label_api
kshitijrajsharma Aug 11, 2023
4aaf36a
Fix bugs on Feedback label overwrite
kshitijrajsharma Aug 14, 2023
ee3bd1c
Merge pull request #150 from hotosm/hotfix/feedback_labels
kshitijrajsharma Aug 14, 2023
147065d
Merge branch 'feature/feedback_v1' of https://github.com/hotosm/fAIr …
omranlm Aug 14, 2023
c7c9e76
fix learn section refresh error
omranlm Aug 14, 2023
9e0a351
Implemented API for apply/feedback
kshitijrajsharma Aug 14, 2023
105d7dd
Added feedback aoi gpx and changed url for feedback submit
kshitijrajsharma Aug 14, 2023
8f76f43
Merge pull request #151 from hotosm/feature/apply_feedback
kshitijrajsharma Aug 14, 2023
9e94555
Merge pull request #152 from hotosm/feature/gpx_feedback_aoi
kshitijrajsharma Aug 14, 2023
c7e1ec6
Merge branch 'feature/feedback_v1' of https://github.com/hotosm/fAIr …
omranlm Aug 14, 2023
f83ffed
feedback till submission and successfull training
omranlm Aug 15, 2023
93e4728
Show original DS AOIs & sucess message
omranlm Aug 15, 2023
9153d9c
dataset edirot enhanced layout
omranlm Aug 15, 2023
faa9991
Fixes #153 , Adds validation for feedbackparam
kshitijrajsharma Aug 15, 2023
5fa4988
Merge pull request #155 from hotosm/fix/validation_epoch_batch
omranlm Aug 15, 2023
35b4a62
Validation for exisiting trainings
kshitijrajsharma Aug 15, 2023
a71eb72
Add volume for postgres !
kshitijrajsharma Aug 15, 2023
fcee3f3
feedback only on published training
omranlm Aug 15, 2023
d9a94e6
load labels on zoom 18
omranlm Aug 16, 2023
1d6df4a
support private TMS
omranlm Aug 21, 2023
050d20f
Fix bug on downloadin image
kshitijrajsharma Aug 21, 2023
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: 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