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-01-16 09:42:22 +01:00
|
|
|
from .error import APIError, ValueWarning, ValueOptionError, ValueErrorWarning, PropertiesOptionError
|
|
|
|
from .setting import undefined
|
|
|
|
from .i18n import _
|
2019-01-10 09:54:22 +01:00
|
|
|
|
2018-12-24 09:28:44 +01:00
|
|
|
|
2019-04-03 19:48:45 +02:00
|
|
|
TIRAMISU_JSON_VERSION = '1.0'
|
|
|
|
|
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,
|
|
|
|
'domain': str}
|
|
|
|
|
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,
|
|
|
|
path):
|
|
|
|
self.path = path
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
class TiramisuOptionOption:
|
2019-01-10 09:54:22 +01:00
|
|
|
# config.option(path).option
|
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-01-10 09:54:22 +01:00
|
|
|
schema: Dict,
|
2019-01-18 14:15:05 +01:00
|
|
|
model: Dict,
|
|
|
|
form: 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
|
2019-01-10 09:54:22 +01:00
|
|
|
self.model = model
|
2019-01-18 14:15:05 +01:00
|
|
|
self.form = form
|
2018-12-24 09:28:44 +01:00
|
|
|
|
|
|
|
def doc(self):
|
|
|
|
return self.schema['title']
|
|
|
|
|
|
|
|
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-01-16 17:45:44 +01:00
|
|
|
model = self.model.get(self._path, {})
|
2019-04-17 19:15:30 +02:00
|
|
|
if self.isfollower():
|
|
|
|
model = model.get(None, {})
|
|
|
|
return self.config.get_properties(model, self._path, None)
|
2019-01-10 09:54:22 +01: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):
|
|
|
|
if self._path in self.form:
|
|
|
|
return self.form[self._path].get('pattern')
|
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
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-03-05 08:26:58 +01:00
|
|
|
index: Optional[int],
|
2018-12-24 09:28:44 +01:00
|
|
|
model: Dict) -> 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
|
|
|
self.model = model
|
|
|
|
|
2019-01-10 09:54:22 +01:00
|
|
|
def get(self, only_raises=False):
|
|
|
|
if not only_raises:
|
2019-03-05 08:26:58 +01:00
|
|
|
props = self.config.get_properties(self.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-03-05 08:26:58 +01: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-03-05 08:26:58 +01:00
|
|
|
withwarning: bool) -> None:
|
|
|
|
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-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-03-05 08:26:58 +01:00
|
|
|
withwarning)
|
|
|
|
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-05-09 20:16:57 +02:00
|
|
|
self._display_warnings(key, value, option['type'], key, withwarning)
|
2019-03-05 08:26:58 +01:00
|
|
|
values.append(value)
|
|
|
|
ret[key] = 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-05-09 20:16:57 +02:00
|
|
|
self._display_warnings(key, value, option['type'], key, withwarning)
|
2019-01-10 09:54:22 +01:00
|
|
|
ret[key] = 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,
|
|
|
|
withwarning: bool=False):
|
|
|
|
ret = {}
|
|
|
|
self._dict_walk(ret,
|
|
|
|
self.schema,
|
|
|
|
self.path,
|
|
|
|
fullpath,
|
|
|
|
withwarning)
|
|
|
|
return ret
|
2018-12-24 09:28:44 +01:00
|
|
|
|
2019-01-10 09:54:22 +01:00
|
|
|
def _display_warnings(self, path, value, type, name, withwarning=True):
|
2019-02-05 07:01:22 +01:00
|
|
|
for err in self.model.get(path, {}).get('error', []):
|
2019-02-12 22:00:59 +01:00
|
|
|
warnings.warn_explicit(ValueErrorWarning(value,
|
|
|
|
type,
|
|
|
|
Option(path),
|
|
|
|
'{0}'.format(err)),
|
2019-02-05 07:01:22 +01:00
|
|
|
ValueErrorWarning,
|
|
|
|
self.__class__.__name__, 0)
|
2018-12-24 09:28:44 +01:00
|
|
|
|
2019-01-10 09:54:22 +01:00
|
|
|
if withwarning and self.model.get(path, {}).get('warnings'):
|
2018-12-24 09:28:44 +01:00
|
|
|
for warn in self.model.get(path, {}).get('warnings'):
|
2019-02-12 22:00:59 +01:00
|
|
|
warnings.warn_explicit(ValueErrorWarning(value,
|
|
|
|
type,
|
|
|
|
Option(path),
|
|
|
|
'{0}'.format(err)),
|
|
|
|
ValueErrorWarning,
|
2018-12-24 09:28:44 +01:00
|
|
|
self.__class__.__name__, 0)
|
|
|
|
|
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,
|
|
|
|
model: List[Dict],
|
|
|
|
form: List[Dict],
|
|
|
|
temp: List[Dict],
|
|
|
|
path: str,
|
|
|
|
index: int) -> None:
|
|
|
|
self.config = config
|
|
|
|
self.schema = schema
|
|
|
|
self.model = model
|
|
|
|
self.form = form
|
|
|
|
self.temp = temp
|
|
|
|
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-01-10 09:54:22 +01:00
|
|
|
class TiramisuOptionValue(_Value):
|
|
|
|
# config.option(path).value
|
|
|
|
def __init__(self,
|
|
|
|
config: 'Config',
|
|
|
|
schema: Dict,
|
|
|
|
model: List[Dict],
|
|
|
|
form: List[Dict],
|
|
|
|
temp: List[Dict],
|
|
|
|
path: str,
|
|
|
|
index: int) -> None:
|
|
|
|
self.config = config
|
|
|
|
self.schema = schema
|
|
|
|
self.model = model
|
|
|
|
self.form = form
|
|
|
|
self.temp = temp
|
|
|
|
self.path = path
|
|
|
|
self.index = index
|
2018-12-24 09:28:44 +01:00
|
|
|
|
|
|
|
def get(self) -> Any:
|
2019-03-05 08:26:58 +01:00
|
|
|
if self.config.isfollower(self.path):
|
|
|
|
if self.index is None:
|
|
|
|
raise APIError(_('index must be set with the follower option "{}"').format(self.path))
|
|
|
|
value = self.config.get_value(self.path, self.index)
|
2019-05-09 20:16:57 +02:00
|
|
|
self._display_warnings(self.path, value, self.schema['type'], self.path)
|
2019-03-05 08:26:58 +01:00
|
|
|
return value
|
|
|
|
if 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-02-14 21:25:00 +01:00
|
|
|
value = self.config.get_value(self.path)
|
2019-05-09 20:16:57 +02:00
|
|
|
self._display_warnings(self.path, value, self.schema['type'], self.path)
|
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
|
|
|
|
if type_ == 'choice':
|
2019-01-16 16:38:32 +01:00
|
|
|
if 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
|
|
|
|
if self.config.isleader(self.path):
|
|
|
|
leader_old_value = self.config.get_value(self.path)
|
2019-03-16 22:51:39 +01:00
|
|
|
remote = self.form.get(self.path, {}).get('remote', False)
|
|
|
|
if self.index is None and self.schema.get('isMulti', False):
|
2019-01-19 10:54:18 +01:00
|
|
|
if not isinstance(value, list):
|
|
|
|
raise Exception('value must be a list')
|
|
|
|
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)
|
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-05-09 20:16:57 +02:00
|
|
|
self._display_warnings(self.path, value, type_, self.path)
|
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):
|
|
|
|
return self.schema.get('value')
|
|
|
|
|
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-01-10 09:54:22 +01:00
|
|
|
type='option'):
|
2019-04-04 20:04:29 +02:00
|
|
|
if type not in ['all', 'option', 'optiondescription']:
|
|
|
|
raise Exception('unknown list type {}'.format(type))
|
2019-01-10 09:54:22 +01:00
|
|
|
for path, schema in self.schema['properties'].items():
|
2019-04-04 20:04:29 +02:00
|
|
|
if not self.config.is_hidden(path, None):
|
2019-04-03 19:42:59 +02:00
|
|
|
if schema['type'] in ['object', 'array']:
|
2019-04-04 20:04:29 +02:00
|
|
|
if type in ['all', 'optiondescription']:
|
|
|
|
yield TiramisuOptionDescription(self.config,
|
|
|
|
schema,
|
|
|
|
self.model,
|
|
|
|
self.form,
|
|
|
|
self.temp,
|
|
|
|
path)
|
|
|
|
elif type in ['all', 'option']:
|
2019-04-03 19:42:59 +02:00
|
|
|
yield TiramisuOption(self.config,
|
|
|
|
schema,
|
|
|
|
self.model,
|
|
|
|
self.form,
|
|
|
|
self.temp,
|
|
|
|
path,
|
|
|
|
self.index)
|
2019-01-10 09:54:22 +01:00
|
|
|
|
|
|
|
|
|
|
|
class TiramisuOptionDescription(_Option):
|
|
|
|
# config.option(path) (with path == OptionDescription)
|
|
|
|
def __init__(self,
|
|
|
|
config: 'Config',
|
|
|
|
schema: Dict,
|
|
|
|
model: List[Dict],
|
|
|
|
form: List[Dict],
|
|
|
|
temp: List[Dict],
|
|
|
|
path: str) -> None:
|
|
|
|
self.config = config
|
|
|
|
self.schema = schema
|
|
|
|
self.model = model
|
|
|
|
self.form = form
|
|
|
|
self.temp = temp
|
|
|
|
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-01-10 09:54:22 +01:00
|
|
|
self.schema,
|
2019-01-18 14:15:05 +01:00
|
|
|
self.model,
|
|
|
|
self.form)
|
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-02-05 07:01:22 +01:00
|
|
|
self.model.get(self.path, {}))
|
2019-05-08 20:15:46 +02:00
|
|
|
if subfunc == 'value' and self.schema['type'] == 'array':
|
|
|
|
return TiramisuLeadershipValue(self.config,
|
|
|
|
self.schema,
|
|
|
|
self.model,
|
|
|
|
self.form,
|
|
|
|
self.temp,
|
|
|
|
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-04-03 19:42:59 +02:00
|
|
|
if self.config.is_hidden(self.path, None):
|
2019-01-10 09:54:22 +01:00
|
|
|
# FIXME
|
|
|
|
return 'default'
|
|
|
|
raise PropertiesOptionError(None, None, None, opt_type='optiondescription')
|
|
|
|
|
|
|
|
|
2019-05-08 20:15:46 +02:00
|
|
|
class TiramisuLeadershipValue:
|
|
|
|
def __init__(self,
|
|
|
|
config,
|
|
|
|
schema,
|
|
|
|
model,
|
|
|
|
form,
|
|
|
|
temp,
|
|
|
|
path):
|
|
|
|
self.config = config
|
|
|
|
self.schema = schema
|
|
|
|
self.model = model
|
|
|
|
self.form = form
|
|
|
|
self.temp = temp
|
|
|
|
self.path = path
|
|
|
|
|
|
|
|
def len(self):
|
|
|
|
return len(self.config.get_value(self.schema['properties'][0]))
|
|
|
|
|
|
|
|
def pop(self,
|
|
|
|
index: int) -> None:
|
|
|
|
leadership_path = self.schema['properties'][0]
|
|
|
|
self.config.delete_value(leadership_path, index)
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
model: List[Dict],
|
|
|
|
form: List[Dict],
|
|
|
|
temp: List[Dict],
|
|
|
|
path: str,
|
|
|
|
index: Optional[int]) -> None:
|
|
|
|
self.config = config
|
|
|
|
self.schema = schema
|
|
|
|
self.model = model
|
|
|
|
self.form = form
|
|
|
|
self.temp = temp
|
|
|
|
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-01-10 09:54:22 +01:00
|
|
|
self.schema,
|
2019-01-18 14:15:05 +01:00
|
|
|
self.model,
|
|
|
|
self.form)
|
2018-12-24 09:28:44 +01:00
|
|
|
if subfunc == 'value':
|
|
|
|
return TiramisuOptionValue(self.config,
|
|
|
|
self.schema,
|
|
|
|
self.model,
|
|
|
|
self.form,
|
|
|
|
self.temp,
|
|
|
|
self.path,
|
|
|
|
self.index)
|
2019-01-16 09:29:01 +01:00
|
|
|
if subfunc == 'owner':
|
|
|
|
return TiramisuOptionOwner(self.config,
|
|
|
|
self.schema,
|
|
|
|
self.model,
|
|
|
|
self.form,
|
|
|
|
self.temp,
|
|
|
|
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-03-05 08:26:58 +01:00
|
|
|
self.index,
|
2019-02-05 07:01:22 +01:00
|
|
|
self.model.get(self.path, {}))
|
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
|
2018-12-24 09:28:44 +01:00
|
|
|
# def __init__(self,
|
|
|
|
# json):
|
|
|
|
# self.json = json
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
# FIXME ?
|
|
|
|
return ['demoting_error_warning']
|
|
|
|
|
|
|
|
|
2019-01-10 09:54:22 +01:00
|
|
|
class ContextOption(_Option):
|
|
|
|
# config.option
|
|
|
|
def __init__(self,
|
|
|
|
config: 'Config',
|
|
|
|
model: Dict,
|
|
|
|
form: Dict,
|
|
|
|
schema: Dict,
|
|
|
|
temp: Dict) -> None:
|
|
|
|
self.config = config
|
|
|
|
self.model = model
|
|
|
|
self.form = form
|
2019-02-05 07:01:22 +01:00
|
|
|
self.schema = {'properties': schema}
|
2019-01-10 09:54:22 +01:00
|
|
|
self.temp = temp
|
2019-02-08 10:51:38 +01: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,
|
|
|
|
self.model,
|
|
|
|
self.form,
|
|
|
|
self.temp,
|
|
|
|
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
|
|
|
self.model,
|
|
|
|
self.form,
|
|
|
|
self.temp,
|
|
|
|
path,
|
|
|
|
index)
|
|
|
|
|
|
|
|
|
|
|
|
class ContextValue(_Value):
|
|
|
|
# config.value
|
|
|
|
def __init__(self,
|
|
|
|
config: 'Config',
|
|
|
|
model: Dict,
|
|
|
|
form: Dict,
|
|
|
|
schema: Dict,
|
|
|
|
temp: Dict) -> None:
|
|
|
|
self.config = config
|
|
|
|
self.model = model
|
|
|
|
self.form = form
|
|
|
|
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
|
|
|
self.temp = temp
|
|
|
|
|
|
|
|
def __call__(self) -> TiramisuOptionValue:
|
|
|
|
return TiramisuOptionValue(self.config,
|
|
|
|
self.schema,
|
|
|
|
self.model,
|
|
|
|
self.form,
|
|
|
|
self.temp,
|
|
|
|
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):
|
|
|
|
if self.model.get(key, {}).get(None, {}).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'):
|
|
|
|
if self.model.get(key, {}).get('required') and (None in value or '' in value):
|
|
|
|
yield key
|
|
|
|
if self.model.get(key, {}).get('needs_len') and not value:
|
|
|
|
yield key
|
|
|
|
elif self.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,
|
|
|
|
json):
|
2019-04-03 19:48:45 +02:00
|
|
|
if json.get('version') != TIRAMISU_JSON_VERSION:
|
|
|
|
raise Exception('incompatible tiramisu-json format version (got {}, expected {})'.format(json.get('version', '0.0'), TIRAMISU_JSON_VERSION))
|
2019-04-03 19:42:59 +02:00
|
|
|
self.model = json['model']
|
|
|
|
self.form = json['form']
|
|
|
|
# support pattern
|
2019-05-08 20:15:46 +02:00
|
|
|
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 = {}
|
|
|
|
self.schema = json['schema']
|
|
|
|
self.updates = []
|
2019-01-10 09:54:22 +01:00
|
|
|
first_path = next(iter(self.schema.keys()))
|
|
|
|
if '.' in first_path:
|
|
|
|
self.root = first_path.rsplit('.', 1)[0]
|
|
|
|
else:
|
|
|
|
self.root = ''
|
|
|
|
|
2018-12-24 09:28:44 +01:00
|
|
|
def __getattr__(self,
|
|
|
|
subfunc: str) -> Any:
|
|
|
|
if subfunc == 'property':
|
|
|
|
return TiramisuContextProperty()
|
2019-01-10 09:54:22 +01:00
|
|
|
if subfunc == 'option':
|
|
|
|
return ContextOption(self,
|
|
|
|
self.model,
|
|
|
|
self.form,
|
|
|
|
self.schema,
|
|
|
|
self.temp)
|
|
|
|
if subfunc == 'value':
|
|
|
|
return ContextValue(self,
|
|
|
|
self.model,
|
|
|
|
self.form,
|
|
|
|
self.schema,
|
|
|
|
self.temp)
|
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-05-08 20:15:46 +02:00
|
|
|
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)
|
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)
|
|
|
|
if value and isinstance(value, list) and value[-1] is undefined:
|
|
|
|
new_value = schema.get('defaultvalue')
|
|
|
|
if new_value is None:
|
|
|
|
len_value = len(value)
|
2019-02-05 07:01:22 +01:00
|
|
|
schema_value = schema.get('value', [])
|
|
|
|
if len(schema_value) >= len_value:
|
|
|
|
new_value = schema_value[len_value - 1]
|
2018-12-24 09:28:44 +01:00
|
|
|
value[-1] = new_value
|
2019-02-07 16:23:41 +01:00
|
|
|
|
2019-05-08 20:15:46 +02:00
|
|
|
self.manage_updates('modify',
|
|
|
|
path,
|
|
|
|
index,
|
|
|
|
value)
|
2018-12-24 09:28:44 +01:00
|
|
|
self.updates_value('modify',
|
|
|
|
path,
|
|
|
|
index,
|
|
|
|
value,
|
|
|
|
remote,
|
2019-05-08 20:15:46 +02:00
|
|
|
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:
|
|
|
|
remote = self.form.get(path, {}).get('remote', False)
|
|
|
|
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)
|
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('.')
|
|
|
|
for subpath in subpaths:
|
|
|
|
if root_path:
|
|
|
|
root_path += '.' + subpath
|
|
|
|
else:
|
|
|
|
root_path = subpath
|
|
|
|
schema = schema['properties'][root_path]
|
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,
|
|
|
|
index: Optional[int]) -> bool:
|
2019-04-03 19:42:59 +02:00
|
|
|
for property_, needs in {'hidden': True, 'display': False}.items():
|
2019-05-08 20:15:46 +02:00
|
|
|
if index is not None and property_ in self.temp.get(path, {}).get(str(index), {}):
|
|
|
|
return self.temp[path][str(index)][property_]
|
2019-04-03 19:42:59 +02:00
|
|
|
if property_ in self.temp.get(path, {}):
|
2019-04-17 19:15:30 +02:00
|
|
|
return self.temp[path][property_]
|
2019-05-08 20:15:46 +02:00
|
|
|
elif self.isfollower(path):
|
|
|
|
if self.model.get(path, {}).get('null', {}).get(property_, None) == needs:
|
2019-04-03 19:42:59 +02:00
|
|
|
return True
|
2019-05-08 20:15:46 +02:00
|
|
|
elif self.model.get(path, {}).get(property_, None) == needs:
|
|
|
|
return True
|
|
|
|
if index is not None:
|
2019-04-03 19:42:59 +02:00
|
|
|
index = str(index)
|
2019-05-08 20:15:46 +02:00
|
|
|
if self.model.get(path, {}).get(index, {}).get(property_) == needs:
|
2019-04-03 19:42:59 +02:00
|
|
|
return True
|
|
|
|
return False
|
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-02-28 22:12:46 +01:00
|
|
|
if 'value' in self.temp.get(path, {}):
|
|
|
|
value = self.temp[path]['value']
|
|
|
|
else:
|
|
|
|
value = self.model.get(path, {}).get('value')
|
2019-04-17 19:15:30 +02:00
|
|
|
if value is None and schema.get('isMulti', False):
|
2019-02-28 22:12:46 +01:00
|
|
|
value = []
|
2019-02-05 07:01:22 +01:00
|
|
|
else:
|
2019-04-03 19:42:59 +02:00
|
|
|
index = str(index)
|
|
|
|
if 'delete' in self.temp.get(path, {}):
|
|
|
|
value = None
|
2019-05-08 20:15:46 +02:00
|
|
|
elif self.temp.get(path, {}).get(index, {}).get('delete') == True:
|
2019-04-17 19:15:30 +02:00
|
|
|
value = None
|
|
|
|
elif 'value' in self.temp.get(path, {}).get(index, {}):
|
|
|
|
value = self.temp[path]
|
2019-03-16 22:51:39 +01:00
|
|
|
else:
|
2019-04-03 19:42:59 +02:00
|
|
|
value = self.model.get(path)
|
|
|
|
if self.isfollower(path):
|
|
|
|
if self.is_hidden(path, index):
|
|
|
|
value = PropertiesOptionError(None, None, None, opt_type='option')
|
2019-04-17 19:15:30 +02:00
|
|
|
elif value is not None and 'value' in value.get(index, {}):
|
2019-04-03 19:42:59 +02:00
|
|
|
value = value[index]['value']
|
2019-03-05 08:26:58 +01:00
|
|
|
else:
|
2019-04-17 19:15:30 +02:00
|
|
|
value = schema.get('default')
|
2019-03-05 08:26:58 +01:00
|
|
|
else:
|
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:
|
|
|
|
if not self.isfollower(path):
|
|
|
|
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-03-05 08:26:58 +01:00
|
|
|
if 'value' in self.temp.get(path, {}):
|
2019-04-03 19:42:59 +02:00
|
|
|
value = self.temp[path]
|
2019-03-05 08:26:58 +01:00
|
|
|
else:
|
2019-04-03 19:42:59 +02:00
|
|
|
value = self.model.get(path, {})
|
|
|
|
index = str(index)
|
|
|
|
if self.is_hidden(path, index):
|
|
|
|
raise PropertiesOptionError(None, None, None, opt_type='option')
|
2019-05-08 20:15:46 +02:00
|
|
|
if '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
|
|
|
|
elif last_body['index'] == index:
|
|
|
|
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-03-16 22:51:39 +01:00
|
|
|
if action != 'delete' and value is not None:
|
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
|
|
|
|
|
|
|
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:
|
2018-12-24 09:28:44 +01:00
|
|
|
if 'pattern' in self.form.get(path, {}) and (not isinstance(value, list) or undefined not in value):
|
|
|
|
match = self.test_value(path, value, remote)
|
|
|
|
else:
|
|
|
|
match = True
|
|
|
|
if match:
|
|
|
|
if remote:
|
2019-02-28 22:12:46 +01:00
|
|
|
self.updates_data(self.send_data({'updates': self.updates,
|
2019-04-03 19:42:59 +02:00
|
|
|
'model': self.model}))
|
2019-02-28 22:12:46 +01:00
|
|
|
|
2018-12-24 09:28:44 +01:00
|
|
|
else:
|
2019-03-16 22:51:39 +01:00
|
|
|
if action == 'delete':
|
|
|
|
if index is None:
|
2019-04-03 19:42:59 +02:00
|
|
|
# leader or standard option
|
|
|
|
# set value to default value
|
2019-03-16 22:51:39 +01:00
|
|
|
value = self.default_value(path)
|
2019-05-08 20:15:46 +02:00
|
|
|
self._set_temp_value(path, None, value, 'default')
|
2019-03-16 22:51:39 +01:00
|
|
|
if self.option(path).option.isleader():
|
2019-04-03 19:42:59 +02:00
|
|
|
# if leader, set follower to default value
|
2019-03-16 22:51:39 +01:00
|
|
|
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:]:
|
2019-04-03 19:42:59 +02:00
|
|
|
# delete all values
|
2019-05-08 20:15:46 +02:00
|
|
|
self._del_temp_value(follower, None)
|
|
|
|
value = self.get_value(path)
|
2019-03-16 22:51:39 +01:00
|
|
|
elif self.option(path).option.isleader():
|
2019-04-03 19:42:59 +02:00
|
|
|
# if remove an indexed leader value
|
2019-05-08 20:15:46 +02:00
|
|
|
leader_value = self.option(path).value.get()
|
|
|
|
leader_value.pop(index)
|
|
|
|
max_value = len(leader_value)
|
|
|
|
self._set_temp_value(path, None, leader_value, 'tmp')
|
2019-03-16 22:51:39 +01:00
|
|
|
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:]:
|
2019-04-03 19:42:59 +02:00
|
|
|
# remove value for this index and reduce len
|
2019-05-08 20:15:46 +02:00
|
|
|
new_temp = {}
|
|
|
|
for idx in range(max_value):
|
|
|
|
if index == idx:
|
|
|
|
continue
|
|
|
|
cur_index = idx
|
|
|
|
if index < cur_index:
|
|
|
|
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:
|
|
|
|
new_temp[str(cur_index)] = self.temp[follower][str(idx)]
|
|
|
|
elif self.model.get(follower, {}).get(str(idx)) is not None:
|
|
|
|
new_temp[str(cur_index)] = self.model[follower][str(idx)]
|
|
|
|
if self.model[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
|
|
|
else:
|
2019-04-03 19:42:59 +02:00
|
|
|
# it's a follower with index
|
|
|
|
self.temp.setdefault(path, {})[str(index)] = {'delete': True}
|
2019-05-08 20:15:46 +02:00
|
|
|
self._del_temp_value(path, index)
|
|
|
|
value = self.get_value(path, index)
|
|
|
|
default_value = True
|
2019-03-16 22:51:39 +01:00
|
|
|
elif index is None:
|
2019-04-03 19:42:59 +02:00
|
|
|
# set a value for a not follower option
|
2019-05-08 20:15:46 +02:00
|
|
|
if default_value is True:
|
|
|
|
self.model[path]['value'] = value
|
|
|
|
else:
|
|
|
|
self._set_temp_value(path, None, value, 'tmp')
|
2019-03-16 22:51:39 +01:00
|
|
|
else:
|
2019-04-03 19:42:59 +02:00
|
|
|
# set a value for a follower option
|
|
|
|
self.temp.setdefault(path, {})[str(index)] = {'value': value, 'owner': 'tmp'}
|
2019-05-08 20:15:46 +02:00
|
|
|
if default_value is True:
|
|
|
|
self.model[path][str(index)]['value'] = value
|
|
|
|
else:
|
|
|
|
self._set_temp_value(path, index, value, 'tmp')
|
|
|
|
self.set_dependencies(path, value, index=index)
|
2018-12-24 09:28:44 +01:00
|
|
|
self.set_not_equal(path, value)
|
|
|
|
self.do_copy(path, value)
|
2019-05-08 20:15:46 +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()
|
|
|
|
follower_value = self.option(follower_path, index).value.get()
|
|
|
|
self.set_dependencies(follower_path, follower_value, None, index)
|
|
|
|
|
|
|
|
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):
|
|
|
|
self.updates = []
|
|
|
|
self.temp.clear()
|
2019-04-03 19:42:59 +02:00
|
|
|
self.model = data['model']
|
2019-02-28 22:12:46 +01:00
|
|
|
|
2018-12-24 09:28:44 +01:00
|
|
|
def test_value(self,
|
|
|
|
path: str,
|
|
|
|
value: Any,
|
|
|
|
remote: bool):
|
|
|
|
if isinstance(value, list):
|
|
|
|
for val in value:
|
|
|
|
if not self.test_value(path, val, remote):
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
if value is None:
|
|
|
|
match = True
|
|
|
|
else:
|
2019-05-09 20:16:57 +02:00
|
|
|
if isinstance(value, int):
|
|
|
|
value = str(value)
|
2018-12-24 09:28:44 +01:00
|
|
|
match = self.form[path]['pattern'].search(value)
|
|
|
|
if not remote:
|
|
|
|
if not match:
|
|
|
|
self.temp.setdefault(path, {})['error'] = ['']
|
|
|
|
elif 'error' in self.model[path]:
|
|
|
|
del self.temp[path]['error']
|
|
|
|
return match
|
|
|
|
|
|
|
|
def set_dependencies(self,
|
|
|
|
path: str,
|
2019-02-05 07:01:22 +01:00
|
|
|
ori_value: Any,
|
2019-05-08 20:15:46 +02:00
|
|
|
force_hide: bool=False,
|
|
|
|
index: Optional[int]=None) -> None:
|
2018-12-24 09:28:44 +01:00
|
|
|
dependencies = self.form.get(path, {}).get('dependencies', {})
|
|
|
|
if dependencies:
|
2019-02-05 07:01:22 +01:00
|
|
|
if ori_value in dependencies['expected']:
|
|
|
|
expected = dependencies['expected'][ori_value]
|
2018-12-24 09:28:44 +01:00
|
|
|
else:
|
|
|
|
expected = dependencies['default']
|
|
|
|
for action in ['hide', 'show']:
|
|
|
|
expected_actions = expected.get(action)
|
|
|
|
if expected_actions:
|
2019-01-12 17:33:26 +01:00
|
|
|
if force_hide:
|
|
|
|
hidden = True
|
|
|
|
else:
|
|
|
|
hidden = action == 'hide'
|
2018-12-24 09:28:44 +01:00
|
|
|
for expected_path in expected_actions:
|
2019-05-08 20:15:46 +02:00
|
|
|
if index is not None:
|
|
|
|
self.temp.setdefault(expected_path, {}).setdefault(str(index), {})['hidden'] = hidden
|
2019-01-12 17:33:26 +01:00
|
|
|
else:
|
2019-05-08 20:15:46 +02:00
|
|
|
self.temp.setdefault(expected_path, {})['hidden'] = hidden
|
|
|
|
value = self.get_value(expected_path, index)
|
|
|
|
self.set_dependencies(expected_path, value, hidden, index)
|
2018-12-24 09:28:44 +01:00
|
|
|
|
|
|
|
def set_not_equal(self,
|
|
|
|
path: str,
|
|
|
|
value: Any) -> None:
|
|
|
|
not_equal = self.form.get(path, {}).get('not_equal', {})
|
|
|
|
if not_equal:
|
|
|
|
vals = []
|
|
|
|
opts = []
|
|
|
|
if isinstance(value, list):
|
|
|
|
for val in value:
|
|
|
|
vals.append(val)
|
|
|
|
opts.append(path)
|
|
|
|
else:
|
|
|
|
vals.append(value)
|
|
|
|
opts.append(path)
|
|
|
|
for path_ in self.form[path]['not_equal']['options']:
|
|
|
|
schema = self.get_schema(path_)
|
2019-02-14 21:25:00 +01:00
|
|
|
p_value = self.get_value(path_)
|
2018-12-24 09:28:44 +01:00
|
|
|
if isinstance(p_value, list):
|
|
|
|
for val in p_value:
|
|
|
|
vals.append(val)
|
|
|
|
opts.append(path_)
|
|
|
|
else:
|
|
|
|
vals.append(p_value)
|
|
|
|
opts.append(path_)
|
|
|
|
equal = []
|
|
|
|
warnings_only = self.form[path]['not_equal'].get('warnings', False)
|
|
|
|
if warnings_only:
|
|
|
|
msg = _('should be different from the value of "{}"')
|
|
|
|
msgcurr = _('value for {} should be different')
|
|
|
|
else:
|
|
|
|
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 = {}
|
|
|
|
for opt in equal:
|
|
|
|
schema = self.get_schema(opt)
|
|
|
|
equal_name[opt] = schema['title']
|
|
|
|
for opt_ in equal:
|
|
|
|
display_equal = []
|
|
|
|
for opt__ in equal:
|
|
|
|
if opt_ != opt__:
|
|
|
|
display_equal.append(equal_name[opt_])
|
|
|
|
display_equal = ', '.join(display_equal)
|
|
|
|
if opt_ == path:
|
|
|
|
msg_ = msgcurr.format(display_equal)
|
|
|
|
else:
|
|
|
|
msg_ = msg.format(display_equal)
|
|
|
|
if warnings_only:
|
|
|
|
self.model[opt_].setdefault('warnings', []).append(msg_)
|
|
|
|
else:
|
|
|
|
self.model[opt_].setdefault('error', []).append(msg_)
|
|
|
|
else:
|
|
|
|
for opt in opts:
|
|
|
|
if 'warnings' in self.model[opt]:
|
|
|
|
del self.model[opt]['warnings']
|
|
|
|
if 'error' in self.model[opt]:
|
|
|
|
del self.model[opt]['error']
|
|
|
|
|
|
|
|
def do_copy(self,
|
|
|
|
path: str,
|
|
|
|
value: Any) -> None:
|
|
|
|
copy = self.form.get(path, {}).get('copy')
|
|
|
|
if copy:
|
|
|
|
for opt in copy:
|
2019-03-05 08:26:58 +01:00
|
|
|
# FIXME follower!
|
|
|
|
owner = self.get_owner(opt, None)
|
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)
|
|
|
|
self.updates_value('modify', opt, None, value, remote, True)
|
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')
|