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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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. import EditorialModel
  10. ## Manages the Editorial Model
  11. class Model(object):
  12. components_class = [EmClass, EmField, EmFieldGroup, EmType]
  13. ## Constructor
  14. #
  15. # @param backend unknown: A backend object instanciated from one of the classes in the backend module
  16. def __init__(self, backend, migration_handler = None):
  17. self.migration_handler = DummyMigrationHandler() if migration_handler is None else migration_handler
  18. self.backend = backend
  19. self._components = {'uids': {}, 'EmClass': [], 'EmType': [], 'EmField': [], 'EmFieldGroup': []}
  20. self.load()
  21. @staticmethod
  22. ## Given a name return an EmComponent child class
  23. # @param class_name str : The name to identify an EmComponent class
  24. # @return A python class or False if the class_name is not a name of an EmComponent child class
  25. def emclass_from_name(class_name):
  26. for cls in Model.components_class:
  27. if cls.__name__ == class_name:
  28. return cls
  29. return False
  30. @staticmethod
  31. ## Given a python class return a name
  32. # @param cls : The python class we want the name
  33. # @return A class name as string or False if cls is not an EmComponent child class
  34. def name_from_emclass(em_class):
  35. if em_class not in Model.components_class:
  36. return False
  37. return em_class.__name__
  38. ## Loads the structure of the Editorial Model
  39. #
  40. # Gets all the objects contained in that structure and creates a dict indexed by their uids
  41. # @todo Change the thrown exception when a components check fails
  42. # @throw ValueError When a component class don't exists
  43. def load(self):
  44. data = self.backend.load()
  45. for uid, component in data.items():
  46. cls_name = component['component']
  47. cls = self.emclass_from_name(cls_name)
  48. if cls:
  49. component['uid'] = uid
  50. # create a dict for the component and one indexed by uids, store instanciated component in it
  51. self._components['uids'][uid] = cls(component, self)
  52. self._components[cls_name].append(self._components['uids'][uid])
  53. else:
  54. raise ValueError("Unknow EmComponent class : '" + cls_name + "'")
  55. #Sorting by rank
  56. for component_class in Model.components_class:
  57. self.sort_components(component_class)
  58. #Check integrity
  59. for uid, component in self._components['uids'].items():
  60. if not component.check():
  61. raise RuntimeError("EmComponent with uid '"+str(uid)+"' is not valid !!!")
  62. ## Saves data using the current backend
  63. def save(self):
  64. return self.backend.save()
  65. ## Given a EmComponent child class return a list of instances
  66. # @param cls EmComponent : A python class
  67. # @return a list of instances or False if the class is not an EmComponent child
  68. def components(self, cls):
  69. key_name = self.name_from_emclass(cls)
  70. return False if key_name is False else self._components[key_name]
  71. ## Return an EmComponent given an uid
  72. # @param uid int : An EmComponent uid
  73. # @return The corresponding instance or False if uid don't exists
  74. def component(self, uid):
  75. return False if uid not in self._components['uids'] else self._components['uids'][uid]
  76. ## Sort components by rank in Model::_components
  77. # @param emclass pythonClass : The type of components to sort
  78. # @throw AttributeError if emclass is not valid
  79. def sort_components(self, component_class):
  80. if component_class not in self.components_class:
  81. raise AttributeError("Bad argument emclass : '"+emclass+"', excpeting one of "+str(self.components_class))
  82. self._components[self.name_from_emclass(component_class)] = sorted(self.components(component_class), key=lambda comp: comp.rank)
  83. ## Return a new uid
  84. # @return a new uid
  85. def new_uid(self):
  86. used_uid = [ int(uid) for uid in self._components['uids'].keys()]
  87. return sorted(used_uid)[-1] + 1 if len(used_uid) > 0 else 1
  88. ## Create a component from a component type and datas
  89. #
  90. # @note if datas does not contains a rank the new component will be added last
  91. # @note datas['rank'] can be an integer or two specials strings 'last' or 'first'
  92. # @param component_type str : a component type ( component_class, component_fieldgroup, component_field or component_type )
  93. # @param datas dict : the options needed by the component creation
  94. # @throw ValueError if datas['rank'] is not valid (too big or too small, not an integer nor 'last' or 'first' )
  95. def create_component(self, component_type, datas):
  96. em_obj = self.emclass_from_name(component_type)
  97. datas['uid'] = self.new_uid()
  98. em_component = em_obj(datas, self)
  99. rank = None
  100. if 'rank' in datas:
  101. rank = datas['rank']
  102. datas['rank'] = None
  103. self._components['uids'][em_component.uid] = em_component
  104. self._components[self.name_from_emclass(em_component.__class__)].append(em_component)
  105. create_fails = None
  106. if rank is None or rank == 'last':
  107. em_component.rank = em_component.get_max_rank()
  108. elif datas['rank'] == 'first':
  109. em_component.set_rank(1)
  110. elif isinstance(rank, int):
  111. em_component.set_rank(rank)
  112. else:
  113. create_fails = ValueError("Invalid rank : '"+str(datas['rank'])+"'")
  114. self.delete(em_component.uid)
  115. if not em_component.check():
  116. create_fails = RuntimeError("After creation the component self check fails")
  117. if not create_fails is None:
  118. raise create_fails
  119. return em_component
  120. ## Delete a component
  121. # @param uid int : Component identifier
  122. # @throw EditorialModel.components.EmComponentNotExistError
  123. # @todo unable uid check
  124. def delete_component(self, uid):
  125. if uid not in self._components[uid]:
  126. raise EditorialModel.components.EmComponentNotExistError()
  127. em_component = self._components[uid]
  128. if em_component.delete_p():
  129. self._components[self.name_from_emclass(em_component.__class__)].remove(em_component)
  130. del self._components['uids'][uid]
  131. return True
  132. ## Changes the current backend
  133. #
  134. # @param backend unknown: A backend object
  135. def set_backend(self, backend):
  136. self.backend = backend
  137. ## Returns a list of all the EmClass objects of the model
  138. def classes(self):
  139. return list(self._components[self.name_from_emclass(EmClass)])