From 9cad3666adb960e2ffd7f0a435968ee5449fe514 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Fri, 28 Aug 2015 14:45:28 +0200 Subject: [PATCH 1/2] [IMP] mis_builder: update min/max function signatures so min(x, y, ...) and min([x, y, ..]) both work as expected. --- mis_builder/models/mis_builder.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mis_builder/models/mis_builder.py b/mis_builder/models/mis_builder.py index 109b9fdc..0c9bde16 100644 --- a/mis_builder/models/mis_builder.py +++ b/mis_builder/models/mis_builder.py @@ -81,16 +81,16 @@ def _avg(l): return sum(l) / float(len(l)) -def _min(l): +def _min(*l): if not l: return None - return min(l) + return min(*l) -def _max(l): +def _max(*l): if not l: return None - return max(l) + return max(*l) class MisReportKpi(models.Model): From f4d0bb25eba3d1b87a3a26c64f19c010ac989265 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Sun, 30 Aug 2015 16:32:34 +0200 Subject: [PATCH 2/2] [FIX] mis_builder: correct implementation and doctests for _min/_max --- mis_builder/models/aggregate.py | 149 ++++++++++++++++++++++++++++++ mis_builder/models/mis_builder.py | 25 +---- 2 files changed, 150 insertions(+), 24 deletions(-) create mode 100644 mis_builder/models/aggregate.py diff --git a/mis_builder/models/aggregate.py b/mis_builder/models/aggregate.py new file mode 100644 index 00000000..4788c4c2 --- /dev/null +++ b/mis_builder/models/aggregate.py @@ -0,0 +1,149 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# mis_builder module for Odoo, Management Information System Builder +# Copyright (C) 2014-2015 ACSONE SA/NV () +# +# This file is a part of mis_builder +# +# mis_builder is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License v3 or later +# as published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mis_builder is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License v3 or later for more details. +# +# You should have received a copy of the GNU Affero General Public License +# v3 or later along with this program. +# If not, see . +# +############################################################################## + + +def _sum(l): + """ Same as stdlib sum but returns None instead of 0 + in case of empty sequence. + + >>> sum([1]) + 1 + >>> _sum([1]) + 1 + >>> sum([1, 2]) + 3 + >>> _sum([1, 2]) + 3 + >>> sum([]) + 0 + >>> _sum([]) + """ + if not l: + return None + return sum(l) + + +def _avg(l): + """ Arithmetic mean of a sequence. Returns None in case of empty sequence. + + >>> _avg([1]) + 1.0 + >>> _avg([1, 2]) + 1.5 + >>> _avg([]) + """ + if not l: + return None + return sum(l) / float(len(l)) + + +def _min(*args): + """ Same as stdlib min but returns None instead of exception + in case of empty sequence. + + >>> min(1, 2) + 1 + >>> _min(1, 2) + 1 + >>> min([1, 2]) + 1 + >>> _min([1, 2]) + 1 + >>> min(1) + Traceback (most recent call last): + File "", line 1, in ? + TypeError: 'int' object is not iterable + >>> _min(1) + Traceback (most recent call last): + File "", line 1, in ? + TypeError: 'int' object is not iterable + >>> min([1]) + 1 + >>> _min([1]) + 1 + >>> min() + Traceback (most recent call last): + File "", line 1, in ? + TypeError: min expected 1 arguments, got 0 + >>> _min() + Traceback (most recent call last): + File "", line 1, in ? + TypeError: min expected 1 arguments, got 0 + >>> min([]) + Traceback (most recent call last): + File "", line 1, in ? + ValueError: min() arg is an empty sequence + >>> _min([]) + """ + if len(args) == 1 and not args[0]: + return None + return min(*args) + + +def _max(*args): + """ Same as stdlib max but returns None instead of exception + in case of empty sequence. + + >>> max(1, 2) + 2 + >>> _max(1, 2) + 2 + >>> max([1, 2]) + 2 + >>> _max([1, 2]) + 2 + >>> max(1) + Traceback (most recent call last): + File "", line 1, in ? + TypeError: 'int' object is not iterable + >>> _max(1) + Traceback (most recent call last): + File "", line 1, in ? + TypeError: 'int' object is not iterable + >>> max([1]) + 1 + >>> _max([1]) + 1 + >>> max() + Traceback (most recent call last): + File "", line 1, in ? + TypeError: max expected 1 arguments, got 0 + >>> _max() + Traceback (most recent call last): + File "", line 1, in ? + TypeError: max expected 1 arguments, got 0 + >>> max([]) + Traceback (most recent call last): + File "", line 1, in ? + ValueError: max() arg is an empty sequence + >>> _max([]) + """ + if len(args) == 1 and not args[0]: + return None + return max(*args) + + +if __name__ == "__main__": + import doctest + doctest.testmod() diff --git a/mis_builder/models/mis_builder.py b/mis_builder/models/mis_builder.py index 0c9bde16..bb3f676d 100644 --- a/mis_builder/models/mis_builder.py +++ b/mis_builder/models/mis_builder.py @@ -35,6 +35,7 @@ from openerp import api, fields, models, _ from openerp.tools.safe_eval import safe_eval from .aep import AccountingExpressionProcessor as AEP +from .aggregate import _sum, _avg, _min, _max _logger = logging.getLogger(__name__) @@ -69,30 +70,6 @@ def _is_valid_python_var(name): return re.match("[_A-Za-z][_a-zA-Z0-9]*$", name) -def _sum(l): - if not l: - return None - return sum(l) - - -def _avg(l): - if not l: - return None - return sum(l) / float(len(l)) - - -def _min(*l): - if not l: - return None - return min(*l) - - -def _max(*l): - if not l: - return None - return max(*l) - - class MisReportKpi(models.Model): """ A KPI is an element (ie a line) of a MIS report.