2024-07-28 17:52:11 +02:00
"""
Silique ( https : / / www . silique . fr )
2025-02-10 09:54:59 +01:00
Copyright ( C ) 2022 - 2025
2024-07-28 17:52:11 +02:00
2024-10-31 18:55:00 +01:00
This program is free software : you can redistribute it and / or modify it
under the terms of the GNU Lesser General Public License as published by the
Free Software Foundation , either version 3 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 Lesser General Public License for more
details .
You should have received a copy of the GNU Lesser General Public License
along with this program . If not , see < http : / / www . gnu . org / licenses / > .
2024-07-28 17:52:11 +02:00
"""
2024-09-04 09:04:08 +02:00
from typing import Any , List , Optional
2024-12-02 20:22:50 +01:00
from io import BytesIO
2024-07-28 17:52:11 +02:00
from rich . tree import Tree
from rich . console import Console
from rich . table import Table
from rich . panel import Panel
2024-12-02 20:22:50 +01:00
from ruamel . yaml import YAML
2024-07-28 17:52:11 +02:00
from tiramisu import undefined
2024-11-27 16:50:48 +01:00
from tiramisu . error import PropertiesOptionError , ConfigError
from . i18n import _
2024-07-28 17:52:11 +02:00
2024-11-27 16:50:48 +01:00
class RougailOutputConsole :
2024-11-01 10:58:53 +01:00
variable_hidden_color = " orange1 "
variable_advanced_color = " bright_blue "
variable_advanced_and_modified_color = " red1 "
value_unmodified_color = " gold1 "
value_default_color = " green "
2024-07-28 17:52:11 +02:00
2024-11-01 10:58:53 +01:00
def __init__ (
self ,
2024-11-27 16:50:48 +01:00
config : " Config " ,
rougailconfig : " RougailConfig " = None ,
user_data_errors : Optional [ list ] = None ,
user_data_warnings : Optional [ list ] = None ,
2024-11-01 10:58:53 +01:00
) - > None :
2024-11-27 16:50:48 +01:00
if rougailconfig is None :
from rougail import RougailConfig
rougailconfig = RougailConfig
2024-07-28 17:52:11 +02:00
self . rougailconfig = rougailconfig
2024-11-27 16:50:48 +01:00
self . config = config
self . read_write = self . rougailconfig [ " console.read_write " ]
self . is_mandatory = self . rougailconfig [ " console.mandatory " ]
self . show_secrets = self . rougailconfig [ " console.show_secrets " ]
self . errors = [ ]
self . warnings = [ ]
if user_data_errors is None :
user_data_errors = [ ]
self . user_data_errors = user_data_errors
if user_data_warnings is None :
user_data_warnings = [ ]
self . user_data_warnings = user_data_warnings
self . console = Console ( force_terminal = True )
2024-07-28 17:52:11 +02:00
self . out = [ ]
2024-11-27 16:50:48 +01:00
self . root = self . get_root ( )
def mandatory ( self ) :
if not self . is_mandatory :
return
title = False
options_with_error = [ ]
try :
mandatories = self . config . value . mandatory ( )
except ( ConfigError , PropertiesOptionError ) as err :
self . errors . append ( f " Error in config: { err } " )
return
2025-02-15 18:42:02 +01:00
except ValueError as err :
self . errors . append ( str ( err ) )
return
2024-11-27 16:50:48 +01:00
for option in mandatories :
try :
option . value . get ( )
if not title :
# self.errors.append("Les variables suivantes sont obligatoires mais n'ont pas de valeur :")
self . errors . append (
_ ( " The following variables are mandatory but have no value: " )
)
title = True
self . errors . append ( f " - { option . description ( ) } " )
except PropertiesOptionError :
options_with_error . append ( option )
if not title :
for idx , option in enumerate ( options_with_error ) :
if not idx :
# self.errors.append("Les variables suivantes sont inaccessibles mais sont vides et obligatoires :")
self . errors . append (
_ (
" The following variables are inaccessible but are empty and mandatory : "
)
)
self . errors . append ( f " - { option . description ( ) } " )
def exporter ( self ) - > bool :
self . config . property . read_write ( )
self . mandatory ( )
if self . read_write :
self . config . property . read_write ( )
else :
self . config . property . read_only ( )
errors = self . user_data_errors + self . errors
if errors :
self . display_errors ( errors )
return False
warnings = self . user_data_warnings + self . warnings
if warnings :
self . display_warnings ( warnings )
self . header ( )
self . parse_options (
self . config ,
self . root ,
)
self . end ( )
return True
2024-11-28 22:06:38 +01:00
def run ( self ) - > str :
with self . console . capture ( ) as capture :
2025-02-10 09:54:59 +01:00
ret = self . print ( )
return ret , capture . get ( )
2024-11-28 22:06:38 +01:00
def print ( self ) - > None :
2025-02-10 09:54:59 +01:00
ret = self . exporter ( )
2024-11-28 22:06:38 +01:00
for out in self . out :
self . console . print ( out )
2025-02-10 09:54:59 +01:00
return ret
2024-11-27 16:50:48 +01:00
def parse_options (
self ,
conf ,
parent ,
) :
for option in conf :
if option . isoptiondescription ( ) :
family = parent . add_family ( option )
if option . isleadership ( ) :
self . parse_leadership (
option ,
family ,
)
else :
self . parse_options (
option ,
family ,
)
else :
parent . add_variable ( option )
def parse_leadership (
self ,
conf ,
parent ,
) :
leader , * followers = list ( conf )
leader_values = leader . value . get ( )
for idx , leader_value in enumerate ( leader_values ) :
leader_obj = parent . add_family ( leader )
leader_obj . add_variable (
leader ,
value = leader_value ,
leader_index = idx ,
)
for follower in followers :
if follower . index ( ) != idx :
continue
leader_obj . add_variable ( follower )
2024-07-28 17:52:11 +02:00
def header ( self ) :
2024-11-01 10:58:53 +01:00
header_variable = " Variable \n "
# header_variable += f'[{self.variable_advanced_color}]Variable non documentée[/{self.variable_advanced_color}]\n'
# header_variable += f'[{self.variable_advanced_and_modified_color}]Variable non documentée mais modifiée[/{self.variable_advanced_and_modified_color}]'
2024-07-28 17:52:11 +02:00
header_variable + = f ' [ { self . variable_advanced_color } ] { _ ( " Undocumented variable " ) } [/ { self . variable_advanced_color } ] \n '
header_variable + = f ' [ { self . variable_advanced_and_modified_color } ] { _ ( " Undocumented but modified variable " ) } [/ { self . variable_advanced_and_modified_color } ] '
if not self . read_write :
2024-11-01 10:58:53 +01:00
# header_variable += f'\n[{self.variable_hidden_color}]Variable non modifiable[/{self.variable_hidden_color}]'
2024-07-28 17:52:11 +02:00
header_variable + = f ' \n [ { self . variable_hidden_color } ] { _ ( " Unmodifiable variable " ) } [/ { self . variable_hidden_color } ] '
2024-11-01 10:58:53 +01:00
# header_value = f'[{self.value_unmodified_color}]Valeur par défaut[/{self.value_unmodified_color}]\n'
# header_value += 'Valeur modifiée\n'
# header_value += f'([{self.value_default_color}]Valeur par défaut originale[/{self.value_default_color}])'
2024-07-28 17:52:11 +02:00
header_value = f ' [ { self . value_unmodified_color } ] { _ ( " Default value " ) } [/ { self . value_unmodified_color } ] \n '
2024-11-01 10:58:53 +01:00
header_value + = _ ( " Modified value " ) + " \n "
2024-07-28 17:52:11 +02:00
header_value + = f ' ([ { self . value_default_color } ] { _ ( " Original default value " ) } [/ { self . value_default_color } ]) '
header = Table . grid ( padding = 1 , collapse_padding = True )
header . pad_edge = False
header . add_row ( header_variable , header_value )
self . out . append ( Panel . fit ( header , title = _ ( " Caption " ) ) )
2024-11-27 16:50:48 +01:00
def display_errors (
2024-11-01 10:58:53 +01:00
self ,
errors ,
) - > None :
tree = Tree (
2025-02-15 18:42:02 +01:00
f " :stop_sign: { _ ( ' ERRORS ' ) } " ,
2024-11-01 10:58:53 +01:00
guide_style = " bold bright_red " ,
)
2024-07-28 17:52:11 +02:00
for error in errors :
tree . add ( error )
self . out . append ( tree )
2024-11-27 16:50:48 +01:00
def display_warnings (
2024-11-01 10:58:53 +01:00
self ,
warnings : list ,
) - > None :
2024-07-28 17:52:11 +02:00
tree = Tree ( " :warning: WARNINGS " )
for warning in warnings :
tree . add ( warning )
self . out . append ( tree )
2024-11-27 16:50:48 +01:00
def get_root ( self ) - > None :
2024-12-02 20:22:50 +01:00
yaml = YAML ( )
yaml . indent ( mapping = 2 , sequence = 4 , offset = 2 )
2024-11-01 10:58:53 +01:00
self . output = OutputFamily (
_ ( " Variables: " ) ,
None ,
self ,
2024-12-02 20:22:50 +01:00
yaml ,
2024-11-01 10:58:53 +01:00
no_icon = True ,
)
2024-07-28 17:52:11 +02:00
return self . output
def end ( self ) :
self . out . append ( self . output . tree )
class OutputFamily :
2024-11-01 10:58:53 +01:00
def __init__ (
2024-12-02 20:22:50 +01:00
self , family , parent , root , _yaml , * , is_leader : bool = False , no_icon : bool = False
2024-11-01 10:58:53 +01:00
) - > None :
2024-07-28 17:52:11 +02:00
if parent is None :
tree = Tree
else :
tree = parent . add
if is_leader :
2024-11-01 10:58:53 +01:00
self . tree = tree (
f " :notebook: { family } : " ,
guide_style = " bold bright_blue " ,
)
2024-07-28 17:52:11 +02:00
elif no_icon :
2024-11-01 10:58:53 +01:00
self . tree = tree (
f " { family } " ,
guide_style = " bold bright_blue " ,
)
2024-07-28 17:52:11 +02:00
else :
2024-11-01 10:58:53 +01:00
self . tree = tree (
f " :open_file_folder: { family } " ,
guide_style = " bold bright_blue " ,
)
2024-07-28 17:52:11 +02:00
self . root = root
2024-12-02 20:22:50 +01:00
self . _yaml = _yaml
2024-11-01 10:58:53 +01:00
def add_family (
self ,
option ,
) - > None :
2024-07-28 17:52:11 +02:00
properties = option . property . get ( )
2024-11-01 10:58:53 +01:00
if " hidden " in properties :
2024-07-28 17:52:11 +02:00
color = self . root . variable_hidden_color
2024-11-01 10:58:53 +01:00
elif " advanced " in properties :
2024-07-28 17:52:11 +02:00
color = self . root . variable_advanced_color
else :
color = None
2024-11-01 10:58:53 +01:00
return OutputFamily (
self . colorize (
None ,
option . name ( ) ,
color ,
None ,
) ,
self . tree ,
self . root ,
2024-12-02 20:22:50 +01:00
self . _yaml ,
2024-11-01 10:58:53 +01:00
)
2024-07-28 17:52:11 +02:00
2024-11-01 10:58:53 +01:00
def add_variable (
self , option , value : Any = undefined , leader_index : Optional [ int ] = None
) :
2024-07-28 17:52:11 +02:00
properties = option . property . get ( )
variable_color = None
if option . owner . isdefault ( ) :
2024-11-01 10:58:53 +01:00
if " hidden " in properties :
2024-07-28 17:52:11 +02:00
variable_color = self . root . variable_hidden_color
2024-11-01 10:58:53 +01:00
elif " advanced " in properties :
2024-07-28 17:52:11 +02:00
variable_color = self . root . variable_advanced_color
color = self . root . value_unmodified_color
default_value = None
else :
2024-11-01 10:58:53 +01:00
if " hidden " in properties :
2024-07-28 17:52:11 +02:00
variable_color = self . root . variable_hidden_color
2024-11-01 10:58:53 +01:00
elif " advanced " in properties :
2024-07-28 17:52:11 +02:00
variable_color = self . root . variable_advanced_and_modified_color
color = None
2025-02-17 09:53:32 +01:00
if option . information . get ( " default_value_makes_sense " , True ) :
try :
default_value = option . value . default ( )
except ConfigError :
if option . ismulti ( ) :
default_value = [ ]
else :
default_value = None
if leader_index is not None :
if len ( default_value ) > leader_index :
default_value = default_value [ leader_index ]
else :
default_value = None
else :
default_value = None
2024-07-28 17:52:11 +02:00
if value is undefined :
value = option . value . get ( )
2024-11-01 10:58:53 +01:00
key = self . colorize (
None ,
option . name ( ) ,
variable_color ,
None ,
)
value = self . colorize (
option ,
value ,
color ,
default_value ,
)
2024-07-28 17:52:11 +02:00
if isinstance ( value , list ) :
2024-11-01 10:58:53 +01:00
subtree = self . tree . add (
f " :notebook: { key } : " ,
guide_style = " bold bright_blue " ,
)
2024-07-28 17:52:11 +02:00
for val in value :
subtree . add ( str ( val ) )
else :
self . tree . add ( f " :notebook: { key } : { value } " )
2024-11-01 10:58:53 +01:00
def colorize (
self ,
option ,
value ,
color : str ,
default_value ,
) - > str :
2024-07-28 17:52:11 +02:00
if isinstance ( value , list ) :
if default_value is None :
default_value = [ ]
2024-12-02 20:22:50 +01:00
# default_value = [self.convert_value(option, val) for val in default_value]
2024-07-28 17:52:11 +02:00
len_value = len ( value )
len_default_value = len ( default_value )
len_values = max ( len_value , len_default_value )
ret = [ ]
for idx in range ( len_values ) :
if idx < len_value :
val = value [ idx ]
else :
2024-11-01 10:58:53 +01:00
val = " "
2024-07-28 17:52:11 +02:00
if idx < len_default_value :
default = default_value [ idx ]
else :
default = None
2024-11-01 10:58:53 +01:00
ret . append (
self . colorize (
option ,
val ,
color ,
default ,
)
)
2024-07-28 17:52:11 +02:00
return ret
2024-12-02 20:22:50 +01:00
if option :
2024-11-01 10:58:53 +01:00
value = self . convert_value (
option ,
value ,
)
2024-07-28 17:52:11 +02:00
if color is not None :
2024-11-01 10:58:53 +01:00
ret = f " [ { color } ] { value } [/ { color } ] "
2024-07-28 17:52:11 +02:00
else :
ret = value
2024-12-02 20:22:50 +01:00
if default_value is not None and " force_store_value " not in option . property . get ( ) :
2024-07-28 17:52:11 +02:00
default_value_color = self . root . value_default_color
2024-12-02 20:22:50 +01:00
default_value = self . convert_value ( option , default_value )
if ret :
ret + = f " ([ { default_value_color } ] { default_value } [/ { default_value_color } ]) "
else :
ret = f " [ { default_value_color } ] { default_value } [/ { default_value_color } ] "
2024-07-28 17:52:11 +02:00
return ret
2024-11-01 10:58:53 +01:00
def convert_value (
self ,
option ,
2024-12-02 20:22:50 +01:00
value : Any ,
) - > str :
if isinstance ( value , list ) :
print ( value )
raise Exception ( ' pfff ' )
""" Dump variable, means transform bool, ... to yaml string """
2024-11-01 10:58:53 +01:00
if not self . root . show_secrets and option . type ( ) == " password " :
2024-07-28 17:52:11 +02:00
return " * " * 10
2024-12-02 20:22:50 +01:00
if isinstance ( value , str ) :
return value
with BytesIO ( ) as ymlfh :
self . _yaml . dump ( value , ymlfh )
ret = ymlfh . getvalue ( ) . decode ( " utf-8 " ) . strip ( )
if ret . endswith ( " ... " ) :
ret = ret [ : - 3 ] . strip ( )
return ret
2024-11-27 16:50:48 +01:00
RougailOutput = RougailOutputConsole
__all__ = ( " RougailOutputConsole " , )