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.

utils.py 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. # -*- coding: utf-8 -*-
  2. import pymongo
  3. from pymongo import MongoClient
  4. from lodel.settings.settings import Settings as settings
  5. common_collections = {
  6. 'object': 'object',
  7. 'relation': 'relation'
  8. }
  9. collection_prefix = {
  10. 'relation': 'rel_',
  11. 'object': 'class_'
  12. }
  13. LODEL_OPERATORS_MAP = {
  14. '=': {'name': '$eq', 'value_type': None},
  15. '<=': {'name': '$lte', 'value_type': None},
  16. '>=': {'name': '$gte', 'value_type': None},
  17. '!=': {'name': '$ne', 'value_type': None},
  18. '<': {'name': '$lt', 'value_type': None},
  19. '>': {'name': '$gt', 'value_type': None},
  20. 'in': {'name': '$in', 'value_type': list},
  21. 'not in': {'name': '$nin', 'value_type': list},
  22. 'OR': {'name': '$or', 'value_type': list},
  23. 'AND': {'name': '$and', 'value_type': list}
  24. }
  25. LODEL_SORT_OPERATORS_MAP = {
  26. 'ASC': pymongo.ASCENDING,
  27. 'DESC': pymongo.DESCENDING
  28. }
  29. MONGODB_SORT_OPERATORS_MAP = {
  30. 'ASC': 1,
  31. 'DESC': -1
  32. }
  33. MANDATORY_CONNECTION_ARGS = ('host', 'port', 'username', 'password', 'db_name')
  34. class MongoDbConnectionError(Exception):
  35. pass
  36. ## @brief Creates a connection to a MongoDb database
  37. def mongodbconnect(connection_name):
  38. connection_args = get_connection_args(connection_name)
  39. checked_connection_args = check_connection_args(connection_args)
  40. if len(checked_connection_args['errors']) > 0:
  41. raise MongoDbConnectionError("\r\n-".join(checked_connection_args['errors']))
  42. # connection arguments are parsed after the check
  43. login, password, host, port, dbname = checked_connection_args['params']
  44. # Connection creation
  45. connection_string = 'mongodb://%s:%s@%s:%s' % (login, password, host, port)
  46. connection = MongoClient(connection_string)
  47. # Setting the database
  48. database = connection[dbname]
  49. return database
  50. ## @brief gets the settings given a connection name
  51. # @param connection_name str
  52. # @return dict
  53. # @todo Use the settings module to store the connections parameters
  54. def get_connection_args(connnection_name='default'):
  55. return {'host': 'localhost', 'port': 28015, 'username': 'lodel_admin', 'password': 'lapwd', 'db_name': 'lodel'}
  56. ## @brief Checks the settings given a connection name
  57. # @param connection_args dict
  58. # @return dict
  59. # @throw MongoDbDataSourceError with the list of all the found errors
  60. # @todo optimize the error management
  61. def check_connection_args(connection_args):
  62. check_result = {'params': connection_args, 'errors': []}
  63. for connection_arg in MANDATORY_CONNECTION_ARGS:
  64. if connection_arg not in connection_args:
  65. check_result['errors'].append('Datasource connection error: %s parameter is missing.' % connection_arg)
  66. return check_result
  67. ## @brief Returns a collection name given a Emclass
  68. # @param class_object EmClass
  69. # @return str
  70. def object_collection_name(class_object):
  71. if class_object.pure_abstract == False:
  72. class_parent = class_object.parents[0].uid
  73. collection_name = ("%s%s" % (collection_prefix['object'], class_parent)).lower()
  74. else:
  75. collection_name = ("%s%s" % (collection_prefix['object'], class_object.name)).lower()
  76. return collection_name
  77. ## @brief converts the query filters into MongoDB filters
  78. # @param query_filters list : list of query_filters as tuples or dicts
  79. # @param as_list bool : defines if the output will be a list (default: False)
  80. # @return dict|list
  81. # @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
  82. def parse_query_filters(query_filters, as_list=False):
  83. parsed_filters = dict() if not as_list else list()
  84. for query_filter in query_filters:
  85. if isinstance(query_filter, tuple):
  86. if as_list:
  87. parsed_filters.append(convert_filter(query_filter))
  88. else:
  89. parsed_filters.update(convert_filter(query_filter))
  90. elif isinstance(query_filter, dict):
  91. query_item = list(query_filter.items())[0]
  92. key = LODEL_OPERATORS_MAP[query_item[0]]
  93. if as_list:
  94. parsed_filters.append({key: parse_query_filters(query_item[1], as_list=True)})
  95. else:
  96. parsed_filters.update({key: parse_query_filters(query_item[1], as_list=True)})
  97. else:
  98. # TODO Add an exception management here in case the filter is neither a tuple nor a dict
  99. pass
  100. return parsed_filters
  101. ## @brief converts a Lodel query filter into a MongoDB filter
  102. # @param filter_params tuple : (FIELD, OPERATOR, VALUE) representing the query filter to convert
  103. # @return dict : {KEY: {OPERATOR:VALUE}}
  104. # @todo Add an error management for the operator mismatch
  105. def convert_filter(filter_params):
  106. key, operator, value = filter_params
  107. if operator not in ('like', 'not like'):
  108. if operator == 'in' and not isinstance(value, list):
  109. raise ValueError('A list should be used as value for an IN operator, %s given' % value.__class__)
  110. converted_operator = LODEL_OPERATORS_MAP[operator]['name']
  111. converted_filter = {key: {converted_operator: value}}
  112. else:
  113. converted_filter = convert_like_filter(filter_params)
  114. return converted_filter
  115. ## @brief Returns a list of sorting options
  116. # @param query_filters_order list
  117. # @return list
  118. def parse_query_order(query_filters_order):
  119. ordering = list()
  120. for query_filter_order in query_filters_order:
  121. field, direction = query_filter_order
  122. ordering.append((field, LODEL_SORT_OPERATORS_MAP[direction]))
  123. return ordering
  124. ## @brief Converts "like" and "not like" filters into MongotDb filters
  125. # @param like_filter tuple
  126. # @return dict
  127. def convert_like_filter(like_filter):
  128. key, operator, value = like_filter
  129. is_starting_with = value.endswith('*')
  130. is_ending_with = value.startswith('*')
  131. if is_starting_with and not is_ending_with:
  132. regex_pattern = value.replace('*', '^')
  133. elif is_ending_with and not is_starting_with:
  134. regex_pattern = value.replace('*', '$')
  135. elif is_starting_with and is_ending_with:
  136. regex_pattern = '%s' % value
  137. else:
  138. regex_pattern = '^%s$' % value
  139. regex_condition = {'$regex': regex_pattern, '$options': 'i'}
  140. converted_filter = {key: regex_condition}
  141. if operator.startswith('not'):
  142. converted_filter = {key: {'$not': regex_condition}}
  143. return converted_filter
  144. ## @brief identifier escaping
  145. # @param idname str: An identifier
  146. # @return str
  147. def escape_idname(idname):
  148. if '`' in idname:
  149. raise ValueError("Invalid name : '%s'" % idname)
  150. return '`%s`' % idname