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

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 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)