rougail/src/rougail/annotator/family.py

292 lines
12 KiB
Python
Raw Normal View History

2021-01-10 22:07:29 +01:00
"""Annotate family
2021-01-30 08:15:26 +01:00
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
2021-01-10 22:07:29 +01:00
"""
2021-04-12 15:04:38 +02:00
from rougail.i18n import _
from rougail.error import DictConsistencyError
from rougail.annotator.variable import Walk
2020-12-24 07:39:51 +01:00
2021-01-19 19:28:29 +01:00
class Mode: # pylint: disable=R0903
2021-01-10 22:07:29 +01:00
"""Class to manage mode level
"""
def __init__(self,
level: int,
) -> None:
2020-12-24 07:39:51 +01:00
self.level = level
2021-01-10 22:07:29 +01:00
def __gt__(self,
other: int,
) -> bool:
return other.level < self.level
2020-12-24 07:39:51 +01:00
2021-04-12 15:04:38 +02:00
class Annotator(Walk):
2021-01-10 22:07:29 +01:00
"""Annotate family
"""
2021-04-12 15:04:38 +02:00
level = 80
2020-12-24 07:39:51 +01:00
def __init__(self,
objectspace,
2021-04-12 15:04:38 +02:00
*args,
2020-12-24 07:39:51 +01:00
):
self.objectspace = objectspace
2021-01-23 21:15:26 +01:00
if not hasattr(self.objectspace.space, 'variables'):
return
2021-02-22 19:28:51 +01:00
self.modes = {name: Mode(idx) for idx, name in enumerate(self.objectspace.rougailconfig['modes_level'])}
2021-01-23 21:15:26 +01:00
self.remove_empty_families()
2021-01-26 13:42:48 +01:00
self.family_names()
self.change_modes()
2021-01-23 21:15:26 +01:00
self.dynamic_families()
self.convert_help()
2021-02-12 07:48:27 +01:00
def _has_variable(self,
family: 'self.objectspace.family',
) -> bool:
if hasattr(family, 'variable'):
for variable in family.variable.values():
if isinstance(variable, self.objectspace.family):
if self._has_variable(variable):
return True
else:
return True
return False
2021-01-26 13:42:48 +01:00
def remove_empty_families(self) -> None:
"""Remove all families without any variable
"""
2021-04-12 15:04:38 +02:00
#FIXME pas sous family
2021-01-26 13:42:48 +01:00
for families in self.objectspace.space.variables.values():
removed_families = []
2021-02-12 07:48:27 +01:00
for family_name, family in families.variable.items():
if isinstance(family, self.objectspace.family) and not self._has_variable(family):
2021-01-26 13:42:48 +01:00
removed_families.append(family_name)
for family_name in removed_families:
2021-02-12 07:48:27 +01:00
del families.variable[family_name]
2021-01-26 13:42:48 +01:00
2021-01-23 21:15:26 +01:00
def family_names(self) -> None:
2021-01-23 21:21:45 +01:00
"""Set doc, path, ... to family
"""
2021-02-12 07:48:27 +01:00
for family in self.get_families():
if not hasattr(family, 'description'):
family.description = family.name
family.doc = family.description
del family.description
2021-01-23 21:15:26 +01:00
2021-01-26 13:42:48 +01:00
def change_modes(self):
2021-01-10 22:07:29 +01:00
"""change the mode of variables
"""
2021-02-22 19:28:51 +01:00
modes_level = self.objectspace.rougailconfig['modes_level']
default_variable_mode = self.objectspace.rougailconfig['default_variable_mode']
if default_variable_mode not in modes_level:
msg = _(f'default variable mode "{default_variable_mode}" is not a valid mode, '
f'valid modes are {modes_level}')
raise DictConsistencyError(msg, 72, None)
default_family_mode = self.objectspace.rougailconfig['default_family_mode']
if default_family_mode not in modes_level:
msg = _(f'default family mode "{default_family_mode}" is not a valid mode, '
f'valid modes are {modes_level}')
raise DictConsistencyError(msg, 73, None)
2021-02-17 22:32:28 +01:00
families = list(self.get_families())
for family in families:
2021-02-22 19:28:51 +01:00
self.valid_mode(family)
2021-02-17 22:32:28 +01:00
self._set_default_mode(family)
families.reverse()
for family in families:
self._change_family_mode(family)
2021-02-22 19:28:51 +01:00
def valid_mode(self,
obj,
) -> None:
modes_level = self.objectspace.rougailconfig['modes_level']
2021-04-14 17:34:38 +02:00
if self._has_mode(obj) and obj.mode not in modes_level:
2021-02-22 19:28:51 +01:00
msg = _(f'mode "{obj.mode}" for "{obj.name}" is not a valid mode, '
f'valid modes are {modes_level}')
raise DictConsistencyError(msg, 71, obj.xmlfiles)
2021-02-17 22:32:28 +01:00
def _set_default_mode(self,
family: 'self.objectspace.family',
) -> None:
2021-04-14 17:34:38 +02:00
if not hasattr(family, 'variable'):
return
if self._has_mode(family):
2021-02-12 07:48:27 +01:00
family_mode = family.mode
else:
2021-02-17 22:32:28 +01:00
family_mode = None
2021-04-14 17:34:38 +02:00
leader = None
2021-02-17 22:32:28 +01:00
for variable in family.variable.values():
2021-04-14 17:34:38 +02:00
if leader is None and hasattr(family, 'leadership') and family.leadership:
leader = variable
2021-02-17 22:32:28 +01:00
if isinstance(variable, self.objectspace.family):
2021-04-14 17:34:38 +02:00
# set default mode a subfamily
if family_mode and not self._has_mode(variable):
self._set_auto_mode(variable, family_mode)
2021-02-17 22:32:28 +01:00
else:
2021-04-14 17:34:38 +02:00
# set default mode to a variable
self.valid_mode(variable)
if leader:
self._set_default_mode_leader(leader, variable)
self._set_default_mode_variable(variable, family_mode)
if leader:
# here because follower can change leader mode
self._set_auto_mode(family, leader.mode)
2021-01-10 22:07:29 +01:00
2021-02-17 22:32:28 +01:00
@staticmethod
2021-02-18 14:02:07 +01:00
def _has_mode(obj) -> bool:
return 'mode' in vars(obj) and not hasattr(obj, 'mode_auto')
def _set_default_mode_variable(self,
variable: 'self.objectspace.variable',
family_mode: str,
) -> None:
2021-01-10 22:07:29 +01:00
# auto_save or auto_freeze variable is set to 'basic' mode
# if its mode is not defined by the user
2021-02-18 14:02:07 +01:00
if not self._has_mode(variable) and \
2021-01-10 22:07:29 +01:00
(variable.auto_save is True or variable.auto_freeze is True):
2021-02-22 19:28:51 +01:00
variable.mode = self.objectspace.rougailconfig['modes_level'][0]
2021-01-26 13:42:48 +01:00
# mandatory variable without value is a basic variable
2021-02-19 17:06:49 +01:00
elif not self._has_mode(variable) and \
variable.mandatory is True and \
2021-02-18 14:02:07 +01:00
not hasattr(variable, 'default') and \
not hasattr(variable, 'default_multi'):
2021-02-22 19:28:51 +01:00
variable.mode = self.objectspace.rougailconfig['modes_level'][0]
2021-02-18 14:02:07 +01:00
elif family_mode and not self._has_mode(variable):
self._set_auto_mode(variable, family_mode)
@staticmethod
def _set_auto_mode(obj, mode: str) -> None:
obj.mode = mode
obj.mode_auto = True
2021-01-10 22:07:29 +01:00
2021-02-17 22:32:28 +01:00
def _set_default_mode_leader(self,
2021-04-14 17:34:38 +02:00
leader: 'self.objectspace.variable',
follower: 'self.objectspace.variable',
2021-02-17 22:32:28 +01:00
) -> None:
2021-04-14 17:34:38 +02:00
if follower.auto_save is True:
msg = _(f'leader/followers "{follower.name}" could not be auto_save')
raise DictConsistencyError(msg, 29, follower.xmlfiles)
if follower.auto_freeze is True:
msg = f'leader/followers "{follower.name}" could not be auto_freeze'
raise DictConsistencyError(_(msg), 30, follower.xmlfiles)
if leader == follower:
# it's a leader
if not hasattr(leader, 'mode'):
self._set_auto_mode(leader, self.objectspace.rougailconfig['default_variable_mode'])
return
if self._has_mode(follower):
follower_mode = follower.mode
else:
follower_mode = self.objectspace.rougailconfig['default_variable_mode']
if self.modes[leader.mode] > self.modes[follower_mode]:
if self._has_mode(follower) and not self._has_mode(leader):
# if follower has mode but not the leader
self._set_auto_mode(leader, follower_mode)
else:
# leader's mode is minimum level
if self._has_mode(follower):
msg = _(f'the follower "{follower.name}" is in "{follower_mode}" mode '
f'but leader have the higher mode "{leader.mode}"')
raise DictConsistencyError(msg, 63, follower.xmlfiles)
self._set_auto_mode(follower, leader.mode)
2021-01-10 22:07:29 +01:00
2021-02-17 22:32:28 +01:00
def _change_family_mode(self,
2021-04-14 17:34:38 +02:00
family: 'self.objectspace.family',
) -> None:
2021-02-17 22:32:28 +01:00
if hasattr(family, 'mode'):
family_mode = family.mode
else:
2021-02-22 19:28:51 +01:00
family_mode = self.objectspace.rougailconfig['default_family_mode']
min_variable_mode = self.objectspace.rougailconfig['modes_level'][-1]
2021-02-17 22:32:28 +01:00
# change variable mode, but not if variables are not in a family
2021-04-14 17:34:38 +02:00
is_leadership = hasattr(family, 'leadership') and family.leadership
2021-02-17 22:32:28 +01:00
if hasattr(family, 'variable'):
2021-04-14 17:34:38 +02:00
for idx, variable in enumerate(family.variable.values()):
if isinstance(variable, self.objectspace.family):
2021-04-14 17:34:38 +02:00
if not hasattr(variable, 'mode'):
variable.mode = self.objectspace.rougailconfig['default_family_mode']
elif idx == 0 and is_leadership:
variable.mode = None
continue
else:
2021-04-14 17:34:38 +02:00
self._change_variable_mode(variable, family_mode, is_leadership)
2021-02-22 19:28:51 +01:00
if self.modes[min_variable_mode] > self.modes[variable.mode]:
2021-02-17 22:32:28 +01:00
min_variable_mode = variable.mode
2021-04-14 17:34:38 +02:00
if not isinstance(family, self.objectspace.family) or is_leadership:
# it's Variable, Service, ... and leadership
return
if not hasattr(family, 'mode'):
2021-02-17 22:32:28 +01:00
# set the lower variable mode to family
2021-02-18 14:02:07 +01:00
self._set_auto_mode(family, min_variable_mode)
2021-04-14 17:34:38 +02:00
if family.mode != min_variable_mode:
msg = _(f'the family "{family.name}" is in "{family.mode}" mode but variables and '
f'families inside have the higher modes "{min_variable_mode}"')
raise DictConsistencyError(msg, 62, family.xmlfiles)
2021-02-17 22:32:28 +01:00
def _change_variable_mode(self,
variable,
family_mode: str,
2021-04-14 17:34:38 +02:00
is_follower: bool,
2021-02-17 22:32:28 +01:00
) -> None:
2021-02-22 19:28:51 +01:00
if hasattr(variable, 'mode'):
variable_mode = variable.mode
else:
variable_mode = self.objectspace.rougailconfig['default_variable_mode']
2021-02-17 22:32:28 +01:00
# none basic variable in high level family has to be in high level
2021-04-14 17:34:38 +02:00
if not is_follower and self.modes[variable_mode] < self.modes[family_mode]:
2021-02-18 14:02:07 +01:00
if self._has_mode(variable):
2021-02-22 19:28:51 +01:00
msg = _(f'the variable "{variable.name}" is in "{variable_mode}" mode '
2021-02-18 14:02:07 +01:00
f'but family has the higher family mode "{family_mode}"')
raise DictConsistencyError(msg, 61, variable.xmlfiles)
self._set_auto_mode(variable, family_mode)
2021-02-22 19:28:51 +01:00
if not hasattr(variable, 'mode'):
variable.mode = variable_mode
2021-02-17 22:32:28 +01:00
2021-01-10 22:07:29 +01:00
def dynamic_families(self):
"""link dynamic families to object
"""
2021-02-12 07:48:27 +01:00
for family in self.get_families():
if 'dynamic' not in vars(family):
continue
2022-03-05 11:13:05 +01:00
family.suffixes = self.objectspace.paths.get_variable(family.dynamic, family.xmlfiles)
2021-02-12 07:48:27 +01:00
del family.dynamic
if not family.suffixes.multi:
msg = _(f'dynamic family "{family.name}" must be linked '
f'to multi variable')
raise DictConsistencyError(msg, 16, family.xmlfiles)
for variable in family.variable.values():
if isinstance(variable, self.objectspace.family) and not variable.leadership:
2021-02-12 07:48:27 +01:00
msg = _(f'dynamic family "{family.name}" cannot contains another family')
2021-02-14 10:10:48 +01:00
raise DictConsistencyError(msg, 22, family.xmlfiles)
2021-01-23 11:57:46 +01:00
def convert_help(self):
"""Convert variable help
"""
2021-02-12 07:48:27 +01:00
for family in self.get_families():
if not hasattr(family, 'help'):
continue
if not hasattr(family, 'information'):
family.information = self.objectspace.information(family.xmlfiles)
family.information.help = family.help
del family.help