No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

model.py 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. #-*- coding:utf-8 -*-
  2. import hashlib
  3. import importlib
  4. import copy
  5. from lodel.utils.mlstring import MlString
  6. from lodel import logger
  7. from lodel.settings import Settings
  8. from lodel.settings.utils import SettingsError
  9. from lodel.editorial_model.exceptions import *
  10. from lodel.editorial_model.components import EmClass, EmField, EmGroup
  11. ##@brief Describe an editorial model
  12. #@ingroup lodel2_em
  13. class EditorialModel(object):
  14. ##@brief Create a new editorial model
  15. # @param name MlString|str|dict : the editorial model name
  16. # @param description MlString|str|dict : the editorial model description
  17. def __init__(self, name, description = None):
  18. self.name = MlString(name)
  19. self.description = MlString(description)
  20. ##@brief Stores all groups indexed by id
  21. self.__groups = dict()
  22. ##@brief Stores all classes indexed by id
  23. self.__classes = dict()
  24. ## @brief Stores all activated groups indexed by id
  25. self.__active_groups = dict()
  26. ## @brief Stores all activated classes indexed by id
  27. self.__active_classes = dict()
  28. self.__set_actives()
  29. ##@brief EmClass uids accessor
  30. #@return a dict of emclasses
  31. def all_classes(self, uid = None):
  32. if uid is None:
  33. return copy.copy(self.__classes)
  34. else:
  35. try:
  36. return copy.copy(self.__classes[uid])
  37. except KeyError:
  38. raise EditorialModelException("EmClass not found : '%s'" % uid)
  39. def all_classes_ref(self, uid = None):
  40. if uid is None:
  41. return self.__classes
  42. else:
  43. try:
  44. return self.__classes[uid]
  45. except KeyError:
  46. raise EditorialModelException("EmGroup not found : '%s'" % uid)
  47. ##@brief active EmClass uids accessor
  48. #@return a list of class uids
  49. def active_classes_uids(self):
  50. return list(self.__active_classes.keys())
  51. ##@brief EmGroups accessor
  52. #@return a dict of groups
  53. def all_groups(self, uid = None):
  54. if uid is None:
  55. return copy.copy(self.__groups)
  56. else:
  57. try:
  58. return copy.copy(self.__groups[uid])
  59. except KeyError:
  60. raise EditorialModelException("EmGroup not found : '%s'" % uid)
  61. ##@brief EmGroups accessor
  62. #@return a dict of groups
  63. def all_groups_ref(self, uid = None):
  64. if uid is None:
  65. return self.__groups
  66. else:
  67. try:
  68. return self.__groups[uid]
  69. except KeyError:
  70. raise EditorialModelException("EmGroup not found : '%s'" % uid)
  71. ##@brief active EmClass uids accessor
  72. #@return a list of class uids
  73. def active_groups_uids(self):
  74. return list(self.__active_groups.keys())
  75. ##@brief EmClass accessor
  76. #@param uid None | str : give this argument to get a specific EmClass
  77. #@return if uid is given returns an EmClass else returns an EmClass
  78. # iterator
  79. #@todo use Settings.editorialmodel.groups to determine wich classes should
  80. # be returned
  81. def classes(self, uid = None):
  82. try:
  83. return self.__elt_getter( self.__active_classes,
  84. uid)
  85. except KeyError:
  86. raise EditorialModelException("EmClass not found : '%s'" % uid)
  87. ##@brief EmClass child list accessor
  88. #@param uid str : the EmClass uid
  89. #@return a set of EmClass
  90. def get_class_childs(self, uid):
  91. res = list()
  92. cur = self.classes(uid)
  93. for cls in self.classes():
  94. if cur in cls.parents_recc:
  95. res.append(cls)
  96. return set(res)
  97. ##@brief EmGroup getter
  98. # @param uid None | str : give this argument to get a specific EmGroup
  99. # @return if uid is given returns an EmGroup else returns an EmGroup iterator
  100. def groups(self, uid = None):
  101. try:
  102. return self.__elt_getter( self.__active_groups,
  103. uid)
  104. except KeyError:
  105. raise EditorialModelException("EmGroup not found : '%s'" % uid)
  106. ##@brief Private getter for __groups or __classes
  107. # @see classes() groups()
  108. def __elt_getter(self, elts, uid):
  109. return list(elts.values()) if uid is None else elts[uid]
  110. ##@brief Update the EditorialModel.__active_groups and
  111. #EditorialModel.__active_classes attibutes
  112. def __set_actives(self):
  113. if Settings.editorialmodel.editormode:
  114. logger.warning("All EM groups active because editormode in ON")
  115. # all groups & classes actives because we are in editor mode
  116. self.__active_groups = self.__groups
  117. self.__active_classes = self.__classes
  118. else:
  119. #determine groups first
  120. self.__active_groups = dict()
  121. self.__active_classes = dict()
  122. for agrp in Settings.editorialmodel.groups:
  123. if agrp not in self.__groups:
  124. raise SettingsError('Invalid group found in settings : %s' % agrp)
  125. logger.debug("Set group '%s' as active" % agrp)
  126. grp = self.__groups[agrp]
  127. self.__active_groups[grp.uid] = grp
  128. for acls in [cls for cls in grp.components() if isinstance(cls, EmClass)]:
  129. self.__active_classes[acls.uid] = acls
  130. if len(self.__active_groups) == 0:
  131. raise RuntimeError("No groups activated, abording...")
  132. if len(self.__active_classes) == 0:
  133. raise RuntimeError("No active class found. Abording")
  134. for clsname, acls in self.__active_classes.items():
  135. acls._set_active_fields(self.__active_groups)
  136. ##@brief EmField getter
  137. # @param uid str : An EmField uid represented by "CLASSUID.FIELDUID"
  138. # @return Fals or an EmField instance
  139. #
  140. # @todo delete it, useless...
  141. def field(self, uid = None):
  142. spl = uid.split('.')
  143. if len(spl) != 2:
  144. raise ValueError("Malformed EmField identifier : '%s'" % uid)
  145. cls_uid = spl[0]
  146. field_uid = spl[1]
  147. try:
  148. emclass = self.classes(cls_uid)
  149. except KeyError:
  150. return False
  151. try:
  152. return emclass.fields(field_uid)
  153. except KeyError:
  154. pass
  155. return False
  156. ##@brief Add a class to the editorial model
  157. # @param emclass EmClass : the EmClass instance to add
  158. # @return emclass
  159. def add_class(self, emclass):
  160. assert_edit()
  161. if not isinstance(emclass, EmClass):
  162. raise ValueError("<class EmClass> expected but got %s " % type(emclass))
  163. if emclass.uid in self.classes():
  164. raise EditorialModelException('Duplicated uid "%s"' % emclass.uid)
  165. self.__classes[emclass.uid] = emclass
  166. return emclass
  167. ##@brief Add a group to the editorial model
  168. # @param emgroup EmGroup : the EmGroup instance to add
  169. # @return emgroup
  170. def add_group(self, emgroup):
  171. assert_edit()
  172. if not isinstance(emgroup, EmGroup):
  173. raise ValueError("<class EmGroup> expected but got %s" % type(emgroup))
  174. if emgroup.uid in self.groups():
  175. raise EditorialModelException('Duplicated uid "%s"' % emgroup.uid)
  176. self.__groups[emgroup.uid] = emgroup
  177. return emgroup
  178. ##@brief Add a new EmClass to the editorial model
  179. #@param uid str : EmClass uid
  180. #@param **kwargs : EmClass constructor options (
  181. # see @ref lodel.editorial_model.component.EmClass.__init__() )
  182. def new_class(self, uid, **kwargs):
  183. assert_edit()
  184. return self.add_class(EmClass(uid, **kwargs))
  185. ##@brief Add a new EmGroup to the editorial model
  186. #@param uid str : EmGroup uid
  187. #@param *kwargs : EmGroup constructor keywords arguments (
  188. # see @ref lodel.editorial_model.component.EmGroup.__init__() )
  189. def new_group(self, uid, **kwargs):
  190. assert_edit()
  191. return self.add_group(EmGroup(uid, **kwargs))
  192. ##@brief Save a model
  193. # @param translator module : The translator module to use
  194. # @param **translator_args
  195. def save(self, translator, **translator_kwargs):
  196. assert_edit()
  197. if isinstance(translator, str):
  198. translator = self.translator_from_name(translator)
  199. return translator.save(self, **translator_kwargs)
  200. ##@brief Raise an error if lodel is not in EM edition mode
  201. @staticmethod
  202. def raise_if_ro():
  203. if not Settings.editorialmodel.editormode:
  204. raise EditorialModelError("Lodel in not in EM editor mode. The EM is in read only state")
  205. ##@brief Load a model
  206. # @param translator module : The translator module to use
  207. # @param **translator_args
  208. @classmethod
  209. def load(cls, translator, **translator_kwargs):
  210. if isinstance(translator, str):
  211. translator = cls.translator_from_name(translator)
  212. res = translator.load(**translator_kwargs)
  213. res.__set_actives()
  214. return res
  215. ##@brief Return a translator module given a translator name
  216. # @param translator_name str : The translator name
  217. # @return the translator python module
  218. # @throw NameError if the translator does not exists
  219. @staticmethod
  220. def translator_from_name(translator_name):
  221. pkg_name = 'lodel.editorial_model.translator.%s' % translator_name
  222. try:
  223. mod = importlib.import_module(pkg_name)
  224. except ImportError:
  225. raise NameError("No translator named %s")
  226. return mod
  227. ##@brief Lodel hash
  228. def d_hash(self):
  229. payload = "%s%s" % (
  230. self.name,
  231. 'NODESC' if self.description is None else self.description.d_hash()
  232. )
  233. for guid in sorted(self.__groups):
  234. payload += str(self.__groups[guid].d_hash())
  235. for cuid in sorted(self.__classes):
  236. payload += str(self.__classes[cuid].d_hash())
  237. return int.from_bytes(
  238. hashlib.md5(bytes(payload, 'utf-8')).digest(),
  239. byteorder='big'
  240. )