228 lines
9.5 KiB
Python
228 lines
9.5 KiB
Python
"""Annotate check
|
|
|
|
Created by:
|
|
EOLE (http://eole.orion.education.fr)
|
|
Copyright (C) 2005-2018
|
|
|
|
Forked by:
|
|
Cadoles (http://www.cadoles.com)
|
|
Copyright (C) 2019-2021
|
|
|
|
distribued with GPL-2 or later license
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
"""
|
|
from typing import List, Any
|
|
|
|
from .target import TargetAnnotator
|
|
from .param import ParamAnnotator
|
|
|
|
from ..utils import load_modules
|
|
from ..i18n import _
|
|
from ..error import DictConsistencyError, display_xmlfiles
|
|
|
|
INTERNAL_FUNCTIONS = ['valid_enum', 'valid_in_network', 'valid_differ', 'valid_entier']
|
|
|
|
class CheckAnnotator(TargetAnnotator, ParamAnnotator):
|
|
"""Annotate check
|
|
"""
|
|
def __init__(self,
|
|
objectspace,
|
|
eosfunc_file,
|
|
):
|
|
if not hasattr(objectspace.space, 'constraints') or \
|
|
not hasattr(objectspace.space.constraints, 'check'):
|
|
return
|
|
self.objectspace = objectspace
|
|
self.let_none = True
|
|
self.only_variable = True
|
|
self.functions = dir(load_modules(eosfunc_file))
|
|
self.functions.extend(INTERNAL_FUNCTIONS)
|
|
self.functions.extend(self.objectspace.rougailconfig['internal_functions'])
|
|
self.target_is_uniq = False
|
|
self.convert_target(self.objectspace.space.constraints.check)
|
|
self.convert_param(self.objectspace.space.constraints.check)
|
|
self.check_check()
|
|
self.check_valid_enum()
|
|
self.check_change_warning()
|
|
self.convert_valid_entier()
|
|
self.convert_check()
|
|
del objectspace.space.constraints.check
|
|
|
|
def valid_type_validation(self,
|
|
obj,
|
|
) -> None:
|
|
variable_type = None
|
|
if obj.name == 'valid_enum':
|
|
for target in obj.target:
|
|
variable_type = target.name.type
|
|
return variable_type
|
|
|
|
def check_check(self): # pylint: disable=R0912
|
|
"""valid and manage <check>
|
|
"""
|
|
remove_indexes = []
|
|
for check_idx, check in enumerate(self.objectspace.space.constraints.check):
|
|
if not check.name in self.functions:
|
|
msg = _(f'cannot find check function "{check.name}"')
|
|
raise DictConsistencyError(msg, 1, check.xmlfiles)
|
|
if hasattr(check, 'param') and check.param == []:
|
|
remove_indexes.append(check_idx)
|
|
remove_indexes.sort(reverse=True)
|
|
for idx in remove_indexes:
|
|
del self.objectspace.space.constraints.check[idx]
|
|
|
|
def check_valid_enum(self):
|
|
"""verify valid_enum
|
|
"""
|
|
remove_indexes = []
|
|
for idx, check in enumerate(self.objectspace.space.constraints.check):
|
|
if check.name != 'valid_enum':
|
|
continue
|
|
for target in check.target:
|
|
if target.name.path in self.objectspace.valid_enums:
|
|
check_xmlfiles = display_xmlfiles(self.objectspace.valid_enums\
|
|
[target.name.path]['xmlfiles'])
|
|
msg = _(f'valid_enum already set in {check_xmlfiles} '
|
|
f'for "{target.name.name}", you may have forget remove_check')
|
|
raise DictConsistencyError(msg, 3, check.xmlfiles)
|
|
if not hasattr(check, 'param'):
|
|
msg = _(f'param is mandatory for a valid_enum of variable "{target.name.name}"')
|
|
raise DictConsistencyError(msg, 4, check.xmlfiles)
|
|
variable_type = target.name.type
|
|
values = self._set_valid_enum(target.name,
|
|
check,
|
|
)
|
|
if values:
|
|
if hasattr(target.name, 'value'):
|
|
# check value
|
|
self.check_valid_enum_value(target.name, values)
|
|
else:
|
|
# no value, set the first choice as default value
|
|
new_value = self.objectspace.value(check.xmlfiles)
|
|
new_value.name = values[0]
|
|
new_value.type = variable_type
|
|
target.name.value = [new_value]
|
|
remove_indexes.append(idx)
|
|
remove_indexes.sort(reverse=True)
|
|
for idx in remove_indexes:
|
|
del self.objectspace.space.constraints.check[idx]
|
|
|
|
def _set_valid_enum(self,
|
|
variable,
|
|
check,
|
|
) -> List[Any]:
|
|
# build choice
|
|
variable.values = []
|
|
variable.ori_type = variable.type
|
|
variable.type = 'choice'
|
|
|
|
has_variable = False
|
|
values = []
|
|
has_nil = False
|
|
for param in check.param:
|
|
if has_variable:
|
|
msg = _(f'only one "variable" parameter is allowed for valid_enum '
|
|
f'of variable "{variable.name}"')
|
|
raise DictConsistencyError(msg, 5, param.xmlfiles)
|
|
param_type = variable.ori_type
|
|
if param.type == 'variable':
|
|
has_variable = True
|
|
if param.optional is True:
|
|
msg = _(f'optional parameter in valid_enum for variable "{variable.name}" '
|
|
f'is not allowed')
|
|
raise DictConsistencyError(msg, 14, param.xmlfiles)
|
|
if not param.text.multi:
|
|
msg = _(f'only multi "variable" parameter is allowed for valid_enum '
|
|
f'of variable "{variable.name}"')
|
|
raise DictConsistencyError(msg, 6, param.xmlfiles)
|
|
param_type = 'calculation'
|
|
elif param.type == 'nil':
|
|
has_nil = True
|
|
values.append(param.text)
|
|
choice = self.objectspace.choice(variable.xmlfiles)
|
|
choice.name = param.text
|
|
choice.type = param_type
|
|
variable.values.append(choice)
|
|
if 'mandatory' not in vars(variable):
|
|
variable.mandatory = not has_nil
|
|
elif variable.mandatory is False:
|
|
choice = self.objectspace.choice(variable.xmlfiles)
|
|
choice.name = None
|
|
choice.type = 'nil'
|
|
variable.values.append(choice)
|
|
if has_variable:
|
|
return None
|
|
self.objectspace.valid_enums[variable.path] = {'type': variable.ori_type,
|
|
'values': values,
|
|
'xmlfiles': check.xmlfiles,
|
|
}
|
|
return values
|
|
|
|
@staticmethod
|
|
def check_valid_enum_value(variable,
|
|
values,
|
|
) -> None:
|
|
"""check that values in valid_enum are valid
|
|
"""
|
|
for value in variable.value:
|
|
if value.name not in values:
|
|
msg = _(f'value "{value.name}" of variable "{variable.name}" is not in list '
|
|
f'of all expected values ({values})')
|
|
raise DictConsistencyError(msg, 15, value.xmlfiles)
|
|
|
|
def check_change_warning(self):
|
|
"""convert level to "warnings_only"
|
|
"""
|
|
for check in self.objectspace.space.constraints.check:
|
|
check.warnings_only = check.level == 'warning'
|
|
check.level = None
|
|
|
|
def convert_valid_entier(self) -> None:
|
|
"""valid and manage <check>
|
|
"""
|
|
remove_indexes = []
|
|
for check_idx, check in enumerate(self.objectspace.space.constraints.check):
|
|
if not check.name == 'valid_entier':
|
|
continue
|
|
remove_indexes.append(check_idx)
|
|
if not hasattr(check, 'param'):
|
|
msg = _(f'{check.name} must have, at least, 1 param')
|
|
raise DictConsistencyError(msg, 17, check.xmlfiles)
|
|
for param in check.param:
|
|
if param.type != 'number':
|
|
msg = _(f'param in "valid_entier" must be an "integer", not "{param.type}"')
|
|
raise DictConsistencyError(msg, 18, check.xmlfiles)
|
|
for target in check.target:
|
|
if param.name == 'mini':
|
|
target.name.min_number = int(param.text)
|
|
elif param.name == 'maxi':
|
|
target.name.max_number = int(param.text)
|
|
else:
|
|
msg = _(f'unknown parameter "{param.name}" in check "valid_entier"')
|
|
raise DictConsistencyError(msg, 19, check.xmlfiles)
|
|
remove_indexes.sort(reverse=True)
|
|
for idx in remove_indexes:
|
|
del self.objectspace.space.constraints.check[idx]
|
|
|
|
def convert_check(self) -> None:
|
|
"""valid and manage <check>
|
|
"""
|
|
for check in self.objectspace.space.constraints.check:
|
|
for target in check.target:
|
|
if not hasattr(target.name, 'validators'):
|
|
target.name.validators = []
|
|
target.name.validators.append(check)
|