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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. # -*- coding: utf-8 -*-
  2. ## @file editorialmodel.py
  3. # Manage instance of an editorial model
  4. from EditorialModel.migrationhandler.dummy import DummyMigrationHandler
  5. from EditorialModel.classes import EmClass
  6. from EditorialModel.fieldgroups import EmFieldGroup
  7. from EditorialModel.fields import EmField
  8. from EditorialModel.types import EmType
  9. from EditorialModel.exceptions import EmComponentCheckError, EmComponentNotExistError, MigrationHandlerChangeError
  10. import hashlib
  11. ## Manages the Editorial Model
  12. class Model(object):
  13. components_class = [EmClass, EmField, EmFieldGroup, EmType]
  14. ## Constructor
  15. #
  16. # @param backend unknown: A backend object instanciated from one of the classes in the backend module
  17. def __init__(self, backend, migration_handler=None):
  18. self.migration_handler = DummyMigrationHandler() if migration_handler is None else migration_handler
  19. self.backend = backend
  20. self._components = {'uids': {}, 'EmClass': [], 'EmType': [], 'EmField': [], 'EmFieldGroup': []}
  21. self.load()
  22. def __hash__(self):
  23. components_dump = ""
  24. for _, comp in self._components['uids'].items():
  25. components_dump += str(hash(comp))
  26. hashstring = hashlib.new('sha512')
  27. hashstring.update(components_dump.encode('utf-8'))
  28. return int(hashstring.hexdigest(), 16)
  29. def __eq__(self, other):
  30. return self.__hash__() == other.__hash__()
  31. @staticmethod
  32. ## Given a name return an EmComponent child class
  33. # @param class_name str : The name to identify an EmComponent class
  34. # @return A python class or False if the class_name is not a name of an EmComponent child class
  35. def emclass_from_name(class_name):
  36. for cls in Model.components_class:
  37. if cls.__name__ == class_name:
  38. return cls
  39. return False
  40. @staticmethod
  41. ## Given a python class return a name
  42. # @param cls : The python class we want the name
  43. # @return A class name as string or False if cls is not an EmComponent child class
  44. def name_from_emclass(em_class):
  45. if em_class not in Model.components_class:
  46. return False
  47. return em_class.__name__
  48. ## Loads the structure of the Editorial Model
  49. #
  50. # Gets all the objects contained in that structure and creates a dict indexed by their uids
  51. # @todo Change the thrown exception when a components check fails
  52. # @throw ValueError When a component class don't exists
  53. def load(self):
  54. datas = self.backend.load()
  55. for uid, kwargs in datas.items():
  56. #Store and delete the EmComponent class name from datas
  57. cls_name = kwargs['component']
  58. del kwargs['component']
  59. cls = self.emclass_from_name(cls_name)
  60. if cls:
  61. kwargs['uid'] = uid
  62. # create a dict for the component and one indexed by uids, store instanciated component in it
  63. self._components['uids'][uid] = cls(self, **kwargs)
  64. self._components[cls_name].append(self._components['uids'][uid])
  65. else:
  66. raise ValueError("Unknow EmComponent class : '" + cls_name + "'")
  67. #Sorting by rank
  68. for component_class in Model.components_class:
  69. self.sort_components(component_class)
  70. #Check integrity
  71. for uid, component in self._components['uids'].items():
  72. try:
  73. component.check()
  74. except EmComponentCheckError as exception_object:
  75. raise EmComponentCheckError("The component with uid %d is not valid. Check returns the following error : \"%s\"" % (uid, str(exception_object)))
  76. #Everything is done. Indicating that the component initialisation is over
  77. component.init_ended()
  78. ## Saves data using the current backend
  79. def save(self):
  80. return self.backend.save()
  81. ## Given a EmComponent child class return a list of instances
  82. # @param cls EmComponent : A python class
  83. # @return a list of instances or False if the class is not an EmComponent child
  84. def components(self, cls):
  85. key_name = self.name_from_emclass(cls)
  86. return False if key_name is False else self._components[key_name]
  87. ## Return an EmComponent given an uid
  88. # @param uid int : An EmComponent uid
  89. # @return The corresponding instance or False if uid don't exists
  90. def component(self, uid):
  91. if not isinstance(uid, int) or not isinstance(uid, str):
  92. raise TypeError
  93. return False if uid not in self._components['uids'] else self._components['uids'][uid]
  94. ## Sort components by rank in Model::_components
  95. # @param emclass pythonClass : The type of components to sort
  96. # @throw AttributeError if emclass is not valid
  97. def sort_components(self, component_class):
  98. if component_class not in self.components_class:
  99. raise AttributeError("Bad argument emclass : '" + component_class + "', excpeting one of " + str(self.components_class))
  100. self._components[self.name_from_emclass(component_class)] = sorted(self.components(component_class), key=lambda comp: comp.rank)
  101. ## Return a new uid
  102. # @return a new uid
  103. def new_uid(self):
  104. used_uid = [int(uid) for uid in self._components['uids'].keys()]
  105. return sorted(used_uid)[-1] + 1 if len(used_uid) > 0 else 1
  106. ## Create a component from a component type and datas
  107. #
  108. # @note if datas does not contains a rank the new component will be added last
  109. # @note datas['rank'] can be an integer or two specials strings 'last' or 'first'
  110. # @param component_type str : a component type ( component_class, component_fieldgroup, component_field or component_type )
  111. # @param datas dict : the options needed by the component creation
  112. # @throw ValueError if datas['rank'] is not valid (too big or too small, not an integer nor 'last' or 'first' )
  113. # @todo Handle a raise from the migration handler
  114. def create_component(self, component_type, datas):
  115. em_obj = self.emclass_from_name(component_type)
  116. rank = 'last'
  117. if 'rank' in datas:
  118. rank = datas['rank']
  119. del datas['rank']
  120. datas['uid'] = self.new_uid()
  121. em_component = em_obj(self, **datas)
  122. self._components['uids'][em_component.uid] = em_component
  123. self._components[self.name_from_emclass(em_component.__class__)].append(em_component)
  124. em_component.rank = em_component.get_max_rank()
  125. if rank != 'last':
  126. em_component.set_rank(1 if rank == 'first' else rank)
  127. #everything done, indicating that initialisation is over
  128. em_component.init_ended()
  129. #register the creation in migration handler
  130. try:
  131. self.migration_handler.register_change(em_component.uid, None, em_component.attr_dump)
  132. except MigrationHandlerChangeError as exception_object:
  133. #Revert the creation
  134. self.components(em_component.__class__).remove(em_component)
  135. del self._components['uids'][em_component.uid]
  136. raise exception_object
  137. self.migration_handler.register_model_state(hash(self))
  138. return em_component
  139. ## Delete a component
  140. # @param uid int : Component identifier
  141. # @throw EmComponentNotExistError
  142. # @todo unable uid check
  143. # @todo Handle a raise from the migration handler
  144. def delete_component(self, uid):
  145. #register the deletion in migration handler
  146. self.migration_handler.register_change(uid, self.component(uid).attr_dump, None)
  147. em_component = self.component(uid)
  148. if not em_component:
  149. raise EmComponentNotExistError()
  150. if em_component.delete_check():
  151. self._components[self.name_from_emclass(em_component.__class__)].remove(em_component)
  152. del self._components['uids'][uid]
  153. #Register the new EM state
  154. self.migration_handler.register_model_state(hash(self))
  155. return True
  156. ## Changes the current backend
  157. #
  158. # @param backend unknown: A backend object
  159. def set_backend(self, backend):
  160. self.backend = backend
  161. ## Returns a list of all the EmClass objects of the model
  162. def classes(self):
  163. return list(self._components[self.name_from_emclass(EmClass)])