Completed
Pull Request — 2.x (#1768)
by Jordi
05:47
created

senaite.core.api.measure.is_volume()   A

Complexity

Conditions 2

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 13
rs 10
c 0
b 0
f 0
cc 2
nop 1
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
import six
23
from bika.lims import APIError
24
from bika.lims import api
25
from bika.lims.api import fail
26
27
from magnitude import Magnitude
28
from magnitude import MagnitudeError
29
from magnitude import mg
30
from magnitude import new_mag
31
32
_marker = object()
33
34
35
def is_magnitude(value):
36
    """Returns whether the value is a magnitude object
37
38
    :param value: the object to check
39
    :return: True if value is a magnitude object
40
    :rtype: bool
41
    """
42
    return isinstance(value, Magnitude)
43
44
45
def get_magnitude(value, default=_marker):
46
    """Returns the Magnitude object that represents the value
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
    :param value: the value to check
106
    :type value: str or Magnitude
107
    :return: True if the value passed in represents a volume
108
    :rtype: bool
109
    """
110
    try:
111
        # Get the magnitude and check if can be converted to "l" (volume unit)
112
        get_quantity(value, "l")
113
        return True
114
    except (MagnitudeError, APIError):
115
        return False
116
117
118
def is_weight(value):
119
    """Returns whether the value passed in represents a valid weight
120
    :param value: the value to check
121
    :type value: str or Magnitude
122
    :return: True if the value passed in represents a weight
123
    :rtype: bool
124
    """
125
    try:
126
        # Get the magnitude and check if can be converted to "g" (weight unit)
127
        get_quantity(value, "g")
128
        return True
129
    except (MagnitudeError, APIError):
130
        return False
131