1
0
Fork 0
mirror of https://github.com/yweber/lodel2.git synced 2026-04-09 19:59:58 +02:00

Deleting old stuff + bugfix in install/Makefile and in install/lodel_admin.py

This commit is contained in:
Yann 2016-06-16 15:57:13 +02:00
commit 006b0c4bc0
14 changed files with 8 additions and 603 deletions

View file

@ -51,7 +51,7 @@ PROJECT_BRIEF = "Logiciel d'edition electronique v2"
# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
# to the output directory.
PROJECT_LOGO = "doc/img/openedition_logo.png"
PROJECT_LOGO = doc/img/openedition_logo.png
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
# into which the generated documentation will be written. If a relative path is
@ -763,7 +763,7 @@ WARN_LOGFILE =
# spaces.
# Note: If this tag is empty the current directory is searched.
INPUT =
INPUT = .
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@ -909,7 +909,7 @@ IMAGE_PATH = doc/img
# code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly.
INPUT_FILTER = "doxypy"
INPUT_FILTER = doxypy
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
# basis. Doxygen will compare the file name with each pattern and apply the

View file

@ -1,7 +0,0 @@
## @package install Contains the base files for a lodel2 instance
#
# An instance is composed of multiple things :
#- a configuration directory ( @ref install/conf.d/ )
#- a bootstraping script ( @ref install/loader.py )
#- an utilities source code ( @ref install/lodel_admin.py )
#- a Makefile that allow to run common instance operations (only refresh the dynamic code for now)

Binary file not shown.

View file

@ -6,4 +6,4 @@ dyncode:
$(python) -c 'import lodel_admin; lodel_admin.refresh_dyncode()'
init_all_dbs:
$(python) -c 'import lodel_admin; lodel_admin.init_all_dbs()'
$(python) -c 'import lodel_admin; lodel_admin.init_all_dbs()'

View file

@ -6,7 +6,6 @@ import loader
from lodel.settings import Settings
from lodel import logger
from .leapi_dyncode import *
## @brief Utility method to generate python code given an emfile and a
# translator
@ -48,5 +47,6 @@ def refresh_dyncode():
def init_all_dbs():
import leapi_dyncode
#TODO
pass

View file

@ -1 +0,0 @@
__author__ = 'roland'

View file

@ -1 +0,0 @@
# -*- coding: utf-8 -*-

View file

@ -1,51 +0,0 @@
# -*- coding: utf-8 -*-
class GenericDataSource(object):
def __init__(self, *conn_args, **conn_kwargs):
self.conn_args = conn_args
self.conn_kwargs = conn_kwargs
## @brief returns a selection of documents from the datasource
# @param target_cls Emclass
# @param field_list list
# @param filters list : List of filters
# @param rel_filters list : List of relational filters
# @param order list : List of column to order. ex: order = [('title', 'ASC'),]
# @param group list : List of tupple representing the column to group together. ex: group = [('title', 'ASC'),]
# @param limit int : Number of records to be returned
# @param offset int: used with limit to choose the start record
# @param instanciate bool : If true, the records are returned as instances, else they are returned as dict
# @return list
def select(self, target_cls, field_list, filters, rel_filters=None, order=None, group=None, limit=None, offset=0,
instanciate=True):
pass
## @brief Deletes one record defined by its uid
# @param target_cls Emclass : class of the record to delete
# @param uid dict|list : a dictionary of fields and values composing the unique identifier of the record or a list of several dictionaries
# @return int : number of deleted records
def delete(self, target_cls, uid):
pass
## @brief updates one or a list of records
# @param target_cls Emclass : class of the object to insert
# @param uids list : list of uids to update
# @param datas dict : datas to update (new values)
# @return int : Number of updated records
def update(self, target_cls, uids, **datas):
pass
## @brief Inserts a record in a given collection
# @param target_cls Emclass : class of the object to insert
# @param datas dict : datas to insert
# @return bool
def insert(self, target_cls, **datas):
pass
## @brief Inserts a list of records in a given collection
# @param target_cls Emclass : class of the objects inserted
# @param datas_list
# @return list : list of the inserted records' ids
def insert_multi(self, target_cls, datas_list):
pass

View file

@ -1,46 +0,0 @@
# -*- coding: utf-8 -*-
## @package lodel.datasource.migrationhandler.generic
# @brief A generic migration handler
#
# According to it, every moditification is possible
#
## Manage model changes
class GenericMigrationHandler(object):
def __init__(self, debug=False):
self.debug = debug
## @brief Records a change in the EditorialModel and indicates whether or not it is possible to make it
# @note The states (initial_state and new_state) contains only fields that changes
def register_change(self, em, uid, initial_state, new_state):
if self.debug:
print("\n##############")
print("GenericMigrationHandler debug. Changes for component with uid %s :" % uid)
if initial_state is None:
print("Component creation (uid = %s): \n\t" % uid, new_state)
elif new_state is None:
print("Component deletion (uid = %s): \n\t" % uid, initial_state)
else:
field_list = set(initial_state.keys()).union(set(new_state.keys()))
for field_name in field_list:
str_chg = "\t%s " % field_name
if field_name in initial_state:
str_chg += "'" + str(initial_state[field_name]) + "'"
else:
str_chg += " creating "
str_chg += " => "
if field_name in new_state:
str_chg += "'" + str(new_state[field_name]) + "'"
else:
str_chg += " deletion "
print(str_chg)
print("##############\n")
## @brief Not usefull for the moment
def register_model_state(self, em, state_hash):
if self.debug:
print("New EditorialModel state registered : '%s'" % state_hash)

View file

@ -1,135 +0,0 @@
# -*- coding: utf-8 -*-
import bson
from bson.son import SON
from collections import OrderedDict
import pymongo
# from pymongo import MongoClient
from pymongo.errors import BulkWriteError
import urllib
import lodel.datasource.mongodb.utils as utils
from lodel.datasource.generic.datasource import GenericDataSource
class MongoDbDataSourceError(Exception):
pass
class MongoDbDataSource(GenericDataSource):
## @brief Instanciates a Database object given a connection name
# @param connection_name str
def __init__(self, connection_name='default'):
self.database = utils.mongodbconnect(connection_name)
## @brief returns a selection of documents from the datasource
# @param target_cls Emclass
# @param field_list list
# @param filters list : List of filters
# @param rel_filters list : List of relational filters
# @param order list : List of column to order. ex: order = [('title', 'ASC'),]
# @param group list : List of tupple representing the column used as "group by" fields. ex: group = [('title', 'ASC'),]
# @param limit int : Number of records to be returned
# @param offset int: used with limit to choose the start record
# @param instanciate bool : If true, the records are returned as instances, else they are returned as dict
# @return list
# @todo Implement the relations
def select(self, target_cls, field_list, filters, rel_filters=None, order=None, group=None, limit=None, offset=0,
instanciate=True):
collection_name = utils.object_collection_name(target_cls.__class__)
collection = self.database[collection_name]
query_filters = utils.parse_query_filters(filters)
query_result_ordering = utils.parse_query_order(order) if order is not None else None
results_field_list = None if len(field_list) == 0 else field_list
limit = limit if limit is not None else 0
if group is None:
cursor = collection.find(
filter=query_filters,
projection=results_field_list,
skip=offset,
limit=limit,
sort=query_result_ordering
)
else:
pipeline = list()
unwinding_list = list()
grouping_dict = OrderedDict()
sorting_list = list()
for group_param in group:
field_name = group_param[0]
field_sort_option = group_param[1]
sort_option = utils.MONGODB_SORT_OPERATORS_MAP[field_sort_option]
unwinding_list.append({'$unwind': '$%s' % field_name})
grouping_dict[field_name] = '$%s' % field_name
sorting_list.append((field_name, sort_option))
sorting_list.extends(query_result_ordering)
pipeline.append({'$match': query_filters})
if results_field_list is not None:
pipeline.append({'$project': SON([{field_name: 1} for field_name in field_list])})
pipeline.extend(unwinding_list)
pipeline.append({'$group': grouping_dict})
pipeline.extend({'$sort': SON(sorting_list)})
if offset > 0:
pipeline.append({'$skip': offset})
if limit is not None:
pipeline.append({'$limit': limit})
results = list()
for document in cursor:
results.append(document)
return results
## @brief Deletes one record defined by its uid
# @param target_cls Emclass : class of the record to delete
# @param uid dict|list : a dictionary of fields and values composing the unique identifier of the record or a list of several dictionaries
# @return int : number of deleted records
# @TODO Implement the error management
def delete(self, target_cls, uid):
if isinstance(uid, dict):
uid = [uid]
collection_name = utils.object_collection_name(target_cls.__class__)
collection = self.database[collection_name]
result = collection.delete_many(uid)
return result.deleted_count
## @brief updates one or a list of records
# @param target_cls Emclass : class of the object to insert
# @param uids list : list of uids to update
# @param datas dict : datas to update (new values)
# @return int : Number of updated records
# @todo check if the values need to be parsed
def update(self, target_cls, uids, **datas):
if not isinstance(uids, list):
uids = [uids]
collection_name = utils.object_collection_name(target_cls.__class__)
collection = self.database[collection_name]
results = collection.update_many({'uid': {'$in': uids}}, datas)
return results.modified_count()
## @brief Inserts a record in a given collection
# @param target_cls Emclass : class of the object to insert
# @param datas dict : datas to insert
# @return bool
# @TODO Implement the error management
def insert(self, target_cls, **datas):
collection_name = utils.object_collection_name(target_cls.__class__)
collection = self.database[collection_name]
result = collection.insert_one(datas)
return len(result.inserted_id)
## @brief Inserts a list of records in a given collection
# @param target_cls Emclass : class of the objects inserted
# @param datas_list
# @return list : list of the inserted records' ids
# @TODO Implement the error management
def insert_multi(self, target_cls, datas_list):
collection_name = utils.object_collection_name(target_cls.__class__)
collection = self.database[collection_name]
result = collection.insert_many(datas_list)
return len(result.inserted_ids)

View file

@ -1,164 +0,0 @@
# -*- coding: utf-8 -*-
import datetime
from lodel.leapi.datahandlers.base_classes import DataHandler
from lodel.datasource.generic.migrationhandler import GenericMigrationHandler
import lodel.datasource.mongodb.utils as utils
from lodel.editorial_model.components import EmClass, EmField
from lodel.editorial_model.model import EditorialModel
class MigrationHandlerChangeError(Exception):
pass
class MongoDbMigrationHandler(GenericMigrationHandler):
COMMANDS_IFEXISTS_DROP = 'drop'
COMMANDS_IFEXISTS_NOTHING = 'nothing'
INIT_COLLECTIONS_NAMES = ['object', 'relation', 'entitie', 'person', 'text', 'entry']
## @brief constructs a MongoDbMigrationHandler
# @param conn_args dict : a dictionary containing the connection options
# @param **kwargs : extra arguments
def __init__(self, conn_args=None, **kwargs):
if conn_args is None:
conn_args = utils.get_connection_args()
self.connection_name = conn_args['name']
self.database = utils.mongodbconnect(self.connection_name)
# TODO : get the following parameters in the settings ?
migrationhandler_settings = {'dry_run': False, 'foreign_keys': True, 'drop_if_exists': False}
self.dryrun = kwargs['dryrun'] if 'dryrun' in kwargs else migrationhandler_settings['dry_run']
self.foreign_keys = kwargs['foreign_keys'] if 'foreign_keys' in kwargs else migrationhandler_settings['foreign_keys']
self.drop_if_exists = kwargs['drop_if_exists'] if 'drop_if_exists' in kwargs else migrationhandler_settings['drop_if_exists']
self._install_collections()
def _install_collections(self):
for collection_name in MongoDbMigrationHandler.INIT_COLLECTIONS_NAMES:
collection_to_create = "%s%s" % ('class_', collection_name)
self._create_collection(collection_name=collection_to_create)
## @brief Performs a change in the EditorialModel and indicates
# @note The states contains only the changing fields in the form of a dict : {field_name1: fieldvalue1, ...}
# @param model EditorialModel
# @param uid str : the uid of the changing component
# @param initial_state dict|None: dict representing the original state, None means the component will be created
# @param new_state dict|None: dict representing the new state, None means the component will be deleted
def register_change(self, model, uid, initial_state, new_state):
if initial_state is not None and new_state is not None:
if initial_state is None:
state_change = 'new'
elif new_state is None:
state_change = 'del'
else:
state_change = 'upgrade'
component_class_name = model.classes(uid).__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:
pass # TODO manage the case where no state at all was given
def register_model_state(self, em, state_hash):
pass
## @brief creates a new collection corresponding to a given uid
def emclass_new(self, model, uid, initial_state, new_state):
emclass = model.classes(uid)
if not isinstance(emclass, EmClass):
raise ValueError("The given uid is not an EmClass uid")
collection_name = utils.object_collection_name(emclass)
self._create_collection(collection_name)
## @brief deletes a collection corresponding to a given uid
def emclass_del(self, model, uid, initial_state, new_state):
emclass = model.classes(uid)
if not isinstance(emclass, EmClass):
raise ValueError("The given uid is not an EmClass uid")
collection_name = utils.object.collection_name(emclass)
self._delete_collection(collection_name)
## @brief creates a new field in a collection
# @param model EditorialModel
# @param uid str
# @param initial_state dict|None: dict representing the original state
# @param new_state dict|None: dict representing the new state
def emfield_new(self, model, uid, initial_state, new_state):
if new_state['data_handler'] == 'relation':
# find relational_type name, and class_name of the field
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_defition(new_state['data_handler'], new_state)
self._create_field_in_collection(collection_name, uid, field_definition)
## @brief deletes a field in a collection
# @param model EditorialModel
# @param uid str
# @param initial_state dict|None: dict representing the original state
# @param new_state dict|None: dict representing the new state
def emfield_del(self, model, uid, initial_state, new_state):
collection_name = self._class_collection_name_from_field(model, initial_state)
field_name = model.field(uid).name
self._delete_field_in_collection(collection_name, field_name)
## @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 = utils.object_collection_name(component_class)
return component_collection
## @brief Creates a new collection in MongoDb Database
# @param collection_name str
# @param charset str
# @param if_exists str : defines the behavior when the collection already exists (default : 'nothing')
def _create_collection(self, collection_name, charset='utf8', if_exists=MongoDbMigrationHandler.COMMANDS_IFEXISTS_NOTHING):
if collection_name in self.database.collection_names(include_system_collections=False):
# The collection already exists
if if_exists == MongoDbMigrationHandler.COMMANDS_IFEXISTS_DROP:
self._delete_collection(collection_name)
self.database.create_collection(name=collection_name)
else:
self.database.create_collection(name=collection_name)
## @brief Delete an existing collection in MongoDb Database
# @param collection_name str
def _delete_collection(self, collection_name):
collection = self.database[collection_name]
collection.drop_indexes()
collection.drop()
## @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):
self.database[collection_name].update_many({field: {'$exists': False}}, {'$set': {field: 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':
self.database[collection_name].update_many({field_name:{'$exists': True}}, {'$unset':{field_name:1}}, False)

View file

@ -1,190 +0,0 @@
# -*- coding: utf-8 -*-
import pymongo
from pymongo import MongoClient
from lodel.settings.settings import Settings as settings
common_collections = {
'object': 'object',
'relation': 'relation'
}
collection_prefix = {
'relation': 'rel_',
'object': 'class_'
}
LODEL_OPERATORS_MAP = {
'=': {'name': '$eq', 'value_type': None},
'<=': {'name': '$lte', 'value_type': None},
'>=': {'name': '$gte', 'value_type': None},
'!=': {'name': '$ne', 'value_type': None},
'<': {'name': '$lt', 'value_type': None},
'>': {'name': '$gt', 'value_type': None},
'in': {'name': '$in', 'value_type': list},
'not in': {'name': '$nin', 'value_type': list},
'OR': {'name': '$or', 'value_type': list},
'AND': {'name': '$and', 'value_type': list}
}
LODEL_SORT_OPERATORS_MAP = {
'ASC': pymongo.ASCENDING,
'DESC': pymongo.DESCENDING
}
MONGODB_SORT_OPERATORS_MAP = {
'ASC': 1,
'DESC': -1
}
MANDATORY_CONNECTION_ARGS = ('host', 'port', 'username', 'password', 'db_name')
class MongoDbConnectionError(Exception):
pass
## @brief Creates a connection to a MongoDb database
def mongodbconnect(connection_name):
connection_args = get_connection_args(connection_name)
checked_connection_args = check_connection_args(connection_args)
if len(checked_connection_args['errors']) > 0:
raise MongoDbConnectionError("\r\n-".join(checked_connection_args['errors']))
# connection arguments are parsed after the check
login, password, host, port, dbname = checked_connection_args['params']
# Connection creation
connection_string = 'mongodb://%s:%s@%s:%s' % (login, password, host, port)
connection = MongoClient(connection_string)
# Setting the database
database = connection[dbname]
return database
## @brief gets the settings given a connection name
# @param connection_name str
# @return dict
# @todo Use the settings module to store the connections parameters
def get_connection_args(connnection_name='default'):
return {'host': 'localhost', 'port': 28015, 'username': 'lodel_admin', 'password': 'lapwd', 'db_name': 'lodel'}
## @brief Checks the settings given a connection name
# @param connection_args dict
# @return dict
# @throw MongoDbDataSourceError with the list of all the found errors
# @todo optimize the error management
def check_connection_args(connection_args):
check_result = {'params': connection_args, 'errors': []}
for connection_arg in MANDATORY_CONNECTION_ARGS:
if connection_arg not in connection_args:
check_result['errors'].append('Datasource connection error: %s parameter is missing.' % connection_arg)
return check_result
## @brief Returns a collection name given a Emclass
# @param class_object EmClass
# @return str
def object_collection_name(class_object):
if class_object.pure_abstract == False:
class_parent = class_object.parents[0].uid
collection_name = ("%s%s" % (collection_prefix['object'], class_parent)).lower()
else:
collection_name = ("%s%s" % (collection_prefix['object'], class_object.name)).lower()
return collection_name
## @brief converts the query filters into MongoDB filters
# @param query_filters list : list of query_filters as tuples or dicts
# @param as_list bool : defines if the output will be a list (default: False)
# @return dict|list
# @todo refactor this function by adding a return_type argument (default= dict) which can be a dict or a list, then delete the convert_filter_list function
def parse_query_filters(query_filters, as_list=False):
parsed_filters = dict() if not as_list else list()
for query_filter in query_filters:
if isinstance(query_filter, tuple):
if as_list:
parsed_filters.append(convert_filter(query_filter))
else:
parsed_filters.update(convert_filter(query_filter))
elif isinstance(query_filter, dict):
query_item = list(query_filter.items())[0]
key = LODEL_OPERATORS_MAP[query_item[0]]
if as_list:
parsed_filters.append({key: parse_query_filters(query_item[1], as_list=True)})
else:
parsed_filters.update({key: parse_query_filters(query_item[1], as_list=True)})
else:
# TODO Add an exception management here in case the filter is neither a tuple nor a dict
pass
return parsed_filters
## @brief converts a Lodel query filter into a MongoDB filter
# @param filter_params tuple : (FIELD, OPERATOR, VALUE) representing the query filter to convert
# @return dict : {KEY: {OPERATOR:VALUE}}
# @todo Add an error management for the operator mismatch
def convert_filter(filter_params):
key, operator, value = filter_params
if operator not in ('like', 'not like'):
if operator == 'in' and not isinstance(value, list):
raise ValueError('A list should be used as value for an IN operator, %s given' % value.__class__)
converted_operator = LODEL_OPERATORS_MAP[operator]['name']
converted_filter = {key: {converted_operator: value}}
else:
converted_filter = convert_like_filter(filter_params)
return converted_filter
## @brief Returns a list of sorting options
# @param query_filters_order list
# @return list
def parse_query_order(query_filters_order):
ordering = list()
for query_filter_order in query_filters_order:
field, direction = query_filter_order
ordering.append((field, LODEL_SORT_OPERATORS_MAP[direction]))
return ordering
## @brief Converts "like" and "not like" filters into MongotDb filters
# @param like_filter tuple
# @return dict
def convert_like_filter(like_filter):
key, operator, value = like_filter
is_starting_with = value.endswith('*')
is_ending_with = value.startswith('*')
if is_starting_with and not is_ending_with:
regex_pattern = value.replace('*', '^')
elif is_ending_with and not is_starting_with:
regex_pattern = value.replace('*', '$')
elif is_starting_with and is_ending_with:
regex_pattern = '%s' % value
else:
regex_pattern = '^%s$' % value
regex_condition = {'$regex': regex_pattern, '$options': 'i'}
converted_filter = {key: regex_condition}
if operator.startswith('not'):
converted_filter = {key: {'$not': regex_condition}}
return converted_filter
## @brief identifier escaping
# @param idname str: An identifier
# @return str
def escape_idname(idname):
if '`' in idname:
raise ValueError("Invalid name : '%s'" % idname)
return '`%s`' % idname

View file

@ -7,8 +7,8 @@ from lodel.editorial_model.model import EditorialModel
from lodel.editorial_model.components import *
from lodel.utils.mlstring import MlString
## @package lodel.editorial_model.translator Lodel 2 Editorial Model Translators
## @brief Packahg for saving a Editorial µModel in a xml fiel and loading an Editorial Model from a xml file
##@package lodel.editorial_model.translator.xmlfile Translator module designed
#to load & save EM in XML
#
# Structure of a xml file which represents an editorial model:
# <ul>