diff --git a/gpio_buttons/.env b/gpio_buttons/.env new file mode 100644 index 0000000000000000000000000000000000000000..f1c91a9d5fd519ed3932adee6cd8670c85138115 --- /dev/null +++ b/gpio_buttons/.env @@ -0,0 +1,52 @@ + +DESCRIPTION='<PLEASE_SET_THIS>' + +# DEBUG='false' +DEBUG='true' +VERBOSE='false' + +### +### DATABASE_TYPE --> 'local_file' = using local running django with SQLite | 'api' = access it with API +### +# DATABASE_TYPE='local_file' +DATABASE_TYPE='api' + +### +### DATABASE_TYPE: local_file +### +# Pi +DATABASE_LOCAL_DB_FILE_PATH='/home/pi/Coding/c3buttons/django/db.sqlite3' +# # Local +# DATABASE_LOCAL_DB_FILE_PATH='/home/fejao/Coding/CCC/git_cccv/c3buttons/django/db.sqlite3' + +### +### DATABASE_TYPE: api +### +# Pi +DATABASE_API_URL='http://django:8000' +DATABASE_API_TOKEN='<PLEASE_SER_THIS>' +# # Local +# DATABASE_API_URL='http://localhost:8000' +# DATABASE_API_TOKEN='eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzQxNjMxMTI3LCJpYXQiOjE3NDEwMjYzMjcsImp0aSI6Ijk5MzFjMjhkNGE2MDQ4MTg5ZGYwYTEzZDcyNzE2NDI5IiwidXNlcl9pZCI6MX0.fNIm-6bai4jJkL5NMSW1K9mWIP_XmHzPEkTxgzqJd_4' + +### +### BOXES CONFIG +### +BOX_LED_BLINK_TIMES=3 + +# BOX_LED_ENABLED='false' +BOX_LED_ENABLED='true' + +# BOX_LED_AWAYS_ON='true' +BOX_LED_AWAYS_ON='false' + +### BOX_BUTTON_BOUNCE_TIME in microseconds +BOX_BUTTON_BOUNCE_TIME= '0.1' + +### +### BOXES USED +### +BUTTON_BOX_1='{"pin_button": 16, "pin_led": 14}' +BUTTON_BOX_2='{"pin_button": 20, "pin_led": 19}' +# This is the list of the boxes used, if you add new ones, please alse add it's configuration +BOXES_USED='BUTTON_BOX_1, BUTTON_BOX_2' diff --git a/gpio_buttons/logs/example.log b/gpio_buttons/logs/example.log new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gpio_buttons/main.py b/gpio_buttons/main.py new file mode 100755 index 0000000000000000000000000000000000000000..426b1997c54dfbbfc97ed9cfa1662a23e6f39621 --- /dev/null +++ b/gpio_buttons/main.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 + +import sys +### +from signal import pause + +try: + from utils.configuration.app_configuration import AppConfiguration +except ImportError as e: + print("Unable to import 'AppConfiguration' from 'utils.configuration.app_configuration.py' file at the 'main.py'") + sys.exit(1) + +try: + from utils.database.app_database import AppDatabase +except ImportError as e: + print("Unable to import 'AppDatabase' from 'utils.database.app_database.py' file at the 'main.py'") + sys.exit(1) + +try: + from utils.boxes.app_boxes import AppBoxes +except ImportError as e: + print("Unable to import 'AppDatabase' from 'utils.database.app_database.py' file at the 'main.py'") + sys.exit(1) + + +############# +############# +############# + + +BTN_COUNTER = 0 + +def btn_pressed(): + global BTN_COUNTER + print(f"button pressed | counter: {BTN_COUNTER}") + BTN_COUNTER += 1 + +def btn_pressed_2(): + global BTN_COUNTER + print(f"button pressed_2 | counter: {BTN_COUNTER}") + BTN_COUNTER += 1 + +# main function +def main(): + print("\n-------> START \n") + + # Get the configuration, from file or parsed... + configuration = AppConfiguration() + + ### + ### DEBUG + ### + from pprint import pp + # import pdb; pdb.set_trace() + # pp(configuration.dict_values) + + # Set the used DB from the configuration... + database = AppDatabase(configuration) + + ### + ### LOCALSQLite + ### + # ### WORKS + # foo = database.db.table_select_all(configuration.database_table_name_button_box) + # import pdb; pdb.set_trace() + # pp(foo) + + ### + ### API + ### + # # import pdb; pdb.set_trace() + # test_data = { + # # "id": "1" + # "id": "2" + # } + # foo = database.db.post(endpoint="api/box/use", json=test_data) + # pp(foo) + + # bar = database.db.get(endpoint="api/box/use/count") + # pp(bar) + + # Set the Button Boxes + boxes = AppBoxes(configuration, database) + + import pdb; pdb.set_trace() + pp(boxes) + + print("\n <------- FINISH \n") + + +if __name__=="__main__": + main() diff --git a/gpio_buttons/requirements.txt b/gpio_buttons/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..51d4fb62a82004bef81395b92e2f75ecf8845bff --- /dev/null +++ b/gpio_buttons/requirements.txt @@ -0,0 +1,7 @@ +# argparse +### +# logging +pyaml-env +dotenv +requests +# gpiozero diff --git a/gpio_buttons/utils/boxes/__init__.py b/gpio_buttons/utils/boxes/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gpio_buttons/utils/boxes/app_boxes.py b/gpio_buttons/utils/boxes/app_boxes.py new file mode 100644 index 0000000000000000000000000000000000000000..73244cb9e5d4aa8778525b827dd101b1ed0d1589 --- /dev/null +++ b/gpio_buttons/utils/boxes/app_boxes.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +""" + utils.boxes.app_boxes.py file for the c3buttons system. +""" + +### +### DEPENDENCIES +### +import sys +from signal import pause + +### LOCAL +try: + from utils.configuration.app_logger import set_logger_from_config +except ImportError as e: + print("Unable to import 'setup_logger' from 'utils.configuration.app_logger.py' file at 'utils.database.app_database.py' file") + sys.exit(1) + +try: + from .box_led import BoxLed +except ImportError as e: + print("Unable to import 'BoxLed' from 'utils.boxes.box_led.py' file at 'utils.boxes.app_boxes.py' file" ) + sys.exit(1) + +try: + from .box_button import BoxButton +except ImportError as e: + print("Unable to import 'BoxButton' from 'utils.boxes.box_button.py' file at 'utils.boxes.app_boxes.py' file" ) + sys.exit(1) + + +class AppBoxes(): + """ + Class for setting the Boxes of the app. + """ + + def __init__(self, configuration: object, database: object) -> None: + """ + Function for initialiazing the AppConfiguration class. + Sets the class variables. + + Returns + ------- + None + """ + + self.configuration = configuration + self.database = database + + self.logger = set_logger_from_config(name=__name__, config=self.configuration) + + self.buttons = {} + self.set_boxes() + self.set_button_functions() + + def set_boxes(self): + + self.logger.debug("set_boxes") + + for box_name in self.configuration.boxes_used: + if self.configuration.boxes.get(box_name) is not None: + box_info = self.configuration.boxes.get(box_name) + if box_info.get('pin_button') is None: + print(f"\n\n The 'pin_button' value for box with name '{box_name}' was not set" ) + sys.exit(1) + + if box_info.get('pin_led') is None: + print(f"\n\n The 'pin_led' value for box with name '{box_name}' was not set" ) + sys.exit(1) + + box_led = None + if self.configuration.box_led_enabled: + box_led = BoxLed( + configuration=self.configuration, + box_name=box_name, + ) + + box_button = BoxButton( + configuration=self.configuration, + box_name=box_name, + database=self.database, + box_led=box_led, + ) + + self.buttons[box_name] = box_button + + def set_button_functions(self): + + self.logger.debug("set_button_functions") + + try: + for btn_name, btn in self.buttons.items(): + btn.button.when_pressed = btn.activated + self.logger.debug(f"Button functions for box: '{btn_name}' activated...") + + pause() + finally: + pass diff --git a/gpio_buttons/utils/boxes/box_button.py b/gpio_buttons/utils/boxes/box_button.py new file mode 100644 index 0000000000000000000000000000000000000000..8a9d3ffd32486cbb6f804f1de66a696d4e2afdcd --- /dev/null +++ b/gpio_buttons/utils/boxes/box_button.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +""" + utils.database.from_api.py file for the c3 buttons. +""" + + +### +### DEPENDENCIES +### +import sys +from gpiozero import Button +from datetime import datetime + +### LOCAL +try: + from .box_pin import BoxPin +except ImportError as e: + print("Unable to import 'BoxPin' from 'utils.boxes.box_pin.py' file at 'utils.boxes.box_button.py' file") + sys.exit(1) + + +class BoxButton(BoxPin): + """ + TODO: Documentation + """ + + def __init__(self, configuration: dict, box_name: str, database: object, box_led: str = None) -> None: + """ + TODO: Documentation + """ + + ### Init super + super().__init__(name=__name__, configuration=configuration, box_name=box_name) + + self.logger.debug(f"__init__ - box_name: '{box_name}'") + + self.box_led = box_led + + self.database = database + + self.set_pin_usage() + + def set_pin_usage(self): + self.logger.debug(f"set_pin_usage - box_name: '{self.box_name}'") + + self.button_press_count = 0 + try: + self.bounce_time = float(self.configuration.box_button_bounce_time) + + except Exception as e: + print(f"\n\n Error setting the bounce_time from button with exception: {e}") + sys.exit(1) + + self.button = Button(self.pin_button, bounce_time=self.bounce_time) + + def update_db_from_activated(self): + self.logger.debug(f"update_db_from_activated - box_name '{self.box_name}' | pin_button: '{self.pin_button}'") + + self.database.add_button_box_usage(box_name=self.box_name) + + def activated(self): + self.logger.debug(f"activated - box_name '{self.box_name}' | pin_button: '{self.pin_button}' | count: '{self.button_press_count}'") + self.button_press_count += 1 + + ### Update DB + self.database.db.add_button_box_usage(box_name=self.box_name) + + ### Set LED activation + if self.box_led: + self.box_led.activated() diff --git a/gpio_buttons/utils/boxes/box_led.py b/gpio_buttons/utils/boxes/box_led.py new file mode 100644 index 0000000000000000000000000000000000000000..440edcf99837330fbc5c400fb2ff00a1b4165d75 --- /dev/null +++ b/gpio_buttons/utils/boxes/box_led.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +""" + utils.database.from_api.py file for the c3 buttons. +""" + + +### +### DEPENDENCIES +### +import sys +import time +from gpiozero import LED + +### LOCAL +try: + from .box_pin import BoxPin +except ImportError as e: + print("Unable to import 'BoxPin' from 'utils.boxes.box_pin.py' file at 'utils.boxes.box_button.py' file" ) + sys.exit(1) + + +class BoxLed(BoxPin): + """ + TODO: Documentation + """ + + def __init__(self, configuration: dict, box_name: str) -> None: + """ + TODO: Documentation + """ + + ### Init super + super().__init__(name=__name__, configuration=configuration, box_name=box_name) + + self.logger.debug(f"__init__ - box_name: '{box_name}'") + + self.blink_times = int(self.configuration.box_led_blink_times) + + self.set_pin_usage() + + def set_pin_usage(self): + self.logger.debug(f"set_pin_usage - box_name: '{self.box_name}'") + + self.led = LED(self.pin_led) + + if self.configuration.box_led_always_on: + self.led.on() + + def activated(self): + self.logger.debug(f"activated - box_name: '{self.box_name}' | pin_led: '{self.pin_led}'") + + i = 0 + if self.configuration.box_led_always_on: + while i in range(self.blink_times): + self.led.off() + time.sleep(0.5) + self.led.on() + time.sleep(0.5) + i += 1 + + self.led.on() + + else: + while i in range(self.blink_times): + self.led.on() + time.sleep(0.5) + self.led.off() + time.sleep(0.5) + i += 1 + + self.led.off() + diff --git a/gpio_buttons/utils/boxes/box_pin.py b/gpio_buttons/utils/boxes/box_pin.py new file mode 100644 index 0000000000000000000000000000000000000000..f4f800e7abaf710be8f02af03945632384652068 --- /dev/null +++ b/gpio_buttons/utils/boxes/box_pin.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +""" + utils.configuration.from_file.py file for the c3 buttons. + This is the super class used from the other classes that fetches configurations from a file. +""" +import sys +from abc import ABC, abstractmethod + +### LOCAL +try: + from utils.configuration.app_logger import set_logger_from_config +except ImportError as e: + print("Unable to import 'set_logger_from_dict' from 'utils.configuration.app_logger.py' file at 'utils.boxes.box_pin.py' file" ) + sys.exit(1) + + +class BoxPin(ABC): + """ TODO + """ + + def __init__(self, name: str, configuration: object, box_name: str) -> None: + + self.configuration = configuration + self.logger = set_logger_from_config(name=name, config=self.configuration) + self.get_box_config(box_name) + + def get_box_config(self, box_name): + self.logger.debug(f"get_box_config - box_name: {box_name}") + + self.box_name = box_name + box_info = self.configuration.boxes.get(box_name) + self.pin_button = box_info.get('pin_button') + self.pin_led = box_info.get('pin_led') + self.enabled_led=self.configuration.box_led_enabled + + @abstractmethod + def set_pin_usage(self): + pass + + @abstractmethod + def activated(self): + pass diff --git a/gpio_buttons/utils/configuration/__init__.py b/gpio_buttons/utils/configuration/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gpio_buttons/utils/configuration/app_configuration.py b/gpio_buttons/utils/configuration/app_configuration.py new file mode 100644 index 0000000000000000000000000000000000000000..0995a98e3ec9de3290785cc28b880abfa885ba04 --- /dev/null +++ b/gpio_buttons/utils/configuration/app_configuration.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 +""" + utils.configuration.app_configuration.py file for the c3buttons system. + In this file the configuration will be setted from this order: + config.yml -> environment vars +""" + +### +### DEPENDENCIES +### +import sys + +### LOCAL +try: + from .default_values import DEFAULT_VALUES +except ImportError as e: + print("Unable to import 'utils.configuration.default_vals.py' from 'utils.configuration.app_configuration.py' file") + sys.exit(1) + +try: + from .app_logger import set_logger_from_default +except ImportError as e: + print("Unable to import 'setup_logger' from 'utils.configuration.app_logger.py' file at the 'utils.configuration.app_configuration.py'") + sys.exit(1) + +try: + from .from_file_environment import ConfigurationFromDotEnv +except ImportError as e: + print("Unable to import 'ConfigurationFromFile' from 'utils.configuration.from_file_environment.py' file at the 'utils.configuration.app_configuration.py'") + sys.exit(1) + +try: + from .from_file_yaml import ConfigurationFromYamlFile +except ImportError as e: + print("Unable to import 'ConfigurationFromYamlFile' from 'utils.configuration.from_file_yaml.py' file at the 'utils.configuration.app_configuration.py'") + sys.exit(1) + + +class AppConfiguration(): + """ + Class for setting the configuration of the app. + """ + + def __init__(self) -> None: + """ + Function for initialiazing the AppConfiguration class. + Sets the class variables. + + Returns + ------- + None + """ + self.logger = set_logger_from_default(name=__name__, values_default=DEFAULT_VALUES) + + self.logger.info("---> STARTED") + + self.__configuration_colors__() + + self.set_attr_from_parsed() + + if self.debug: + self.__debug_attrs__() + + def check_missing_attrs(self, from_env: dict, from_yaml: dict) -> dict: + + self.logger.debug("check_missin_attrs") + + ret_dict = {} + + # If in from_env and not in from_yaml + for name, value in from_env.dict_values.items(): + + if name not in from_yaml.dict_values.keys(): + ret_dict[name] = value + + # if name in from_yaml.dict_values.keys(): + else: + + if name == "boxes": + yaml_value = from_yaml.dict_values.get("boxes") + if len(value) >= len(yaml_value): + boxes_dict = value + boxes_list = from_env.dict_values.get("boxes_used") + self.logger.info("---> Using boxes configuration from OS environments") + + else: + boxes_dict = yaml_value + boxes_list = from_yaml.dict_values.get("boxes_used") + self.logger.info("---> Using boxes configuration from config.yml file") + + # ret_dict[name] = new_value + ret_dict["boxes"] = boxes_dict + ret_dict["boxes_used"] = boxes_list + + elif name == "boxes_used": + pass + + else: + ret_dict[name] = value + + + ### DEBUG + # from pprint import pp + # print(f"name: {name} | ret_dict: {ret_dict}") + + # If in from_yaml and not in from_env + for name, value in from_yaml.dict_values.items(): + if name not in ret_dict.keys(): + ret_dict[name] = value + + # from pprint import pp + # import pdb; pdb.set_trace() + # pp(ret_dict) + + return ret_dict + + def attr_set_from_dict(self, dict_values: dict) -> None: + + self.logger.debug("attr_set_from_dict") + + for name, value in dict_values.items(): + setattr(self, name.lower(), value) + + def set_attr_from_parsed(self) -> None: + + self.logger.debug("set_attr_from_parsed") + + ### Get attrs from env + from_env = ConfigurationFromDotEnv(default_values= DEFAULT_VALUES) + + ### If not using, set it from the os.env + if not DEFAULT_VALUES.get("DEFAULT_USING_PARSED_YAML"): + dict_values = from_env.dict_values + + else: + ### Fetch params from YAML file + from_yaml = ConfigurationFromYamlFile(default_values= DEFAULT_VALUES) + dict_values = self.check_missing_attrs(from_env=from_env, from_yaml=from_yaml) + + ### Set the attributes as a dict aswell + self.attr_set_from_dict(dict_values) + + self.dict_values = dict_values + + def __configuration_colors__(self) -> None: + """ + Method for setting the terminal output with colors. + + Returns + ------- + None + """ + self.logger.debug("__configuration_colors__") + + self.color_gray = "\033[90m" + self.color_red = "\033[91m" + self.color_green = "\033[92m" + self.color_orange = "\033[93m" + self.color_blue = "\033[94m" + self.color_pink = "\033[95m" + self.color_yellow = "\033[93m" + self.color_end = "\033[0m" + + def __debug_attrs__(self) -> None: + """ + Function for printing the configuration parameters from the app. + + Returns + ------- + None + """ + self.logger.debug("__debug_attrs__") + ### PRINT ARGS + print("\n" + self.color_blue + "CONFIGURATION ATTRS:" + self.color_end) + for name, value in self.dict_values.items(): + print(self.color_green + f"\t- {name}: " + self.color_end + f"'{value}'") + + print("\n") diff --git a/gpio_buttons/utils/configuration/app_logger.py b/gpio_buttons/utils/configuration/app_logger.py new file mode 100644 index 0000000000000000000000000000000000000000..6f3da52282e1a67e933f2eca604d96d531f74d95 --- /dev/null +++ b/gpio_buttons/utils/configuration/app_logger.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +""" + utils.configuration.app_logger.py file for the c3 printing system. +""" + +### +### DEPENDENCIES +### +import sys +import logging + + +def setup_logger(name: str, + level: str | None = "INFO", + log_formatter: str | None = None, + log_file: str | None = None, + log_file_append: bool | None = False + ) -> logging.Logger: + + if level == "DEBUG": + lvl = logging.DEBUG + + elif level == "INFO": + lvl = logging.INFO + + elif level == "WARNING": + lvl = logging.WARNING + + elif level == "ERROR": + lvl = logging.ERROR + + else: + print(f"\nERROR ---> Could not set logger for: {name}.\n LogLevel parsed '{level}' is unknown...\n\n") + sys.exit() + + # Create a logger + logger = logging.getLogger(name) + + # Set the logging level + logger.setLevel(lvl) + + # Avoid adding multiple handlers + if not logger.hasHandlers(): + + # Set log formatter + if log_formatter is None: + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + + else: + formatter = logging.Formatter(log_formatter) + + if log_file is not None: + if log_file_append: + fh = logging.FileHandler(log_file, mode="a") + else: + fh = logging.FileHandler(log_file) + + fh.setLevel(lvl) + fh.setFormatter(formatter) + logger.addHandler(fh) + + # Create a console handler (optional, if you still want console output) + ch = logging.StreamHandler() + ch.setLevel(lvl) + # # Change to WARNING to only see warnings/errors in the terminal + # ch.setLevel(logging.WARNING) + # ch.setLevel(logging.INFO) + + # Add formatter to both handlers + ch.setFormatter(formatter) + + # Add handlers to the logger + logger.addHandler(ch) + + return logger + + +def set_logger_from_default(name: str, values_default: dict) -> logging.Logger: + + log_file = None + if values_default.get('DEFAULT_LOG_TO_FILE'): + log_file = values_default.get('DEFAULT_LOG_FILE_PATH') + + if values_default.get('DEFAULT_DEBUG'): + logger = setup_logger( + name=name, + level="DEBUG", + log_formatter=values_default.get('DEFAULT_LOG_FORMAT'), + log_file=log_file, + ) + + else: + logger = setup_logger( + name=name, + # level=logging.DEBUG, + log_formatter=values_default.get('DEFAULT_LOG_FORMAT'), + log_file=log_file, + ) + + return logger + + +def set_logger_from_config(name: str, config: object) -> logging.Logger: + + log_file = None + log_file_append = True + + if config.log_to_file: + log_file = config.log_file_path + log_file_append = config.log_file_append_mode + + if config.debug: + logger = setup_logger( + name=name, + level="DEBUG", + log_formatter=config.log_format, + log_file=log_file, + log_file_append=log_file_append, + ) + + else: + logger = setup_logger( + name=name, + # level="DEBUG", + log_formatter=config.log_format, + log_file=log_file, + log_file_append=log_file_append, + ) + + return logger + + +def set_logger_from_dict(name: str, dict_values: dict) -> logging.Logger: + + log_file = None + log_file_append = True + if dict_values.get('log_to_file'): + log_file = dict_values.get('log_file_path') + log_file_append = dict_values.get('log_file_append_mode') + + if dict_values.get('debug'): + logger = setup_logger( + name=name, + level="DEBUG", + log_formatter=dict_values.get('log_format'), + log_file=log_file, + log_file_append=log_file_append, + ) + + else: + logger = setup_logger( + name=name, + # level="DEBUG", + log_formatter=dict_values.get('log_format'), + log_file=log_file, + log_file_append=log_file_append, + ) + + return logger \ No newline at end of file diff --git a/gpio_buttons/utils/configuration/config.yml b/gpio_buttons/utils/configuration/config.yml new file mode 100644 index 0000000000000000000000000000000000000000..21a79ee1cba02b007c18a4016d3082790b0d965f --- /dev/null +++ b/gpio_buttons/utils/configuration/config.yml @@ -0,0 +1,27 @@ +--- + +default: + debug: true + verbose: true + enabled_led: True + description: "Updated default app description" + + boxes_used: "box_1, box_2" + + box_1: + pin_button: 21 + pin_led: 22 + + box_2: + pin_button: 23 + pin_led: 23 + + + ### + ### DEBUG + ### + foo: "bar" + # boxes_used: "box_1, box_2, box_3" + # box_3: + # pin_button: 24 + # pin_led: 25 diff --git a/gpio_buttons/utils/configuration/default_values.py b/gpio_buttons/utils/configuration/default_values.py new file mode 100644 index 0000000000000000000000000000000000000000..13c9a88f28b2dd9f1a34acc1f415e2ef7128a9ac --- /dev/null +++ b/gpio_buttons/utils/configuration/default_values.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +""" + utils.config.default_vals.py file for the c3 buttons. + Used for setting the default values from the 'utils.app_configuration.py' file. +""" + +DEFAULT_VALUES = { + 'DEFAULT_DESCRIPTION': 'UPDATE_THIS', + # 'DEFAULT_PATH_CONFIG_ENV': './.env', + 'DEFAULT_PATH_CONFIG_ENV': '.env', + ### + ### config.yml + ### + 'DEFAULT_USING_PARSED_YAML': False, + 'DEFAULT_PATH_CONFIG_YML': './utils/configuration/config.yml', + ### + ### DEBUG/VEBOSE + ### + 'DEFAULT_DEBUG': False, + 'DEFAULT_VERBOSE': False, + ### + ### LOG + ### + # "DEFAULT_LOG_TO_FILE": True, + "DEFAULT_LOG_TO_FILE": False, + "DEFAULT_LOG_FILE_APPEND_MODE": True, + "DEFAULT_LOG_FILE_PATH": "./logs/example.log", + "DEFAULT_LOG_FORMAT": '%(asctime)s - %(levelname)s - %(name)s - %(message)s', + ### + ### DB + ### + "DEFAULT_DATABASE_TYPE":"local_file", + "DEFAULT_DATABASE_TABLE_NAME_BUTTON_BOX": "button_box", + "DEFAULT_DATABASE_TABLE_NAME_BUTTON_BOX_USE": "button_box_use", + "DEFAULT_DATABASE_LOCAL_DB_FILE_PATH":"Please set --> DATABASE_LOCAL_DB_FILE_PATH", + "DEFAULT_DATABASE_API_URL":"http://localhost:8000", + "DEFAULT_DATABASE_API_TOKEN":"Please set --> DATABASE_API_TOKEN", + ### + ### BOXES CONFIG + ### + 'DEFAULT_BOX_LED_ENABLED': False, + 'DEFAULT_BOX_LED_AWAYS_ON': False, + 'DEFAULT_BOX_LED_BLINK_TIMES': 5, + + ### DEFAULT_BOX_BUTTON_BOUNCE_TIME in microseconds + 'DEFAULT_BOX_BUTTON_BOUNCE_TIME': "0.1", + + ### + ### BOXES USED + ### + "DEFAULT_BOXES_USED": "DEFAULT_BOX_1, DEFAULT_BOX_2, DEFAULT_BOX_3, DEFAULT_BOX_4, DEFAULT_BOX_5", + "DEFAULT_BOX_1": '{"pin_button": 14, "pin_led": 15}', + "DEFAULT_BOX_2": '{"pin_button": 18, "pin_led": 23}', + "DEFAULT_BOX_3": '{"pin_button": 24, "pin_led": 25}', + "DEFAULT_BOX_4": '{"pin_button": 8, "pin_led": 7}', + "DEFAULT_BOX_5": '{"pin_button": 12, "pin_led": 16}', +} diff --git a/gpio_buttons/utils/configuration/from_file.py b/gpio_buttons/utils/configuration/from_file.py new file mode 100644 index 0000000000000000000000000000000000000000..b32f6563d97918cab8669edc148ece1bc1947d0b --- /dev/null +++ b/gpio_buttons/utils/configuration/from_file.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +""" + utils.configuration.from_file.py file for the c3 buttons. + This is the super class used from the other classes that fetches configurations from a file. +""" +import sys +from abc import ABC, abstractmethod + +### LOCAL +try: + from .app_logger import set_logger_from_default +except ImportError as e: + print("Unable to import 'setup_logger' from 'utils.app_logger.py' file at 'utils.configuration.from_file.py' file" ) + sys.exit(1) + + +class ConfigurationFromFile(ABC): + """ TODO + """ + + def __init__(self, name: str, default_values: dict) -> None: + + self.default_values = default_values + # self.logger = set_logger_from_default(name=__name__, values_default=self.default_values) + self.logger = set_logger_from_default(name=name, values_default=self.default_values) + + self.path_config_file = None + self.dict_values = {} + + @abstractmethod + def get_values(self): + pass diff --git a/gpio_buttons/utils/configuration/from_file_environment.py b/gpio_buttons/utils/configuration/from_file_environment.py new file mode 100644 index 0000000000000000000000000000000000000000..c8612a7fc2196c028c8e6c52afff2ec4c62444e4 --- /dev/null +++ b/gpio_buttons/utils/configuration/from_file_environment.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 +""" + utils.configuration.from_file.py file for the c3 buttons. +""" + +### +### DEPENDENCIES +### +import sys +import os +import json + +try: + from dotenv import load_dotenv +except ImportError as e: + print("Unable to import 'dotenv'") + print("Please install it with 'pip(3) install dotenv --user'") + sys.exit(1) + +### LOCAL +try: + from .from_file import ConfigurationFromFile +except ImportError as e: + print("Unable to import 'ConfigurationFromFile' from 'utils.configuration.from_file.py' file at 'utils.configuration.from_file_environment.py' file" ) + sys.exit(1) + + +class ConfigurationFromDotEnv(ConfigurationFromFile): + """ TODO + """ + + def __init__(self, default_values: dict) -> None: + """ + TODO: Documentation + """ + + ### Init super + super().__init__(name=__name__, default_values=default_values) + + self.path_config_file = self.default_values.get('DEFAULT_PATH_CONFIG_ENV') + self.dict_values["boxes"] = {} + self.get_values() + + def __get_env_bool__(self, name_config: str, name_config_default: str) -> bool: + """ + Method to fetch the environment variable from a boolean type + + Returns + ------- + bool: + Return a boolean value from fetching the setted environment variable + """ + self.logger.debug(f"__get_env_bool__ - {name_config}") + try: + fetched = os.getenv(name_config) + + if fetched: + if fetched in ('YES', 'yes', 'true', 'True', 'TRUE'): + ret_bool = True + elif fetched in ('NO', 'no', 'false', 'False', 'FALSE'): + ret_bool = False + else: + print(f"\n\n Unable to fetch the parsed environment '{name_config}'. It is set but not 'true' or 'false' \n\n") + sys.exit(1) + else: + ret_bool = self.default_values.get(name_config_default) + + except Exception as e: + print(f"\n\n Unable to fetch the parsed environment '{name_config}' \n| The exception is: '{e}' \n\n") + sys.exit(1) + + return ret_bool + + def __get_env__(self, name_config: str, name_config_default: str) -> (str | int): + """ + Method to fetch the environment variable from a string or int type. + + Returns + ------- + str | int: + Return a the value from fetching the setted environment variable. + """ + self.logger.debug(f"__get_env__ - {name_config}") + try: + fetched = os.getenv(name_config) + + ### If not setted, get it from config + if fetched is None: + fetched = self.default_values.get(name_config_default) + + except Exception as e: + print(f"\n\n Unable to fetch the parsed environment '{name_config}' \n| The exception is: '{e}' \n\n") + sys.exit(1) + + return fetched + + def __get_env_dict__(self, name_config: str, name_config_default: str): + """ + Method to fetch the environment variable from a string or int type. + + Returns + ------- + str | int: + Return a the value from fetching the setted environment variable. + """ + self.logger.debug(f"__get_env_dict__ - {name_config}") + + fetched = self.__get_env__( + name_config=name_config, + name_config_default=name_config_default + ) + + try: + ret_dict = json.loads(fetched) + + except Exception as e: + print(f"\n\n Unable to parse the dictionary environment from: '{name_config}' \n| The exception is: '{e}' \n\n") + sys.exit(1) + + self.dict_values["boxes"][name_config.lower()] = ret_dict + + def get_values(self) -> None: + """ + Used for fetching the parsed values from the system. + If the value is not parsed, it will use the default values settend at: utils.configuration.default_vals.py + """ + self.logger.debug("get_values") + + if not load_dotenv(dotenv_path=self.path_config_file): + print("\n\n ERROR!: \ņ\n Unable to load the '.env' file at the path: '{self.path_config_file}'. Change it at the '.configuration.default_vals.py' file") + sys.exit() + + self.dict_values["description"] = self.__get_env__( + name_config="DESCRIPTION", + name_config_default="DEFAULT_DESCRIPTION" + ) + ### + ### DEBUG/VEBOSE + ### + self.dict_values["debug"] = self.__get_env_bool__( + name_config="DEBUG", + name_config_default="DEFAULT_DEBUG" + ) + self.dict_values["verbose"] = self.__get_env_bool__( + name_config="VERBOSE", + name_config_default="DEFAULT_VERBOSE" + ) + ### + ### LOG + ### + self.dict_values["log_file_append_mode"] = self.__get_env_bool__( + name_config="LOG_FILE_APPEND_MODE", + name_config_default="DEFAULT_LOG_FILE_APPEND_MODE" + ) + self.dict_values["log_to_file"] = self.__get_env_bool__( + name_config="LOG_TO_FILE", + name_config_default="DEFAULT_LOG_TO_FILE" + ) + self.dict_values["log_file_path"] = self.__get_env__( + name_config="LOG_FILE_PATH", + name_config_default="DEFAULT_LOG_FILE_PATH" + ) + self.dict_values["log_format"] = self.__get_env__( + name_config="LOG_FORMAT", + name_config_default="DEFAULT_LOG_FORMAT" + ) + ### + ### DB + ### + database_type = self.__get_env__( + name_config="DATABASE_TYPE", + name_config_default="DEFAULT_DATABASE_TYPE" + ) + + if database_type == "local_file": + self.dict_values["database_table_name_button_box"] = self.__get_env__( + name_config="DATABASE_TABLE_NAME_BUTTON_BOX", + name_config_default="DEFAULT_DATABASE_TABLE_NAME_BUTTON_BOX" + ) + self.dict_values["database_table_name_button_box_use"] = self.__get_env__( + name_config="DATABASE_TABLE_NAME_BUTTON_BOX_USE", + name_config_default="DEFAULT_DATABASE_TABLE_NAME_BUTTON_BOX_USE" + ) + self.dict_values["database_local_db_file_path"] = self.__get_env__( + name_config="DATABASE_LOCAL_DB_FILE_PATH", + name_config_default="DEFAULT_DATABASE_LOCAL_DB_FILE_PATH" + ) + + elif database_type == "api": + self.dict_values["database_api_url"] = self.__get_env__( + name_config="DATABASE_API_URL", + name_config_default="DEFAULT_DATABASE_API_URL" + ) + self.dict_values["database_api_token"] = self.__get_env__( + name_config="DATABASE_API_TOKEN", + name_config_default="DEFAULT_DATABASE_API_TOKEN" + ) + + else: + print(f"\n\n Unable to set the database_type from '{database_type}'\n Accepted values are: 'local_file' or 'api' \n\n") + sys.exit(1) + + self.dict_values["database_type"] = database_type + + ### + ### BOXES CONFIG + ### + self.dict_values["box_button_bounce_time"] = self.__get_env__( + name_config="BOX_BUTTON_BOUNCE_TIME", + name_config_default="DEFAULT_BOX_BUTTON_BOUNCE_TIME" + ) + self.dict_values["box_led_enabled"] = self.__get_env_bool__( + name_config="BOX_LED_ENABLED", + name_config_default="DEFAULT_BOX_LED_ENABLED" + ) + self.dict_values["box_led_always_on"] = self.__get_env_bool__( + name_config="BOX_LED_AWAYS_ON", + name_config_default="DEFAULT_BOX_LED_AWAYS_ON" + ) + self.dict_values["box_led_blink_times"] = self.__get_env__( + name_config="BOX_LED_BLINK_TIMES", + name_config_default="DEFAULT_BOX_LED_BLINK_TIMES" + ) + ### + ### BOXES USED + ### + # Set boxes_used as a list + self.dict_values["boxes_used"] = self.__get_env__( + name_config="BOXES_USED", + name_config_default="DEFAULT_BOXES_USED" + ).replace(' ','').replace("'", "").replace('"', '').split(',') + + + # import pdb; pdb.set_trace() + # self.dict_values["boxes_used"] + # os.getenv("BOXES_USED") + # load_dotenv(dotenv_path=self.path_config_file) + # # "'BUTTON_BOX_1, BUTTON_BOX_2'" + + # Adds dict_values with 'boxes' infos from 'boxes_used' list + for box_name in self.dict_values.get("boxes_used"): + + self.app_description = self.__get_env_dict__( + name_config=box_name, + name_config_default="DEFAULT_" + box_name + ) + + # Fix lower boxes names from boxes_used list + for index, item in enumerate(self.dict_values.get("boxes_used")): + self.dict_values.get("boxes_used")[index] = item.lower() diff --git a/gpio_buttons/utils/configuration/from_file_yaml.py b/gpio_buttons/utils/configuration/from_file_yaml.py new file mode 100644 index 0000000000000000000000000000000000000000..0b37c9f27d66c81aabe5a0772ab109e8c17b0af2 --- /dev/null +++ b/gpio_buttons/utils/configuration/from_file_yaml.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +""" + utils.configuration.from_file_yaml.py file for the c3 buttons. +""" + +### +### DEPENDENCIES +### +import sys +import json + +try: + import pyaml_env +except ImportError as e: + print("Unable to import 'pyaml_env'") + print("Please install it with 'pip(3) install pyaml_env --user'") + sys.exit(1) + +### LOCAL +try: + from .from_file import ConfigurationFromFile +except ImportError as e: + print("Unable to import 'ConfigurationFromFile' from 'utils.configuration.from_file.py' file at the 'utils.configuration.from_file_yaml.py' file" ) + sys.exit(1) + + +class ConfigurationFromYamlFile(ConfigurationFromFile): + + def __init__(self, default_values: dict) -> None: + + ### Init super + super().__init__(name=__name__, default_values=default_values) + + self.path_config_file = self.default_values.get('DEFAULT_PATH_CONFIG_YML') + self.dict_values = self.get_values() + + def __get_env_dict__(self, parsed: dict) -> dict: + + self.logger.debug(f"__get_env_dict__ - {parsed}") + + parsed['boxes_used'] = parsed.get("boxes_used").replace(' ',''). split(',') + parsed['boxes'] = {} + + for box_name in parsed.get("boxes_used"): + + box_info = parsed.get(box_name) + if box_info is None: + print(f"\n\n Error getting the configuration from the box '{box_name}' from the 'config.yml' file. \n If you want to use this box, configure it also.\n\n") + sys.exit(1) + + if isinstance(box_info, dict): + ret_dict = box_info + + else: + try: + ret_dict = json.loads(box_info) + + except Exception as e: + print(f"\n\n Unable to parse the dictionary from the '{box_name}' with value: '{box_info}' \n| The exception is: '{e}' \n\n") + sys.exit(1) + + parsed["boxes"][box_name.lower()] = ret_dict + parsed.pop(box_name) + + return parsed + + def get_values(self) -> dict: + + self.logger.debug(f"get_values") + + try: + parsed_all = pyaml_env.parse_config(self.path_config_file) + + except Exception as e: + print(f"\n\n Error reading the 'yaml' configurtion file. \n Exception: {e}") + sys.exit(1) + + default_parsed = parsed_all.get('default') + if default_parsed is None: + print(f"\n\n Error getting the configuration from the 'config.yml' file. \n Exception: {e}") + sys.exit(1) + + return self.__get_env_dict__(parsed=default_parsed) diff --git a/gpio_buttons/utils/database/__init__.py b/gpio_buttons/utils/database/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gpio_buttons/utils/database/app_database.py b/gpio_buttons/utils/database/app_database.py new file mode 100644 index 0000000000000000000000000000000000000000..d7047e66b8416884f0033e2e6149895ee97fa7e7 --- /dev/null +++ b/gpio_buttons/utils/database/app_database.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +""" + utils.database.app_database.py file for the c3 printing system. +""" + +import sys +import logging + +### LOCAL +try: + from utils.configuration.app_logger import set_logger_from_config +except ImportError as e: + print("Unable to import 'setup_logger' from 'utils.configuration.app_logger.py' file at 'utils.database.app_database.py' file") + sys.exit(1) + +try: + from .from_local_file import DataBaseLocalSqLite +except ImportError as e: + print("Unable to import 'DataBaseUsed' from 'utils.database.from_local_file.py' file at 'utils.database.app_database.py' file") + sys.exit(1) + +try: + from .from_api import DataBaseApi +except ImportError as e: + print("Unable to import 'DataBaseApi' from 'utils.database.from_api.py' file at 'utils.database.app_database.py' file") + sys.exit(1) + + +class AppDatabase(): + """ + Class for setting the configuration of the app. + """ + + def __init__(self, configuration) -> None: + """ + Function for initialiazing the AppConfiguration class. + Sets the class variables. + + Returns + ------- + None + """ + + self.configuration = configuration + + self.logger = set_logger_from_config(name=__name__, config=self.configuration) + + self.set_database() + + def set_database(self) -> None: + + self.logger.debug("set_database") + + if self.configuration.database_type == "local_file": + self.logger.info("---> Using local SQLite3 file as Database") + self.db = DataBaseLocalSqLite(configuration=self.configuration) + + elif self.configuration.database_type == "api": + self.logger.info("---> Using API calls as Database") + self.db = DataBaseApi(configuration=self.configuration) + + else: + print(f"\n\n ERROR! ---> Unable to set database from the parsed 'database_type' with value: {self.configuration.database_type}") + sys.exit(1) diff --git a/gpio_buttons/utils/database/from_api.py b/gpio_buttons/utils/database/from_api.py new file mode 100644 index 0000000000000000000000000000000000000000..3d9839e440deab795ea47d49e4440c015186e64d --- /dev/null +++ b/gpio_buttons/utils/database/from_api.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +""" + utils.database.from_api.py file for the c3 buttons. +""" + + +### +### DEPENDENCIES +### +import sys +import requests + +### LOCAL +try: + from .from_database import DataBaseUsed +except ImportError as e: + print("Unable to import 'DataBaseUsed' from 'utils.database.from_database.py' file at 'utils.database.from_api.py' file" ) + sys.exit(1) + + +class DataBaseApi(DataBaseUsed): + """ + TODO: Documentation + """ + + def __init__(self, configuration: dict) -> None: + """ + TODO: Documentation + """ + + ### Init super + super().__init__(name=__name__, configuration=configuration) + + self.__check_token__() + + def __check_token__(self) -> None: + """ + TODO: Documentation + """ + + self.logger.debug("__check_token__") + + response = self.get(endpoint="auth/test/closed") + if response is None: + self.logger.error("__check_token__ - Token is NOT working...") + print("\nERROR ---> Initial Token is not working.\n You should Generate a new one and set in the configuration...\n\n") + sys.exit() + + else: + if response.get('error'): + print("\nERROR ---> Some error happend trying to get the initial token.\n ") + sys.exit() + + else: + self.logger.info("__check_token__ - Token is working...") + + def __get_token_headers__(self) -> dict: + + return { + "Content-Type": "application/json; charset=utf-8", + "Authorization": f"Bearer {self.configuration.database_api_token}" + } + + def get(self, endpoint, params=None) -> (dict | None): + + self.logger.debug(f"get - endpoint: '{endpoint}' | params: '{params}'") + + url = f"{self.configuration.database_api_url}/{endpoint}" + try: + response = requests.get( + url, + headers=self.__get_token_headers__(), + params=params + ) + response.raise_for_status() + return response.json() + + except requests.RequestException as e: + self.logger.error(f"GET request failed with exception: {e}") + return None + + def post(self, endpoint, json=None, data=None) -> (dict | None): + + self.logger.debug(f"post - endpoint: '{endpoint}' | json: '{json}' | data: '{data}'") + + url = f"{self.configuration.database_api_url}/{endpoint}" + try: + response = requests.post( + url, + json=json, + data=data, + headers=self.__get_token_headers__() + ) + response.raise_for_status() + return response.json() + + except requests.RequestException as e: + self.logger.error(f"POST request failed with exception: {e}") + return None + + def add_button_box_usage(self, box_name: str): + + self.logger.debug(f"add_button_box_usage - box_name: '{box_name}'") + + post_data = { + "name": box_name + } + resp = self.post(endpoint="api/box/use", json=post_data) + if resp.get('error'): + self.logger.error(f"Error adding button usage with response: {resp}") diff --git a/gpio_buttons/utils/database/from_database.py b/gpio_buttons/utils/database/from_database.py new file mode 100644 index 0000000000000000000000000000000000000000..447d934f1a0eaf9a59208fde93a337fee2ecd126 --- /dev/null +++ b/gpio_buttons/utils/database/from_database.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +""" + utils.configuration.from_file.py file for the c3 buttons. + This is the super class used from the other classes that fetches configurations from a file. +""" + +import sys +from abc import ABC, abstractmethod + +try: + from utils.configuration.app_logger import set_logger_from_config +except ImportError as e: + print("Unable to import 'set_logger_from_dict' from 'utils.configuration.app_logger.py' file at 'utils.database.from_api.py' file" ) + sys.exit(1) + +class DataBaseUsed(ABC): + """ TODO + """ + + def __init__(self, name: str, configuration: dict) -> None: + + self.configuration = configuration + # self.logger = set_logger_from_config(name=__name__, config=self.configuration) + self.logger = set_logger_from_config(name=name, config=self.configuration) + self.debug = configuration.debug + self.conn = None + self.cursor = None + + @abstractmethod + def add_button_box_usage(self, box_name: str): + pass diff --git a/gpio_buttons/utils/database/from_local_file.py b/gpio_buttons/utils/database/from_local_file.py new file mode 100644 index 0000000000000000000000000000000000000000..7017e52feb9b7673f324c8683c6a1b1361fa219b --- /dev/null +++ b/gpio_buttons/utils/database/from_local_file.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +""" + utils.database.from_local_file.py file for the c3 buttons. +""" + +### +### DEPENDENCIES +### +import sys +import sqlite3 +from datetime import datetime + +### LOCAL +try: + from .from_database import DataBaseUsed +except ImportError as e: + print("Unable to import 'DataBaseUsed' from 'utils.database.from_database.py' file at 'utils.database.from_local_file.py' file" ) + sys.exit(1) + + +class DataBaseLocalSqLite(DataBaseUsed): + """ TODO + """ + + def __init__(self, configuration: dict) -> None: + + ### Init super + super().__init__(name=__name__, configuration=configuration) + + self.file_path = self.configuration.database_local_db_file_path + + ### + ### CONNECTION + ### + def conn_create(self): + self.conn = sqlite3.connect(self.file_path) + self.cursor = self.conn.cursor() + + def conn_close(self): + + if self.conn: + self.cursor.close() + self.conn.close() + self.cursor = None + self.conn = None + + ### + ### QUERY + ### + def query_fetch_one(self, query: str) -> None: + + # self.logger.debug(f"query_fetch_one - query: {query}") + self.logger.debug("query_fetch_one") + + if self.conn is None: + self.conn_create() + + try: + resp = self.cursor.execute(query) + return resp.fetchone()[0] + + except Exception as e: + self.logger.error(f"ERROR executing query: {query} | with execption: {e}") + + def query_fetch_all(self, query: str) -> None: + + # self.logger.debug(f"query_fetch_all - query: {query}") + self.logger.debug("query_fetch_all") + + if self.conn is None: + self.conn_create() + + try: + self.cursor.execute(query) + resp = self.cursor.execute(query) + + return resp.fetchall() + + except Exception as e: + self.logger.error(f"ERROR executing query: {query} | with execption: {e}") + + finally: + self.conn_close() + + def query_exec(self, query: str) -> None: + + # self.logger.debug(f"query_exec - query: {query}") + self.logger.debug("query_exec") + + if self.conn is None: + self.conn_create() + + try: + self.cursor.execute(query) + + except Exception as e: + self.logger.error(f"ERROR executing query: {query} | with execption: {e}") + + finally: + if self.conn: + self.conn.commit() + + ### + ### TABLES + ### + def table_select_all(self, table_name: str) -> (list | None): + + self.logger.debug(f"table_select_all - table_name: {table_name}") + + if self.conn is None: + self.conn_create() + + query = "SELECT * FROM " + table_name + ";" + + return self.query_fetch_all(query=query) + + def table_get_button_box_id(self, box_name: str): + + self.logger.debug(f"table_get_button_box_id - box_name '{box_name}'") + + table_name = self.configuration.database_table_name_button_box + + query = """ + SELECT id FROM {table_name} WHERE {field_name}="{field_value}"; + """.format( + table_name=table_name, + field_name="name", + field_value=box_name + ) + + # box_id = self.query_fetch_one(query=query) + # self.conn_close() + # return box_id + + ################## + ################## + ################## + + return self.query_fetch_one(query=query) + + def add_button_box_usage(self, box_name: str): + + self.logger.debug(f"table_add_button_box_usage - box_name '{box_name}'") + + if self.conn is None: + self.conn_create() + + button_box_id = self.table_get_button_box_id(box_name=box_name) + values = (str(datetime.now()), button_box_id) + + query = """ + INSERT INTO "{table_name}" {columns_list} VALUES {values} + """.format( + table_name=self.configuration.database_table_name_button_box_use, + columns_list=("date", "box_used_id"), + values=values + ) + self.query_exec(query) + self.conn_close() diff --git a/pycodestyle.cfg b/pycodestyle.cfg new file mode 100644 index 0000000000000000000000000000000000000000..77841abb932bc1f6d54a2f8e46ea00027025c12d --- /dev/null +++ b/pycodestyle.cfg @@ -0,0 +1,8 @@ +[pycodestyle] +count = False +; E266 too many leading '#' for block comment +; E401 multiple imports on one line +ignore = E266, E401 +max-line-length = 160 +; max-line-length = 81 +statistics = True \ No newline at end of file