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.

151 lines
5.5 KiB

10 years ago
10 years ago
10 years ago
  1. # -*- coding: utf-8 -*-
  2. # © 2010-2013 OpenERP s.a. (<http://openerp.com>).
  3. # © 2014 initOS GmbH & Co. KG (<http://www.initos.com>).
  4. # © 2015-Today GRAP
  5. # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
  6. import datetime
  7. import time
  8. from dateutil.relativedelta import relativedelta
  9. from openerp import api, fields, models
  10. from openerp.tools.safe_eval import safe_eval as eval
  11. from openerp.tools.translate import _
  12. from openerp.exceptions import ValidationError
  13. class TileTile(models.Model):
  14. _name = 'tile.tile'
  15. _order = 'sequence, name'
  16. def median(self, aList):
  17. # https://docs.python.org/3/library/statistics.html#statistics.median
  18. # TODO : refactor, using statistics.median when Odoo will be available
  19. # in Python 3.4
  20. even = (0 if len(aList) % 2 else 1) + 1
  21. half = (len(aList) - 1) / 2
  22. return sum(sorted(aList)[half:half + even]) / float(even)
  23. def _get_eval_context(self):
  24. def _context_today():
  25. return fields.Date.from_string(fields.Date.context_today(self))
  26. context = self.env.context.copy()
  27. context.update({
  28. 'time': time,
  29. 'datetime': datetime,
  30. 'relativedelta': relativedelta,
  31. 'context_today': _context_today,
  32. 'current_date': time.strftime('%Y-%m-%d'),
  33. })
  34. return context
  35. # Column Section
  36. name = fields.Char(required=True)
  37. sequence = fields.Integer(default=0, required=True)
  38. user_id = fields.Many2one('res.users', 'User')
  39. background_color = fields.Char(default='#0E6C7E', oldname='color')
  40. font_color = fields.Char(default='#FFFFFF')
  41. model_id = fields.Many2one('ir.model', 'Model', required=True)
  42. domain = fields.Text(default='[]')
  43. action_id = fields.Many2one('ir.actions.act_window', 'Action')
  44. count = fields.Integer(compute='_compute_data')
  45. computed_value = fields.Float(compute='_compute_data')
  46. field_function = fields.Selection([
  47. ('min', 'Minimum'),
  48. ('max', 'Maximum'),
  49. ('sum', 'Sum'),
  50. ('avg', 'Average'),
  51. ('median', 'Median'),
  52. ], string='Function')
  53. field_id = fields.Many2one(
  54. 'ir.model.fields',
  55. string='Field',
  56. domain="[('model_id', '=', model_id),"
  57. " ('ttype', 'in', ['float', 'int'])]")
  58. helper = fields.Char(compute='_compute_function_helper')
  59. @api.one
  60. def _compute_data(self):
  61. self.count = 0
  62. self.computed_value = 0
  63. # Compute count item
  64. model = self.env[self.model_id.model]
  65. eval_context = self._get_eval_context()
  66. self.count = model.search_count(eval(self.domain, eval_context))
  67. # Compute datas for field_id depending of field_function
  68. if self.field_function and self.field_id and self.count != 0:
  69. records = model.search(eval(self.domain, eval_context))
  70. vals = [x[self.field_id.name] for x in records]
  71. if self.field_function == 'min':
  72. self.computed_value = min(vals)
  73. elif self.field_function == 'max':
  74. self.computed_value = max(vals)
  75. elif self.field_function == 'sum':
  76. self.computed_value = sum(vals)
  77. elif self.field_function == 'avg':
  78. self.computed_value = sum(vals) / len(vals)
  79. elif self.field_function == 'median':
  80. self.computed_value = self.median(vals)
  81. @api.one
  82. @api.onchange('field_function', 'field_id')
  83. def _compute_function_helper(self):
  84. self.helper = ''
  85. if self.field_function and self.field_id:
  86. desc = self.field_id.field_description
  87. helpers = {
  88. 'min': "Minimum value of '%s'",
  89. 'max': "Maximum value of '%s'",
  90. 'sum': "Total value of '%s'",
  91. 'avg': "Average value of '%s'",
  92. 'median': "Median value of '%s'",
  93. }
  94. self.helper = _(helpers.get(self.field_function, '')) % desc
  95. # Constraints and onchanges
  96. @api.one
  97. @api.constrains('model_id', 'field_id')
  98. def _check_model_id_field_id(self):
  99. if self.field_id and self.field_id.model_id.id != self.model_id.id:
  100. raise ValidationError(
  101. _("Please select a field of the selected model."))
  102. @api.one
  103. @api.constrains('field_id', 'field_function')
  104. def _check_field_id_field_function(self):
  105. if self.field_id and not self.field_function or\
  106. self.field_function and not self.field_id:
  107. raise ValidationError(
  108. _("Please set both: 'Field' and 'Function'."))
  109. # Action methods
  110. @api.multi
  111. def open_link(self):
  112. res = {
  113. 'name': self.name,
  114. 'view_type': 'form',
  115. 'view_mode': 'tree',
  116. 'view_id': [False],
  117. 'res_model': self.model_id.model,
  118. 'type': 'ir.actions.act_window',
  119. 'context': self.env.context,
  120. 'nodestroy': True,
  121. 'target': 'current',
  122. 'domain': self.domain,
  123. }
  124. if self.action_id:
  125. res.update(self.action_id.read(
  126. ['view_type', 'view_mode', 'type'])[0])
  127. # FIXME: restore original Domain + Filter would be better
  128. return res
  129. @api.model
  130. def add(self, vals):
  131. if 'model_id' in vals and not vals['model_id'].isdigit():
  132. # need to replace model_name with its id
  133. vals['model_id'] = self.env['ir.model'].search(
  134. [('model', '=', vals['model_id'])]).id
  135. self.create(vals)