# HG changeset patch # User Dennis Fink # Date 2015-07-04 16:43:26 # Node ID 00d1f8ab367582618bdebf8573e1305d1b5f79c3 # Parent f60824dd1290585ebf56d3ffbb17bb50666339e4 Added more development files diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1,13 @@ +alabaster==0.7.4 +Babel==1.3 +docutils==0.12 +Jinja2==2.7.3 +MarkupSafe==0.23 +pip-tools==0.3.6 +Pygments==2.0.2 +pytz==2015.4 +six==1.9.0 +snowballstemmer==1.2.0 +sphinx-rtd-theme==0.1.8 +Sphinx==1.3.1 +wheel==0.24.0 diff --git a/requirements.txt b/requirements.txt new file mode 100644 diff --git a/run.py b/run.py new file mode 100644 --- /dev/null +++ b/run.py @@ -0,0 +1,3 @@ +from spaceapi import app + +app.run(debug=True) diff --git a/setup.py b/setup.py new file mode 100644 --- /dev/null +++ b/setup.py @@ -0,0 +1,23 @@ +from pprint import pprint + +from setuptools import setup, find_packages + +packages = find_packages() +packages.append('spaceapi.templates') +packages.append('spaceapi.static') + +setup( + name='c3l_spaceapi', + version='0.0.1', + url=None, + license='GPLv3+', + author='Dennis Fink', + author_email='dennis.fink@c3l.lu', + description='spaceapi endpoint for c3l.lu', + packages=packages, + package_data={ + 'spaceapi.templates': ['*'], + 'spaceapi.static': ['*'] + }, + install_requires=['Flask', 'Flask-HTTPAuth'] +) diff --git a/spaceapi/__init__.py b/spaceapi/__init__.py --- a/spaceapi/__init__.py +++ b/spaceapi/__init__.py @@ -5,28 +5,56 @@ import calendar import base64 import copy -from datetime import datetime +from time import time +from ast import literal_eval from flask import Flask, jsonify, request, render_template from flask.ext.httpauth import HTTPDigestAuth +import jsonschema + +from pkg_resources import resource_filename + +ALLOWED_STATE_KEYS = { + 'open': bool, + 'lastchange': int, + 'trigger_person': str, + 'message': str +} + +ALLOWED_SENSORS_KEYS = json.load( + open(resource_filename('spaceapi', 'schema/sensors.json'), + encoding='utf-8') +) + config_file = os.path.abspath('config.json') default_json_file = os.path.abspath('default.json') last_state_file = os.path.abspath('laststate.json') -default_json = json.load(open(default_json_file, encoding='utf-8')) +default_json = {} +active_json = {} + -if os.path.exists(last_state_file): - with open(last_state_file, encoding='utf-8') as f: - active_json = json.load(f) +def reload_json(): + global default_json + global active_json + + default_json = json.load(open(default_json_file, encoding='utf-8')) - if os.path.getmtime(last_state_file) < os.path.getmtime(default_json_file): - backup = copy.deepcopy(active_json) - active_json.update(default_json) - active_json['state']['open'] = backup['state']['open'] - active_json['state']['lastchange'] = backup['state']['lastchange'] -else: - active_json = copy.deepcopy(default_json) + if os.path.exists(last_state_file): + with open(last_state_file, encoding='utf-8') as f: + active_json = json.load(f) + + if os.path.getmtime(last_state_file) \ + < os.path.getmtime(default_json_file): + backup = copy.deepcopy(active_json) + active_json.update(default_json) + active_json['state']['open'] = backup['state']['open'] + active_json['state']['lastchange'] = backup['state']['lastchange'] + else: + active_json = copy.deepcopy(default_json) + +reload_json() app = Flask(__name__) auth = HTTPDigestAuth() @@ -79,6 +107,7 @@ def save_last_state(): @app.route('/') def index(): + if request_wants_json(): return jsonify(active_json) return render_template('index.html', status=active_json) @@ -89,21 +118,93 @@ def status_json(): return jsonify(active_json) -@app.route('/set_state/', methods=('PUT',)) +@app.route('/set/state//', methods=['POST']) @auth.login_required -def set_state(state): +def set_state(key, value): + + value = literal_eval(value) - if state == 'open': - active_json['state']['open'] = True - elif state == 'close': - active_json['state']['open'] = False + if key in ALLOWED_STATE_KEYS and isinstance(value, + ALLOWED_STATE_KEYS[key]): + active_json['state'][key] = value + + if key == 'open': + active_json['state']['lastchange'] = int(time.time()) else: return 400 - active_json['state']['lastchange'] = calendar.timegm( - datetime.now().timetuple() - ) - save_last_state() return jsonify(active_json) + + +def fuzzy_list_find(lst, key, value): + + for i, dic in enumerate(lst): + if dic[key] == value: + return i + + raise ValueError + + +@app.route('/set/sensors/', methods=['POST']) +@auth.login_required +def set_sensors(key): + + if key in ALLOWED_SENSORS_KEYS and key in active_json['sensors']: + data = request.data + try: + data = json.loads(data) + try: + jsonschema.validate(data, ALLOWED_SENSORS_KEYS[key]) + + if key != 'radiation': + for subkey in ('name', 'location'): + if subkey in data: + index = fuzzy_list_find( + active_json['sensors'][key], + subkey, data[subkey] + ) + active_json['sensors'][key][index].update(data) + else: + return 400 + else: + for first_subkey in ('alpha', 'beta', + 'gamma' 'beta_gamma'): + for second_subkey in ('name', 'location'): + if second_subkey in data[first_subkey]: + index = fuzzy_list_find( + active_json['sensors'][key][first_subkey], + second_subkey, + data[first_subkey][second_subkey]) + active_json['sensors'][ + key + ][first_subkey][index].update(data) + else: + return 400 + + except jsonschema.ValidationError: + return 400 + except: + return 400 + else: + return 400 + + save_last_state() + + return jsonify(active_json) + + +@app.route('/reload') +@auth.login_required +def reload(): + reload_json() + return 200 + + +@app.after_request +def add_headers(response): + response.headers.setdefault('Access-Control-Allow-Origin', '*') + response.headers.setdefault('Cache-Control', 'no-cache') + + return response