|
@ -22,7 +22,28 @@ |
|
|
from dateutil.relativedelta import relativedelta |
|
|
from dateutil.relativedelta import relativedelta |
|
|
from datetime import datetime |
|
|
from datetime import datetime |
|
|
from osv import fields, osv |
|
|
from osv import fields, osv |
|
|
|
|
|
from openerp.tools.translate import _ |
|
|
|
|
|
import openerp.tools as tools |
|
|
import time |
|
|
import time |
|
|
|
|
|
import logging |
|
|
|
|
|
_logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
|
|
|
def is_one_value(result): |
|
|
|
|
|
# check if sql query returns only one value |
|
|
|
|
|
if type(result) is dict and 'value' in result.dictfetchone(): |
|
|
|
|
|
return True |
|
|
|
|
|
elif type(result) is list and 'value' in result[0]: |
|
|
|
|
|
return True |
|
|
|
|
|
else: |
|
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
def is_select_query(query): |
|
|
|
|
|
# check if sql query is a SELECT statement |
|
|
|
|
|
|
|
|
|
|
|
for statement in ('INSERT', 'UPDATE', 'DELETE', 'CREATE', 'ALTER', 'DROP', 'GRANT', 'REVOKE', 'INDEX'): |
|
|
|
|
|
if statement in query: |
|
|
|
|
|
return False |
|
|
|
|
|
return True |
|
|
|
|
|
|
|
|
class mgmtsystem_kpi_category(osv.osv): |
|
|
class mgmtsystem_kpi_category(osv.osv): |
|
|
""" |
|
|
""" |
|
@ -44,67 +65,94 @@ class mgmtsystem_kpi_threshold_range(osv.osv): |
|
|
_name = "mgmtsystem.kpi.threshold.range" |
|
|
_name = "mgmtsystem.kpi.threshold.range" |
|
|
_description = "KPI Threshold Range" |
|
|
_description = "KPI Threshold Range" |
|
|
|
|
|
|
|
|
def _compute_min_value(self, cr, uid, ids, field_name, arg, context=None): |
|
|
|
|
|
|
|
|
def compute_min_value(self, cr, uid, ids, field_name, arg, context=None): |
|
|
if context is None: |
|
|
if context is None: |
|
|
context = {} |
|
|
context = {} |
|
|
result = {} |
|
|
result = {} |
|
|
|
|
|
|
|
|
for obj in self.browse(cr, uid, ids): |
|
|
for obj in self.browse(cr, uid, ids): |
|
|
value = 0 |
|
|
|
|
|
if obj.min_type == 'local': |
|
|
|
|
|
|
|
|
value = None |
|
|
|
|
|
if obj.min_type == 'local' and is_select_query(obj.min_code): |
|
|
cr.execute(obj.min_code) |
|
|
cr.execute(obj.min_code) |
|
|
value = cr.dictfetchone()['value'] |
|
|
|
|
|
elif obj.max_type == 'external': |
|
|
|
|
|
if not dbsource_id: |
|
|
|
|
|
dbsrc_obj = self.pool.get('base.external.dbsource').browse(cr, uid, min_dbsource_id, context) |
|
|
|
|
|
|
|
|
dic = cr.dictfetchall() |
|
|
|
|
|
if is_one_value(dic): |
|
|
|
|
|
value = dic[0]['value'] |
|
|
|
|
|
elif obj.min_type == 'external' and obj.min_dbsource_id.id and is_select_query(obj.min_code): |
|
|
|
|
|
dbsrc_obj = self.pool.get('base.external.dbsource').browse(cr, uid, obj.min_dbsource_id.id, context) |
|
|
res = dbsrc_obj.execute(obj.min_code) |
|
|
res = dbsrc_obj.execute(obj.min_code) |
|
|
|
|
|
if is_one_value(res): |
|
|
value = res[0]['value'] |
|
|
value = res[0]['value'] |
|
|
elif obj.min_type == 'python': |
|
|
elif obj.min_type == 'python': |
|
|
value = eval(obj.min_code) |
|
|
value = eval(obj.min_code) |
|
|
else: |
|
|
else: |
|
|
value = obj.min_fixed_value |
|
|
value = obj.min_fixed_value |
|
|
|
|
|
|
|
|
result[obj.id] = value |
|
|
result[obj.id] = value |
|
|
|
|
|
|
|
|
return result |
|
|
return result |
|
|
|
|
|
|
|
|
def _compute_max_value(self, cr, uid, ids, field_name, arg, context=None): |
|
|
|
|
|
|
|
|
def compute_max_value(self, cr, uid, ids, field_name, arg, context=None): |
|
|
if context is None: |
|
|
if context is None: |
|
|
context = {} |
|
|
context = {} |
|
|
result = {} |
|
|
result = {} |
|
|
|
|
|
|
|
|
for obj in self.browse(cr, uid, ids): |
|
|
|
|
|
value = 0 |
|
|
|
|
|
if obj.max_type == 'local': |
|
|
|
|
|
|
|
|
for obj in self.browse(cr, uid, ids, context): |
|
|
|
|
|
value = None |
|
|
|
|
|
if obj.max_type == 'local' and is_select_query(obj.max_code): |
|
|
cr.execute(obj.max_code) |
|
|
cr.execute(obj.max_code) |
|
|
value = cr.dictfetchone()['value'] |
|
|
|
|
|
|
|
|
dic = cr.dictfetchall() |
|
|
|
|
|
if is_one_value(dic): |
|
|
|
|
|
value = dic[0]['value'] |
|
|
elif obj.max_type == 'python': |
|
|
elif obj.max_type == 'python': |
|
|
value = eval(obj.max_code) |
|
|
value = eval(obj.max_code) |
|
|
elif obj.max_type == 'external': |
|
|
|
|
|
if not dbsource_id: |
|
|
|
|
|
dbsrc_obj = self.pool.get('base.external.dbsource').browse(cr, uid, max_dbsource_id, context) |
|
|
|
|
|
|
|
|
elif obj.max_type == 'external' and obj.max_dbsource_id.id and is_select_query(obj.max_code): |
|
|
|
|
|
dbsrc_obj = self.pool.get('base.external.dbsource').browse(cr, uid, obj.max_dbsource_id.id, context) |
|
|
res = dbsrc_obj.execute(obj.max_code) |
|
|
res = dbsrc_obj.execute(obj.max_code) |
|
|
|
|
|
if is_one_value(res): |
|
|
value = res[0]['value'] |
|
|
value = res[0]['value'] |
|
|
else: |
|
|
else: |
|
|
value = obj.max_fixed_value |
|
|
value = obj.max_fixed_value |
|
|
|
|
|
|
|
|
result[obj.id] = value |
|
|
result[obj.id] = value |
|
|
|
|
|
return result |
|
|
|
|
|
|
|
|
|
|
|
def _is_valid_range(self, cr, uid, ids, field_name, arg, context=None): |
|
|
|
|
|
if context is None: |
|
|
|
|
|
context = {} |
|
|
|
|
|
result = {} |
|
|
|
|
|
for obj in self.browse(cr, uid, ids, context): |
|
|
|
|
|
if obj.max_value < obj.min_value: |
|
|
|
|
|
result[obj.id] = False |
|
|
|
|
|
else: |
|
|
|
|
|
result[obj.id] = True |
|
|
|
|
|
return result |
|
|
|
|
|
|
|
|
|
|
|
def _generate_invalid_message(self, cr, uid, ids, field_name, arg, context=None): |
|
|
|
|
|
if context is None: |
|
|
|
|
|
context = {} |
|
|
|
|
|
result = {} |
|
|
|
|
|
for obj in self.browse(cr, uid, ids, context): |
|
|
|
|
|
if obj.valid: |
|
|
|
|
|
result[obj.id] = "" |
|
|
|
|
|
else: |
|
|
|
|
|
result[obj.id] = "Minimum value is greater than the maximum value! Please adjust them." |
|
|
return result |
|
|
return result |
|
|
|
|
|
|
|
|
_columns = { |
|
|
_columns = { |
|
|
'name': fields.char('Name', size=50, required=True), |
|
|
'name': fields.char('Name', size=50, required=True), |
|
|
|
|
|
'valid': fields.function(_is_valid_range, string='Valid', type='boolean', required=True), |
|
|
|
|
|
'invalid_message': fields.function(_generate_invalid_message, string='Message', type='char', size=100), |
|
|
'min_type': fields.selection((('static','Fixed value'), ('python','Python Code'), ('local', 'SQL - Local DB'), ('external', 'SQL - Externa DB')), 'Min Type', required=True), |
|
|
'min_type': fields.selection((('static','Fixed value'), ('python','Python Code'), ('local', 'SQL - Local DB'), ('external', 'SQL - Externa DB')), 'Min Type', required=True), |
|
|
'min_value': fields.function(_compute_min_value, string='Minimum', type='float'), |
|
|
|
|
|
|
|
|
'min_value': fields.function(compute_min_value, string='Minimum', type='float'), |
|
|
'min_fixed_value': fields.float('Minimum'), |
|
|
'min_fixed_value': fields.float('Minimum'), |
|
|
'min_code': fields.text('Minimum Computation Code'), |
|
|
'min_code': fields.text('Minimum Computation Code'), |
|
|
'min_dbsource_id': fields.many2one('base.external.dbsource','External DB Source'), |
|
|
'min_dbsource_id': fields.many2one('base.external.dbsource','External DB Source'), |
|
|
'max_type': fields.selection((('static','Fixed value'), ('python','Python Code'), ('local', 'SQL - Local DB'), ('external', 'SQL - External DB')), 'Max Type', required=True), |
|
|
'max_type': fields.selection((('static','Fixed value'), ('python','Python Code'), ('local', 'SQL - Local DB'), ('external', 'SQL - External DB')), 'Max Type', required=True), |
|
|
'max_value': fields.function(_compute_max_value, string='Maximum', type='float'), |
|
|
|
|
|
|
|
|
'max_value': fields.function(compute_max_value, string='Maximum', type='float'), |
|
|
'max_fixed_value': fields.float('Maximum'), |
|
|
'max_fixed_value': fields.float('Maximum'), |
|
|
'max_code': fields.text('Maximum Computation Code'), |
|
|
'max_code': fields.text('Maximum Computation Code'), |
|
|
'max_dbsource_id': fields.many2one('base.external.dbsource','External DB Source'), |
|
|
'max_dbsource_id': fields.many2one('base.external.dbsource','External DB Source'), |
|
|
'color': fields.char('Color', help='RGB code with #', size=7, required=True), |
|
|
'color': fields.char('Color', help='RGB code with #', size=7, required=True), |
|
|
|
|
|
'threshold_ids': fields.many2many('mgmtsystem.kpi.threshold','mgmtsystem_kpi_threshold_range_rel', 'range_id', 'Threshold_id', 'Thresholds'), |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
_defaults = { |
|
|
|
|
|
'valid': True, |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
mgmtsystem_kpi_threshold_range() |
|
|
mgmtsystem_kpi_threshold_range() |
|
@ -116,18 +164,61 @@ class mgmtsystem_kpi_threshold(osv.osv): |
|
|
_name = "mgmtsystem.kpi.threshold" |
|
|
_name = "mgmtsystem.kpi.threshold" |
|
|
_description = "KPI Threshold" |
|
|
_description = "KPI Threshold" |
|
|
|
|
|
|
|
|
|
|
|
def _is_valid_threshold(self, cr, uid, ids, field_name, arg, context=None): |
|
|
|
|
|
if context is None: |
|
|
|
|
|
context = {} |
|
|
|
|
|
result = {} |
|
|
|
|
|
for obj in self.browse(cr, uid, ids, context): |
|
|
|
|
|
# check if ranges overlap |
|
|
|
|
|
for range_obj1 in obj.range_ids: |
|
|
|
|
|
for range_obj2 in obj.range_ids: |
|
|
|
|
|
if range_obj1.valid and range_obj2.valid and range_obj1.min_value < range_obj2.min_value: |
|
|
|
|
|
if range_obj1.max_value <= range_obj2.min_value: |
|
|
|
|
|
result[obj.id] = True |
|
|
|
|
|
else: |
|
|
|
|
|
result[obj.id] = False |
|
|
|
|
|
return result |
|
|
|
|
|
|
|
|
|
|
|
def _generate_invalid_message(self, cr, uid, ids, field_name, arg, context=None): |
|
|
|
|
|
if context is None: |
|
|
|
|
|
context = {} |
|
|
|
|
|
result = {} |
|
|
|
|
|
for obj in self.browse(cr, uid, ids, context): |
|
|
|
|
|
if obj.valid: |
|
|
|
|
|
result[obj.id] = "" |
|
|
|
|
|
else: |
|
|
|
|
|
result[obj.id] = "2 of your ranges are overlapping! Please make sure your ranges do not overlap." |
|
|
|
|
|
return result |
|
|
|
|
|
|
|
|
_columns = { |
|
|
_columns = { |
|
|
'name': fields.char('Name', size=50, required=True), |
|
|
'name': fields.char('Name', size=50, required=True), |
|
|
'range_ids': fields.many2many('mgmtsystem.kpi.threshold.range','mgmtsystem_kpi_threshold_range_rel', 'threshold_id', 'range_id', 'Range', required=True), |
|
|
|
|
|
|
|
|
'range_ids': fields.many2many('mgmtsystem.kpi.threshold.range','mgmtsystem_kpi_threshold_range_rel', 'threshold_id', 'range_id', 'Ranges'), |
|
|
|
|
|
'valid': fields.function(_is_valid_threshold, string='Valid', type='boolean', required=True), |
|
|
|
|
|
'invalid_message': fields.function(_generate_invalid_message, string='Message', type='char', size=100), |
|
|
|
|
|
'kpi_ids': fields.one2many('mgmtsystem.kpi', 'threshold_id', 'KPIs'), |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
def write(self, cr, uid, ids, vals, context=None): |
|
|
|
|
|
|
|
|
_defaults = { |
|
|
|
|
|
'valid': True, |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
def create(self, cr, uid, data, context=None): |
|
|
if context is None: |
|
|
if context is None: |
|
|
context = {} |
|
|
context = {} |
|
|
|
|
|
|
|
|
# TODO: check if ranges overlap |
|
|
|
|
|
|
|
|
|
|
|
return super(mgmtsystem_kpi_threshold, self).write(cr, uid, ids, vals, context=context) |
|
|
|
|
|
|
|
|
# check if ranges overlap |
|
|
|
|
|
range_obj1 = self.pool.get('mgmtsystem.kpi.threshold.range') |
|
|
|
|
|
range_obj2 = self.pool.get('mgmtsystem.kpi.threshold.range') |
|
|
|
|
|
for range1 in data['range_ids'][0][2]: |
|
|
|
|
|
range_obj1 = range_obj1.browse(cr, uid, range1, context) |
|
|
|
|
|
for range2 in data['range_ids'][0][2]: |
|
|
|
|
|
range_obj2 = range_obj2.browse(cr, uid, range2, context) |
|
|
|
|
|
if range_obj1.valid and range_obj2.valid and range_obj1.min_value < range_obj2.min_value: |
|
|
|
|
|
if range_obj1.max_value > range_obj2.min_value: |
|
|
|
|
|
raise osv.except_osv(_("2 of your ranges are overlapping!"), _("Please make sure your ranges do not overlap!")) |
|
|
|
|
|
range_obj2 = self.pool.get('mgmtsystem.kpi.threshold.range') |
|
|
|
|
|
range_obj1 = self.pool.get('mgmtsystem.kpi.threshold.range') |
|
|
|
|
|
return super(mgmtsystem_kpi_threshold, self).create(cr, uid, data, context) |
|
|
|
|
|
|
|
|
def get_color(self, cr, uid, ids, kpi_value, context=None): |
|
|
def get_color(self, cr, uid, ids, kpi_value, context=None): |
|
|
if context is None: |
|
|
if context is None: |
|
@ -137,7 +228,7 @@ class mgmtsystem_kpi_threshold(osv.osv): |
|
|
for obj in self.browse(cr, uid, ids, context): |
|
|
for obj in self.browse(cr, uid, ids, context): |
|
|
for range_id in obj.range_ids: |
|
|
for range_id in obj.range_ids: |
|
|
range_obj = self.pool.get('mgmtsystem.kpi.threshold.range').browse(cr, uid, range_id.id, context) |
|
|
range_obj = self.pool.get('mgmtsystem.kpi.threshold.range').browse(cr, uid, range_id.id, context) |
|
|
if range_obj.min_value <= kpi_value <= range_obj.max_value: |
|
|
|
|
|
|
|
|
if range_obj.min_value <= kpi_value <= range_obj.max_value and range_obj.valid: |
|
|
color = range_obj.color |
|
|
color = range_obj.color |
|
|
return color |
|
|
return color |
|
|
|
|
|
|
|
@ -191,17 +282,16 @@ class mgmtsystem_kpi(osv.osv): |
|
|
def compute_kpi_value(self, cr, uid, ids, context=None): |
|
|
def compute_kpi_value(self, cr, uid, ids, context=None): |
|
|
if context is None: |
|
|
if context is None: |
|
|
context = {} |
|
|
context = {} |
|
|
kpi_value = 0 |
|
|
|
|
|
|
|
|
|
|
|
for obj in self.browse(cr, uid, ids): |
|
|
for obj in self.browse(cr, uid, ids): |
|
|
kpi_value = 0 |
|
|
kpi_value = 0 |
|
|
if obj.kpi_type == 'local': |
|
|
|
|
|
|
|
|
if obj.kpi_type == 'local' and is_select_query(obj.kpi_code): |
|
|
cr.execute(obj.kpi_code) |
|
|
cr.execute(obj.kpi_code) |
|
|
|
|
|
if is_one_value(cr.dictfetchall()): |
|
|
kpi_value = cr.dictfetchone()['value'] |
|
|
kpi_value = cr.dictfetchone()['value'] |
|
|
elif obj.kpi_type == 'external': |
|
|
|
|
|
if obj.dbsource_id.id: |
|
|
|
|
|
|
|
|
elif obj.kpi_type == 'external' and obj.dbsource_id.id and is_select_query(obj.kpi_code): |
|
|
dbsrc_obj = self.pool.get('base.external.dbsource').browse(cr, uid, obj.dbsource_id.id, context) |
|
|
dbsrc_obj = self.pool.get('base.external.dbsource').browse(cr, uid, obj.dbsource_id.id, context) |
|
|
res = dbsrc_obj.execute(obj.kpi_code) |
|
|
res = dbsrc_obj.execute(obj.kpi_code) |
|
|
|
|
|
if is_one_value(res): |
|
|
kpi_value = res[0]['value'] |
|
|
kpi_value = res[0]['value'] |
|
|
elif obj.kpi_type == 'python': |
|
|
elif obj.kpi_type == 'python': |
|
|
kpi_value = eval(obj.kpi_code) |
|
|
kpi_value = eval(obj.kpi_code) |
|
|