From 6ef16787ab778a42263148a6272bab72f7c0fa33 Mon Sep 17 00:00:00 2001 From: Laurent Bossavit Date: Wed, 4 May 2016 19:22:56 +0200 Subject: [PATCH 1/3] Rework Docker setup, provide for remote DB, add example JSON --- .gitignore | 2 +- Dockerfile | 15 -------------- Dockerfile-backend | 10 +++++++++ Dockerfile-postgres | 4 ++++ backend.py | 13 +++++++++--- docker-compose.yml | 21 +++++++++++++++++++ examples/event.json | 50 +++++++++++++++++++++++++++++++++++++++++++++ setup.sh | 3 --- 8 files changed, 96 insertions(+), 22 deletions(-) delete mode 100644 Dockerfile create mode 100644 Dockerfile-backend create mode 100644 Dockerfile-postgres create mode 100644 docker-compose.yml create mode 100644 examples/event.json delete mode 100755 setup.sh diff --git a/.gitignore b/.gitignore index 3b242c7..904be3d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ __pycache__/ *.py[cod] *$py.class - +.DS_Store diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 2ff707c..0000000 --- a/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM ubuntu:15.10 -WORKDIR /setup -RUN apt-get update -RUN apt-get install -y postgresql-9.4 -RUN apt-get install -y postgresql-server-dev-9.4 -RUN apt-get install -y postgis -RUN apt-get install -y python3-dev -RUN apt-get install -y python3-pip -RUN pip3 install --system uwsgi -ADD /setup* /setup/ -ADD /requirements.txt /setup/ -USER postgres -RUN service postgresql start && /setup/setup.sh -WORKDIR /app -CMD service postgresql start && uwsgi --http :8080 --wsgi-file backend.py --callable app diff --git a/Dockerfile-backend b/Dockerfile-backend new file mode 100644 index 0000000..5fe5e19 --- /dev/null +++ b/Dockerfile-backend @@ -0,0 +1,10 @@ +FROM ubuntu:15.10 +RUN apt-get update +RUN apt-get install -y postgresql-server-dev-all +RUN apt-get install -y python3-dev +RUN apt-get install -y python3-pip +RUN pip3 install uwsgi +ADD /requirements.txt /app/ +WORKDIR /app +RUN pip3 install -r requirements.txt +CMD uwsgi --http :8080 --wsgi-file backend.py --callable app diff --git a/Dockerfile-postgres b/Dockerfile-postgres new file mode 100644 index 0000000..211806d --- /dev/null +++ b/Dockerfile-postgres @@ -0,0 +1,4 @@ +FROM postgres:9.5 +RUN apt-get update +RUN apt-get install -y postgis +ADD /setup.sql /docker-entrypoint-initdb.d/ diff --git a/backend.py b/backend.py index 74ab95b..95ab272 100644 --- a/backend.py +++ b/backend.py @@ -1,14 +1,21 @@ # backend.py # openeventdatabase +import os import falcon import psycopg2 import uuid import json +def db_connect(): + db_host = os.getenv("DB_HOST","localhost") + db_password = os.getenv("POSTGRES_PASSWORD","") + db = psycopg2.connect(dbname="oedb",host=db_host,password=db_password,user="postgres") + return db + class StatsResource(object): def on_get(self, req, resp): - db = psycopg2.connect("dbname=oedb") + db = db_connect() cur = db.cursor() cur.execute("SELECT count(*) as events_count, max(createdate) as last_created, max(lastupdate) as last_updated from events;") stat = cur.fetchone() @@ -23,7 +30,7 @@ class StatsResource(object): class EventResource(object): def on_get(self, req, resp, id): - db = psycopg2.connect("dbname=oedb") + db = db_connect() cur = db.cursor() # get event geojson Feature cur.execute(""" @@ -66,7 +73,7 @@ WHERE events_id=%s;""", (id,)) else: when = "["+event_start+", "+event_stop+")" # connect to db and insert - db = psycopg2.connect("dbname=oedb") + db = db_connect() cur = db.cursor() # get the geometry part geometry=json.dumps(j['geometry']) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..c881130 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,21 @@ +version: '2' + +services: + postgres: + build: + context: . + dockerfile: Dockerfile-postgres + environment: + POSTGRES_DB: oedb + POSTGRES_PASSWORD: + backend: + build: + context: . + dockerfile: Dockerfile-backend + ports: + - 8080:8080 + environment: + DB_HOST: postgres + POSTGRES_PASSWORD: + volumes: + - .:/app/ diff --git a/examples/event.json b/examples/event.json new file mode 100644 index 0000000..e8c8ee7 --- /dev/null +++ b/examples/event.json @@ -0,0 +1,50 @@ +{ + "geometry": + { + "type": "Polygon", + "coordinates": [ + [ + [ + 2.3581337928771973, + 48.9261508727225 + ], + [ + 2.3581552505493164, + 48.925847757044224 + ], + [ + 2.3575007915496826, + 48.9258266093701 + ], + [ + 2.3577260971069336, + 48.92278125079759 + ], + [ + 2.3581552505493164, + 48.92259795940312 + ], + [ + 2.3625540733337402, + 48.92277420114101 + ], + [ + 2.362285852432251, + 48.92628480697097 + ], + [ + 2.3581337928771973, + 48.9261508727225 + ] + ] + ] + } + , + "properties":{ + "type":"football" + ,"what":"France-Roumanie 2016" + ,"what:series":"Euro 2016" + ,"start":"2016-06-10T21:00+01:00" + ,"stop":"2016-06-10T23:30+01:00" + } +} \ No newline at end of file diff --git a/setup.sh b/setup.sh deleted file mode 100755 index 36301d8..0000000 --- a/setup.sh +++ /dev/null @@ -1,3 +0,0 @@ -pip3 install -r requirements.txt -createdb oedb -psql oedb < setup.sql From 8bf79a12881075437aa6e0aeaf3a753cd372da45 Mon Sep 17 00:00:00 2001 From: Laurent Bossavit Date: Thu, 5 May 2016 22:31:54 +0200 Subject: [PATCH 2/3] Adds an editor for geometry and properties --- backend.py | 42 +++++++--- editor/event-schema.json | 17 ++++ editor/index.html | 167 +++++++++++++++++++++++++++++++++++++++ examples/other.json | 69 ++++++++++++++++ 4 files changed, 285 insertions(+), 10 deletions(-) create mode 100644 editor/event-schema.json create mode 100644 editor/index.html create mode 100644 examples/other.json diff --git a/backend.py b/backend.py index 95ab272..9e493b8 100644 --- a/backend.py +++ b/backend.py @@ -6,6 +6,7 @@ import falcon import psycopg2 import uuid import json +import codecs def db_connect(): db_host = os.getenv("DB_HOST","localhost") @@ -13,6 +14,11 @@ def db_connect(): db = psycopg2.connect(dbname="oedb",host=db_host,password=db_password,user="postgres") return db +def standard_headers(resp): + resp.set_header('X-Powered-By', 'OpenEventDatabase') + resp.set_header('Access-Control-Allow-Origin', '*') + resp.set_header('Access-Control-Allow-Headers', 'X-Requested-With') + class StatsResource(object): def on_get(self, req, resp): db = db_connect() @@ -22,10 +28,21 @@ class StatsResource(object): cur.close() db.close() + standard_headers(resp) resp.body = """{"events_count": %s, "last_created": "%s", "last_updated": "%s"}""" % (stat[0], stat[1],stat[2]) - resp.set_header('X-Powered-By', 'OpenEventDatabase') - resp.set_header('Access-Control-Allow-Origin', '*') - resp.set_header('Access-Control-Allow-Headers', 'X-Requested-With') + resp.status = falcon.HTTP_200 + +class EventsResource(object): + def on_get(self,req,resp): + db = db_connect() + cur = db.cursor() + # get event geojson Feature + cur.execute(""" +SELECT format('{"type":"Feature", "id": "'|| events_id::text ||'", "properties": '|| events_tags::text ||', "geometry":'|| st_asgeojson(geom)) ||' }' +FROM events +JOIN geo ON (hash=events_geo)"""); + standard_headers(resp) + resp.body = '{"type": "FeatureCollection","features": ['+','.join([x[0] for x in cur.fetchall()])+']}' resp.status = falcon.HTTP_200 class EventResource(object): @@ -39,9 +56,7 @@ FROM events JOIN geo ON (hash=events_geo) WHERE events_id=%s;""", (id,)) e = cur.fetchone() - resp.set_header('X-Powered-By', 'OpenEventDatabase') - resp.set_header('Access-Control-Allow-Origin', '*') - resp.set_header('Access-Control-Allow-Headers', 'X-Requested-With') + standard_headers(resp) if e is not None: resp.body = e[0] resp.status = falcon.HTTP_200 @@ -50,9 +65,7 @@ WHERE events_id=%s;""", (id,)) db.close() def on_post(self, req, resp): - resp.set_header('X-Powered-By', 'OpenEventDatabase') - resp.set_header('Access-Control-Allow-Origin', '*') - resp.set_header('Access-Control-Allow-Headers', 'X-Requested-With') + standard_headers(resp) # get request body payload (geojson Feature) body = req.stream.read().decode('utf-8') @@ -94,16 +107,25 @@ WHERE events_id=%s;""", (id,)) resp.body = """{"id":"%s"}""" % (e[0]) resp.status = falcon.HTTP_201 +class StaticResource(object): + def on_get(self, req, resp): + resp.status = falcon.HTTP_200 + resp.content_type = 'text/html' + with codecs.open('editor/index.html', 'r', 'utf-8') as f: + resp.body = f.read() # falcon.API instances are callable WSGI apps app = falcon.API() # Resources are represented by long-lived class instances +events = EventsResource() event = EventResource() stats = StatsResource() +editor = StaticResource() # things will handle all requests to the matching URL path +app.add_route('/events', events) app.add_route('/event/{id}', event) # handle single event requests app.add_route('/event', event) # handle single event requests app.add_route('/stats', stats) - +app.add_route('/editor', editor) diff --git a/editor/event-schema.json b/editor/event-schema.json new file mode 100644 index 0000000..f93009c --- /dev/null +++ b/editor/event-schema.json @@ -0,0 +1,17 @@ + +{ + "title": "Evenement", + "type": "object", + "id": "event", + "properties": { + "what" : { + "type":"string", + "title":"Libellé de l'événement" + }, + "when" : { + "title":"Date et heure de début", + "type" : "string", + "format" : "datetime-local" + } + } +} diff --git a/editor/index.html b/editor/index.html new file mode 100644 index 0000000..9c2bd6f --- /dev/null +++ b/editor/index.html @@ -0,0 +1,167 @@ + + + + + Détail d'un événement + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+

Events

+
+
+
+
+
+ +
+ + + + + + \ No newline at end of file diff --git a/examples/other.json b/examples/other.json new file mode 100644 index 0000000..b67f41e --- /dev/null +++ b/examples/other.json @@ -0,0 +1,69 @@ +{ + "geometry": + { + "type": "Polygon", + "coordinates": [ + [ + [ + 2.396119236946106, + 48.84943571607033 + ], + [ + 2.395314574241638, + 48.84936511598567 + ], + [ + 2.394644021987915, + 48.8490791846248 + ], + [ + 2.394316792488098, + 48.84860968884877 + ], + [ + 2.3943114280700684, + 48.84820020021693 + ], + [ + 2.3946386575698853, + 48.84773775644264 + ], + [ + 2.3950570821762085, + 48.84749064733159 + ], + [ + 2.3957008123397827, + 48.84733532098047 + ], + [ + 2.396720051765442, + 48.8474694664939 + ], + [ + 2.3973584175109863, + 48.84796368370562 + ], + [ + 2.39751398563385, + 48.84849672680203 + ], + [ + 2.3970580101013184, + 48.849114484881156 + ], + [ + 2.396119236946106, + 48.84943571607033 + ] + ] + ] + } + , + "properties":{ + "type":"protest" + ,"what":"Loi travail" + ,"start":"2016-06-10T21:00+01:00" + ,"stop":"2016-06-10T23:30+01:00" + } +} \ No newline at end of file From c2a7e37d85c11ee0ebc85ad936707b7212c22189 Mon Sep 17 00:00:00 2001 From: Laurent Bossavit Date: Fri, 6 May 2016 12:14:56 +0200 Subject: [PATCH 3/3] Remove editor, moved to its own repo --- backend.py | 9 --- editor/event-schema.json | 17 ---- editor/index.html | 167 --------------------------------------- 3 files changed, 193 deletions(-) delete mode 100644 editor/event-schema.json delete mode 100644 editor/index.html diff --git a/backend.py b/backend.py index 9e493b8..987c277 100644 --- a/backend.py +++ b/backend.py @@ -107,13 +107,6 @@ WHERE events_id=%s;""", (id,)) resp.body = """{"id":"%s"}""" % (e[0]) resp.status = falcon.HTTP_201 -class StaticResource(object): - def on_get(self, req, resp): - resp.status = falcon.HTTP_200 - resp.content_type = 'text/html' - with codecs.open('editor/index.html', 'r', 'utf-8') as f: - resp.body = f.read() - # falcon.API instances are callable WSGI apps app = falcon.API() @@ -121,11 +114,9 @@ app = falcon.API() events = EventsResource() event = EventResource() stats = StatsResource() -editor = StaticResource() # things will handle all requests to the matching URL path app.add_route('/events', events) app.add_route('/event/{id}', event) # handle single event requests app.add_route('/event', event) # handle single event requests app.add_route('/stats', stats) -app.add_route('/editor', editor) diff --git a/editor/event-schema.json b/editor/event-schema.json deleted file mode 100644 index f93009c..0000000 --- a/editor/event-schema.json +++ /dev/null @@ -1,17 +0,0 @@ - -{ - "title": "Evenement", - "type": "object", - "id": "event", - "properties": { - "what" : { - "type":"string", - "title":"Libellé de l'événement" - }, - "when" : { - "title":"Date et heure de début", - "type" : "string", - "format" : "datetime-local" - } - } -} diff --git a/editor/index.html b/editor/index.html deleted file mode 100644 index 9c2bd6f..0000000 --- a/editor/index.html +++ /dev/null @@ -1,167 +0,0 @@ - - - - - Détail d'un événement - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
-

Events

-
-
-
-
-
- -
- - - - - - \ No newline at end of file