diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..51668a2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,4 @@ +FROM python:3.6 +ADD . /todo +WORKDIR /todo +RUN pip install -r requirements.txt diff --git a/README.md b/README.md index 3096154..3746c4f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,38 @@ # bald-server -Web server for BALD graphs +This repo provides landing pages and web APIs for BALD graphs - exposed as RDF triples or JSON-LD (schema.org profile). + +## Current features +* REST API +* HTML Templated content with schema.org embedded in + +## To do +* RDF Triple store backend serving BALD graphs + + +## Pre-requisites +* Docker 1.6+ +* docker-compose 1.3+ + +## Quickstart + +Spin up the python flask app and mongo via docker-compose +``` +$ docker-compose up -d +``` + +Your application should be now running on http://localhost:4000 + +## Usage + +## Loading schema.org descriptions + +Assuming you have a directory of schema.org descriptions as json-ld, you can use the `example-uploader.sh` (as-is or customised), +to upload content to via APIs. This will then be listed in the application running on http://localhost:4000 + +A possible way to get a list of these descriptions is to use the threddsnc2rdf.py tool in the `bald` library. + +### Routes + +* GET '/' - list of datasets +* GET '/view?id=param1&format=param2' - view the dataset. param1 is the ID of the dataset, param2 (optional) currently allows 'jsonld' +* POST '/new' - add a new dataset description in JSON format (schema.org) diff --git a/app.py b/app.py new file mode 100644 index 0000000..39bc967 --- /dev/null +++ b/app.py @@ -0,0 +1,92 @@ +import os +from functools import wraps +from flask import Flask, redirect, url_for, request, render_template, jsonify, abort +from pymongo import MongoClient +from bson import json_util +from bson.json_util import dumps +import json +import uuid +from flask.logging import default_handler + +app = Flask(__name__) + +client = MongoClient( + os.environ['DB_PORT_27017_TCP_ADDR'], + 27017) +db = client.datasets + +uuid.uuid4().hex + +if 'SECRET_KEY' in os.environ and os.environ['SECRET_KEY'] is not None: + authtoken = os.environ['SECRET_KEY'] +# app.logger.debug("Auth token is " + str(authtoken)) +else: + #generate a unique auth token + authtoken = uuid.uuid4().hex +# app.logger.debug("Auth token is " + str(authtoken)) + app.logger.debug("Use this for posting content") +authtoken = "SECRET" +print("Auth token is " + str(authtoken)) + +def authorize(f): + @wraps(f) + def decorated_function(*args, **kws): + print(request) + if not 'Authorization' in request.headers: + abort(401) + + user = None + data = request.headers['Authorization'].encode('ascii','ignore') + token = str.replace(str(data), 'Bearer ','') + #try: + # user = jwt.decode(token, JWT_SECRET, algorithms=['HS256'])['sub'] + #except: + # abort(401) + if token != authtoken: + abort(401) + return f(user, *args, **kws) + return decorated_function + +def isAuthorised(token): + if token == authtoken: + return True + return False + +@app.route('/') +def list(): + _items = db.datasets.find() + items = [item for item in _items] + return render_template('index.html', items=items) + +@app.route('/view') +def view(): + app.logger.debug(request.args) + id = request.args.get('id') + item = db.datasets.find_one( { 'id': id }) + item.pop('_id') + jsonldStr = dumps(item,default=json_util.default, indent=4) + app.logger.debug(jsonldStr) + + if request.args.get('format') is not None and request.args.get('format') == 'jsonld': + return jsonify(item) + + return render_template('view.html', dataset=item, jsonld=jsonldStr) + +@app.route('/new', methods=['POST']) +def new(): + app.logger.debug("JSON received...") + if not 'Authorization' in request.headers: + abort(401) + print(request.headers['Authorization']) + data = request.headers['Authorization'] + token = str.replace(str(data), 'Bearer ', '') + if token != authtoken: + return jsonify({ 'result' : 'not authorised' }),401 + content = request.json + #db.datasets.insert_one(content, check_keys=False) + db.datasets.insert(content, check_keys=False) + + return jsonify({ 'result' : 'success' }),200 + +if __name__ == "__main__": + app.run(host='0.0.0.0', debug=True) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b5b4496 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,20 @@ +web: + build: . + command: python -u app.py + ports: + - "4000:5000" + volumes: + - .:/todo + links: + - db +db: + image: mongo:latest + ports: + - "27017:27017" + +fuseki: + image: stain/jena-fuseki + volumes: + - ./rdf:/staging + ports: + - "3030:3030" diff --git a/example-uploader.sh b/example-uploader.sh new file mode 100755 index 0000000..e65e9d3 --- /dev/null +++ b/example-uploader.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +DIR=$1 +AUTHTOKEN=$2 +declare -a curlArgs=('-H' "Content-Type: application/json" '-H' "Authorization: Bearer ${AUTHTOKEN}") + +for f in $(find $1 -name '*.json'); +do + echo "curl -vX POST http://localhost:4000/new?token=${AUTHTOKEN} -d @${f} ${curlArgs[@]}\"" + curl -vX POST http://localhost:4000/new -d @${f} "${curlArgs[@]}" +done diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a98715e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +flask +pymongo diff --git a/static/styles.css b/static/styles.css new file mode 100644 index 0000000..8dd0728 --- /dev/null +++ b/static/styles.css @@ -0,0 +1,23 @@ +@import url('https://fonts.googleapis.com/css?family=Open+Sans'); + +body { +font-family: 'Open Sans', sans-serif; +} + +table { + border-collapse: collapse; +} + +table, th, td { + border: 1px solid black; +} + +td { + padding: 2px 2px 2px 2px; + vertical-align: top; +} + +.pullright { + float: right; + clear: both; +} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..420d6df --- /dev/null +++ b/templates/index.html @@ -0,0 +1,17 @@ + + + + + + + + +

List of datasets

+ +{% for item in items %} +

{{ item.id}}

+{% endfor %} + + + + diff --git a/templates/view.html b/templates/view.html new file mode 100644 index 0000000..475addd --- /dev/null +++ b/templates/view.html @@ -0,0 +1,50 @@ + + + + + + + + + + +

Viewing Dataset '{{dataset.id}}'

+ +
+ View json-ld +
+ +
+ + + + + +{% for key, value in dataset.items() %} + + + + +{% endfor %} +
KeyValue
{{key}} + {% if key == 'keywords' %} + {% for var in value %} +

{{var}}

+ {% endfor %} + {% else %} + {{value}} + {% endif %} +
+
+
+ +
+
+Back to index + + +