''' Created on 24.10.2013 @author: Philipp Rauch @version: 0.6 ''' from sys import stderr from datetime import datetime from flask import Flask, jsonify, abort, make_response, request from socket import gethostname from config import Config import ems ### LOAD CONFIG ### c = Config() conf = c.readConf() api_host = gethostname() if conf['flask_server'] == '0.0.0.0' else conf['flask_server'] api_url = 'http://%s:%s' % (api_host, conf['flask_port']) #### BUFFER #### class Buffer(object): device = { 'battery' : { 'id': 1, 'voltage' : 400.11, 'current' : 20.264, 'power' : 53.465, 'capacity' : 80.34, 'isCharging' : False, 'isMaster' : True, 'dyn' : None }, 'ac_grid' : { 'id': 2, 'voltage' : 400.23, 'current' : 10.423, 'power' : 23.35, 'isOn' : True, 'isFeed' : True, 'isMaster' : False, 'dyn' : None } } error = { 'db_error' : { 'id' : 1, 'content' : None } } dc_labor = { 'device' : device, 'error' : error } dc_grid = { 'device' : 'device', 'error' : 'error' } ac_grid = { 'device' : 'device', 'error' : 'error' } system = { '00_config' : conf, 'dc_labor' : dc_labor, 'dc_grid' : dc_grid, 'ac_grid' : ac_grid, 'request' : {} } _instance = None def __new__(cls, *args, **kwargs): # http://stackoverflow.com/questions/42558/python-and-the-singleton-pattern if not cls._instance: cls._instance = super(Buffer, cls).__new__( cls, *args, **kwargs) return cls._instance def gen_start_url(self, l): ''' generate a URL form a list @param l: list of aspacts of the URL @return: URL ''' url = api_url for i in l: url = '%s/%s' % (url, i) return url def get_level(self, l): ''' iterating over a dictionary on a given path @param l: items witch tell the path to the value @return: dictionary with the key and value of the last item in the list ''' level = l.pop(0) for i in l: if isinstance(level, dict) and i in level: level = level.get(i) else: abort(404) self.set_href(level, self.gen_start_url(l)) #set all links above return {l[-1] : level} def generate_links(self, dic, url, postfix): ''' generats a link to dic if dic is a dictionary @param dic: variable that is being tested on a dictionary @param url: previous url @param postfix: appendix to the given url ''' if isinstance(dic, dict): if '00_config' in url.split('/'): return url = '%s/%s' % (url, postfix) self.set_href(dic, url) else: return def set_href(self, dic, url): ''' set the ref link if dic is a dictionary @param dic: variable that is being tested on a dictionary @param url: url to the first dictionary ''' if isinstance(dic, dict): for i in dic.keys(): self.generate_links(dic.get(i), url, i) dic.update({'000_href': url}) dic.update({'000_timestamp' : str(datetime.now())}) def foo(self, l, args): ''' @param l: list @param args: arguments from the reqest @return: Dictionary ''' del l[-1] dic = self.get_level(l).get(l[-1]) if isinstance(dic, dict) and 'dyn' in dic: message = 'generating view for %s' % (l[-1]) else: abort(404) return { l[-1] : message, 'args' : args.to_dict(flat=False)} def update_buffer(self, push): ''' Method to update the Buffer on the given path @param push: message to push in the buffer construction: key is the path value is the dict ''' ## Test of valid push message ## if not isinstance(push, dict): stderr.write('error wrong parameter: Type is %s expect dict' % push.__class__.__name__) return if len(push.keys()) not in [1]: stderr.write('error wrong number of arguments: %s expect 1' % len(push.keys())) return if not isinstance(push.get(push.keys()[0]) ,dict): stderr.write('error value is not dict') return key = push.keys()[0] value = push[key] path = key.split('/') if path[0] == '': path.remove('') sys = self.system for key in path: try: sys = sys[key] except KeyError: stderr.write('error wrong path: %s' % key) return sys.update(value) def init_buffer(self): #ToDo pass class API(object): def __init__(self): self.app = Flask(__name__) ### Start EMS thread ### self.EMS = ems.ems(buf) self.emsthread = self.EMS.start() if conf['config_debug']: print 'EMS-Thread:\t', self.EMS print '\tAPI-BUFFER:\t', buf ### ADD URL RULES ### self.app.error_handler_spec[None][400] = bad_reqest self.app.error_handler_spec[None][404] = not_found self.app.error_handler_spec[None][405] = not_allowed self.app.add_url_rule('/', 'get_root', get_root, methods = ['GET']) self.app.add_url_rule('/', 'get_catch_all', get_catch_all, methods = ['GET']) buf = Buffer() ########## ERROR Handler ########## def not_allowed(error): return make_response(jsonify( { 'error': '405 Not Allowed' } ), 405) def not_found(error): return make_response(jsonify( { 'error': '404 Not Found' } ), 404) def bad_reqest(error): return make_response(jsonify( { 'error': '400 Bad Reqest' } ), 400) ########## GET Handler ########## def get_root(): buf.set_href(Buffer.system, api_url) return jsonify( { 'system' : Buffer.system } ) def get_catch_all(path): l = path.split('/') l.insert(0, Buffer.system) if '' == l[-1]: # if last element of list is empty del l[-1] # remove it out = buf.get_level(l) elif 'dyn' == l[-1]: args = request.args # returns a dictionary with a list of values for each key # each value and each key is represented as a string # to convert it to a dictionary use to_dict(flat=False) # to_dict(flat=True) returns the key and the first item of the value list. out = buf.foo(l, args) else: out = buf.get_level(l) return jsonify( out ) def API_start(): api = API() api.app.run(host = conf['flask_server'], port = int(conf['flask_port']), debug = conf['flask_debug']) API_start()