OCA reporting engine fork for dev and update.
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.

174 lines
5.7 KiB

  1. # -*- coding: utf-8 -*-
  2. # Copyright 2012 - Now Savoir-faire Linux <https://www.savoirfairelinux.com/>
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
  4. from openerp import fields, models, api
  5. from openerp.tools.safe_eval import safe_eval
  6. import re
  7. def is_one_value(result):
  8. # check if sql query returns only one value
  9. if type(result) is dict and 'value' in result.dictfetchone():
  10. return True
  11. elif type(result) is list and 'value' in result[0]:
  12. return True
  13. else:
  14. return False
  15. RE_SELECT_QUERY = re.compile('.*(' + '|'.join((
  16. 'INSERT',
  17. 'UPDATE',
  18. 'DELETE',
  19. 'CREATE',
  20. 'ALTER',
  21. 'DROP',
  22. 'GRANT',
  23. 'REVOKE',
  24. 'INDEX',
  25. )) + ')')
  26. def is_sql_or_ddl_statement(query):
  27. """Check if sql query is a SELECT statement"""
  28. return not RE_SELECT_QUERY.match(query.upper())
  29. class KPIThresholdRange(models.Model):
  30. """
  31. KPI Threshold Range
  32. """
  33. _name = "kpi.threshold.range"
  34. _description = "KPI Threshold Range"
  35. name = fields.Char('Name', size=50, required=True)
  36. valid = fields.Boolean(string='Valid', required=True,
  37. compute="_compute_is_valid_range", default=True)
  38. invalid_message = fields.Char(string='Message', size=100,
  39. compute="_compute_is_valid_range")
  40. min_type = fields.Selection((
  41. ('static', 'Fixed value'),
  42. ('python', 'Python Code'),
  43. ('local', 'SQL - Local DB'),
  44. ('external', 'SQL - Externa DB'),
  45. ), 'Min Type', required=True)
  46. min_value = fields.Float(string='Minimum', compute="_compute_min_value")
  47. min_fixed_value = fields.Float('Minimum')
  48. min_code = fields.Text('Minimum Computation Code')
  49. min_error = fields.Char('Error', compute="_compute_min_value")
  50. min_dbsource_id = fields.Many2one(
  51. 'base.external.dbsource',
  52. 'External DB Source',
  53. )
  54. max_type = fields.Selection((
  55. ('static', 'Fixed value'),
  56. ('python', 'Python Code'),
  57. ('local', 'SQL - Local DB'),
  58. ('external', 'SQL - External DB'),
  59. ), 'Max Type', required=True)
  60. max_value = fields.Float(string='Maximum', compute="_compute_max_value")
  61. max_fixed_value = fields.Float('Maximum')
  62. max_code = fields.Text('Maximum Computation Code')
  63. max_error = fields.Char('Error', compute="_compute_max_value")
  64. max_dbsource_id = fields.Many2one(
  65. 'base.external.dbsource',
  66. 'External DB Source',
  67. )
  68. color = fields.Char(
  69. string="Color",
  70. help="Choose your color"
  71. )
  72. threshold_ids = fields.Many2many(
  73. 'kpi.threshold',
  74. 'kpi_threshold_range_rel',
  75. 'range_id',
  76. 'threshold_id',
  77. 'Thresholds',
  78. )
  79. company_id = fields.Many2one(
  80. 'res.company', 'Company',
  81. default=lambda self: self.env.user.company_id.id)
  82. @api.multi
  83. def _compute_min_value(self):
  84. result = {}
  85. for obj in self:
  86. value = None
  87. error = None
  88. try:
  89. if obj.min_type == 'local' and is_sql_or_ddl_statement(
  90. obj.min_code):
  91. self.env.cr.execute(obj.min_code)
  92. dic = self.env.cr.dictfetchall()
  93. if is_one_value(dic):
  94. value = dic[0]['value']
  95. elif (obj.min_type == 'external' and obj.min_dbsource_id.id and
  96. is_sql_or_ddl_statement(obj.min_code)):
  97. dbsrc_obj = obj.min_dbsource_id
  98. res = dbsrc_obj.execute(obj.min_code)
  99. if is_one_value(res):
  100. value = res[0]['value']
  101. elif obj.min_type == 'python':
  102. value = safe_eval(obj.min_code)
  103. else:
  104. value = obj.min_fixed_value
  105. except Exception as e:
  106. value = None
  107. error = str(e)
  108. obj.min_value = value
  109. obj.min_error = error
  110. return result
  111. @api.multi
  112. def _compute_max_value(self):
  113. result = {}
  114. for obj in self:
  115. value = None
  116. error = None
  117. try:
  118. if obj.max_type == 'local' and is_sql_or_ddl_statement(
  119. obj.max_code):
  120. self.env.cr.execute(obj.max_code)
  121. dic = self.env.cr.dictfetchall()
  122. if is_one_value(dic):
  123. value = dic[0]['value']
  124. elif (obj.max_type == 'external' and obj.max_dbsource_id.id and
  125. is_sql_or_ddl_statement(obj.max_code)):
  126. dbsrc_obj = obj.max_dbsource_id
  127. res = dbsrc_obj.execute(obj.max_code)
  128. if is_one_value(res):
  129. value = res[0]['value']
  130. elif obj.max_type == 'python':
  131. value = safe_eval(obj.max_code)
  132. else:
  133. value = obj.max_fixed_value
  134. except Exception as e:
  135. value = None
  136. error = str(e)
  137. obj.max_value = value
  138. obj.max_error = error
  139. return result
  140. @api.multi
  141. def _compute_is_valid_range(self):
  142. result = {}
  143. for obj in self:
  144. if obj.min_error or obj.max_error:
  145. obj.valid = False
  146. obj.invalid_message = (
  147. "Either minimum or maximum value has "
  148. "computation errors. Please fix them.")
  149. elif obj.max_value < obj.min_value:
  150. obj.valid = False
  151. obj.invalid_message = (
  152. "Minimum value is greater than the maximum "
  153. "value! Please adjust them.")
  154. else:
  155. obj.valid = True
  156. obj.invalid_message = ""
  157. return result