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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. # -*- coding: utf-8 -*-
  2. import datetime
  3. from lodel.leapi.datahandlers.base_classes import DataHandler
  4. from lodel.datasource.generic.migrationhandler import GenericMigrationHandler
  5. import lodel.datasource.mongodb.utils as utils
  6. from lodel.editorial_model.components import EmClass, EmField
  7. from lodel.editorial_model.model import EditorialModel
  8. class MigrationHandlerChangeError(Exception):
  9. pass
  10. class MongoDbMigrationHandler(GenericMigrationHandler):
  11. COMMANDS_IFEXISTS_DROP = 'drop'
  12. COMMANDS_IFEXISTS_NOTHING = 'nothing'
  13. INIT_COLLECTIONS_NAMES = ['object', 'relation', 'entitie', 'person', 'text', 'entry']
  14. ## @brief constructs a MongoDbMigrationHandler
  15. # @param conn_args dict : a dictionary containing the connection options
  16. # @param **kwargs : extra arguments
  17. def __init__(self, conn_args=None, **kwargs):
  18. if conn_args is None:
  19. conn_args = utils.get_connection_args()
  20. self.connection_name = conn_args['name']
  21. self.database = utils.mongodbconnect(self.connection_name)
  22. # TODO : get the following parameters in the settings ?
  23. migrationhandler_settings = {'dry_run': False, 'foreign_keys': True, 'drop_if_exists': False}
  24. self.dryrun = kwargs['dryrun'] if 'dryrun' in kwargs else migrationhandler_settings['dry_run']
  25. self.foreign_keys = kwargs['foreign_keys'] if 'foreign_keys' in kwargs else migrationhandler_settings['foreign_keys']
  26. self.drop_if_exists = kwargs['drop_if_exists'] if 'drop_if_exists' in kwargs else migrationhandler_settings['drop_if_exists']
  27. self._install_collections()
  28. def _install_collections(self):
  29. for collection_name in MongoDbMigrationHandler.INIT_COLLECTIONS_NAMES:
  30. collection_to_create = "%s%s" % ('class_', collection_name)
  31. self._create_collection(collection_name=collection_to_create)
  32. ## @brief Performs a change in the EditorialModel and indicates
  33. # @note The states contains only the changing fields in the form of a dict : {field_name1: fieldvalue1, ...}
  34. # @param model EditorialModel
  35. # @param uid str : the uid of the changing component
  36. # @param initial_state dict|None: dict representing the original state, None means the component will be created
  37. # @param new_state dict|None: dict representing the new state, None means the component will be deleted
  38. def register_change(self, model, uid, initial_state, new_state):
  39. if initial_state is not None and new_state is not None:
  40. if initial_state is None:
  41. state_change = 'new'
  42. elif new_state is None:
  43. state_change = 'del'
  44. else:
  45. state_change = 'upgrade'
  46. component_class_name = model.classes(uid).__class__.name
  47. handler_func(component_class_name.lower() + '_' + state_change)
  48. if hasattr(self, handler_func):
  49. getattr(self, handler_func)(model, uid, initial_state, new_state)
  50. else:
  51. pass # TODO manage the case where no state at all was given
  52. def register_model_state(self, em, state_hash):
  53. pass
  54. ## @brief creates a new collection corresponding to a given uid
  55. def emclass_new(self, model, uid, initial_state, new_state):
  56. emclass = model.classes(uid)
  57. if not isinstance(emclass, EmClass):
  58. raise ValueError("The given uid is not an EmClass uid")
  59. collection_name = utils.object_collection_name(emclass)
  60. self._create_collection(collection_name)
  61. ## @brief deletes a collection corresponding to a given uid
  62. def emclass_del(self, model, uid, initial_state, new_state):
  63. emclass = model.classes(uid)
  64. if not isinstance(emclass, EmClass):
  65. raise ValueError("The given uid is not an EmClass uid")
  66. collection_name = utils.object.collection_name(emclass)
  67. self._delete_collection(collection_name)
  68. ## @brief creates a new field in a collection
  69. # @param model EditorialModel
  70. # @param uid str
  71. # @param initial_state dict|None: dict representing the original state
  72. # @param new_state dict|None: dict representing the new state
  73. def emfield_new(self, model, uid, initial_state, new_state):
  74. if new_state['data_handler'] == 'relation':
  75. # find relational_type name, and class_name of the field
  76. class_name = self._class_collection_name_from_field(model, new_state)
  77. self._create_field_in_collection(class_name, uid, new_state)
  78. else:
  79. collection_name = self._class_collection_name_from_field(model, new_state)
  80. field_definition = self._field_defition(new_state['data_handler'], new_state)
  81. self._create_field_in_collection(collection_name, uid, field_definition)
  82. ## @brief deletes a field in a collection
  83. # @param model EditorialModel
  84. # @param uid str
  85. # @param initial_state dict|None: dict representing the original state
  86. # @param new_state dict|None: dict representing the new state
  87. def emfield_del(self, model, uid, initial_state, new_state):
  88. collection_name = self._class_collection_name_from_field(model, initial_state)
  89. field_name = model.field(uid).name
  90. self._delete_field_in_collection(collection_name, field_name)
  91. ## @brief Defines the default value when a new field is added to a collection's items
  92. # @param fieldtype str : name of the field's type
  93. # @param options dict : dictionary giving the options to use to initiate the field's value.
  94. # @return dict (containing a 'default' key with the default value)
  95. def _field_definition(self, fieldtype, options):
  96. basic_type = DataHandler.from_name(fieldtype).ftype
  97. if basic_type == 'datetime':
  98. if 'now_on_create' in options and options['now_on_create']:
  99. return {'default': datetime.datetime.utcnow()}
  100. if basic_type == 'relation':
  101. return {'default': []}
  102. return {'default': ''}
  103. def _class_collection_name_from_field(self, model, field):
  104. class_id = field['class_id']
  105. component_class = model.classes(class_id)
  106. component_collection = utils.object_collection_name(component_class)
  107. return component_collection
  108. ## @brief Creates a new collection in MongoDb Database
  109. # @param collection_name str
  110. # @param charset str
  111. # @param if_exists str : defines the behavior when the collection already exists (default : 'nothing')
  112. def _create_collection(self, collection_name, charset='utf8', if_exists=MongoDbMigrationHandler.COMMANDS_IFEXISTS_NOTHING):
  113. if collection_name in self.database.collection_names(include_system_collections=False):
  114. # The collection already exists
  115. if if_exists == MongoDbMigrationHandler.COMMANDS_IFEXISTS_DROP:
  116. self._delete_collection(collection_name)
  117. self.database.create_collection(name=collection_name)
  118. else:
  119. self.database.create_collection(name=collection_name)
  120. ## @brief Delete an existing collection in MongoDb Database
  121. # @param collection_name str
  122. def _delete_collection(self, collection_name):
  123. collection = self.database[collection_name]
  124. collection.drop_indexes()
  125. collection.drop()
  126. ## @brief Creates a new field in a collection
  127. # @param collection_name str
  128. # @param field str
  129. # @param options dict
  130. def _create_field_in_collection(self, collection_name, field, options):
  131. self.database[collection_name].update_many({field: {'$exists': False}}, {'$set': {field: options['default']}}, False)
  132. ## @brief Deletes a field in a collection
  133. # @param collection_name str
  134. # @param field_name str
  135. def _delete_field_in_collection(self, collection_name, field_name):
  136. if field_name != '_id':
  137. self.database[collection_name].update_many({field_name:{'$exists': True}}, {'$unset':{field_name:1}}, False)