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.

179 lines
7.1 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. # -*- coding: utf-8 -*-
  2. ##############################################################################
  3. #
  4. # OpenERP, Open Source Management Solution
  5. # Copyright (C) 2010-2013 OpenERP s.a. (<http://openerp.com>).
  6. # Copyright (C) 2014 initOS GmbH & Co. KG (<http://www.initos.com>).
  7. # Copyright (C) 2015-Today GRAP
  8. # Author Markus Schneider <markus.schneider at initos.com>
  9. # @author Sylvain LE GAL (https://twitter.com/legalsylvain)
  10. #
  11. # This program is free software: you can redistribute it and/or modify
  12. # it under the terms of the GNU Affero General Public License as
  13. # published by the Free Software Foundation, either version 3 of the
  14. # License, or (at your option) any later version.
  15. #
  16. # This program is distributed in the hope that it will be useful,
  17. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. # GNU Affero General Public License for more details.
  20. #
  21. # You should have received a copy of the GNU Affero General Public License
  22. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  23. #
  24. ##############################################################################
  25. from openerp import api, fields
  26. from openerp.models import Model
  27. from openerp.exceptions import except_orm
  28. from openerp.tools.translate import _
  29. class TileTile(Model):
  30. _name = 'tile.tile'
  31. _order = 'sequence, name'
  32. def median(self, aList):
  33. # https://docs.python.org/3/library/statistics.html#statistics.median
  34. # TODO : refactor, using statistics.median when Odoo will be available
  35. # in Python 3.4
  36. even = (0 if len(aList) % 2 else 1) + 1
  37. half = (len(aList) - 1) / 2
  38. return sum(sorted(aList)[half:half + even]) / float(even)
  39. def _get_tile_info(self):
  40. ima_obj = self.env['ir.model.access']
  41. res = {}
  42. for r in self:
  43. r.active = False
  44. r.count = 0
  45. r.computed_value = 0
  46. r.helper = ''
  47. if ima_obj.check(r.model_id.model, 'read', False):
  48. # Compute count item
  49. model = self.env[r.model_id.model]
  50. r.count = model.search_count(eval(r.domain))
  51. r.active = True
  52. # Compute datas for field_id depending of field_function
  53. if r.field_function and r.field_id and r.count != 0:
  54. records = model.search(eval(r.domain))
  55. vals = [x[r.field_id.name] for x in records]
  56. desc = r.field_id.field_description
  57. if r.field_function == 'min':
  58. r.computed_value = min(vals)
  59. r.helper = _("Minimum value of '%s'") % desc
  60. elif r.field_function == 'max':
  61. r.computed_value = max(vals)
  62. r.helper = _("Maximum value of '%s'") % desc
  63. elif r.field_function == 'sum':
  64. r.computed_value = sum(vals)
  65. r.helper = _("Total value of '%s'") % desc
  66. elif r.field_function == 'avg':
  67. r.computed_value = sum(vals) / len(vals)
  68. r.helper = _("Average value of '%s'") % desc
  69. elif r.field_function == 'median':
  70. r.computed_value = self.median(vals)
  71. r.helper = _("Median value of '%s'") % desc
  72. return res
  73. def _search_active(self, operator, value):
  74. cr = self.env.cr
  75. if operator != '=':
  76. raise except_orm(
  77. 'Unimplemented Feature',
  78. 'Search on Active field disabled.')
  79. ima_obj = self.env['ir.model.access']
  80. ids = []
  81. cr.execute("""
  82. SELECT tt.id, im.model
  83. FROM tile_tile tt
  84. INNER JOIN ir_model im
  85. ON tt.model_id = im.id""")
  86. for result in cr.fetchall():
  87. if (ima_obj.check(result[1], 'read', False) == value):
  88. ids.append(result[0])
  89. return [('id', 'in', ids)]
  90. # Column Section
  91. name = fields.Char(required=True)
  92. model_id = fields.Many2one(
  93. comodel_name='ir.model', string='Model', required=True)
  94. user_id = fields.Many2one(
  95. comodel_name='res.users', string='User')
  96. domain = fields.Text(default='[]')
  97. action_id = fields.Many2one(
  98. comodel_name='ir.actions.act_window', string='Action')
  99. count = fields.Integer(compute='_get_tile_info')
  100. computed_value = fields.Float(compute='_get_tile_info')
  101. helper = fields.Char(compute='_get_tile_info')
  102. field_function = fields.Selection(selection=[
  103. ('min', 'Minimum'),
  104. ('max', 'Maximum'),
  105. ('sum', 'Sum'),
  106. ('avg', 'Average'),
  107. ('median', 'Median'),
  108. ], string='Function')
  109. field_id = fields.Many2one(
  110. comodel_name='ir.model.fields', string='Field',
  111. domain="[('model_id', '=', model_id),"
  112. " ('ttype', 'in', ['float', 'int'])]")
  113. active = fields.Boolean(
  114. compute='_get_tile_info', readonly=True, search='_search_active')
  115. background_color = fields.Char(default='#0E6C7E', oldname='color')
  116. font_color = fields.Char(default='#FFFFFF')
  117. sequence = fields.Integer(default=0, required=True)
  118. # Constraint Section
  119. def _check_model_id_field_id(self, cr, uid, ids, context=None):
  120. for t in self.browse(cr, uid, ids, context=context):
  121. if t.field_id and t.field_id.model_id.id != t.model_id.id:
  122. return False
  123. return True
  124. def _check_field_id_field_function(self, cr, uid, ids, context=None):
  125. for t in self.browse(cr, uid, ids, context=context):
  126. if t.field_id and not t.field_function or\
  127. t.field_function and not t.field_id:
  128. return False
  129. return True
  130. _constraints = [
  131. (
  132. _check_model_id_field_id,
  133. "Error ! Please select a field of the selected model.",
  134. ['model_id', 'field_id']),
  135. (
  136. _check_field_id_field_function,
  137. "Error ! Please set both fields: 'Field' and 'Function'.",
  138. ['field_id', 'field_function']),
  139. ]
  140. # View / action Section
  141. @api.multi
  142. def open_link(self):
  143. res = {
  144. 'name': self.name,
  145. 'view_type': 'form',
  146. 'view_mode': 'tree',
  147. 'view_id': [False],
  148. 'res_model': self.model_id.model,
  149. 'type': 'ir.actions.act_window',
  150. 'context': self.env.context,
  151. 'nodestroy': True,
  152. 'target': 'current',
  153. 'domain': self.domain,
  154. }
  155. if self.action_id:
  156. res.update(self.action_id.read(
  157. ['view_type', 'view_mode', 'view_id', 'type'])[0])
  158. # FIXME: restore original Domain + Filter would be better
  159. return res
  160. @api.model
  161. def add(self, vals):
  162. if 'model_id' in vals and not vals['model_id'].isdigit():
  163. # need to replace model_name with its id
  164. vals['model_id'] = self.env['ir.model'].search(
  165. [('model', '=', vals['model_id'])]).id
  166. self.create(vals)