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 a74eece..a8906cd 100644 --- a/backend.py +++ b/backend.py @@ -1,29 +1,53 @@ # backend.py # openeventdatabase +import os import falcon import psycopg2 import uuid import json +import codecs + +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) + 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 = 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() 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): def on_get(self, req, resp, id = None): - db = psycopg2.connect("dbname=oedb") + db = db_connect() cur = db.cursor() if id is None: cur.execute(""" @@ -44,9 +68,7 @@ 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 @@ -55,9 +77,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') @@ -78,7 +98,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']) @@ -99,16 +119,16 @@ WHERE events_id=%s;""", (id,)) resp.body = """{"id":"%s"}""" % (e[0]) resp.status = falcon.HTTP_201 - # falcon.API instances are callable WSGI apps app = falcon.API() # Resources are represented by long-lived class instances +events = EventsResource() event = EventResource() stats = StatsResource() # 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) - 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/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 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