diff --git a/spaceapi/__init__.py b/spaceapi/__init__.py --- a/spaceapi/__init__.py +++ b/spaceapi/__init__.py @@ -56,4 +56,7 @@ def create_app(): from .sensors import sensors_views app.register_blueprint(sensors_views, url_prefix='/sensors') + from .utils import ActiveStatus + ActiveStatus().reload() + return app diff --git a/spaceapi/active.py b/spaceapi/active.py --- a/spaceapi/active.py +++ b/spaceapi/active.py @@ -1,7 +1,7 @@ -import copy import json import os.path +from .utils import ActiveStatus default_json_file = os.path.abspath('default.json') last_state_file = os.path.abspath('laststate.json') @@ -13,10 +13,10 @@ elif not os.path.isfile(default_json_fil default_json = {} -active_json = {} +active_json = ActiveStatus() -def reload_json(): +def _reload_json(): global default_json global active_json @@ -25,18 +25,19 @@ def reload_json(): if os.path.exists(last_state_file) and os.path.isfile(last_state_file): with open(last_state_file, encoding='utf-8') as f: - active_json = json.load(f) + active_json.update(json.load(f)) if os.path.getmtime(last_state_file) \ < os.path.getmtime(default_json_file): - backup = copy.deepcopy(active_json) + backup_state_open = active_json['state']['open'] + backup_state_lastchange = active_json['state']['lastchange'] active_json.update(default_json) - active_json['state']['open'] = backup['state']['open'] - active_json['state']['lastchange'] = backup['state']['lastchange'] + active_json['state']['open'] = backup_state_open + active_json['state']['lastchange'] = backup_state_lastchange else: - active_json = copy.deepcopy(default_json) + active_json.update(default_json) -reload_json() +_reload_json() def save_last_state(): diff --git a/spaceapi/sensors.py b/spaceapi/sensors.py --- a/spaceapi/sensors.py +++ b/spaceapi/sensors.py @@ -8,8 +8,8 @@ import jsonschema from flask import Blueprint, jsonify, request, abort, current_app from .auth import httpauth -from .active import active_json, save_last_state -from .utils import first, fuzzy_list_find +from .active import save_last_state +from .utils import first, fuzzy_list_find, ActiveStatus sensors_views = Blueprint('sensors', __name__) @@ -25,24 +25,28 @@ get_identification_key = partial(first, def set_value(data, key): + active = ActiveStatus() + try: subkey = get_identification_key(data) except ValueError: return abort(400) try: - index = fuzzy_list_find(active_json['sensors'][key], + index = fuzzy_list_find(active['sensors'][key], subkey, data[subkey]) - active_json['sensors'][key][index].update(data) + active['sensors'][key][index].update(data) except ValueError: - active_json['sensors'][key].append(data) + active['sensors'][key].append(data) - save_last_state() - return jsonify(active_json) + active.save_last_state() + return jsonify(active) def set_radiation_value(data): + active = ActiveStatus() + radiation_keys = [k for k in RADIATON_SUBKEYS if k in data] if not radiation_keys: @@ -57,23 +61,25 @@ def set_radiation_value(data): try: index = fuzzy_list_find( - active_json['sensors']['radiation'][first_subkey], + active['sensors']['radiation'][first_subkey], second_subkey, data[first_subkey][second_subkey]) - active_json['sensors']['radiation'][first_subkey][index].update( + active['sensors']['radiation'][first_subkey][index].update( data) except ValueError: - active_json['sensors']['radiaton'][first_subkey].append(data) + active['sensors']['radiaton'][first_subkey].append(data) - save_last_state() - return jsonify(active_json) + active.save_last_state() + return jsonify(active) @sensors_views.route('/set/', methods=['POST']) @httpauth.login_required def set_sensors(key): - if key in ALLOWED_SENSORS_KEYS and key in active_json['sensors']: + active = ActiveStatus() + + if key in ALLOWED_SENSORS_KEYS and key in active['sensors']: data = request.data.decode('utf-8') try: data = json.loads(data) diff --git a/spaceapi/state.py b/spaceapi/state.py --- a/spaceapi/state.py +++ b/spaceapi/state.py @@ -4,7 +4,7 @@ from time import time from flask import Blueprint, jsonify, request, current_app, abort -from .active import active_json, save_last_state +from .utils import ActiveStatus from .auth import httpauth state_views = Blueprint('state', __name__) @@ -17,17 +17,18 @@ ALLOWED_STATE_KEYS = frozenset(('open', @httpauth.login_required def set_state(key): + active = ActiveStatus() value = json.loads(request.data.decode('utf-8'))['value'] current_app.logger.info(value) current_app.logger.info(type(value)) if key in ALLOWED_STATE_KEYS: - active_json['state'][key] = value + active['state'][key] = value if key == 'open': - active_json['state']['lastchange'] = int(time()) + active['state']['lastchange'] = int(time()) - save_last_state() - return jsonify(active_json) + active.save_last_state() + return jsonify(active) else: return abort(400) diff --git a/spaceapi/utils.py b/spaceapi/utils.py --- a/spaceapi/utils.py +++ b/spaceapi/utils.py @@ -1,6 +1,66 @@ +import json +import os.path + from flask import request +default_json_file = os.path.abspath('default.json') +last_state_file = os.path.abspath('laststate.json') + +if not os.path.exists(default_json_file): + raise RuntimeError('default.json does not exists!') +elif not os.path.isfile(default_json_file): + raise RuntimeError('default.json is not a file!') + +_default_json = {} + + +class Singleton: + + def __new__(cls, *args, **kwargs): + key = str(hash(cls)) + + if not hasattr(cls, '_instance_dict'): + cls._instance_dict = {} + + if key not in cls._instance_dict: + cls._instance_dict[key] = super().__new__(cls, *args, **kwargs) + + return cls._instance_dict[key] + + +class ActiveStatus(Singleton, dict): + + def reload(self): + global _default_json + + with open(default_json_file, encoding='utf-8') as f: + _default_json = json.load(f) + + if os.path.exists(last_state_file) and os.path.isfile(last_state_file): + with open(last_state_file, encoding='utf-8') as f: + self.update(json.load(f)) + + if os.path.getmtime(last_state_file) \ + < os.path.getmtime(default_json_file): + backup_open = self['state']['open'] + backup_lastchange = self['state']['lastchange'] + backup_sensors = self['sensors'] + + self.update(_default_json) + + self['state']['open'] = backup_open + self['state']['lastchange'] = backup_lastchange + self['sensors'].update(backup_sensors) + else: + self.update(_default_json) + + def save_last_state(self): + + with open(last_state_file, mode='w', encoding='utf-8') as f: + json.dump(self, f, sort_keys=True) + + def request_wants_json(): best = request.accept_mimetypes.best_match( ['application/json', 'text/html'] diff --git a/spaceapi/views.py b/spaceapi/views.py --- a/spaceapi/views.py +++ b/spaceapi/views.py @@ -1,7 +1,7 @@ from flask import Blueprint, jsonify, render_template, abort -from .active import active_json -from .utils import request_wants_json +from .utils import request_wants_json, ActiveStatus +from .auth import httpauth root_views = Blueprint('root', __name__) @@ -9,10 +9,18 @@ root_views = Blueprint('root', __name__) @root_views.route('/') def index(): if request_wants_json(): - return jsonify(active_json) - return render_template('index.html', status=active_json) + return jsonify(ActiveStatus()) + return render_template('index.html', status=ActiveStatus()) @root_views.route('/status.json') def status_json(): - return jsonify(active_json) + return jsonify(ActiveStatus()) + + +@root_views.route('/reload') +@httpauth.login_required +def reload(): + active = ActiveStatus() + active.reload() + return jsonify(active)