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.