2018-12-24 09:28:44 +01:00
from typing import Optional , Dict , List , Any
2019-02-28 22:12:46 +01:00
from copy import copy
2018-12-24 09:28:44 +01:00
import warnings
import re
2019-07-06 15:01:13 +02:00
from . error import APIError , ConfigError , ValueWarning , ValueOptionError , ValueErrorWarning , PropertiesOptionError , display_list
2019-01-16 09:42:22 +01:00
from . setting import undefined
from . i18n import _
2019-01-10 09:54:22 +01:00
2018-12-24 09:28:44 +01:00
2019-07-28 22:49:52 +02:00
TIRAMISU_FORMAT = ' 1.0 '
2019-07-06 15:01:13 +02:00
DEBUG = False
2019-04-03 19:48:45 +02:00
2019-01-16 16:38:32 +01:00
TYPE = { ' boolean ' : bool ,
2019-03-11 15:59:29 +01:00
' integer ' : int ,
2019-01-19 10:54:18 +01:00
' string ' : str ,
' password ' : str ,
2019-07-06 15:01:13 +02:00
' filename ' : str ,
' email ' : str ,
' url ' : str ,
' ip ' : str ,
' network ' : str ,
' netmask ' : str ,
' broadcast_address ' : str ,
' port ' : str ,
' domainname ' : str ,
' date ' : str }
2019-01-19 10:54:18 +01:00
2019-01-16 16:38:32 +01:00
2018-12-24 09:28:44 +01:00
class Option :
2019-01-10 09:54:22 +01:00
# fake Option (IntOption, StrOption, ...)
2019-01-19 10:54:18 +01:00
# only usefull for warnings
2018-12-24 09:28:44 +01:00
def __init__ ( self ,
2019-07-06 15:01:13 +02:00
path ,
display_name ) :
2018-12-24 09:28:44 +01:00
self . path = path
2019-07-06 15:01:13 +02:00
self . display_name = display_name
2018-12-24 09:28:44 +01:00
def __call__ ( self ) :
2019-02-05 07:01:22 +01:00
# suppose to be a weakref
2018-12-24 09:28:44 +01:00
return self
def impl_getpath ( self ) :
return self . path
2019-07-06 15:01:13 +02:00
def impl_get_display_name ( self ) :
return self . display_name
2018-12-24 09:28:44 +01:00
class TiramisuOptionOption :
2019-01-10 09:54:22 +01:00
# config.option(path).option
2019-07-26 08:57:10 +02:00
def __call__ ( self ,
path : str ,
index : Optional [ int ] = None ) - > ' TiramisuOption ' :
# config.option(path).option(path)
path = self . _path + ' . ' + path
schema = self . config . get_schema ( path )
if schema [ ' type ' ] in [ ' object ' , ' array ' ] :
return TiramisuOptionDescription ( self . config ,
schema ,
path )
return TiramisuOption ( self . config ,
schema ,
path ,
index )
2018-12-24 09:28:44 +01:00
def __init__ ( self ,
2019-02-08 11:11:06 +01:00
config : ' Config ' ,
2018-12-24 09:28:44 +01:00
path : str ,
2019-07-26 08:57:10 +02:00
schema : Dict ) - > None :
2019-02-08 11:11:06 +01:00
self . config = config
2018-12-24 09:28:44 +01:00
self . _path = path
self . schema = schema
def doc ( self ) :
2019-07-29 22:06:04 +02:00
description = self . description ( )
if not description :
return self . name ( )
return description
def description ( self ) :
2019-07-06 15:01:13 +02:00
if self . issymlinkoption ( ) :
schema = self . config . get_schema ( self . schema [ ' opt_path ' ] )
else :
schema = self . schema
return schema [ ' title ' ]
2018-12-24 09:28:44 +01:00
def path ( self ) :
return self . _path
2019-01-18 14:15:05 +01:00
def name ( self ,
follow_symlink : bool = False ) - > str :
if not follow_symlink or \
self . isoptiondescription ( ) or \
not self . issymlinkoption ( ) :
path = self . _path
else :
path = self . schema [ ' opt_path ' ]
return path . rsplit ( ' . ' , 1 ) [ - 1 ]
2019-01-10 09:54:22 +01:00
2018-12-24 09:28:44 +01:00
def isoptiondescription ( self ) :
return self . schema [ ' type ' ] in [ ' object ' , ' array ' ]
2019-03-05 08:26:58 +01:00
def isleadership ( self ) :
2018-12-24 09:28:44 +01:00
return self . schema [ ' type ' ] == ' array '
2019-03-05 08:26:58 +01:00
def isleader ( self ) :
2019-05-08 20:15:46 +02:00
return self . config . isleader ( self . _path )
2019-03-05 08:26:58 +01:00
def isfollower ( self ) :
return self . config . isfollower ( self . _path )
2018-12-24 09:28:44 +01:00
def issymlinkoption ( self ) - > bool :
2019-01-18 14:15:05 +01:00
return self . schema [ ' type ' ] == ' symlink '
2018-12-24 09:28:44 +01:00
def ismulti ( self ) - > bool :
return self . schema . get ( ' isMulti ' , False )
2019-04-17 19:15:30 +02:00
def issubmulti ( self ) - > bool :
return self . schema . get ( ' isSubMulti ' , False )
2018-12-24 09:28:44 +01:00
def type ( self ) - > str :
2019-03-05 08:26:58 +01:00
if self . isleadership ( ) :
return ' leadership '
2018-12-24 09:28:44 +01:00
if self . isoptiondescription ( ) :
return ' optiondescription '
2019-04-17 19:15:30 +02:00
if self . issymlinkoption ( ) :
return self . config . get_schema ( self . schema [ ' opt_path ' ] ) [ ' type ' ]
2019-03-05 08:26:58 +01:00
return self . schema [ ' type ' ]
2018-12-24 09:28:44 +01:00
2019-01-10 09:54:22 +01:00
def properties ( self ) - > List [ str ] :
2019-07-06 15:01:13 +02:00
model = self . config . model . get ( self . _path , { } )
2019-04-17 19:15:30 +02:00
if self . isfollower ( ) :
2019-07-09 08:13:50 +02:00
model = model . get ( ' null ' , { } )
2019-04-17 19:15:30 +02:00
return self . config . get_properties ( model , self . _path , None )
2019-07-06 15:01:13 +02:00
#
# def requires(self) -> None:
# # FIXME
# return None
2018-12-24 09:28:44 +01:00
2019-05-08 20:15:46 +02:00
def pattern ( self ) :
2019-07-26 08:57:10 +02:00
if self . _path in self . config . form :
return self . config . form [ self . _path ] . get ( ' pattern ' )
2019-05-08 20:15:46 +02:00
else :
return None
2019-07-06 15:01:13 +02:00
def defaultmulti ( self ) :
if not self . schema . get ( ' isMulti ' ) :
raise Exception ( ' defaultmulti avalaible only for a multi ' )
return self . schema . get ( ' defaultmulti ' )
2019-05-08 20:15:46 +02:00
2018-12-24 09:28:44 +01:00
class TiramisuOptionProperty :
2019-01-10 09:54:22 +01:00
# config.option(path).property
2018-12-24 09:28:44 +01:00
def __init__ ( self ,
2019-02-08 11:30:29 +01:00
config : ' Config ' ,
2019-02-05 07:01:22 +01:00
path : str ,
2019-07-06 15:01:13 +02:00
index : Optional [ int ] ) - > None :
2019-02-08 11:26:59 +01:00
self . config = config
2019-02-05 07:01:22 +01:00
self . path = path
2019-03-05 08:26:58 +01:00
self . index = index
2018-12-24 09:28:44 +01:00
2019-01-10 09:54:22 +01:00
def get ( self , only_raises = False ) :
if not only_raises :
2019-07-09 08:13:50 +02:00
model = self . config . model . get ( self . path , { } )
props = self . config . get_properties ( model , self . path , self . index , only_raises )
2019-01-10 09:54:22 +01:00
else :
2019-02-16 20:23:50 +01:00
props = [ ]
2019-04-03 19:42:59 +02:00
if self . config . is_hidden ( self . path ,
2019-07-06 15:01:13 +02:00
self . index ) :
2019-02-16 20:23:50 +01:00
props . append ( ' hidden ' )
return props
2018-12-24 09:28:44 +01:00
2019-01-10 09:54:22 +01:00
class _Value :
def _dict_walk ( self ,
ret : Dict ,
schema : Dict ,
root : str ,
fullpath : bool ,
2019-07-06 15:01:13 +02:00
withwarning : bool ,
flatten : bool ,
len_parent : Optional [ int ] ) - > None :
2019-03-05 08:26:58 +01:00
leadership_len = None
2019-01-10 09:54:22 +01:00
for key , option in schema [ ' properties ' ] . items ( ) :
2019-04-03 19:42:59 +02:00
if self . config . is_hidden ( key , None ) is False :
2019-07-06 15:01:13 +02:00
if flatten :
nkey = key . split ( ' . ' ) [ - 1 ]
2019-07-26 11:05:52 +02:00
elif not fullpath and len_parent is not None :
2019-07-06 15:01:13 +02:00
nkey = key [ len_parent : ]
else :
nkey = key
2019-02-28 22:12:46 +01:00
if option [ ' type ' ] in [ ' object ' , ' array ' ] :
2019-03-05 08:26:58 +01:00
# optiondescription or leadership
2019-01-10 09:54:22 +01:00
self . _dict_walk ( ret ,
option ,
root ,
fullpath ,
2019-07-06 15:01:13 +02:00
withwarning ,
flatten ,
len_parent )
2019-03-05 08:26:58 +01:00
elif schema . get ( ' type ' ) == ' array ' and leadership_len is not None :
# followers
values = [ ]
for index in range ( leadership_len ) :
value = self . config . get_value ( key ,
index )
2019-07-06 15:01:13 +02:00
self . config . _check_raises_warnings ( key , index , value , option [ ' type ' ] , withwarning )
2019-03-05 08:26:58 +01:00
values . append ( value )
2019-07-06 15:01:13 +02:00
ret [ nkey ] = values
2019-01-10 09:54:22 +01:00
else :
2019-03-05 08:26:58 +01:00
value = self . config . get_value ( key )
2019-07-06 15:01:13 +02:00
self . config . _check_raises_warnings ( key , None , value , option [ ' type ' ] , withwarning )
ret [ nkey ] = value
2019-03-05 08:26:58 +01:00
if schema . get ( ' type ' ) == ' array ' :
leadership_len = len ( value )
elif schema . get ( ' type ' ) == ' array ' and leadership_len is None :
# if leader is hidden, followers are hidden too
2019-02-28 22:12:46 +01:00
break
2019-01-10 09:54:22 +01:00
def dict ( self ,
fullpath : bool = False ,
2019-07-06 15:01:13 +02:00
withwarning : bool = False ,
flatten : bool = False ) :
2019-01-10 09:54:22 +01:00
ret = { }
self . _dict_walk ( ret ,
self . schema ,
self . path ,
fullpath ,
2019-07-06 15:01:13 +02:00
withwarning ,
flatten ,
None )
2019-01-10 09:54:22 +01:00
return ret
2018-12-24 09:28:44 +01:00
2019-01-10 09:54:22 +01:00
2019-01-16 09:29:01 +01:00
class TiramisuOptionOwner :
# config.option(path).owner
def __init__ ( self ,
config : ' Config ' ,
schema : Dict ,
path : str ,
index : int ) - > None :
self . config = config
self . schema = schema
self . path = path
self . index = index
def isdefault ( self ) - > Any :
2019-03-05 08:26:58 +01:00
return self . config . get_owner ( self . path , self . index ) == ' default '
2019-01-16 09:29:01 +01:00
2019-02-16 20:23:50 +01:00
def get ( self ) - > str :
2019-03-05 08:26:58 +01:00
return self . config . get_owner ( self . path , self . index )
2019-02-16 20:23:50 +01:00
2019-01-16 09:29:01 +01:00
2019-07-06 15:01:13 +02:00
class TiramisuOptionDescriptionValue ( _Value ) :
# config.option(descr_path).value
def __init__ ( self ,
config : ' Config ' ,
schema : Dict ,
path : str ,
index : int ) - > None :
self . config = config
self . schema = schema
self . path = path
self . index = index
def dict ( self ,
fullpath : bool = False ,
withwarning : bool = False ,
flatten : bool = False ) :
ret = { }
len_parent = len ( self . path ) + 1
self . _dict_walk ( ret ,
self . schema ,
self . path ,
fullpath ,
withwarning ,
flatten ,
len_parent )
return ret
2019-01-10 09:54:22 +01:00
class TiramisuOptionValue ( _Value ) :
# config.option(path).value
def __init__ ( self ,
config : ' Config ' ,
schema : Dict ,
path : str ,
index : int ) - > None :
self . config = config
self . schema = schema
self . path = path
self . index = index
2018-12-24 09:28:44 +01:00
def get ( self ) - > Any :
2019-07-06 15:01:13 +02:00
if self . schema [ ' type ' ] == ' symlink ' :
# FIXME should tested it too
pass
elif self . config . isfollower ( self . path ) :
2019-03-05 08:26:58 +01:00
if self . index is None :
raise APIError ( _ ( ' index must be set with the follower option " {} " ' ) . format ( self . path ) )
2019-07-06 15:01:13 +02:00
elif self . index is not None :
2019-03-28 07:53:29 +01:00
raise APIError ( _ ( ' index must only be set with a follower option, not for " {} " ' ) . format ( self . path ) )
2019-07-06 15:01:13 +02:00
if self . config . is_hidden ( self . path , self . index ) :
raise PropertiesOptionError ( None , { ' disabled ' } , None , opt_type = ' option ' )
value = self . config . get_value ( self . path , self . index )
self . config . _check_raises_warnings ( self . path , self . index , value , self . schema [ ' type ' ] )
2018-12-24 09:28:44 +01:00
return value
def list ( self ) :
return self . schema [ ' enum ' ]
2019-01-19 10:54:18 +01:00
def _validate ( self , type_ , value ) :
if value in [ None , undefined ] :
return
2019-07-06 15:01:13 +02:00
if type_ == ' symlink ' :
raise ConfigError ( _ ( " can ' t assign to a SymLinkOption " ) )
2019-01-19 10:54:18 +01:00
if type_ == ' choice ' :
2019-07-06 15:01:13 +02:00
if ' enum ' in self . schema and value not in self . schema [ ' enum ' ] :
2019-04-17 19:15:30 +02:00
raise ValueError ( ' value {} is not in {} ' . format ( value , self . schema [ ' enum ' ] ) )
2019-01-19 10:54:18 +01:00
elif not isinstance ( value , TYPE [ type_ ] ) :
2019-04-17 19:15:30 +02:00
raise ValueError ( ' value {} is not a valid {} ' . format ( value , type_ ) )
2019-01-19 10:54:18 +01:00
def set ( self , value ) :
type_ = self . schema [ ' type ' ]
2019-05-08 20:15:46 +02:00
leader_old_value = undefined
2019-08-23 13:59:22 +02:00
remote = self . config . form . get ( self . path , { } ) . get ( ' remote ' , False )
if not remote and self . config . is_hidden ( self . path , self . index ) :
2019-07-06 15:01:13 +02:00
raise PropertiesOptionError ( None , { ' disabled ' } , None , opt_type = ' option ' )
2019-05-08 20:15:46 +02:00
if self . config . isleader ( self . path ) :
leader_old_value = self . config . get_value ( self . path )
2019-03-16 22:51:39 +01:00
if self . index is None and self . schema . get ( ' isMulti ' , False ) :
2019-01-19 10:54:18 +01:00
if not isinstance ( value , list ) :
2019-07-06 15:01:13 +02:00
raise ValueError ( ' value must be a list ' )
2019-01-19 10:54:18 +01:00
for val in value :
2019-04-17 19:15:30 +02:00
if self . schema . get ( ' isSubMulti ' , False ) :
for v in val :
self . _validate ( type_ , v )
else :
self . _validate ( type_ , val )
2019-01-19 10:54:18 +01:00
else :
2019-04-17 19:15:30 +02:00
if self . schema . get ( ' isSubMulti ' , False ) :
for val in value :
self . _validate ( type_ , val )
else :
self . _validate ( type_ , value )
2019-07-26 08:57:10 +02:00
if self . path in self . config . temp :
2019-07-25 15:22:00 +02:00
obj = None
if self . index is None :
2019-07-26 08:57:10 +02:00
obj = self . config . temp [ self . path ]
elif str ( self . index ) in self . config . temp [ self . path ] :
obj = self . config . temp [ self . path ] [ str ( self . index ) ]
2019-07-25 15:22:00 +02:00
if obj is not None :
for attr in [ ' error ' , ' warnings ' , ' invalid ' , ' hasWarnings ' ] :
if attr in obj :
del obj [ attr ]
2018-12-24 09:28:44 +01:00
self . config . modify_value ( self . path ,
self . index ,
value ,
2019-05-08 20:15:46 +02:00
remote ,
leader_old_value )
2019-07-06 15:01:13 +02:00
self . config . _check_raises_warnings ( self . path , self . index , value , type_ )
2018-12-24 09:28:44 +01:00
def reset ( self ) :
self . config . delete_value ( self . path ,
2019-05-08 20:15:46 +02:00
self . index )
2018-12-24 09:28:44 +01:00
2019-03-11 15:57:32 +01:00
def default ( self ) :
2019-07-06 15:01:13 +02:00
if self . schema . get ( ' isMulti ' ) :
if self . config . isfollower ( self . path ) :
2019-07-09 08:13:50 +02:00
if ' defaultmulti ' in self . schema :
defaultmulti = self . schema [ ' defaultmulti ' ]
else :
defaultmulti = None
2019-07-06 15:01:13 +02:00
if self . index is not None :
2019-07-09 08:13:50 +02:00
value = defaultmulti
2019-07-06 15:01:13 +02:00
else :
leader = next ( iter ( self . config . option ( self . path . rsplit ( ' . ' , 1 ) [ 0 ] ) . schema [ ' properties ' ] ) )
len_value = len ( self . config . get_value ( leader ) )
2019-07-09 08:13:50 +02:00
value = [ defaultmulti ] * len_value
2019-07-06 15:01:13 +02:00
else :
value = self . schema . get ( ' value ' , [ ] )
else :
value = self . schema . get ( ' value ' )
return value
2019-03-11 15:57:32 +01:00
2019-07-25 15:22:00 +02:00
def valid ( self ) :
temp = self . config . temp . get ( self . path , { } )
model = self . config . model . get ( self . path , { } )
if self . index is None :
if ' invalid ' in temp :
return not temp [ ' invalid ' ]
return not model . get ( ' invalid ' , False )
elif str ( self . index ) in temp and ' invalid ' in temp [ str ( self . index ) ] :
return not temp [ str ( self . index ) ] [ ' invalid ' ]
elif str ( self . index ) in model :
return not model [ str ( self . index ) ] . get ( ' invalid ' , False )
return True
def warning ( self ) :
temp = self . config . temp . get ( self . path , { } )
model = self . config . model . get ( self . path , { } )
if self . index is None :
if ' hasWarnings ' in temp :
return temp [ ' hasWarnings ' ]
return model . get ( ' hasWarnings ' , False )
elif str ( self . index ) in temp and ' hasWarnings ' in temp [ str ( self . index ) ] :
return temp [ str ( self . index ) ] [ ' hasWarnings ' ]
elif str ( self . index ) in model :
return model [ str ( self . index ) ] . get ( ' hasWarnings ' , False )
return False
def error_message ( self ) :
temp = self . config . temp . get ( self . path , { } )
model = self . config . model . get ( self . path , { } )
if self . valid ( ) :
return [ ]
if self . index is None :
if temp . get ( ' invalid ' ) == True :
return temp [ ' error ' ]
return model [ ' error ' ]
elif str ( self . index ) in temp and ' invalid ' in temp [ str ( self . index ) ] :
return temp [ str ( self . index ) ] [ ' error ' ]
else :
return model [ str ( self . index ) ] [ ' error ' ]
return [ ]
def warning_message ( self ) :
temp = self . config . temp . get ( self . path , { } )
model = self . config . model . get ( self . path , { } )
if not self . warning ( ) :
return [ ]
if self . index is None :
if temp . get ( ' hasWarnings ' ) == True :
return temp [ ' warnings ' ]
return model [ ' warnings ' ]
elif str ( self . index ) in temp and ' hasWarnings ' in temp [ str ( self . index ) ] :
return temp [ str ( self . index ) ] [ ' warnings ' ]
else :
return model [ str ( self . index ) ] [ ' warnings ' ]
return [ ]
2018-12-24 09:28:44 +01:00
2019-01-10 09:54:22 +01:00
class _Option :
2019-02-07 16:23:41 +01:00
def list ( self ,
2019-07-06 15:01:13 +02:00
type = ' option ' ,
recursive = False ) :
2019-04-04 20:04:29 +02:00
if type not in [ ' all ' , ' option ' , ' optiondescription ' ] :
raise Exception ( ' unknown list type {} ' . format ( type ) )
2019-07-25 15:22:00 +02:00
for idx_path , path in enumerate ( self . schema [ ' properties ' ] ) :
subschema = self . schema [ ' properties ' ] [ path ]
2019-04-04 20:04:29 +02:00
if not self . config . is_hidden ( path , None ) :
2019-07-25 15:22:00 +02:00
if subschema [ ' type ' ] in [ ' object ' , ' array ' ] :
2019-04-04 20:04:29 +02:00
if type in [ ' all ' , ' optiondescription ' ] :
yield TiramisuOptionDescription ( self . config ,
2019-07-25 15:22:00 +02:00
subschema ,
2019-04-04 20:04:29 +02:00
path )
2019-07-06 15:01:13 +02:00
if recursive :
yield from TiramisuOptionDescription ( self . config ,
2019-07-25 15:22:00 +02:00
subschema ,
2019-07-06 15:01:13 +02:00
path ) . list ( type , recursive )
2019-04-04 20:04:29 +02:00
elif type in [ ' all ' , ' option ' ] :
2019-04-03 19:42:59 +02:00
yield TiramisuOption ( self . config ,
2019-07-25 15:22:00 +02:00
subschema ,
2019-04-03 19:42:59 +02:00
path ,
self . index )
2019-07-25 15:22:00 +02:00
elif self . schema . get ( ' type ' ) == ' array ' and idx_path == 0 :
# if a leader is hidden, follower are hidden too
break
2019-01-10 09:54:22 +01:00
class TiramisuOptionDescription ( _Option ) :
# config.option(path) (with path == OptionDescription)
def __init__ ( self ,
config : ' Config ' ,
schema : Dict ,
path : str ) - > None :
self . config = config
self . schema = schema
self . path = path
self . index = None
def __getattr__ ( self ,
subfunc : str ) - > Any :
if subfunc == ' option ' :
2019-02-08 11:11:06 +01:00
return TiramisuOptionOption ( self . config ,
self . path ,
2019-07-26 08:57:10 +02:00
self . schema )
2019-01-10 09:54:22 +01:00
if subfunc == ' property ' :
2019-02-08 11:30:29 +01:00
return TiramisuOptionProperty ( self . config ,
self . path ,
2019-07-06 15:01:13 +02:00
None )
if subfunc == ' value ' :
return TiramisuOptionDescriptionValue ( self . config ,
self . schema ,
self . path ,
self . index )
2019-01-10 09:54:22 +01:00
raise APIError ( _ ( ' please specify a valid sub function ( {} ) ' ) . format ( subfunc ) )
def group_type ( self ) :
2019-07-06 15:01:13 +02:00
if not self . config . is_hidden ( self . path , None ) :
2019-01-10 09:54:22 +01:00
# FIXME
return ' default '
2019-07-06 15:01:13 +02:00
raise PropertiesOptionError ( None , { ' disabled ' } , None , opt_type = ' optiondescription ' )
2019-01-10 09:54:22 +01:00
2019-07-06 15:01:13 +02:00
class TiramisuLeadershipValue ( TiramisuOptionValue ) :
2019-05-08 20:15:46 +02:00
def len ( self ) :
2019-07-06 15:01:13 +02:00
return len ( self . config . get_value ( self . path ) )
2019-05-08 20:15:46 +02:00
def pop ( self ,
index : int ) - > None :
2019-07-06 15:01:13 +02:00
self . config . delete_value ( self . path , index )
2019-05-08 20:15:46 +02:00
2018-12-24 09:28:44 +01:00
class TiramisuOption :
2019-01-10 09:54:22 +01:00
# config.option(path) (with path == Option)
2018-12-24 09:28:44 +01:00
def __init__ ( self ,
config : ' Config ' ,
schema : Dict ,
path : str ,
index : Optional [ int ] ) - > None :
self . config = config
self . schema = schema
self . path = path
self . index = index
def __getattr__ ( self ,
subfunc : str ) - > Any :
if subfunc == ' option ' :
if self . index != None :
raise NotImplementedError ( )
2019-02-08 11:11:06 +01:00
return TiramisuOptionOption ( self . config ,
self . path ,
2019-07-26 08:57:10 +02:00
self . schema )
2018-12-24 09:28:44 +01:00
if subfunc == ' value ' :
2019-07-06 15:01:13 +02:00
if self . config . isleader ( self . path ) :
obj = TiramisuLeadershipValue
else :
obj = TiramisuOptionValue
return obj ( self . config ,
self . schema ,
self . path ,
self . index )
2019-01-16 09:29:01 +01:00
if subfunc == ' owner ' :
return TiramisuOptionOwner ( self . config ,
self . schema ,
self . path ,
self . index )
2018-12-24 09:28:44 +01:00
if subfunc == ' property ' :
2019-02-08 11:30:29 +01:00
return TiramisuOptionProperty ( self . config ,
self . path ,
2019-07-06 15:01:13 +02:00
self . index )
2018-12-24 09:28:44 +01:00
raise APIError ( _ ( ' please specify a valid sub function ( {} ) ' ) . format ( subfunc ) )
class TiramisuContextProperty :
2019-01-10 09:54:22 +01:00
# config.property
2019-07-06 15:01:13 +02:00
def __init__ ( self ,
config ) :
self . config = config
2018-12-24 09:28:44 +01:00
def get ( self ) :
2019-07-09 08:13:50 +02:00
return self . config . global_model . get ( ' properties ' , [ ] )
2018-12-24 09:28:44 +01:00
2019-01-10 09:54:22 +01:00
class ContextOption ( _Option ) :
# config.option
def __init__ ( self ,
config : ' Config ' ,
2019-07-26 08:57:10 +02:00
schema : Dict ) - > None :
2019-01-10 09:54:22 +01:00
self . config = config
2019-02-05 07:01:22 +01:00
self . schema = { ' properties ' : schema }
2019-08-23 13:59:22 +02:00
self . index = None
2019-01-10 09:54:22 +01:00
def __call__ ( self ,
path : str ,
index : Optional [ int ] = None ) - > TiramisuOption :
2019-01-12 17:33:26 +01:00
schema = self . config . get_schema ( path )
if schema [ ' type ' ] in [ ' object ' , ' array ' ] :
return TiramisuOptionDescription ( self . config ,
schema ,
path )
2019-01-10 09:54:22 +01:00
return TiramisuOption ( self . config ,
2019-01-12 17:33:26 +01:00
schema ,
2019-01-10 09:54:22 +01:00
path ,
index )
2019-07-06 15:01:13 +02:00
class ContextOwner :
# config.owner
def __init__ ( self ,
config : ' Config ' ,
2019-07-26 08:57:10 +02:00
schema : Dict ) - > None :
2019-07-06 15:01:13 +02:00
self . config = config
self . schema = { ' properties ' : schema }
2019-08-23 13:59:22 +02:00
self . index = None
2019-07-06 15:01:13 +02:00
def get ( self ) :
return self . config . global_model . get ( ' owner ' , ' tmp ' )
2019-01-10 09:54:22 +01:00
class ContextValue ( _Value ) :
# config.value
def __init__ ( self ,
config : ' Config ' ,
2019-07-26 08:57:10 +02:00
schema : Dict ) - > None :
2019-01-10 09:54:22 +01:00
self . config = config
first = next ( iter ( schema . keys ( ) ) )
self . path = first . rsplit ( ' . ' , 1 ) [ 0 ]
2019-01-16 17:45:44 +01:00
self . schema = { ' properties ' : schema }
2019-01-10 09:54:22 +01:00
def __call__ ( self ) - > TiramisuOptionValue :
return TiramisuOptionValue ( self . config ,
self . schema ,
path ,
index )
2019-01-16 17:45:44 +01:00
def mandatory ( self ) :
for key , value in self . dict ( ) . items ( ) :
2019-04-17 19:15:30 +02:00
if self . config . isfollower ( key ) :
2019-07-09 08:13:50 +02:00
if self . config . model . get ( key , { } ) . get ( ' null ' , { } ) . get ( ' required ' ) :
2019-05-09 20:16:57 +02:00
# FIXME test with index
2019-04-17 19:15:30 +02:00
if self . config . get_schema ( key ) . get ( ' isSubMulti ' ) :
for val in value :
if not val or None in val or ' ' in val :
yield key
break
elif None in value or ' ' in value :
yield key
2019-05-09 20:16:57 +02:00
elif self . config . get_schema ( key ) . get ( ' isMulti ' ) :
2019-07-06 15:01:13 +02:00
if self . config . model . get ( key , { } ) . get ( ' required ' ) and ( None in value or ' ' in value ) :
2019-05-09 20:16:57 +02:00
yield key
2019-07-06 15:01:13 +02:00
if self . config . model . get ( key , { } ) . get ( ' needs_len ' ) and not value :
2019-05-09 20:16:57 +02:00
yield key
2019-07-06 15:01:13 +02:00
elif self . config . model . get ( key , { } ) . get ( ' required ' ) and value is None :
2019-01-16 17:45:44 +01:00
yield key
2019-01-10 09:54:22 +01:00
2018-12-24 09:28:44 +01:00
class Config :
2019-01-10 09:54:22 +01:00
# config
2018-12-24 09:28:44 +01:00
def __init__ ( self ,
2019-07-28 22:49:52 +02:00
dico ) :
2019-08-23 10:54:50 +02:00
self . _unrestraint = False
2019-07-06 15:01:13 +02:00
if DEBUG :
from pprint import pprint
2019-07-28 22:49:52 +02:00
pprint ( dico )
if dico . get ( ' version ' ) != TIRAMISU_FORMAT :
raise Exception ( ' incompatible version of tiramisu (got {} , expected {} ) ' . format ( dico . get ( ' version ' , ' 0.0 ' ) , TIRAMISU_FORMAT ) )
self . model = dico . get ( ' model ' )
self . global_model = dico . get ( ' global ' )
self . form = dico . get ( ' form ' )
2019-04-03 19:42:59 +02:00
# support pattern
2019-07-06 15:01:13 +02:00
if self . form :
for key , option in self . form . items ( ) :
if key != ' null ' and ' pattern ' in option :
option [ ' pattern ' ] = re . compile ( option [ ' pattern ' ] )
2018-12-24 09:28:44 +01:00
self . temp = { }
2019-07-28 22:49:52 +02:00
self . schema = dico . get ( ' schema ' )
2018-12-24 09:28:44 +01:00
self . updates = [ ]
2019-07-06 15:01:13 +02:00
if self . schema :
first_path = next ( iter ( self . schema . keys ( ) ) )
else :
first_path = ' '
2019-01-10 09:54:22 +01:00
if ' . ' in first_path :
self . root = first_path . rsplit ( ' . ' , 1 ) [ 0 ]
else :
self . root = ' '
2019-08-23 10:54:50 +02:00
self . dico = dico
2019-01-10 09:54:22 +01:00
2018-12-24 09:28:44 +01:00
def __getattr__ ( self ,
subfunc : str ) - > Any :
if subfunc == ' property ' :
2019-07-06 15:01:13 +02:00
return TiramisuContextProperty ( self )
2019-01-10 09:54:22 +01:00
if subfunc == ' option ' :
return ContextOption ( self ,
2019-07-26 08:57:10 +02:00
self . schema )
2019-01-10 09:54:22 +01:00
if subfunc == ' value ' :
return ContextValue ( self ,
2019-07-26 08:57:10 +02:00
self . schema )
2019-07-06 15:01:13 +02:00
if subfunc == ' owner ' :
return ContextOwner ( self ,
2019-07-26 08:57:10 +02:00
self . schema )
2019-08-23 10:54:50 +02:00
if subfunc == ' unrestraint ' :
ret = Config ( self . dico )
ret . _unrestraint = True
ret . temp = self . temp
return ret
2018-12-24 09:28:44 +01:00
raise APIError ( _ ( ' please specify a valid sub function ( {} ) ' ) . format ( subfunc ) )
def add_value ( self ,
path : str ,
index : Optional [ int ] ,
value : Any ,
remote : bool ) - > None :
2019-07-06 15:01:13 +02:00
if remote :
self . manage_updates ( ' add ' ,
path ,
index ,
value )
2018-12-24 09:28:44 +01:00
self . updates_value ( ' add ' ,
path ,
index ,
value ,
2019-05-08 20:15:46 +02:00
remote )
2019-07-06 15:01:13 +02:00
if not remote :
self . manage_updates ( ' add ' ,
path ,
index ,
value )
2018-12-24 09:28:44 +01:00
def modify_value ( self ,
path : str ,
index : Optional [ int ] ,
value : Any ,
2019-05-08 20:15:46 +02:00
remote : bool ,
leader_old_value : Any ) - > None :
2018-12-24 09:28:44 +01:00
schema = self . get_schema ( path )
2019-07-25 15:22:00 +02:00
has_undefined = value is not None and isinstance ( value , list ) and undefined in value
new_value = schema . get ( ' defaultmulti ' )
if not remote :
if has_undefined :
2019-07-06 15:01:13 +02:00
while undefined in value :
undefined_index = value . index ( undefined )
schema_value = schema . get ( ' value ' , [ ] )
if len ( schema_value ) > undefined_index :
value [ undefined_index ] = schema_value [ undefined_index ]
else :
value [ undefined_index ] = new_value
2019-07-25 15:22:00 +02:00
self . updates_value ( ' modify ' ,
path ,
index ,
value ,
remote ,
False ,
leader_old_value )
self . manage_updates ( ' modify ' ,
path ,
index ,
value )
elif has_undefined :
for idx , val in enumerate ( value ) :
2019-07-06 15:01:13 +02:00
self . manage_updates ( ' modify ' ,
path ,
2019-07-25 15:22:00 +02:00
idx ,
val )
2019-07-06 15:01:13 +02:00
else :
self . manage_updates ( ' modify ' ,
path ,
index ,
value )
if remote :
self . updates_value ( ' modify ' ,
path ,
index ,
value ,
remote ,
False ,
leader_old_value )
2018-12-24 09:28:44 +01:00
def delete_value ( self ,
path : str ,
2019-05-08 20:15:46 +02:00
index : Optional [ int ] ) - > None :
2019-07-06 15:01:13 +02:00
if self . get_schema ( path ) [ ' type ' ] == ' symlink ' :
raise ConfigError ( _ ( " can ' t delete a SymLinkOption " ) )
2019-05-08 20:15:46 +02:00
remote = self . form . get ( path , { } ) . get ( ' remote ' , False )
2019-07-06 15:01:13 +02:00
if remote :
self . manage_updates ( ' delete ' ,
path ,
index ,
None )
2018-12-24 09:28:44 +01:00
self . updates_value ( ' delete ' ,
path ,
index ,
2019-03-16 22:51:39 +01:00
None ,
2019-05-08 20:15:46 +02:00
remote )
2019-07-06 15:01:13 +02:00
if not remote :
self . manage_updates ( ' delete ' ,
path ,
index ,
None )
2018-12-24 09:28:44 +01:00
2019-02-16 20:23:50 +01:00
def get_properties ( self ,
model ,
path ,
2019-03-05 08:26:58 +01:00
index ,
2019-02-16 20:23:50 +01:00
only_raises = True ) :
props = model . get ( ' properties ' , [ ] ) [ : ]
if model . get ( ' required ' ) :
2019-04-17 19:15:30 +02:00
if self . get_schema ( path ) . get ( ' isMulti ' , False ) and not self . isfollower ( path ) :
2019-02-16 20:23:50 +01:00
props . append ( ' empty ' )
else :
props . append ( ' mandatory ' )
if model . get ( ' needs_len ' ) :
props . append ( ' mandatory ' )
if model . get ( ' readOnly ' ) :
props . append ( ' frozen ' )
2019-04-03 19:42:59 +02:00
if only_raises and self . is_hidden ( path ,
2019-03-05 08:26:58 +01:00
index ) :
2019-02-16 20:23:50 +01:00
props . append ( ' hidden ' )
if self . form . get ( path , { } ) . get ( ' clearable ' ) :
props . append ( ' clearable ' )
return props
2018-12-24 09:28:44 +01:00
def get_schema ( self ,
path ) :
2019-01-10 09:54:22 +01:00
root_path = self . root
2019-01-19 10:54:18 +01:00
schema = { ' properties ' : self . schema ,
' type ' : ' object ' }
2019-01-10 09:54:22 +01:00
if root_path :
root = self . root . split ( ' . ' )
2019-02-14 21:51:49 +01:00
if not path . startswith ( self . root ) :
2019-02-05 07:01:22 +01:00
raise Exception ( ' cannot find {0} ' . format ( path ) )
2019-01-10 09:54:22 +01:00
subpaths = path . split ( ' . ' ) [ len ( root ) : ]
2018-12-24 09:28:44 +01:00
else :
2019-01-12 17:33:26 +01:00
subpaths = path . split ( ' . ' )
2019-08-03 21:27:36 +02:00
current_subpath = ' root '
2019-01-12 17:33:26 +01:00
for subpath in subpaths :
if root_path :
root_path + = ' . ' + subpath
else :
root_path = subpath
2019-08-03 21:27:36 +02:00
schema = schema [ ' properties ' ] . get ( root_path )
if schema is None :
2019-08-22 15:51:30 +02:00
raise AttributeError ( _ ( ' option " {0} " inconnue dans l \' optiondescription " {1} " ' ) . format ( subpath , current_subpath ) )
2019-08-03 21:27:36 +02:00
current_subpath = subpath
2018-12-24 09:28:44 +01:00
return schema
2019-05-08 20:15:46 +02:00
def isleader ( self ,
path ) :
if ' . ' in path :
parent_schema = self . get_schema ( path . rsplit ( ' . ' , 1 ) [ 0 ] )
if parent_schema [ ' type ' ] == ' array ' :
leader = next ( iter ( parent_schema [ ' properties ' ] . keys ( ) ) )
return leader == path
return False
2019-03-05 08:26:58 +01:00
def isfollower ( self ,
2019-02-05 07:01:22 +01:00
path : str ) - > bool :
2019-03-05 08:26:58 +01:00
if ' . ' in path :
parent_schema = self . get_schema ( path . rsplit ( ' . ' , 1 ) [ 0 ] )
leader = next ( iter ( parent_schema [ ' properties ' ] . keys ( ) ) )
if parent_schema [ ' type ' ] == ' array ' and \
leader != path :
return True
return False
2019-04-03 19:42:59 +02:00
def is_hidden ( self ,
2019-03-05 08:26:58 +01:00
path : str ,
2019-07-06 15:01:13 +02:00
index : Optional [ int ] ,
permissive : bool = False ) - > bool :
2019-08-23 10:54:50 +02:00
if self . _unrestraint :
return False
2019-07-06 15:01:13 +02:00
if permissive :
property_ = ' hidden '
needs = True
if property_ in self . global_model . get ( ' permissives ' , [ ] ) :
return False
else :
property_ = ' display '
needs = False
if index is not None and property_ in self . temp . get ( path , { } ) . get ( str ( index ) , { } ) :
return self . temp [ path ] [ str ( index ) ] [ property_ ] == needs
if property_ in self . temp . get ( path , { } ) :
return self . temp [ path ] [ property_ ] == needs
elif self . isfollower ( path ) :
if self . model . get ( path , { } ) . get ( ' null ' , { } ) . get ( property_ , None ) == needs :
return True
elif self . model . get ( path , { } ) . get ( property_ , None ) == needs :
return True
if index is not None :
index = str ( index )
if self . model . get ( path , { } ) . get ( index , { } ) . get ( property_ ) == needs :
2019-05-08 20:15:46 +02:00
return True
2019-04-03 19:42:59 +02:00
return False
2019-02-05 07:01:22 +01:00
2019-07-06 15:01:13 +02:00
def get_from_temp_model ( self ,
path ,
index ) :
if path in self . temp :
is_delete = ' delete ' in self . temp [ path ] and self . temp [ path ] [ ' delete ' ] == True
if index is not None and not is_delete and ' delete ' in self . temp [ path ] . get ( index , { } ) :
is_delete = self . temp [ path ] [ index ] [ ' delete ' ] == True
if is_delete :
return None
if index is None and ' value ' in self . temp [ path ] or ' value ' in self . temp [ path ] . get ( index , { } ) :
return self . temp [ path ]
return self . model . get ( path )
2019-02-05 07:01:22 +01:00
def get_value ( self ,
2019-02-28 22:12:46 +01:00
path : str ,
2019-03-05 08:26:58 +01:00
index : int = None ) - > Any :
2019-04-17 19:15:30 +02:00
schema = self . get_schema ( path )
if schema [ ' type ' ] == ' symlink ' :
path = schema [ ' opt_path ' ]
schema = self . get_schema ( path )
2019-03-05 08:26:58 +01:00
if index is None :
2019-07-06 15:01:13 +02:00
if self . isfollower ( path ) :
2019-02-28 22:12:46 +01:00
value = [ ]
2019-07-06 15:01:13 +02:00
parent_schema = self . get_schema ( path . rsplit ( ' . ' , 1 ) [ 0 ] )
leader = next ( iter ( parent_schema [ ' properties ' ] . keys ( ) ) )
leadership_len = len ( self . get_value ( leader ) )
for idx in range ( leadership_len ) :
val = self . get_value ( path ,
idx )
self . _check_raises_warnings ( path , idx , val , schema [ ' type ' ] )
value . append ( val )
else :
value = self . get_from_temp_model ( path , index )
if value is not None and ' value ' in value :
value = value [ ' value ' ]
else :
value = schema . get ( ' value ' )
if value is None and schema . get ( ' isMulti ' , False ) :
value = [ ]
2019-02-05 07:01:22 +01:00
else :
2019-04-03 19:42:59 +02:00
index = str ( index )
if self . isfollower ( path ) :
if self . is_hidden ( path , index ) :
2019-07-06 15:01:13 +02:00
value = PropertiesOptionError ( None , { ' disabled ' } , None , opt_type = ' option ' )
2019-03-05 08:26:58 +01:00
else :
2019-07-06 15:01:13 +02:00
value = self . get_from_temp_model ( path , index )
if value is not None and ' value ' in value . get ( index , { } ) :
value = value [ index ] [ ' value ' ]
else :
value = schema . get ( ' defaultmulti ' )
2019-03-05 08:26:58 +01:00
else :
2019-07-06 15:01:13 +02:00
value = self . get_from_temp_model ( path , index )
2019-04-03 19:42:59 +02:00
if value is not None and index in value and ' value ' in value [ index ] :
value = value [ index ] [ ' value ' ]
else :
2019-04-17 19:15:30 +02:00
value = schema . get ( ' default ' )
if value is None and schema . get ( ' isSubMulti ' , False ) :
value = [ ]
2019-05-08 20:15:46 +02:00
if isinstance ( value , list ) :
value = value . copy ( )
2019-02-05 07:01:22 +01:00
return value
2018-12-24 09:28:44 +01:00
2019-02-05 07:01:22 +01:00
def get_owner ( self ,
2019-03-05 08:26:58 +01:00
path : str ,
index : int ) - > str :
2019-07-06 15:01:13 +02:00
schema = self . get_schema ( path )
if schema [ ' type ' ] == ' symlink ' :
opt_path = schema [ ' opt_path ' ]
index = str ( index )
if self . is_hidden ( opt_path , index ) :
raise PropertiesOptionError ( None , { ' disabled ' } , None , opt_type = ' option ' )
return self . get_owner ( opt_path , index )
elif not self . isfollower ( path ) :
2019-03-05 08:26:58 +01:00
if ' owner ' in self . temp . get ( path , { } ) :
owner = self . temp [ path ] [ ' owner ' ]
else :
owner = self . model . get ( path , { } ) . get ( ' owner ' , ' default ' )
2019-02-05 07:01:22 +01:00
else :
2019-04-03 19:42:59 +02:00
index = str ( index )
if self . is_hidden ( path , index ) :
2019-07-06 15:01:13 +02:00
raise PropertiesOptionError ( None , { ' disabled ' } , None , opt_type = ' option ' )
value = self . get_from_temp_model ( path , index )
if value is not None and ' owner ' in value . get ( index , { } ) :
2019-04-03 19:42:59 +02:00
owner = value [ index ] [ ' owner ' ]
2019-03-05 08:26:58 +01:00
else :
owner = ' default '
2019-02-05 07:01:22 +01:00
return owner
2018-12-24 09:28:44 +01:00
2019-05-08 20:15:46 +02:00
def manage_updates ( self ,
action ,
path ,
index ,
value ) :
2018-12-24 09:28:44 +01:00
update_last_action = False
if self . updates :
last_body = self . updates [ - 1 ]
if last_body [ ' name ' ] == path :
2019-02-05 07:01:22 +01:00
if index is None and not ' index ' in last_body :
2019-02-07 16:23:41 +01:00
last_action = last_body [ ' action ' ]
2018-12-24 09:28:44 +01:00
if last_action == action or \
last_action in [ ' delete ' , ' modify ' ] and action in [ ' delete ' , ' modify ' ] :
2019-02-07 16:23:41 +01:00
update_last_action = True
2018-12-24 09:28:44 +01:00
elif index == None and action == ' delete ' :
for update in reversed ( self . updates ) :
2019-05-08 20:15:46 +02:00
if update [ ' name ' ] == path :
2018-12-24 09:28:44 +01:00
del self . updates [ - 1 ]
else :
break
2019-07-06 15:01:13 +02:00
elif last_body . get ( ' index ' ) == index :
2018-12-24 09:28:44 +01:00
if last_body [ ' action ' ] == ' add ' and action == ' modify ' :
action = ' add '
update_last_action = True
elif last_body [ ' action ' ] == action and action != ' delete ' or \
last_body [ ' action ' ] == ' modify ' and action == ' delete ' :
update_last_action = True
elif last_body [ ' action ' ] == ' add ' and action == ' delete ' :
2019-01-10 09:54:22 +01:00
del self . updates [ - 1 ]
2018-12-24 09:28:44 +01:00
if update_last_action :
2019-03-16 22:51:39 +01:00
if action == ' delete ' and value is None :
2018-12-24 09:28:44 +01:00
if ' value ' in last_body :
del last_body [ ' value ' ]
else :
last_body [ ' value ' ] = value
if index is None and ' index ' in last_body :
del last_body [ ' index ' ]
last_body [ ' action ' ] = action
else :
data = { ' action ' : action ,
' name ' : path }
2019-07-06 15:01:13 +02:00
if action != ' delete ' and value is not undefined :
2018-12-24 09:28:44 +01:00
data [ ' value ' ] = value
if index is not None :
data [ ' index ' ] = index
self . updates . append ( data )
2019-05-08 20:15:46 +02:00
2019-07-06 15:01:13 +02:00
def send ( self ) :
if DEBUG :
print ( ' <===== send ' )
print ( self . updates )
2019-07-25 15:22:00 +02:00
self . send_data ( { ' updates ' : self . updates ,
' model ' : self . model } )
2019-07-06 15:01:13 +02:00
2019-05-08 20:15:46 +02:00
def updates_value ( self ,
action : str ,
path : str ,
index : Optional [ int ] ,
value : Optional [ Any ] ,
remote : bool ,
default_value : bool = False ,
leader_old_value : Optional [ Any ] = undefined ) - > None :
2019-07-25 15:22:00 +02:00
# if 'pattern' in self.form.get(path, {}) and (not isinstance(value, list) or undefined not in value) and not self.test_value(path, index, value, remote):
# return
2019-07-06 15:01:13 +02:00
if remote :
self . send ( )
2018-12-24 09:28:44 +01:00
else :
2019-07-25 15:22:00 +02:00
changes = [ ]
if self . test_value ( path , index , value ) and not self . is_hidden ( path , index ) :
changes . append ( path )
2019-07-06 15:01:13 +02:00
if path in self . model and ( index is None or str ( index ) in self . model [ path ] ) :
model = self . model [ path ]
if index is not None :
model = model [ str ( index ) ]
if ' warnings ' in model :
del model [ ' warnings ' ]
if ' error ' in model :
del model [ ' error ' ]
if action == ' delete ' :
if self . option ( path ) . option . isleader ( ) :
# if remove an indexed leader value
if index is not None :
2019-05-08 20:15:46 +02:00
leader_value = self . option ( path ) . value . get ( )
leader_value . pop ( index )
2019-07-06 15:01:13 +02:00
owner = self . global_model . get ( ' owner ' , ' tmp ' )
else :
leader_value = self . default_value ( path )
owner = ' default '
max_value = len ( leader_value ) + 1
self . _set_temp_value ( path , None , leader_value , owner )
leadership_path = path . rsplit ( ' . ' , 1 ) [ 0 ]
parent_schema = self . get_schema ( leadership_path )
iter_leadership = list ( parent_schema [ ' properties ' ] . keys ( ) )
for follower in iter_leadership [ 1 : ] :
# remove value for this index and reduce len
new_temp = { }
for idx in range ( max_value ) :
cur_index = idx
if index is not None :
2019-05-08 20:15:46 +02:00
if index == idx :
continue
if index < cur_index :
2019-07-06 15:01:13 +02:00
cur_index - = 1
if ' delete ' in self . temp . get ( follower , { } ) :
#FIXME copier les attributs hidden, ... depuis self.temp[follower][index] ?
new_temp [ str ( cur_index ) ] = { ' delete ' : True }
elif self . temp . get ( follower , { } ) . get ( str ( idx ) ) is not None :
if index is None or index == idx :
2019-05-08 20:15:46 +02:00
new_temp [ str ( cur_index ) ] = { ' delete ' : True }
2019-07-06 15:01:13 +02:00
else :
2019-05-08 20:15:46 +02:00
new_temp [ str ( cur_index ) ] = self . temp [ follower ] [ str ( idx ) ]
2019-07-06 15:01:13 +02:00
elif self . model . get ( follower , { } ) . get ( str ( idx ) ) is not None :
if index is None or index == idx :
new_temp [ str ( cur_index ) ] = { ' delete ' : True }
else :
2019-05-08 20:15:46 +02:00
new_temp [ str ( cur_index ) ] = self . model [ follower ] [ str ( idx ) ]
2019-07-06 15:01:13 +02:00
if self . model . get ( follower , { } ) . get ( str ( max_value ) ) is not None :
# FIXME copier les attributs hidden, ... depuis self.temp[follower][index] ?
new_temp [ str ( max_value ) ] = { ' delete ' : True }
self . temp [ follower ] = new_temp
value = leader_value
index = None
2019-03-16 22:51:39 +01:00
elif index is None :
2019-07-06 15:01:13 +02:00
self . _del_temp_value ( path , index )
value = self . get_value ( path )
2019-03-16 22:51:39 +01:00
else :
2019-07-06 15:01:13 +02:00
# it's a follower with index
self . temp . setdefault ( path , { } ) [ str ( index ) ] = { ' delete ' : True }
self . _del_temp_value ( path , index )
value = self . get_value ( path , index )
default_value = True
elif index is None :
# set a value for a not follower option
self . set_not_equal ( path , value , index )
if default_value is True :
self . model [ path ] [ ' value ' ] = value
else :
self . _set_temp_value ( path , None , value , self . global_model . get ( ' owner ' , ' tmp ' ) )
else :
self . set_not_equal ( path , value , index )
# set a value for a follower option
if default_value is True :
self . model [ path ] [ str ( index ) ] [ ' value ' ] = value
else :
self . _set_temp_value ( path , index , value , self . global_model . get ( ' owner ' , ' tmp ' ) )
2019-07-25 15:22:00 +02:00
if not self . is_hidden ( path , index ) :
changes . append ( path )
self . set_dependencies ( path , value , False , changes , index )
self . do_copy ( path , index , value , changes )
2019-07-06 15:01:13 +02:00
if leader_old_value is not undefined and len ( leader_old_value ) < len ( value ) :
# if leader and length is change, display/hide follower from follower's default value
index = len ( value ) - 1
parent_path = ' . ' . join ( path . split ( ' . ' ) [ : - 1 ] )
followers = list ( self . option ( parent_path ) . list ( ) ) [ 1 : ]
for follower in followers :
follower_path = follower . option . path ( )
try :
2019-05-08 20:15:46 +02:00
follower_value = self . option ( follower_path , index ) . value . get ( )
self . set_dependencies ( follower_path , follower_value , None , index )
2019-07-06 15:01:13 +02:00
except PropertiesOptionError :
pass
2019-07-25 15:22:00 +02:00
for path in changes :
self . send_event ( ' tiramisu-change ' , path )
2019-05-08 20:15:46 +02:00
def _set_temp_value ( self , path , index , value , owner ) :
if index is not None :
obj = self . temp . setdefault ( path , { } ) . setdefault ( str ( index ) , { } )
else :
obj = self . temp . setdefault ( path , { } )
if ' delete ' in obj :
del obj [ ' delete ' ]
obj [ ' value ' ] = value
obj [ ' owner ' ] = owner
def _del_temp_value ( self , path , index ) :
if index is not None :
obj = self . temp . setdefault ( path , { } ) . setdefault ( str ( index ) , { } )
else :
obj = self . temp . setdefault ( path , { } )
for key in [ ' value ' , ' owner ' ] :
if key in obj :
del obj [ key ]
obj [ ' delete ' ] = True
2018-12-24 09:28:44 +01:00
2019-03-16 22:51:39 +01:00
def default_value ( self , path ) :
schema = self . get_schema ( path )
2019-05-08 20:15:46 +02:00
value = schema . get ( ' value ' )
2019-03-16 22:51:39 +01:00
if value is None and schema . get ( ' isMulti ' , False ) :
value = [ ]
2019-05-08 20:15:46 +02:00
elif isinstance ( value , list ) :
value = value . copy ( )
2019-03-16 22:51:39 +01:00
return value
2019-02-28 22:12:46 +01:00
def updates_data ( self , data ) :
2019-07-06 15:01:13 +02:00
if DEBUG :
from pprint import pprint
print ( ' ====> updates_data ' )
pprint ( data )
2019-02-28 22:12:46 +01:00
self . updates = [ ]
self . temp . clear ( )
2019-04-03 19:42:59 +02:00
self . model = data [ ' model ' ]
2019-07-25 15:22:00 +02:00
for path in data [ ' updates ' ] :
self . send_event ( ' tiramisu-change ' , path )
2019-02-28 22:12:46 +01:00
2018-12-24 09:28:44 +01:00
def test_value ( self ,
path : str ,
2019-07-06 15:01:13 +02:00
index : Optional [ int ] ,
2019-07-25 15:22:00 +02:00
value : Any ) - > bool :
2018-12-24 09:28:44 +01:00
if isinstance ( value , list ) :
for val in value :
2019-07-25 15:22:00 +02:00
if not self . _test_value ( path , index , val ) :
# when a value is invalid, all are invalid
2018-12-24 09:28:44 +01:00
return False
return True
2019-07-25 15:22:00 +02:00
return not self . _test_value ( path , index , value )
def _test_value ( self ,
path : str ,
index : Optional [ int ] ,
value : Any ) - > bool :
if not path in self . form or not ' pattern ' in self . form [ path ] :
return True
if value is None or value is ' ' :
match = True
2018-12-24 09:28:44 +01:00
else :
2019-07-25 15:22:00 +02:00
if isinstance ( value , int ) :
value = str ( value )
match = self . form [ path ] [ ' pattern ' ] . search ( value ) is not None
if not path in self . temp :
self . temp [ path ] = { }
if index is None :
if not match :
self . temp [ path ] [ ' invalid ' ] = True
self . temp [ path ] [ ' error ' ] = [ ' invalid value ' ]
2018-12-24 09:28:44 +01:00
else :
2019-07-25 15:22:00 +02:00
self . temp [ path ] [ ' invalid ' ] = False
else :
if not str ( index ) in self . temp [ path ] :
self . temp [ path ] [ str ( index ) ] = { }
if not match :
self . temp [ path ] [ str ( index ) ] [ ' invalid ' ] = True
self . temp [ path ] [ str ( index ) ] [ ' error ' ] = [ ' invalid value ' ]
else :
self . temp [ path ] [ str ( index ) ] [ ' invalid ' ] = False
return match
2018-12-24 09:28:44 +01:00
def set_dependencies ( self ,
path : str ,
2019-02-05 07:01:22 +01:00
ori_value : Any ,
2019-07-25 15:22:00 +02:00
force_hide : bool ,
changes : List ,
2019-05-08 20:15:46 +02:00
index : Optional [ int ] = None ) - > None :
2018-12-24 09:28:44 +01:00
dependencies = self . form . get ( path , { } ) . get ( ' dependencies ' , { } )
if dependencies :
2019-07-06 15:01:13 +02:00
if not isinstance ( ori_value , list ) :
2019-07-25 15:22:00 +02:00
self . _set_dependencies ( path , ori_value , dependencies , force_hide , changes , index )
2018-12-24 09:28:44 +01:00
else :
2019-07-06 15:01:13 +02:00
for idx , ori_val in enumerate ( ori_value ) :
2019-07-25 15:22:00 +02:00
self . _set_dependencies ( path , ori_val , dependencies , force_hide , changes , idx )
2019-07-06 15:01:13 +02:00
def _set_dependencies ( self ,
path : str ,
ori_value : Any ,
dependencies : Dict ,
force_hide : bool ,
2019-07-25 15:22:00 +02:00
changes : List ,
2019-07-06 15:01:13 +02:00
index : Optional [ int ] ) - > None :
if ori_value in dependencies [ ' expected ' ] :
expected = dependencies [ ' expected ' ] [ ori_value ]
else :
expected = dependencies [ ' default ' ]
for action in [ ' hide ' , ' show ' ] :
expected_actions = expected . get ( action )
if expected_actions :
if force_hide :
2019-07-25 15:22:00 +02:00
display = True
2019-01-12 17:33:26 +01:00
else :
2019-07-06 15:01:13 +02:00
display = action == ' show '
for expected_path in expected_actions :
2019-07-25 15:22:00 +02:00
if expected_path not in self . temp :
self . temp [ expected_path ] = { }
old_hidden = self . is_hidden ( expected_path ,
index )
leader_path = None
2019-07-06 15:01:13 +02:00
if index is not None :
2019-07-25 15:22:00 +02:00
if str ( index ) not in self . temp [ expected_path ] :
self . temp [ expected_path ] [ str ( index ) ] = { }
self . temp [ expected_path ] [ str ( index ) ] [ ' display ' ] = display
2019-07-06 15:01:13 +02:00
else :
2019-07-25 15:22:00 +02:00
self . temp [ expected_path ] [ ' display ' ] = display
schema = self . get_schema ( expected_path )
if schema [ ' type ' ] == ' array ' :
leader_path = next ( iter ( schema [ ' properties ' ] . keys ( ) ) )
if leader_path not in self . temp :
self . temp [ leader_path ] = { }
self . temp [ leader_path ] [ ' display ' ] = display
if old_hidden == display :
if expected_path not in changes :
changes . append ( expected_path )
if leader_path not in changes :
changes . append ( leader_path )
2019-07-06 15:01:13 +02:00
value = self . get_value ( expected_path , index )
2019-07-25 15:22:00 +02:00
self . set_dependencies ( expected_path , value , not display , changes , index )
2018-12-24 09:28:44 +01:00
def set_not_equal ( self ,
path : str ,
2019-07-06 15:01:13 +02:00
value : Any ,
index : Optional [ int ] ) - > None :
not_equals = self . form . get ( path , { } ) . get ( ' not_equal ' , [ ] )
if not_equals :
2018-12-24 09:28:44 +01:00
vals = [ ]
opts = [ ]
if isinstance ( value , list ) :
for val in value :
vals . append ( val )
opts . append ( path )
else :
vals . append ( value )
opts . append ( path )
2019-07-06 15:01:13 +02:00
for not_equal in not_equals :
for path_ in not_equal [ ' options ' ] :
if self . is_hidden ( path_ , index , permissive = True ) :
raise PropertiesOptionError ( None , { ' hidden ' } , None , opt_type = ' option ' )
schema = self . get_schema ( path_ )
p_value = self . get_value ( path_ )
if isinstance ( p_value , list ) :
for val in p_value :
vals . append ( val )
opts . append ( path_ )
else :
vals . append ( p_value )
2018-12-24 09:28:44 +01:00
opts . append ( path_ )
2019-07-06 15:01:13 +02:00
equal = [ ]
warnings_only = not_equal . get ( ' warnings ' , False )
if warnings_only and ' warnings ' not in self . global_model . get ( ' properties ' , [ ] ) :
continue
if warnings_only :
msg = _ ( ' should be different from the value of {} ' )
#msgcurr = _('value for {} should be different')
2018-12-24 09:28:44 +01:00
else :
2019-07-06 15:01:13 +02:00
msg = _ ( ' must be different from the value of {} ' )
#msgcurr = _('value for {} must be different')
for idx_inf , val_inf in enumerate ( vals ) :
for idx_sup , val_sup in enumerate ( vals [ idx_inf + 1 : ] ) :
if val_inf == val_sup is not None :
for opt_ in [ opts [ idx_inf ] , opts [ idx_inf + idx_sup + 1 ] ] :
if opt_ not in equal :
equal . append ( opt_ )
if equal :
equal_name = { }
2018-12-24 09:28:44 +01:00
display_equal = [ ]
2019-07-06 15:01:13 +02:00
for opt_ in equal :
display_equal . append ( ' " ' + self . get_schema ( opt_ ) [ ' title ' ] + ' " ' )
display_equal = display_list ( display_equal )
msg_ = msg . format ( display_equal )
2019-07-25 15:22:00 +02:00
is_demoting_error_warning = ' demoting_error_warning ' in self . global_model . get ( ' properties ' , [ ] )
if warnings_only or is_demoting_error_warning :
paths = not_equal [ ' options ' ] + [ path ]
else :
paths = [ path ]
for path_ in paths :
if path_ not in self . temp :
self . temp [ path_ ] = { }
temp = self . temp [ path_ ]
2019-07-06 15:01:13 +02:00
if index is not None :
2019-07-25 15:22:00 +02:00
if str ( index ) not in temp :
temp [ str ( index ) ] = { }
temp = temp [ str ( index ) ]
2019-07-06 15:01:13 +02:00
if warnings_only :
2019-07-25 15:22:00 +02:00
temp [ ' hasWarnings ' ] = True
temp . setdefault ( ' warnings ' , [ ] ) . append ( msg_ )
2019-07-06 15:01:13 +02:00
else :
2019-07-25 15:22:00 +02:00
if not is_demoting_error_warning :
raise ValueError ( msg )
temp [ ' invalid ' ] = True
temp . setdefault ( ' error ' , [ ] ) . append ( msg_ )
2018-12-24 09:28:44 +01:00
def do_copy ( self ,
path : str ,
2019-07-25 15:22:00 +02:00
index : Optional [ int ] ,
value : Any ,
changes : List ) - > None :
2018-12-24 09:28:44 +01:00
copy = self . form . get ( path , { } ) . get ( ' copy ' )
if copy :
for opt in copy :
2019-07-25 15:22:00 +02:00
owner = self . get_owner ( opt , index )
2018-12-24 09:28:44 +01:00
if owner == ' default ' :
2019-05-08 20:15:46 +02:00
# do not change in self.temp, it's default value
if self . model [ opt ] [ ' value ' ] != value :
if isinstance ( value , list ) :
value = value . copy ( )
# self.model[opt]['value'] = value
remote = self . form . get ( opt , { } ) . get ( ' remote ' , False )
2019-07-25 15:22:00 +02:00
self . updates_value ( ' modify ' , opt , index , value , remote , True )
if not self . is_hidden ( opt , index ) and opt not in changes :
changes . append ( opt )
2018-12-24 09:28:44 +01:00
2019-07-06 15:01:13 +02:00
2019-07-25 15:22:00 +02:00
def _check_raises_warnings ( self , path , index , value , type , withwarning = True ) :
subconfig_value = self . option ( path , index ) . value
if not subconfig_value . valid ( ) :
is_demoting_error_warning = ' demoting_error_warning ' in self . global_model . get ( ' properties ' , [ ] )
for err in subconfig_value . error_message ( ) :
if is_demoting_error_warning :
warnings . warn_explicit ( ValueErrorWarning ( value ,
type ,
Option ( path , path ) ,
' {0} ' . format ( err ) ,
index ) ,
ValueErrorWarning ,
' Option ' , 0 )
else :
if path not in self . temp :
self . temp [ path ] = { }
if index is None :
obj = self . temp [ path ]
else :
if str ( index ) not in self . temp [ path ] :
self . temp [ path ] [ str ( index ) ] = { }
obj = self . temp [ path ] [ str ( index ) ]
obj [ ' invalid ' ] = False
raise ValueError ( err )
if withwarning and subconfig_value . warning ( ) :
for warn in subconfig_value . warning_message ( ) :
2019-07-06 15:01:13 +02:00
warnings . warn_explicit ( ValueErrorWarning ( value ,
type ,
Option ( path , path ) ,
' {0} ' . format ( warn ) ,
index ) ,
ValueErrorWarning ,
' Option ' , 0 )
2018-12-24 09:28:44 +01:00
def send_data ( self ,
updates ) :
2019-05-08 20:15:46 +02:00
raise NotImplementedError ( ' please implement send_data method ' )
2019-07-25 15:22:00 +02:00
def send_event ( self ,
evt : str ,
path : str ) :
pass