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.

182 lines
7.2 KiB

  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. 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(
  100. compute='_get_tile_info', readonly=True) #readonly usefull ?
  101. computed_value=fields.Float(
  102. compute='_get_tile_info', readonly=True) #readonly usefull ?
  103. helper=fields.Char(
  104. compute='_get_tile_info', readonly=True) #readonly usefull ?
  105. field_function=fields.Selection(selection=[
  106. ('min', 'Minimum'),
  107. ('max', 'Maximum'),
  108. ('sum', 'Sum'),
  109. ('avg', 'Average'),
  110. ('median', 'Median'),
  111. ], string='Function')
  112. field_id=fields.Many2one(
  113. comodel_name='ir.model.fields', string='Field',
  114. domain="[('model_id', '=', model_id),"
  115. " ('ttype', 'in', ['float', 'int'])]")
  116. active=fields.Boolean(
  117. compute='_get_tile_info', readonly=True, search='_search_active')
  118. color=fields.Char(default='#0E6C7E', string='Background Color')
  119. font_color=fields.Char(default='#FFFFFF')
  120. sequence=fields.Integer(default=0, required=True)
  121. # Constraint Section
  122. def _check_model_id_field_id(self, cr, uid, ids, context=None):
  123. for t in self.browse(cr, uid, ids, context=context):
  124. if t.field_id and t.field_id.model_id.id != t.model_id.id:
  125. return False
  126. return True
  127. def _check_field_id_field_function(self, cr, uid, ids, context=None):
  128. for t in self.browse(cr, uid, ids, context=context):
  129. if t.field_id and not t.field_function or\
  130. t.field_function and not t.field_id:
  131. return False
  132. return True
  133. _constraints = [
  134. (
  135. _check_model_id_field_id,
  136. "Error ! Please select a field of the selected model.",
  137. ['model_id', 'field_id']),
  138. (
  139. _check_field_id_field_function,
  140. "Error ! Please set both fields: 'Field' and 'Function'.",
  141. ['field_id', 'field_function']),
  142. ]
  143. # View / action Section
  144. @api.multi
  145. def open_link(self):
  146. res = {
  147. 'name': self.name,
  148. 'view_type': 'form',
  149. 'view_mode': 'tree',
  150. 'view_id': [False],
  151. 'res_model': self.model_id.model,
  152. 'type': 'ir.actions.act_window',
  153. 'context': self.env.context,
  154. 'nodestroy': True,
  155. 'target': 'current',
  156. 'domain': self.domain,
  157. }
  158. if self.action_id:
  159. res.update(self.action_id.read(
  160. ['view_type', 'view_mode', 'view_id', 'type'])[0])
  161. # FIXME: restore original Domain + Filter would be better
  162. return res
  163. @api.model
  164. def add(self, vals):
  165. if 'model_id' in vals and not vals['model_id'].isdigit():
  166. # need to replace model_name with its id
  167. vals['model_id'] = self.env['ir.model'].search(
  168. [('model', '=', vals['model_id'])]).id
  169. self.create(vals)