#!/usr/bin/env python3 """ Silique (https://www.silique.fr) Copyright (C) 2024 distribued with GPL-2 or later license This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ from rougail.object_model import CONVERT_OPTION from rougail.config import RougailConfig from tiramisu.error import ValueOptionError from questionary import text, select, confirm, password, Validator, ValidationError, print as qprint class RougailUserDataQuestionary: def __init__(self, config: 'Config', *, rougailconfig: RougailConfig=None, ): self.config = config if rougailconfig is None: rougailconfig = RougailConfig user_data = rougailconfig['step.user_data'] if 'questionary' not in user_data: user_data.append('questionary') rougailconfig['step.user_data'] = user_data user_data = rougailconfig['step.user_data'] if 'questionary' not in user_data: raise Exception('questionary is not set in step.user_data') self.rougailconfig = rougailconfig self.errors = [] self.warnings = [] def run(self): self.config.property.read_write() if self.rougailconfig['questionary.mandatory']: current_titles = [] while True: mandatories = self.config.value.mandatory() if not mandatories: break mandatory = mandatories[0] path = mandatory.path() if '.' in path: current_config = self.config for idx, p in enumerate(path.split('.')[0:-1]): current_config = current_config.option(p) if idx < len(current_titles): if current_titles[idx] == p: continue current_titles = current_titles[0:idx] current_titles.append(p) self.print(current_config.description(), idx) self.display_questionary(mandatory) else: self.parse(self.config) self.config.property.read_only() def parse(self, config, title_level=0): for option in config: if option.isoptiondescription(): self.print(option.description(), title_level) self.parse(option, title_level + 1) else: self.display_questionary(option) def print(self, title, title_level): qprint(' ' * title_level + '📂 ' + title, 'bold') def display_questionary(self, option): kwargs = {} option_type = option.information.get('type') isdefault = option.owner.isdefault() default = option.value.get() ismulti = option.ismulti() type_obj = None type_obj = CONVERT_OPTION.get(option_type, {}).get("func") RougailValidator.option = option RougailValidator.option_type = {'type': option_type, 'func': type_obj, } RougailValidator.ismulti = ismulti if option_type == 'choice': question_funtion = select RougailValidator.default = default kwargs['choices'] = option.value.list() elif option_type == 'boolean': question_funtion = confirm elif option_type == 'secret' and not self.rougailconfig['questionary.show_secrets']: question_funtion = password else: question_funtion = text kwargs['validate'] = RougailValidator args = ['📓 ' + option.description()] if ismulti: kwargs['multiline'] = True if default: kwargs['default'] = "\n".join(default) elif default is not None: kwargs['default'] = default value = RougailValidator().convert_value(question_funtion(*args, **kwargs).ask(), False) if isdefault and value == default: option.value.reset() else: option.value.set(value) class RougailValidator(Validator): def validate(self, document): return self.convert_value(document.text) def convert_value(self, document, validate=True, ): if not self.ismulti: value = self._convert_a_value(document, document, validate) else: value = [] if document is not None: for val in document.strip().split('\n'): val = self._convert_a_value(val, document, validate) if val is not None: value.append(val) if validate: try: self.option.value.set(value) except ValueOptionError as err: err.prefix = '' raise ValidationError( message=str(err), cursor_position=len(document), ) return value def _convert_a_value(self, value, document, validate): if value is None: if self.option_type['type'] == 'choice': return self.default return if isinstance(value, str): value = value.strip() if value == '': if validate and "mandatory" in self.option.property.get(): raise ValidationError( message=f"Value must not be empty", cursor_position=len(document), ) return if self.option_type['func']: try: return self.option_type['func'](value) except: raise ValidationError( message=f"Not a valid {self.option_type['type']}", cursor_position=len(document), ) return value