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.

datas.py 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. #
  2. # This file is part of Lodel 2 (https://github.com/OpenEdition)
  3. #
  4. # Copyright (C) 2015-2017 Cléo UMS-3287
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU Affero General Public License as published
  8. # by the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU Affero General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU Affero General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. #
  19. ## @package lodel.leapi.datahandlers.datas
  20. # This module contains specific datahandlers extending the basic ones from the lodel.leapi.datahandlers.datas_base module.
  21. import warnings
  22. import inspect
  23. import re
  24. from lodel.context import LodelContext
  25. LodelContext.expose_modules(globals(), {
  26. 'lodel.leapi.datahandlers.datas_base': ['Boolean', 'Integer', 'Varchar',
  27. 'DateTime', 'Text', 'File'],
  28. 'lodel.exceptions': ['LodelException', 'LodelExceptions',
  29. 'LodelFatalError', 'DataNoneValid', 'FieldValidationError']})
  30. ## @brief Data field designed to handle formated strings
  31. class FormatString(Varchar):
  32. help = 'Automatic string field, designed to use the str % operator to build its content'
  33. base_type = 'char'
  34. ## @brief Constructor
  35. # @param _field_list list : List of fields to use
  36. # @param _format_string str : formatted string
  37. # @param **kwargs : additional options
  38. def __init__(self, format_string, field_list, **kwargs):
  39. self._field_list = field_list
  40. self._format_string = format_string
  41. super().__init__(internal='automatic', **kwargs)
  42. ## @brief constructs the formatted string data
  43. # The string can be truncated depending on the maximum length defined for this field.
  44. #
  45. # @param emcomponent EmComponent
  46. # @param fname str
  47. # @param datas dict
  48. # @param cur_value str
  49. # @return str
  50. def _construct_data(self, emcomponent, fname, datas, cur_value):
  51. ret = self._format_string % tuple(
  52. datas[fname] for fname in self._field_list)
  53. if len(ret) > self.max_length:
  54. warnings.warn("Format field overflow. Truncating value")
  55. ret = ret[:self.max_length]
  56. return ret
  57. ## @brief Varchar validated by a regex
  58. class Regex(Varchar):
  59. help = 'String field validated with a regex. Takes two options : \
  60. max_length and regex'
  61. base_type = 'char'
  62. ## @brief A string field validated by a regex
  63. # @param regex str : a regex string (passed as argument to re.compile()), default value is an empty string
  64. # @param max_length int : the max length for this field (default : 10)
  65. # @param **kwargs : additional options
  66. def __init__(self, regex='', max_length=10, **kwargs):
  67. self.regex = regex
  68. self.compiled_re = re.compile(regex) # trigger an error if invalid regex
  69. super(self.__class__, self).__init__(max_length=max_length, **kwargs)
  70. ## @brief Check and cast value in appropriate type
  71. # @param value *
  72. # @throw FieldValidationError if value is unappropriate or can not be cast
  73. # @return str
  74. def _check_data_value(self, value):
  75. value = super()._check_data_value(value)
  76. if not self.compiled_re.match(value) or len(value) > self.max_length:
  77. msg = '"%s" doesn\'t match the regex "%s"' % (value, self.regex)
  78. raise FieldValidationError(msg)
  79. return value
  80. ## @brief checks if another datahandler can override this one
  81. #
  82. # @param data_handler Datahandler
  83. # @return bool
  84. def can_override(self, data_handler):
  85. if not super().can_override(data_handler):
  86. return False
  87. if data_handler.max_length != self.max_length:
  88. return False
  89. return True
  90. ##@brief Handles uniq ID
  91. class UniqID(Integer):
  92. help = 'Fieldtype designed to handle editorial model UID'
  93. base_type = 'int'
  94. ## @brief A uid field
  95. #
  96. # @param **kwargs dict
  97. def __init__(self, **kwargs):
  98. kwargs['internal'] = 'automatic'
  99. super(self.__class__, self).__init__(primary_key = True, **kwargs)
  100. ## @brief Constructs the field's data
  101. # @param emcomponent EmComponent : Component corresponding to the field
  102. # @param fname
  103. # @param datas
  104. # @param cur_value str : current value to use (is retrieved from the datasource if not given)
  105. # @return str
  106. # @remarks fname and datas are not used and should become non mandatory, cur_value should have a None default value
  107. def construct_data(self, emcomponent, fname, datas, cur_value):
  108. if cur_value is None:
  109. #Ask datasource to provide a new uniqID
  110. return emcomponent._ro_datasource.new_numeric_id(emcomponent)
  111. return cur_value
  112. ## @brief Class representing a LeObject subclass
  113. class LeobjectSubclassIdentifier(Varchar):
  114. help = 'Datahandler designed to handle LeObject subclass identifier in DB'
  115. base_type = 'varchar'
  116. ## @brief Constructor
  117. # @param kwargs dict : additional options
  118. # @throw RuntimeError
  119. # @todo define the "internal" option that can be given in the kwargs, and document its meaning
  120. def __init__(self, **kwargs):
  121. if 'internal' in kwargs and not kwargs['internal']:
  122. raise RuntimeError(self.__class__.__name__+" datahandler can only \
  123. be internal")
  124. kwargs['internal'] = True
  125. super().__init__(**kwargs)
  126. ## @brief Returns the class' name
  127. # @param emcomponent EmComponent : Component correponding to the field
  128. # @param fname
  129. # @param datas
  130. # @param cur_value
  131. # @return str
  132. # @remarks fname, datas and cur_value should be given default values as they are not mandatory here.
  133. def construct_data(self, emcomponent, fname, datas, cur_value):
  134. cls = emcomponent
  135. if not inspect.isclass(emcomponent):
  136. cls = emcomponent.__class__
  137. return cls.__name__
  138. ## @brief Data field designed to handle concatenated fields
  139. class Concat(FormatString):
  140. help = 'Automatic strings concatenation'
  141. base_type = 'char'
  142. ## @brief Build its content with a field list and a separator
  143. # @param field_list list : List of fields to concatenate
  144. # @param separator str
  145. # @param **kwargs
  146. def __init__(self, field_list, separator=' ', **kwargs):
  147. format_string = separator.join(['%s' for _ in field_list])
  148. super().__init__(format_string=format_string,
  149. field_list=field_list,
  150. **kwargs)
  151. ## @brief Datahandler managing a password
  152. class Password(Varchar):
  153. help = 'Handle passwords'
  154. base_type = 'password'
  155. pass
  156. ## @brief Datahandler turning a string into a list
  157. class VarcharList(Varchar):
  158. help = 'DataHandler designed to make a list out of a string.'
  159. base_type = 'varchar'
  160. ## @brief Constructor
  161. # @param delimiter str : default value is a whitespace character
  162. # @param **kwargs : additional options
  163. # @throw LodelException : this exception is raised when the delimiter is not a string
  164. def __init__(self, delimiter=' ', **kwargs):
  165. if not isinstance(delimiter, str):
  166. raise LodelException("The delimiter has to be a string, %s given" % type(delimiter))
  167. self.delimiter = str(delimiter)
  168. super().__init__(**kwargs)
  169. ## @brief Constructs the field's data
  170. # @param emcomponent EmComponent
  171. # @param fname
  172. # @param datas
  173. # @param cur_value : current value to use
  174. # @return list
  175. # @remarks emcomponent, fname and datas should be given a default value as they seem to be non mandatory
  176. def construct_data(self, emcomponent, fname, datas, cur_value):
  177. result = cur_value.split(self.delimiter)
  178. return result