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

Initial code for serving bald triples #1

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM python:3.6
ADD . /todo
WORKDIR /todo
RUN pip install -r requirements.txt
38 changes: 37 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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)
92 changes: 92 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
@@ -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)
20 changes: 20 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -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"
11 changes: 11 additions & 0 deletions example-uploader.sh
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
flask
pymongo
23 changes: 23 additions & 0 deletions static/styles.css
Original file line number Diff line number Diff line change
@@ -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;
}
17 changes: 17 additions & 0 deletions templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<html>

<head>
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>

<body>

<h2>List of datasets</h2>

{% for item in items %}
<p> <a href="/view?id={{item.id}}">{{ item.id}} </a><p>
{% endfor %}

</body>

</html>
50 changes: 50 additions & 0 deletions templates/view.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<html>
<head>

<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">

<script type="application/ld+json">
{% autoescape false %}
{{jsonld}}
{% endautoescape %}
</script>

</head>

<body>
<h2> Viewing Dataset '{{dataset.id}}' </h2>

<div class='pullright' style="padding-bottom: 10px">
<a href="/view?id={{dataset.id}}&format=jsonld">View json-ld</a>
</div>

<div style="clear:both">
<table>
<tr>
<th>Key</th>
<th>Value</th>
</tr>
{% for key, value in dataset.items() %}
<tr>
<td>{{key}}</td>
<td>
{% if key == 'keywords' %}
{% for var in value %}
<p>{{var}}</p>
{% endfor %}
{% else %}
{{value}}
{% endif %}
</td>
</tr>
{% endfor %}
</table>
</div>
<br/>

<br/>
<br/>
<a href='/'>Back to index</a>
</body>

</html>