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

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