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.

migration_handler.py 8.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. # -*- coding: utf-8 -*-
  2. import datetime
  3. from lodel.context import LodelContext
  4. LodelContext.expose_modules(globals(), {
  5. 'lodel.editorial_model.components': ['EmClass', 'EmField'],
  6. 'lodel.editorial_model.model': ['EditorialModel'],
  7. 'lodel.leapi.datahandlers.base_classes': ['DataHandler'],
  8. 'lodel.plugin': ['LodelHook'],
  9. 'lodel.logger': 'logger'})
  10. from leapi_dyncode import * #<-- TODO : handle this !!!
  11. from .utils import connect, object_collection_name, mongo_fieldname
  12. from .datasource import MongoDbDatasource
  13. from .exceptions import *
  14. class MigrationHandler(object):
  15. ## @brief Constructs a MongoDbMigrationHandler
  16. # @param host str
  17. # @param port str
  18. # @param db_name str
  19. # @param username str
  20. # @param password str
  21. # @param charset str
  22. # @param dry_run bool
  23. # @param drop_if_exists bool : drops the table if it already exists
  24. def __init__(self, host, port, db_name, username, password,
  25. charset='utf-8', dry_run = False, drop_if_exists = False):
  26. self.database = connect(host, port, db_name, username, password)
  27. self.dry_run = dry_run
  28. self.drop_if_exists = drop_if_exists
  29. self.charset = charset # Useless ?
  30. logger.debug("MongoDb migration handler instanciated on db : \
  31. %s@%s:%s" % (db_name, host, port))
  32. ## @brief Installs the basis collections of the database
  33. def init_db(self, emclass_list):
  34. for collection_name in [ object_collection_name(cls)
  35. for cls in emclass_list]:
  36. self._create_collection(collection_name)
  37. ## @brief Creates a collection in the database
  38. # @param collection_name str
  39. def _create_collection(self, collection_name):
  40. existing = self.database.collection_names(
  41. include_system_collections=False)
  42. if collection_name in existing:
  43. if self.drop_if_exists:
  44. self._delete_collection(collection_name)
  45. logger.debug("Collection %s deleted before creating \
  46. it again" % collection_name)
  47. self.database.create_collection(name=collection_name)
  48. else:
  49. logger.info("Collection %s allready exists. \
  50. Doing nothing..." % collection_name)
  51. else:
  52. self.database.create_collection(name=collection_name)
  53. logger.debug("Collection %s created" % collection_name)
  54. ## @brief Deletes a collection in the database
  55. # @param collection_name str
  56. def _delete_collection(self, collection_name):
  57. collection = self.database[collection_name]
  58. collection.drop_indexes()
  59. collection.drop()
  60. ## @brief Performs a change in the Database, corresponding to an Editorial Model change
  61. # @param model EditorialModel
  62. # @param uid str : the uid of the changing component
  63. # @param initial_state dict|None : dictionnary of the initial state of the component, None means it's a creation
  64. # @param new_state dict|None: dictionnary of the new state of the component, None means it's a deletion
  65. # @note Only the changing properties are added in these state dictionaries
  66. # @throw ValueError if no state has been precised or if the component considered in the change is neither an EmClass nor an EmField instance
  67. def register_change(self, model, uid, initial_state, new_state):
  68. if initial_state is None and new_state is None:
  69. raise ValueError('An Editorial Model change should have at least one state precised (initial or new), '
  70. 'none given here')
  71. if initial_state is None:
  72. state_change = 'new'
  73. elif new_state is None:
  74. state_change = 'del'
  75. else:
  76. state_change = 'upgrade'
  77. component_class_name = None
  78. if isinstance(model.classes(uid), EmClass):
  79. component_class_name = 'emclass'
  80. elif isinstance(model.classes(uid), EmField):
  81. component_class_name = 'emfield'
  82. if component_class_name:
  83. handler_func = '_'+component_class_name.lower()+'_'+state_change
  84. if hasattr(self, handler_func):
  85. getattr(self, handler_func)(model, uid, initial_state, new_state)
  86. else:
  87. raise ValueError("The component concerned should be an EmClass or EmField instance, %s given",
  88. model.classes(uid).__class__)
  89. def register_model_state(self, em, state_hash):
  90. pass
  91. ## @brief creates a new collection corresponding to a given uid
  92. # @see register_change()
  93. def _emclass_new(self, model, uid, initial_state, new_state):
  94. collection_name = object_collection_name(model.classes(uid))
  95. self._create_collection(collection_name)
  96. ## @brief deletes a collection corresponding to a given uid
  97. # @see register_change()
  98. def _emclass_delete(self, model, uid, initial_state, new_state):
  99. collection_name = object_collection_name(model.classes(uid))
  100. self._delete_collection(collection_name)
  101. ## @brief creates a new field in a collection
  102. # @see register_change()
  103. def _emfield_new(self, model, uid, initial_state, new_state):
  104. if new_state['data_handler'] == 'relation':
  105. class_name = self.class_collection_name_from_field(model, new_state)
  106. self._create_field_in_collection(class_name, uid, new_state)
  107. else:
  108. collection_name = self._class_collection_name_from_field(model, new_state)
  109. field_definition = self._field_definition(new_state['data_handler'], new_state)
  110. self._create_field_in_collection(collection_name, uid, field_definition)
  111. ## @brief deletes a field in a collection
  112. # @see register_change()
  113. def _emfield_del(self, model, uid, initial_state, new_state):
  114. collection_name = self._class_collection_name_from_field(model, initial_state)
  115. field_name = mongo_fieldname(model.field(uid).name)
  116. self._delete_field_in_collection(collection_name, field_name)
  117. ## @brief upgrades a field
  118. def _emfield_upgrade(self, model, uid, initial_state, new_state):
  119. collection_name = self._class_collection_name_from_field(model, initial_state)
  120. field_name = mongo_fieldname(model.field(uid).name)
  121. self._check_field_in_collection(collection_name, field_name, initial_state, new_state)
  122. def _check_field_in_collection(self,collection_name, field_name, initial_sate, new_state):
  123. collection = self.database[collection_name]
  124. field_name = mongo_fieldname(field_name)
  125. cursor = collection.find({field_name: {'$exists': True}}, {field_name: 1})
  126. for document in cursor:
  127. # TODO vérifier que le champ contient une donnée compatible (document[field_name])
  128. pass
  129. ## @brief Defines the default value when a new field is added to a collection's items
  130. # @param fieldtype str : name of the field's type
  131. # @param options dict : dictionary giving the options to use to initiate the field's value.
  132. # @return dict (containing a 'default' key with the default value)
  133. def _field_definition(self, fieldtype, options):
  134. basic_type = DataHandler.from_name(fieldtype).ftype
  135. if basic_type == 'datetime':
  136. if 'now_on_create' in options and options['now_on_create']:
  137. return {'default': datetime.datetime.utcnow()}
  138. if basic_type == 'relation':
  139. return {'default': []}
  140. return {'default': ''}
  141. def _class_collection_name_from_field(self, model, field):
  142. class_id = field['class_id']
  143. component_class = model.classes(class_id)
  144. component_collection = object_collection_name(component_class)
  145. return component_collection
  146. ## @brief Creates a new field in a collection
  147. # @param collection_name str
  148. # @param field str
  149. # @param options dict
  150. def _create_field_in_collection(self, collection_name, field, options):
  151. emfield = EmField(field)
  152. field_name = mongo_fieldname(field)
  153. self.database[collection_name].update_many({'uid': emfield.get_emclass_uid(), field_name: {'$exists': False}},
  154. {'$set': {field_name: options['default']}}, False)
  155. ## @brief Deletes a field in a collection
  156. # @param collection_name str
  157. # @param field_name str
  158. def _delete_field_in_collection(self, collection_name, field_name):
  159. if field_name != '_id':
  160. field_name = mongo_fieldname(field_name)
  161. self.database[collection_name].update_many({field_name: {'$exists': True}},
  162. {'$unset': {field_name:1}}, False)