craftbeerpi4-pione/core/craftbeerpi.py
2018-11-16 20:35:59 +01:00

245 lines
9.5 KiB
Python

import asyncio
import logging
from os import urandom
import os
import yaml
from aiohttp import web
from aiohttp_auth import auth
from aiohttp_session import session_middleware
from aiohttp_session.cookie_storage import EncryptedCookieStorage
from aiohttp_swagger import setup_swagger
from aiojobs.aiohttp import setup, get_scheduler_from_app
from core.controller.actor_controller import ActorController
from core.controller.notification_controller import NotificationController
from core.controller.plugin_controller import PluginController
from core.controller.sensor_controller import SensorController
from core.controller.system_controller import SystemController
from core.database.model import DBModel
from core.eventbus import EventBus
from core.http_endpoints.http_login import Login
from core.utils import *
from core.websocket import WebSocket
logger = logging.getLogger(__file__)
logging.basicConfig(level=logging.INFO)
"""
This is a module docstring
"""
class CraftBeerPi():
"""
This is a Hello class docstring
"""
def __init__(self):
this_directory = os.path.dirname(__file__)
self.config = load_config(os.path.join(this_directory, '../config/config.yaml'))
logger.info("Init CraftBeerPI")
policy = auth.SessionTktAuthentication(urandom(32), 60, include_ip=True)
middlewares = [session_middleware(EncryptedCookieStorage(urandom(32))), auth.auth_middleware(policy)]
self.app = web.Application(middlewares=middlewares)
self.initializer = []
setup(self.app)
self.bus = EventBus()
self.ws = WebSocket(self)
self.actor = ActorController(self)
self.sensor = SensorController(self)
self.plugin = PluginController(self)
self.system = SystemController(self)
self.notification = NotificationController(self)
self.login = Login(self)
def register_events(self, obj):
for method in [getattr(obj, f) for f in dir(obj) if callable(getattr(obj, f)) and hasattr(getattr(obj, f), "eventbus")]:
doc = None
if method.__doc__ is not None:
doc = yaml.load(method.__doc__)
doc["topic"] = method.__getattribute__("topic")
self.bus.register(method.__getattribute__("topic"), method, doc)
def register_background_task(self, obj):
'''
This method parses all method for the @background_task decorator and registers the background job
which will be launched during start up of the server
:param obj: the object to parse
:return:
'''
async def job_loop(app, name, interval, method):
logger.info("Start Background Task %s Interval %s Method %s" % (name, interval, method))
while True:
logger.debug("Execute Task %s - interval(%s second(s)" % (name, interval))
await asyncio.sleep(interval)
await method()
async def spawn_job(app):
scheduler = get_scheduler_from_app(self.app)
for method in [getattr(obj, f) for f in dir(obj) if callable(getattr(obj, f)) and hasattr(getattr(obj, f), "background_task")]:
name = method.__getattribute__("name")
interval = method.__getattribute__("interval")
await scheduler.spawn(job_loop(self.app, name, interval, method))
self.app.on_startup.append(spawn_job)
def register_on_startup(self, obj):
for method in [getattr(obj, f) for f in dir(obj) if callable(getattr(obj, f)) and hasattr(getattr(obj, f), "on_startup")]:
name = method.__getattribute__("name")
order = method.__getattribute__("order")
self.initializer.append(dict(name=name, method=method, order=order))
def register_ws(self, obj):
if self.ws is None:
return
for method in [getattr(obj, f) for f in dir(obj) if callable(getattr(obj, f)) and hasattr(getattr(obj, f), "ws")]:
self.ws.add_callback(method, method.__getattribute__("key"))
def register(self, obj, url_prefix=None):
'''
This method parses the provided object
:param obj: the object wich will be parsed for registration
:param url_prefix: that prefix for HTTP Endpoints
:return: None
'''
self.register_http_endpoints(obj, url_prefix)
self.register_events(obj)
self.register_ws(obj)
self.register_background_task(obj)
self.register_on_startup(obj)
def register_http_endpoints(self, obj, url_prefix=None):
'''
This method parses the provided object for @request_mapping decorator
:param obj: the object which will be analyzed
:param url_prefix: the prefix which will be used for the all http endpoints of the object
:return:
'''
routes = []
for method in [getattr(obj, f) for f in dir(obj) if callable(getattr(obj, f)) and hasattr(getattr(obj, f), "route")]:
http_method = method.__getattribute__("method")
path = method.__getattribute__("path")
class_name = method.__self__.__class__.__name__
logger.info("Register Endpoint : %s.%s %s %s%s " % (class_name, method.__name__, http_method, url_prefix, path))
def add_post():
routes.append(web.post(method.__getattribute__("path"), method))
def add_get():
routes.append(web.get(method.__getattribute__("path"), method))
def add_delete():
routes.append(web.delete(path, method))
def add_put():
routes.append(web.put(path, method))
switcher = {
"POST": add_post,
"GET": add_get,
"DELETE": add_delete,
"PUT": add_put
}
switcher[http_method]()
if url_prefix is not None:
sub = web.Application()
sub.add_routes(routes)
self.app.add_subapp(url_prefix, sub)
else:
self.app.add_routes(routes)
def _swagger_setup(self):
'''
Internatl method to expose REST API documentation by swagger
:return:
'''
long_description = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vehicula, metus et sodales fringilla, purus leo aliquet odio, non tempor ante urna aliquet nibh. Integer accumsan laoreet tincidunt. Vestibulum semper vehicula sollicitudin. Suspendisse dapibus neque vitae mattis bibendum. Morbi eu pulvinar turpis, quis malesuada ex. Vestibulum sed maximus diam. Proin semper fermentum suscipit. Duis at suscipit diam. Integer in augue elementum, auctor orci ac, elementum est. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Maecenas condimentum id arcu quis volutpat. Vestibulum sit amet nibh sodales, iaculis nibh eget, scelerisque justo.
Nunc eget mauris lectus. Proin sit amet volutpat risus. Aliquam auctor nunc sit amet feugiat tempus. Maecenas nec ex dolor. Nam fermentum, mauris ut suscipit varius, odio purus luctus mauris, pretium interdum felis sem vel est. Proin a turpis vitae nunc volutpat tristique ac in erat. Pellentesque consequat rhoncus libero, ac sollicitudin odio tempus a. Sed vestibulum leo erat, ut auctor turpis mollis id. Ut nec nunc ex. Maecenas eu turpis in nibh placerat ullamcorper ac nec dui. Integer ac lacus neque. Donec dictum tellus lacus, a vulputate justo venenatis at. Morbi malesuada tellus quis orci aliquet, at vulputate lacus imperdiet. Nulla eu diam quis orci aliquam vulputate ac imperdiet elit. Quisque varius mollis dolor in interdum.
"""
setup_swagger(self.app,
description=long_description,
title=self.config.get("name", "CraftBeerPi"),
api_version=self.config.get("version", ""),
contact="info@craftbeerpi.com")
def notify(self, key, message, type="info"):
'''
This is a convinience method to send notification to the client
:param key: notification key
:param message: notification message
:param type: notification type (info,warning,danger,successs)
:return:
'''
self.bus.fire(topic="notification/1", key=key, message=message, type=type)
def setup(self):
'''
This method will start the server
:return:
'''
print("INIT CONTROLLER")
from pyfiglet import Figlet
f = Figlet(font='big')
print(f.renderText("%s %s" % (self.config.get("name"), self.config.get("version"))))
async def init_database(app):
await DBModel.test_connection()
async def init_controller(app):
await self.actor.init()
async def load_plugins(app):
await PluginController.load_plugin_list()
await self.plugin.load_plugins()
async def call_initializer(app):
self.initializer = sorted(self.initializer, key=lambda k: k['order'])
for i in self.initializer:
logger.info("CALL INITIALIZER %s - %s " % (i["name"], i["method"].__name__))
await i["method"]()
self.app.on_startup.append(init_database)
self.app.on_startup.append(call_initializer)
self.app.on_startup.append(load_plugins)
self.app.on_startup.append(init_controller)
self._swagger_setup()
def start(self):
web.run_app(self.app, port=self.config.get("port", 8080))