From 3fe346610b2ab37a928922294dd0a0e2407c9eb0 Mon Sep 17 00:00:00 2001 From: Eli Gothill Date: Thu, 24 Nov 2016 14:41:25 +0100 Subject: [PATCH 1/4] Verbose map instance endpoint --- places/api.py | 51 +++++++++++++++++++++++++++++++++++------------- places/models.py | 2 +- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/places/api.py b/places/api.py index 03382bd..59c5827 100644 --- a/places/api.py +++ b/places/api.py @@ -15,17 +15,50 @@ class MapOwnerViewSet(viewsets.ModelViewSet): queryset = MapOwner.objects.all() serializer_class = MapOwnerSerializer -# Map Instance -class MapInstanceSerializer(serializers.HyperlinkedModelSerializer): + +# Map Data + +class MapDataSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = MapData + fields = ('map_instance', 'map_object', 'schema_field', 'field_value', 'date_created', 'date_modified') + +class MapDataViewSet(viewsets.ModelViewSet): + queryset = MapData.objects.all() + serializer_class = MapDataSerializer + + + +# Map Instance (lite) + +class MapInstanceSerializer(serializers.ModelSerializer): + class Meta: model = MapInstance - fields = ('schema', 'map_definition', 'date_created', 'date_modified') + fields = ('id', 'schema', 'map_definition', 'date_created', 'date_modified',) class MapInstanceViewSet(viewsets.ModelViewSet): queryset = MapInstance.objects.all() serializer_class = MapInstanceSerializer + +# Map Instance (verbose) + +class MapInstanceVerboseSerializer(serializers.ModelSerializer): + + class Meta: + model = MapInstance + depth = 2 + fields = ('id', 'schema', 'map_definition', 'date_created', 'date_modified', 'mapdata_set') + +class MapInstanceVerboseViewSet(viewsets.ModelViewSet): + queryset = MapInstance.objects.all() + serializer_class = MapInstanceVerboseSerializer + + + + # Map Object class MapObjectSerializer(serializers.HyperlinkedModelSerializer): @@ -36,17 +69,6 @@ class Meta: class MapObjectViewSet(viewsets.ModelViewSet): queryset = MapObject.objects.all() serializer_class = MapObjectSerializer - -# Map Data - -class MapDataSerializer(serializers.HyperlinkedModelSerializer): - class Meta: - model = MapData - fields = ('map_instance', 'map_object', 'schema_field', 'field_value', 'date_created', 'date_modified') - -class MapDataViewSet(viewsets.ModelViewSet): - queryset = MapData.objects.all() - serializer_class = MapDataSerializer # Map Definition @@ -88,6 +110,7 @@ class SchemaVersionViewSet(viewsets.ModelViewSet): router.register('map_owners', MapOwnerViewSet) router.register('map_instance', MapInstanceViewSet) +router.register('map_instance_verbose', MapInstanceVerboseViewSet) router.register('map_object', MapObjectViewSet) router.register('map_data', MapDataViewSet) router.register('map_definition', MapDefinitionViewSet) diff --git a/places/models.py b/places/models.py index a1ef4c2..95ffea1 100644 --- a/places/models.py +++ b/places/models.py @@ -49,7 +49,7 @@ class MapInstance(models.Model): date_modified = models.DateTimeField(auto_now=True) def __unicode__(self): - return u'%s' % self.map_definition + return u'%s %s' % (self.map_definition, self.id) class Meta: db_table = 'map_instances' From ef4b0facb1c85525bc0cd2a179f35d195f10d0d8 Mon Sep 17 00:00:00 2001 From: Eli Gothill Date: Tue, 29 Nov 2016 14:07:18 +0100 Subject: [PATCH 2/4] New API endpoint --- places/views.py | 77 +++++++++++++++++++++++++++++++++++++++++++++++++ tfm/urls.py | 3 +- 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/places/views.py b/places/views.py index 91ea44a..ac45130 100644 --- a/places/views.py +++ b/places/views.py @@ -1,3 +1,80 @@ +from urlparse import urlparse + from django.shortcuts import render +from django.http import HttpResponse, JsonResponse +from django.core.exceptions import ObjectDoesNotExist + +from places.models import MapInstance, MapData, MapObject # Create your views here. + +def map_instance_geojson(request, map_instance_id): + + ''' + Fetched all objects (and properties) for a given map instance, + returns a GEOJSON feature collection. + ''' + + # Fetch the map instance + + try: + map_instance = MapInstance.objects.get(id=map_instance_id) + except ObjectDoesNotExist: + return JsonResponse({"Error" : "Map Instance with ID %s does not exist!" % map_instance_id}) + + # Deduce URL from request + + request_url = urlparse(request.build_absolute_uri()) + base_url = 'http://%s%s' % (request_url.netloc, request_url.path) + + # Set pagination params + + page = int(request.GET.get('page', 1)) + x = (page - 1) * 25 + y = page * 25 + + # Prepare GEOJSON response + + response = { + "type" : "Feature Collection", + "count" : 25, + "next" : "%s?page=%s" % (base_url, page + 1), + } + + if page <= 1: + response['previous'] = None + else: + response['previous'] = "%s?page=%s" % (base_url, page - 1) + + # Fetch and serialise map objects + + map_objects = MapObject.objects.filter(map_instance=map_instance).order_by('id')[x:y] + + features = [] + for map_object in map_objects: + feature = { + "type" : "Feature", + "geometry" : { + "type" : "Point", + "coordinates": [ + float(map_object.longitude), + float(map_object.latitude), + ] + }, + "properties" : {} + } + + object_data = MapData.objects.filter(map_object=map_object) + + for field in object_data: + field_name = field.schema_field + feature["properties"][field.schema_field.field_name] = field.field_value + + features.append(feature) + + response['features'] = features + + return JsonResponse(response, safe=False) + + + \ No newline at end of file diff --git a/tfm/urls.py b/tfm/urls.py index 8a8ce27..77d0fb6 100644 --- a/tfm/urls.py +++ b/tfm/urls.py @@ -1,10 +1,11 @@ from django.conf.urls import url, include from django.contrib import admin -import places.api +import places.api, places.views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^places-api/', include(places.api.router.urls)), + url(r'^map-instance/(?P\d+)/$', places.views.map_instance_geojson) ] From 6f61a4297fdf571cc9779c1aae24dc7ceb789058 Mon Sep 17 00:00:00 2001 From: Eli Gothill Date: Tue, 29 Nov 2016 14:10:42 +0100 Subject: [PATCH 3/4] Removed old endpoint --- places/api.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/places/api.py b/places/api.py index 59c5827..a6efdf2 100644 --- a/places/api.py +++ b/places/api.py @@ -41,22 +41,6 @@ class Meta: class MapInstanceViewSet(viewsets.ModelViewSet): queryset = MapInstance.objects.all() serializer_class = MapInstanceSerializer - - -# Map Instance (verbose) - -class MapInstanceVerboseSerializer(serializers.ModelSerializer): - - class Meta: - model = MapInstance - depth = 2 - fields = ('id', 'schema', 'map_definition', 'date_created', 'date_modified', 'mapdata_set') - -class MapInstanceVerboseViewSet(viewsets.ModelViewSet): - queryset = MapInstance.objects.all() - serializer_class = MapInstanceVerboseSerializer - - # Map Object @@ -110,7 +94,6 @@ class SchemaVersionViewSet(viewsets.ModelViewSet): router.register('map_owners', MapOwnerViewSet) router.register('map_instance', MapInstanceViewSet) -router.register('map_instance_verbose', MapInstanceVerboseViewSet) router.register('map_object', MapObjectViewSet) router.register('map_data', MapDataViewSet) router.register('map_definition', MapDefinitionViewSet) From 3c4f34610c084e091c57bd8bd40e58602e0a3482 Mon Sep 17 00:00:00 2001 From: Eli Gothill Date: Tue, 29 Nov 2016 14:19:46 +0100 Subject: [PATCH 4/4] Updated readme --- README.md | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index be3cea5..6d96b53 100644 --- a/README.md +++ b/README.md @@ -18,18 +18,30 @@ several operations for querying data inside JSONB fields. See: https://docs.djangoproject.com/en/1.10/ref/contrib/postgres/fields/#containment-and-key-operations -## Usage - - python manage.py transformap -i jobs/jobfile.json - -Admin interface: /admin -API URL: /places-api/ -New jobs should be created as .YAML files in jobs/ - ## Misc ETL business logic: places/management/commands REST API configuration: places/api.py +## Deployment + +For development, testing or demo purposes, use the Django development server: + +python manage.py runserver 0.0.0.0:9000 + +Navigate to http://localhost:9000 ... + +- /admin for the Admin area +- /places-api for the REST API interface +- /map-instance/MAP-INSTANCE-ID for the GEOJSON API + +For production use, deploy Django using WSGI. See: +https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/ + +## Usage + +To run a job, create a YAML job file in /jobs, then: + python manage.py transformap -i jobs/jobfile.yaml +This fetch and save the partner's data to the database. \ No newline at end of file