mirror of
https://github.com/yweber/lodel2.git
synced 2025-11-13 01:19:16 +01:00
Theorically all of those imports were tested by unit testing, but we've got no inssurance about that. And even if unit tests had check for syntax errors, all pieces of code were not tested. We cannot be sure that an import was missed or forgotten...
190 lines
8.5 KiB
Python
190 lines
8.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
import datetime
|
|
|
|
from lodel.context import LodelContext
|
|
LodelContext.expose_modules(globals(), {
|
|
'lodel.editorial_model.components': ['EmClass', 'EmField'],
|
|
'lodel.editorial_model.model': ['EditorialModel'],
|
|
'lodel.leapi.datahandlers.base_classes': ['DataHandler'],
|
|
'lodel.plugin': ['LodelHook'],
|
|
'lodel.logger': 'logger'})
|
|
|
|
from leapi_dyncode import * #<-- TODO : handle this !!!
|
|
|
|
from .utils import connect, object_collection_name, mongo_fieldname
|
|
from .datasource import MongoDbDatasource
|
|
from .exceptions import *
|
|
|
|
class MigrationHandler(object):
|
|
|
|
## @brief Constructs a MongoDbMigrationHandler
|
|
# @param host str
|
|
# @param port str
|
|
# @param db_name str
|
|
# @param username str
|
|
# @param password str
|
|
# @param charset str
|
|
# @param dry_run bool
|
|
# @param drop_if_exists bool : drops the table if it already exists
|
|
def __init__(self, host, port, db_name, username, password,
|
|
charset='utf-8', dry_run = False, drop_if_exists = False):
|
|
|
|
self.database = connect(host, port, db_name, username, password)
|
|
self.dry_run = dry_run
|
|
self.drop_if_exists = drop_if_exists
|
|
self.charset = charset # Useless ?
|
|
|
|
logger.debug("MongoDb migration handler instanciated on db : \
|
|
%s@%s:%s" % (db_name, host, port))
|
|
|
|
## @brief Installs the basis collections of the database
|
|
def init_db(self, emclass_list):
|
|
for collection_name in [ object_collection_name(cls)
|
|
for cls in emclass_list]:
|
|
self._create_collection(collection_name)
|
|
|
|
## @brief Creates a collection in the database
|
|
# @param collection_name str
|
|
def _create_collection(self, collection_name):
|
|
existing = self.database.collection_names(
|
|
include_system_collections=False)
|
|
if collection_name in existing:
|
|
if self.drop_if_exists:
|
|
self._delete_collection(collection_name)
|
|
logger.debug("Collection %s deleted before creating \
|
|
it again" % collection_name)
|
|
self.database.create_collection(name=collection_name)
|
|
else:
|
|
logger.info("Collection %s allready exists. \
|
|
Doing nothing..." % collection_name)
|
|
else:
|
|
self.database.create_collection(name=collection_name)
|
|
logger.debug("Collection %s created" % collection_name)
|
|
|
|
## @brief Deletes a collection in the database
|
|
# @param collection_name str
|
|
def _delete_collection(self, collection_name):
|
|
collection = self.database[collection_name]
|
|
collection.drop_indexes()
|
|
collection.drop()
|
|
|
|
## @brief Performs a change in the Database, corresponding to an Editorial Model change
|
|
# @param model EditorialModel
|
|
# @param uid str : the uid of the changing component
|
|
# @param initial_state dict|None : dictionnary of the initial state of the component, None means it's a creation
|
|
# @param new_state dict|None: dictionnary of the new state of the component, None means it's a deletion
|
|
# @note Only the changing properties are added in these state dictionaries
|
|
# @throw ValueError if no state has been precised or if the component considered in the change is neither an EmClass nor an EmField instance
|
|
def register_change(self, model, uid, initial_state, new_state):
|
|
|
|
if initial_state is None and new_state is None:
|
|
raise ValueError('An Editorial Model change should have at least one state precised (initial or new), '
|
|
'none given here')
|
|
|
|
if initial_state is None:
|
|
state_change = 'new'
|
|
elif new_state is None:
|
|
state_change = 'del'
|
|
else:
|
|
state_change = 'upgrade'
|
|
|
|
component_class_name = None
|
|
if isinstance(model.classes(uid), EmClass):
|
|
component_class_name = 'emclass'
|
|
elif isinstance(model.classes(uid), EmField):
|
|
component_class_name = 'emfield'
|
|
|
|
if component_class_name:
|
|
handler_func = '_'+component_class_name.lower()+'_'+state_change
|
|
if hasattr(self, handler_func):
|
|
getattr(self, handler_func)(model, uid, initial_state, new_state)
|
|
else:
|
|
raise ValueError("The component concerned should be an EmClass or EmField instance, %s given",
|
|
model.classes(uid).__class__)
|
|
|
|
def register_model_state(self, em, state_hash):
|
|
pass
|
|
|
|
## @brief creates a new collection corresponding to a given uid
|
|
# @see register_change()
|
|
def _emclass_new(self, model, uid, initial_state, new_state):
|
|
collection_name = object_collection_name(model.classes(uid))
|
|
self._create_collection(collection_name)
|
|
|
|
## @brief deletes a collection corresponding to a given uid
|
|
# @see register_change()
|
|
def _emclass_delete(self, model, uid, initial_state, new_state):
|
|
collection_name = object_collection_name(model.classes(uid))
|
|
self._delete_collection(collection_name)
|
|
|
|
## @brief creates a new field in a collection
|
|
# @see register_change()
|
|
def _emfield_new(self, model, uid, initial_state, new_state):
|
|
if new_state['data_handler'] == 'relation':
|
|
class_name = self.class_collection_name_from_field(model, new_state)
|
|
self._create_field_in_collection(class_name, uid, new_state)
|
|
else:
|
|
collection_name = self._class_collection_name_from_field(model, new_state)
|
|
field_definition = self._field_definition(new_state['data_handler'], new_state)
|
|
self._create_field_in_collection(collection_name, uid, field_definition)
|
|
|
|
## @brief deletes a field in a collection
|
|
# @see register_change()
|
|
def _emfield_del(self, model, uid, initial_state, new_state):
|
|
collection_name = self._class_collection_name_from_field(model, initial_state)
|
|
field_name = mongo_fieldname(model.field(uid).name)
|
|
self._delete_field_in_collection(collection_name, field_name)
|
|
|
|
## @brief upgrades a field
|
|
def _emfield_upgrade(self, model, uid, initial_state, new_state):
|
|
collection_name = self._class_collection_name_from_field(model, initial_state)
|
|
field_name = mongo_fieldname(model.field(uid).name)
|
|
self._check_field_in_collection(collection_name, field_name, initial_state, new_state)
|
|
|
|
def _check_field_in_collection(self,collection_name, field_name, initial_sate, new_state):
|
|
collection = self.database[collection_name]
|
|
field_name = mongo_fieldname(field_name)
|
|
cursor = collection.find({field_name: {'$exists': True}}, {field_name: 1})
|
|
for document in cursor:
|
|
# TODO vérifier que le champ contient une donnée compatible (document[field_name])
|
|
pass
|
|
|
|
## @brief Defines the default value when a new field is added to a collection's items
|
|
# @param fieldtype str : name of the field's type
|
|
# @param options dict : dictionary giving the options to use to initiate the field's value.
|
|
# @return dict (containing a 'default' key with the default value)
|
|
def _field_definition(self, fieldtype, options):
|
|
basic_type = DataHandler.from_name(fieldtype).ftype
|
|
if basic_type == 'datetime':
|
|
if 'now_on_create' in options and options['now_on_create']:
|
|
return {'default': datetime.datetime.utcnow()}
|
|
if basic_type == 'relation':
|
|
return {'default': []}
|
|
|
|
return {'default': ''}
|
|
|
|
def _class_collection_name_from_field(self, model, field):
|
|
class_id = field['class_id']
|
|
component_class = model.classes(class_id)
|
|
component_collection = object_collection_name(component_class)
|
|
return component_collection
|
|
|
|
|
|
## @brief Creates a new field in a collection
|
|
# @param collection_name str
|
|
# @param field str
|
|
# @param options dict
|
|
def _create_field_in_collection(self, collection_name, field, options):
|
|
emfield = EmField(field)
|
|
field_name = mongo_fieldname(field)
|
|
self.database[collection_name].update_many({'uid': emfield.get_emclass_uid(), field_name: {'$exists': False}},
|
|
{'$set': {field_name: options['default']}}, False)
|
|
|
|
## @brief Deletes a field in a collection
|
|
# @param collection_name str
|
|
# @param field_name str
|
|
def _delete_field_in_collection(self, collection_name, field_name):
|
|
if field_name != '_id':
|
|
field_name = mongo_fieldname(field_name)
|
|
self.database[collection_name].update_many({field_name: {'$exists': True}},
|
|
{'$unset': {field_name:1}}, False)
|