""" Module that manages the user interface for configure environment variables with npyscreen """ import os import json import re from difflib import get_close_matches import curses import npyscreen from tools.envConf.def_conf import DEFAULTS, _FILEPATH_KEY, _TEXTBOX_KEY, _PASSWORD_KEY, _SECTION_KEY from django.conf import settings from tools.crypto_tools import decrypt as crypto_decrypt, encrypt as crypto_encrypt from tools.envConf.def_conf import DEFAULTS, _FILEPATH_KEY, _TEXTBOX_KEY, _PASSWORD_KEY,\ _SECTION_KEY CONF_PATH = '.config' CONF_PASS_PATH = '.confpass.pem' class Configure: """ Main class that control the render of the interface, import default values and export the values updated. """ parameter_not_found = type('ParameterNotFound', (Exception,), {}) def __init__(self): self.__parameters = {} parameters = self.__read_parameters() self.__load_parameters(parameters) @classmethod def __find_on_dict(cls, key, dict_where_seek): if hasattr(dict_where_seek, 'items'): childrens_dicts = [] for k, v in dict_where_seek.items(): if k == key: yield v if isinstance(v, dict): childrens_dicts.append(v) for children in childrens_dicts: for result in cls.__find_on_dict(key, children): yield result @classmethod def __find_all_keys(cls, dict_where_seek): childrens_dicts = [] for key in dict_where_seek: yield key if isinstance(dict_where_seek[key], dict): childrens_dicts.append(dict_where_seek[key]) for children in childrens_dicts: for result in cls.__find_all_keys(children): yield result @staticmethod def __read_parameters(): if settings.DEBUG: with open(CONF_PATH, 'r') as in_file: parameters = json.loads(in_file.read()) else: result_bytes = crypto_decrypt(CONF_PASS_PATH, CONF_PATH) parameters = json.loads(result_bytes.decode('UTF-8')) return parameters def __load_parameters(self, parameters): for section in parameters: section_params = {} for parameter in parameters[section]: section_params[parameter] = parameters[section][parameter] self.add_parameter(section, section_params) def add_parameter(self, key, value): """ Add a new parameter Parameters ---------- key: str Name of the parameter value: Any Value of the parameter """ self.__parameters[key] = value def __getitem__(self, item): results = self.__find_on_dict(item, self.__parameters) results = list(results) if len(results) == 0: close_matches = get_close_matches(item, self.__find_all_keys(self.__parameters)) raise self.parameter_not_found('No parameter {} found. Maybe you ' 'want something like {}'.format(item, close_matches)) return results[0][0] def __setitem__(self, key, value): results = self.__find_on_dict(key, self.__parameters) results = list(results) if len(results) == 0: self.add_parameter(key, value) else: results[0] = value def search(self, regex): """ Search parameter by name and expression `regex` Parameters ---------- regex: str Pattern Returns ------- list: Result of the search """ results = {} for key in self.__find_all_keys(self.__parameters): if re.match(regex, key): results[key] = self[key] return results class ConfigureApp(npyscreen.NPSAppManaged): """ Class that trigger the main class that render the interface """ def onStart(self): self.registerForm("MAIN", MainForm()) class FormBaseNewWithMenusAndFotter(npyscreen.FormBaseNewWithMenus): """ Class to render the Menus and the footers. """ def __init__(self, *args, **keywords): self.footer = "{}: Menu".format(self.MENU_KEY) super().__init__(*args, **keywords) self.initialize_menus() def display_menu_advert_at(self): """ Method to display the advert menu """ return self.lines - 1, 1 def add_footer(self, footer): """ Method to add new footer Parameters ---------- footer: str Footer to add """ self.footer = self.footer+" "+footer def draw_form(self): """ Method to draw the form """ super().draw_form() menu_advert = " " + self.footer if isinstance(menu_advert, bytes): menu_advert = menu_advert.decode('utf-8', 'replace') y, x = self.display_menu_advert_at() self.add_line(y, x, menu_advert, self.make_attributes_list(menu_advert, curses.A_NORMAL), self.columns - x - 1 ) class SectionLine(npyscreen.BoxBasic): """ Class to render the line between sections """ def __init__(self, screen, *args, **keywords): keywords['max_height'] = 2 super().__init__(screen, *args, **keywords) self.how_exited = False def edit(self): """ Edit """ self.how_exited = True self.editing = False self.editable = False def update(self, clear=True): """ Update Parameters ---------- clear: bool Clear the render Returns ------- bool: Render or not """ if clear: self.clear() if self.hidden: self.clear() return False width = self.width - 1 # draw Line. self.parent.curses_pad.hline(self.rely, self.relx, curses.ACS_HLINE, width) # draw title if self.name: if isinstance(self.name, bytes): name = self.name.decode(self.encoding, 'replace') else: name = self.name name = self.safe_string(name) name = " " + name + " " if isinstance(name, bytes): name = name.decode(self.encoding, 'replace') name_attributes = curses.A_NORMAL if self.do_colors() and not self.editing: name_attributes = name_attributes | \ self.parent.theme_manager.findPair(self, self.color) elif self.editing: name_attributes = name_attributes | \ self.parent.theme_manager.findPair(self, 'HILIGHT') else: pass if self.editing: name_attributes = name_attributes | curses.A_BOLD self.add_line(self.rely, self.relx + 4, name, self.make_attributes_list(name, name_attributes), self.width - 8) # end draw title self.editing = False return True class MainForm(FormBaseNewWithMenusAndFotter): """ Main class that render the interface """ MENU_KEY = '^X' DEFAULTS = DEFAULTS CLASS_DICT = {_FILEPATH_KEY: npyscreen.TitleFilename, _TEXTBOX_KEY: npyscreen.TitleText, _PASSWORD_KEY: npyscreen.TitlePassword, _SECTION_KEY: SectionLine} def __init__(self, *args, **kwargs): self.read_conf() super().__init__(*args, **kwargs) self.active_widgets = [] self.active_category = '' def read_conf(self): """ Read configuration file """ file_name = os.path.join(CONF_PATH) if settings.DEBUG: file_read = self.__read_debug else: file_read = self.__read_no_debug if os.path.exists(file_name): _conf_params = file_read(file_name) self.DEFAULTS.update(_conf_params) def write_conf(self): """ Method to write the configuration parameters to disk Returns ------- """ file_name = os.path.join(CONF_PATH) if settings.DEBUG: file_write = self.__write_debug else: file_write = self.__write_no_debug file_write(self.DEFAULTS, file_name) @staticmethod def __read_debug(filename): with open(filename, 'r') as in_file: result = json.loads(in_file.read(), encoding='UTF-8') return result @staticmethod def __write_debug(params, filename): with open(filename, 'w', encoding='UTF-8') as out_file: out_file.write(json.dumps(params)) @staticmethod def __read_no_debug(filename): result_bytes = crypto_decrypt(CONF_PASS_PATH, filename) return json.loads(result_bytes.decode('UTF-8')) @staticmethod def __write_no_debug(parameters, filename): crypto_encrypt(parameters, CONF_PASS_PATH, filename) def create(self): """ Creating the interface by category. """ self.active_widgets = [] self.active_category = '' items = [] for category in self.DEFAULTS: self.active_category = category for field_index, field in enumerate(self.DEFAULTS[category]): wid = self.add(self.CLASS_DICT[self.DEFAULTS[category][field][1]], w_id=field_index, name="{}:".format(field), value=self.DEFAULTS[category][field][0]) self.active_widgets.append([wid, field_index]) break for category in self.DEFAULTS: items.append((category, self.change_menu, None, None, (category,))) self.how_exited_handers[npyscreen.wgwidget.EXITED_ESCAPE] = self.exit_application self.m = self.add_menu(name="Categories") self.m.addItemsFromList(items) self.add_footer("| ^S: Save | ^C: Cancel") self.add_handlers({'^S': self.save, '^C': self.exit_application}) def save(self, *_, **__): """ Rendering the save pop-up Parameters ---------- _ __ Returns ------- """ res = npyscreen.notify_yes_no('Are you sure to save?') if res: npyscreen.notify_wait('Saving') # Recolecting information for wid in self.active_widgets: self.DEFAULTS[self.active_category][wid[0].name.split(':')[0]][0] = wid[0].value self.write_conf() self.exit_application() else: npyscreen.notify_wait('Cancelling') res = npyscreen.notify_yes_no('Do you want exit without saving changes?') if res: npyscreen.notify_wait('Exiting') self.exit_application() def change_menu(self, argument): """ Rendering the menu to change category Parameters ---------- argument: str New category """ for wid in self.active_widgets: self.DEFAULTS[self.active_category][wid[0].name.split(':')[0]][0] = wid[0].value # Loading new arguments self._clear_all_widgets() self.active_category = argument self.active_widgets = [] for field_index, field in enumerate(self.DEFAULTS[argument]): wid = self.add(self.CLASS_DICT[self.DEFAULTS[argument][field][1]], w_id=field_index, name="{}:".format(field), value=self.DEFAULTS[argument][field][0]) self.active_widgets.append([wid, field_index]) self.edit() def exit_application(self): """ Exit the application """ curses.beep() self.parentApp.setNextForm(None) self.editing = False self.parentApp.switchFormNow() def main(): """ Main """ app = ConfigureApp() app.run()