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 11KB

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