Passed
Pull Request — 2.x (#1769)
by Jordi
08:37 queued 03:35
created

senaite.core.api.measure   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 133
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 16
eloc 49
dl 0
loc 133
rs 10
c 0
b 0
f 0

5 Functions

Rating   Name   Duplication   Size   Complexity  
A get_quantity() 0 15 2
A is_volume() 0 14 2
A is_weight() 0 14 2
A is_magnitude() 0 8 1
C get_magnitude() 0 40 9
1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of SENAITE.CORE.
4
#
5
# SENAITE.CORE is free software: you can redistribute it and/or modify it under
6
# the terms of the GNU General Public License as published by the Free Software
7
# Foundation, version 2.
8
#
9
# This program is distributed in the hope that it will be useful, but WITHOUT
10
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
# details.
13
#
14
# You should have received a copy of the GNU General Public License along with
15
# this program; if not, write to the Free Software Foundation, Inc., 51
16
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
#
18
# Copyright 2018-2021 by it's authors.
19
# Some rights reserved, see README and LICENSE.
20
21
import re
22
23
import six
24
from bika.lims import APIError
25
from bika.lims.api import fail
26
from magnitude import Magnitude
27
from magnitude import MagnitudeError
28
from magnitude import mg
29
from magnitude import new_mag
30
31
_marker = object()
32
33
34
def is_magnitude(value):
35
    """Returns whether the value is a magnitude object
36
37
    :param value: the object to check
38
    :return: True if value is a magnitude object
39
    :rtype: bool
40
    """
41
    return isinstance(value, Magnitude)
42
43
44
def get_magnitude(value, default=_marker):
45
    """Returns the Magnitude object that represents the value
46
47
    :param value: a string that representing a number and unit (e.g. 10 mL)
48
    :param default: default value to convert to a magnitude object
49
    :type value: str or Magnitude
50
    :return: Magnitude object
51
    :rtype: magnitude.Magnitude
52
    """
53
    if is_magnitude(value):
54
        # A Magnitude object already
55
        return value
56
57
    if not value or not isinstance(value, six.string_types):
58
        # Handle empty and non-str values properly
59
        if default is _marker:
60
            fail("{} is not supported.".format(repr(value)))
61
        return get_magnitude(default)
62
63
    # Split number and unit(s)
64
    matches = re.match(r"([.\d]+)\s*(\w+.*)", value)
65
    groups = matches and matches.groups() or []
66
    if len(groups) != 2:
67
        if default is _marker:
68
            fail("No valid format: {}".format(value))
69
        return get_magnitude(default)
70
71
    # L (litter) unit is commonly used to avoid confusion with the number 1,
72
    # but is not supported by magnitude
73
    # Create the magnitude for SI derived unit L (=litter)
74
    # https://github.com/juanre/magnitude/blob/master/magnitude.py#L1147
75
    new_mag("L", Magnitude(0.001, m=3))
76
77
    # Get the magnitude
78
    try:
79
        return mg(float(groups[0]), groups[1])
80
    except MagnitudeError as e:
81
        if default is _marker:
82
            fail(e.message)
83
        return get_magnitude(default)
84
85
86
def get_quantity(value, unit=None, num_digits=16):
87
    """Returns the quantity of the value that represents a number and unit
88
89
    :param value: the value to extract the quantity from
90
    :param unit: the basic or derived SI unit to convert to
91
    :param num_digits: the number of digits to consider for rounding
92
    :type value: str or Magnitude
93
    :type unit: str
94
    :return: the quantity of the value in the given units
95
    :rtype: float
96
    """
97
    mag = get_magnitude(value)
98
    if unit:
99
        mag.ounit(unit)
100
    return round(mag.toval(), num_digits)
101
102
103
def is_volume(value):
104
    """Returns whether the value passed in represents a valid volume
105
106
    :param value: the value to check
107
    :type value: str or Magnitude
108
    :return: True if the value passed in represents a volume
109
    :rtype: bool
110
    """
111
    try:
112
        # Get the magnitude and check if can be converted to "l" (volume unit)
113
        get_quantity(value, "l")
114
        return True
115
    except (MagnitudeError, APIError):
116
        return False
117
118
119
def is_weight(value):
120
    """Returns whether the value passed in represents a valid weight
121
122
    :param value: the value to check
123
    :type value: str or Magnitude
124
    :return: True if the value passed in represents a weight
125
    :rtype: bool
126
    """
127
    try:
128
        # Get the magnitude and check if can be converted to "g" (weight unit)
129
        get_quantity(value, "g")
130
        return True
131
    except (MagnitudeError, APIError):
132
        return False
133