diff --git a/.codebeatignore b/.codebeatignore new file mode 100644 index 0000000..c28e2d4 --- /dev/null +++ b/.codebeatignore @@ -0,0 +1 @@ +modules/ui/static/** \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..eba4a1d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +language: python +python: + - "2.7" + +#before_install: +# - pip install pytest pytest-cov + +# command to install dependencies +install: + - pip install -r requirements.txt + +script: + - py.test \ No newline at end of file diff --git a/README.md b/README.md index 84730e1..6a679b9 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ +[![Build Status](https://travis-ci.org/isarvalley/craftbeerpi3.svg?branch=core_refactoring)](https://travis-ci.org/isarvalley/craftbeerpi3) + +[![codebeat badge](https://codebeat.co/badges/72fa1981-c82a-488f-9a53-9e21ed9de4cc)](https://codebeat.co/projects/github-com-isarvalley-craftbeerpi3-core_refactoring) + +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/0a970c7873f84953bd772c36d5ffc05c)](https://www.codacy.com/app/isar_valley/craftbeerpi3?utm_source=github.com&utm_medium=referral&utm_content=isarvalley/craftbeerpi3&utm_campaign=Badge_Grade) + +[![BCH compliance](https://bettercodehub.com/edge/badge/isarvalley/craftbeerpi3?branch=core_refactoring)](https://bettercodehub.com/) + # CraftBeerPi V3.0 This is CraftBeerPi version 3.0. It's currently in beta status. diff --git a/config/logger.yaml b/config/logger.yaml new file mode 100644 index 0000000..adbd2e5 --- /dev/null +++ b/config/logger.yaml @@ -0,0 +1,19 @@ +version: 1 +formatters: + simple: + format: '%(asctime)s - %(levelname)-8s - %(name)s - %(message)s' +handlers: + console: + class: logging.StreamHandler + level: DEBUG + formatter: simple + stream: ext://sys.stdout + file: + class : logging.handlers.RotatingFileHandler + formatter: simple + filename: ./logs/app.log + maxBytes: 10000000 + backupCount: 3 +root: + level: DEBUG + handlers: [console, file] \ No newline at end of file diff --git a/modules/action/__init__.py b/modules/action/__init__.py index c9d5761..a378fdc 100644 --- a/modules/action/__init__.py +++ b/modules/action/__init__.py @@ -1,8 +1,12 @@ import json +import logging from flask_classy import FlaskView, route from modules.core.core import cbpi + class ActionView(FlaskView): + def __init__(self): + self.logger = logging.getLogger(__name__) @route('/', methods=['POST']) def action(self, action): diff --git a/modules/base_plugins/actor.py b/modules/base_plugins/actor.py index 12b61a0..491ea43 100644 --- a/modules/base_plugins/actor.py +++ b/modules/base_plugins/actor.py @@ -1,10 +1,13 @@ +import logging from modules.core.baseapi import Buzzer from modules.core.basetypes import Actor, KettleController, FermenterController from modules.core.core import cbpi + @cbpi.addon.actor.type("Dummy Actor") class Dummy(Actor): - + def __init__(self): + self.logger = logging.getLogger(__name__) @cbpi.addon.actor.action("WOHOO") def myaction(self): @@ -16,10 +19,10 @@ class Dummy(Actor): :param power: int value between 0 - 100 :return: ''' - print "ON" + self.logger.info("ON") def off(self): - print "OFF" + self.logger.info("OFF") @@ -33,11 +36,12 @@ class MyController(KettleController): @cbpi.addon.fermenter.controller() class MyController2(FermenterController): - + def __init__(self): + self.logger = logging.getLogger(__name__) def run(self): while self.is_running(): - print "HALLO" + self.logger.debug("HALLO") self.sleep(1) @cbpi.addon.core.initializer(order=200) diff --git a/modules/base_plugins/sensor.py b/modules/base_plugins/sensor.py index 0a34ee1..9179023 100644 --- a/modules/base_plugins/sensor.py +++ b/modules/base_plugins/sensor.py @@ -3,10 +3,12 @@ import os from os.path import join -from modules.core.basetypes import Actor, Sensor +from modules.core.basetypes import Sensor from modules.core.core import cbpi from modules.core.proptypes import Property -import random + +import logging + @cbpi.addon.sensor.type("Dummy Sensor") class Dummy(Sensor): @@ -14,6 +16,10 @@ class Dummy(Sensor): text = Property.Text(label="Text", required=True, description="This is a parameter", configurable=True) p = Property.Select(label="hallo",options=[1,2,3]) + def __init__(self): + self.logger = logging.getLogger(__name__) + self.logger.info("INIT SENSOR") + def init(self): if self.api.get_config_parameter("unit","C") == "C": @@ -23,8 +29,7 @@ class Dummy(Sensor): @cbpi.addon.sensor.action("WOHOO") def myaction(self): - - print "SENSOR ACTION HALLO!!!" + self.logger.debug("SENSOR ACTION HALLO!!!") def execute(self): while True: diff --git a/modules/core/baseapi.py b/modules/core/baseapi.py index 1f0d96e..183c42f 100755 --- a/modules/core/baseapi.py +++ b/modules/core/baseapi.py @@ -1,3 +1,5 @@ +import logging + from proptypes import * class BaseAPI(object): @@ -148,6 +150,8 @@ class CoreAPI(BaseAPI): key = "core" def __init__(self, cbpi): + self.logger = logging.getLogger(__name__) + self.cbpi = cbpi self.cbpi.cache["actions"] = {} self.cbpi.cache["init"] = [] @@ -168,7 +172,7 @@ class CoreAPI(BaseAPI): try: method(self.cbpi) except Exception as e: - print e + self.logger.debug(e) self.cbpi._socketio.sleep(interval) for value in self.cbpi.cache.get("background"): @@ -220,5 +224,5 @@ class CoreAPI(BaseAPI): class Buzzer(object): - def beep(): + def beep(self): pass \ No newline at end of file diff --git a/modules/core/basetypes.py b/modules/core/basetypes.py index deb803f..44e768c 100755 --- a/modules/core/basetypes.py +++ b/modules/core/basetypes.py @@ -1,3 +1,5 @@ +import logging + from modules.core.proptypes import Property import time @@ -12,7 +14,9 @@ class Base(object): self.value = None self.__dirty = False + class Actor(Base): + __logger = logging.getLogger(__name__) @classmethod def init_global(cls): @@ -26,15 +30,15 @@ class Actor(Base): pass def on(self, power=100): - print "SWITCH ON" + self._logger.info("SWITCH ON") pass def off(self): - print "SWITCH OFF" + self._logger.info("SWITCH OFF") pass def power(self, power): - print "SET POWER", power + self._logger.info("SET POWER TO [%s]", power) pass def state(self): @@ -42,6 +46,7 @@ class Actor(Base): class Sensor(Base): + __logger = logging.getLogger(__name__) unit = "" @@ -60,12 +65,13 @@ class Sensor(Base): def update_value(self, value): self.value = value + self.__logger.info("Updated value for sensor [%s] with value [%s].", self.id, value) self.cbpi.sensor.write_log(self.id, value) self.cbpi.emit("SENSOR_UPDATE", id=self.id, value=value) self.cbpi.ws_emit("SENSOR_UPDATE", self.cbpi.cache["sensors"][self.id]) def execute(self): - print "EXECUTE" + self.__logger.info("EXECUTE") pass @@ -75,9 +81,11 @@ class ControllerBase(object): __dirty = False __running = False + __logger = logging.getLogger(__name__) + @staticmethod def init_global(): - print "GLOBAL CONTROLLER INIT" + ControllerBase.__logger.info("GLOBAL CONTROLLER INIT") def notify(self, headline, message, type="success", timeout=5000): self.api.notify(headline, message, type, timeout) @@ -251,12 +259,13 @@ class Step(Base, Timer): pass def execute(self): - - print "Step Info" - print "Kettle ID: %s" % self.kettle_id - print "ID: %s" % self.id + self.logger.info("-------------") + self.logger.info("Step Info") + self.logger.info("Kettle ID: %s" % self.kettle_id) + self.logger.info("ID: %s" % self.id) def __init__(self, *args, **kwds): + self.logger = logging.getLogger(__name__) for a in kwds: super(Step, self).__setattr__(a, kwds.get(a)) diff --git a/modules/core/core.py b/modules/core/core.py index 3f647f5..34af8ca 100755 --- a/modules/core/core.py +++ b/modules/core/core.py @@ -1,13 +1,16 @@ import json import logging +import logging.config import os import sqlite3 import uuid +import yaml from datetime import datetime from functools import wraps, update_wrapper from importlib import import_module from time import localtime, strftime import time +import os.path from flask import Flask, redirect, json, g, make_response from flask_socketio import SocketIO @@ -62,6 +65,8 @@ class ActorCore(object): key = "actor_types" def __init__(self, cbpi): + self.logger = logging.getLogger(__name__) + self.cbpi = cbpi self.cbpi.cache["actors"] = {} self.cbpi.cache[self.key] = {} @@ -82,8 +87,7 @@ class ActorCore(object): actor.power = 100 self.cbpi.emit("INIT_ACTOR", id=id) except Exception as e: - print e - self.cbpi._app.logger.error(e) + self.logger.error(e) def stop_one(self, id): self.cbpi.cache["actors"][id]["instance"].stop() @@ -99,7 +103,7 @@ class ActorCore(object): self.cbpi.emit("SWITCH_ACTOR_ON", id=id, power=power) return True except Exception as e: - print e + self.logger.error(e) return False def off(self, id): @@ -111,7 +115,7 @@ class ActorCore(object): self.cbpi.emit("SWITCH_ACTOR_OFF", id=id) return True except Exception as e: - print e + self.logger.error(e) return False def toggle(self, id): @@ -129,7 +133,7 @@ class ActorCore(object): self.cbpi.emit("SWITCH_ACTOR_POWER_CHANGE", id=id, power=power) return True except Exception as e: - print e + self.logger.error(e) return False def action(self, id, method): @@ -154,6 +158,8 @@ class SensorCore(object): key = "sensor_types" def __init__(self, cbpi): + self.logger = logging.getLogger(__name__) + self.cbpi = cbpi self.cbpi.cache["sensors"] = {} self.cbpi.cache["sensor_instances"] = {} @@ -181,8 +187,7 @@ class SensorCore(object): self.cbpi.emit("INIT_SENSOR", id=id) except Exception as e: - print "ERROR" - self.cbpi._app.logger.error(e) + self.logger.error(e) def stop_one(self, id): @@ -240,7 +245,7 @@ class BrewingCore(object): # Start controller if kettle.logic is not None: cfg = kettle.config.copy() - cfg.update(dict(api=cbpi, kettle_id=kettle.id, heater=kettle.heater, sensor=kettle.sensor)) + cfg.update(dict(api=self.cbpi, kettle_id=kettle.id, heater=kettle.heater, sensor=kettle.sensor)) instance = self.get_controller(kettle.logic).get("class")(**cfg) instance.init() kettle.instance = instance @@ -250,13 +255,13 @@ class BrewingCore(object): t = self.cbpi._socketio.start_background_task(target=run, instance=instance) kettle.state = not kettle.state - self.cbpi.ws_emit("UPDATE_KETTLE", cbpi.cache.get("kettle").get(id)) + self.cbpi.ws_emit("UPDATE_KETTLE", self.cbpi.cache.get("kettle").get(id)) self.cbpi.emit("KETTLE_CONTROLLER_STARTED", id=id) else: # Stop controller kettle.instance.stop() kettle.state = not kettle.state - self.cbpi.ws_emit("UPDATE_KETTLE", cbpi.cache.get("kettle").get(id)) + self.cbpi.ws_emit("UPDATE_KETTLE", self.cbpi.cache.get("kettle").get(id)) self.cbpi.emit("KETTLE_CONTROLLER_STOPPED", id=id) @@ -271,12 +276,19 @@ class FermentationCore(object): class CraftBeerPI(object): + + _logger_configuration_file = './config/logger.yaml' + cache = {} eventbus = {} def __init__(self): - FORMAT = '%(asctime)-15s - %(levelname)s - %(message)s' - logging.basicConfig(filename='./logs/app.log', level=logging.INFO, format=FORMAT) + if os.path.isfile(self._logger_configuration_file): + logging.config.dictConfig(yaml.load(open(self._logger_configuration_file, 'r'))) + + self.logger = logging.getLogger(__name__) + self.logger.info("Logger got initialized.") + self.cache["messages"] = [] self.cache["version"] = "3.1" self.modules = {} @@ -306,7 +318,7 @@ class CraftBeerPI(object): port = int(cbpi.get_config_parameter('port', '5000')) except ValueError: port = 5000 - print port + self.logger.info("port [%s]", port) self._socketio.run(self._app, host='0.0.0.0', port=port) def beep(self): @@ -331,7 +343,7 @@ class CraftBeerPI(object): db.cursor().executescript(f.read()) db.commit() except Exception as e: - + self.logger.error(e) pass def nocache(self, view): @@ -388,9 +400,8 @@ class CraftBeerPI(object): try: self.modules[filename] = import_module("modules.plugins.%s" % (filename)) except Exception as e: - + self.logger.error(e) self.notify("Failed to load plugin %s " % filename, str(e), type="danger", timeout=None) - cbpi = CraftBeerPI() -addon = cbpi.addon +addon = cbpi.addon \ No newline at end of file diff --git a/modules/core/db_migrate.py b/modules/core/db_migrate.py index 11e4bb3..c8af526 100644 --- a/modules/core/db_migrate.py +++ b/modules/core/db_migrate.py @@ -19,8 +19,7 @@ def execute_file(curernt_version, data): conn.commit() except sqlite3.OperationalError as err: - - print err + cbpi._app.logger.error(err) @cbpi.addon.core.initializer(order=-9999) def init(cbpi): diff --git a/modules/example_plugins/WebViewJquery/__init__.py b/modules/example_plugins/WebViewJquery/__init__.py index 6e96ba1..ea2b9b0 100644 --- a/modules/example_plugins/WebViewJquery/__init__.py +++ b/modules/example_plugins/WebViewJquery/__init__.py @@ -5,9 +5,13 @@ from flask_swagger import swagger from flask import json from flask import Blueprint +import logging + @addon.core.initializer(order=22) def web(cbpi): + logger = logging.getLogger(__name__) + s = Blueprint('web_view', __name__, template_folder='templates', static_folder='static') @s.route('/', methods=["GET"]) diff --git a/modules/example_plugins/WebViewReactJs/__init__.py b/modules/example_plugins/WebViewReactJs/__init__.py index 9d82cc0..2050609 100644 --- a/modules/example_plugins/WebViewReactJs/__init__.py +++ b/modules/example_plugins/WebViewReactJs/__init__.py @@ -5,8 +5,11 @@ from flask_swagger import swagger from flask import json from flask import Blueprint +import logging + @addon.core.initializer(order=22) def web(cbpi): + logger = logging.getLogger(__name__) s = Blueprint('webviewreact', __name__, template_folder='templates', static_folder='static') diff --git a/modules/fermenter/__init__.py b/modules/fermenter/__init__.py index 5704a26..b9cb7c9 100644 --- a/modules/fermenter/__init__.py +++ b/modules/fermenter/__init__.py @@ -1,3 +1,4 @@ +import logging import time from flask import request from flask_classy import route @@ -12,6 +13,8 @@ class FermenterView(BaseView): model = Fermenter cache_key = "fermenter" + def __init__(self): + self.logger = logging.getLogger(__name__) def _post_post_callback(self, m): m.state = False diff --git a/modules/login/__init__.py b/modules/login/__init__.py index 938ba62..c80405f 100644 --- a/modules/login/__init__.py +++ b/modules/login/__init__.py @@ -3,6 +3,8 @@ from flask import request from modules.core.core import cbpi, addon +import logging + class User(flask_login.UserMixin): pass diff --git a/modules/logs/__init__.py b/modules/logs/__init__.py index d3dfc4e..0193261 100755 --- a/modules/logs/__init__.py +++ b/modules/logs/__init__.py @@ -7,6 +7,8 @@ from modules.core.core import cbpi class LogView(FlaskView): + _log_directory = "./logs" + @route('/', methods=['GET']) def get_all_logfiles(self): """ @@ -19,7 +21,7 @@ class LogView(FlaskView): description: List of all log files """ result = [] - for filename in os.listdir("./logs"): + for filename in os.listdir(self._log_directory): if filename.endswith(".log"): result.append(filename) return json.dumps(result) diff --git a/modules/recipe_import/kbh.py b/modules/recipe_import/kbh.py index 8cdfd4b..e43b57f 100644 --- a/modules/recipe_import/kbh.py +++ b/modules/recipe_import/kbh.py @@ -1,3 +1,5 @@ +import logging + from flask import json, request from flask_classy import FlaskView, route from git import Repo, Git @@ -11,6 +13,8 @@ from modules.step import Step, StepView class KBH(FlaskView): + def __init__(self): + self.logger = logging.getLogger(__name__) @route('/', methods=['GET']) def get(self): @@ -39,7 +43,7 @@ class KBH(FlaskView): result.append({"id": row[0], "name": row[1], "brewed": row[2]}) return json.dumps(result) except Exception as e: - print e + self.logger.error(e) self.api.notify(headline="Failed to load KHB database", message="ERROR", type="danger") return ('', 500) finally: diff --git a/run.py b/run.py index 8a6a549..659fb35 100755 --- a/run.py +++ b/run.py @@ -1,12 +1,6 @@ #!/usr/bin/env python from modules.core.core import * - -cbpi = CraftBeerPI() - -addon = cbpi.addon - - from modules.core.db_migrate import * from modules.buzzer import * from modules.config import * diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/logs/__init__.py b/tests/logs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/logs/test_log_view.py b/tests/logs/test_log_view.py new file mode 100644 index 0000000..7e67920 --- /dev/null +++ b/tests/logs/test_log_view.py @@ -0,0 +1,13 @@ +from modules.logs import LogView +from tests.testlib import utils + + +class TestLogView(object): + + def test_get_all_logfiles(self): + LogView._log_directory = utils.get_base_path() + "/logs" + + logfiles = LogView().get_all_logfiles() + + # if no log file is found then two square brackets "[]" are returned and string length is 2 + assert (len(logfiles) > 2) diff --git a/tests/testlib/__init__.py b/tests/testlib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/testlib/utils.py b/tests/testlib/utils.py new file mode 100644 index 0000000..c35922f --- /dev/null +++ b/tests/testlib/utils.py @@ -0,0 +1,13 @@ +import os + + +def get_base_path(): + marker_file = "run.py" + + current_path = os.getcwd() + + while current_path != 0: + if marker_file in os.listdir(current_path): + return current_path + + current_path = os.path.dirname(os.path.normpath(current_path)) diff --git a/update/5_kairosdb_config.sql b/update/5_kairosdb_config.sql new file mode 100644 index 0000000..e69de29