2 # -------------------------------------------------------------------------
3 # Copyright (c) 2015-2017 AT&T Intellectual Property
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # -------------------------------------------------------------------------
25 class ThresholdException(Exception):
30 """Returns True if the value is a number"""
32 if type(input) is int or type(input) is float:
34 elif isinstance(input, six.string_types) and float(input):
41 class Threshold(object):
42 OPERATORS = ['=', '<', '>', '<=', '>=']
62 def __init__(self, expression, base_unit):
63 if not isinstance(expression, six.string_types):
64 raise ThresholdException("Expression must be a string")
65 if not isinstance(base_unit, six.string_types):
66 raise ThresholdException("Base unit must be a string")
67 if base_unit not in self.UNITS:
68 raise ThresholdException(
69 "Base unit {} unsupported, must be one of: {}".format(
70 base_unit, ', '.join(self.UNITS.keys())))
72 self._expression = expression
73 self._base_unit = base_unit
77 """Object representation"""
78 return "<Threshold expression: '{}', base_unit: '{}', " \
79 "parts: {}>".format(self.expression, self.base_unit, self.parts)
82 """Returns a single list of all supported units"""
83 unit_lists = [self.UNITS[k].keys() for k in self.UNITS.keys()]
84 return list(itertools.chain.from_iterable(unit_lists))
86 def _default_for_base_unit(self, base_unit):
87 """Returns the default unit (1.0 multiplier) for a given base unit
89 Returns None if not found.
91 units = self.UNITS.get(base_unit)
93 for name, multiplier in units.items():
98 def _multiplier_for_unit(self, unit):
99 """Returns the multiplier for a given unit
101 Returns None if not found.
103 return self.UNITS.get(self.base_unit).get(unit)
106 """Resets parsed components"""
107 self._operator = None
109 self._min_value = None
110 self._max_value = None
115 """Parses the expression into parts"""
117 parts = self.expression.split()
120 if not self.operator and part in self.OPERATORS:
122 raise ThresholdException(
123 "Value {} encountered before operator {} "
124 "in expression '{}'".format(
125 self.value, part, self.expression))
127 raise ThresholdException(
128 "Range {}-{} encountered before operator {} "
129 "in expression '{}'".format(
130 self.min_value, self.max_value,
131 part, self.expression))
133 raise ThresholdException(
134 "Unit '{}' encountered before operator {} "
135 "in expression '{}'".format(
136 self.unit, part, self.expression))
138 self._operator = part
140 # Is it a lone value?
141 elif not self.value and is_number(part):
143 raise ThresholdException(
144 "Range {}-{} encountered before value {} "
145 "in expression '{}'".format(
146 self.min_value, self.max_value,
147 part, self.expression))
149 raise ThresholdException(
150 "Unit '{}' encountered before value {} "
151 "in expression '{}'".format(
152 self.unit, part, self.expression))
153 self._value = float(part)
154 if not self.operator:
157 # Is it a value range?
158 elif not self.has_range and part.count('-') == 1:
159 part1, part2 = part.split('-')
160 if is_number(part1) and is_number(part2):
161 if self.operator and self.operator != '=':
162 raise ThresholdException(
163 "Operator {} not supported with range {} "
164 "in expression '{}'".format(
165 self.operator, part, self.expression))
167 raise ThresholdException(
168 "Value {} encountered before range {} "
169 "in expression '{}'".format(
170 self.value, part, self.expression))
172 raise ThresholdException(
173 "Unit '{}' encountered before range {} "
174 "in expression '{}'".format(
175 self.unit, part, self.expression))
176 self._min_value = min(float(part1), float(part2))
177 self._max_value = max(float(part1), float(part2))
178 if not self.operator:
182 elif part in self._all_units():
183 if not self.value and not self.has_range:
185 raise ThresholdException(
186 "Value {} encountered before unit {} "
187 "in expression '{}'".format(
188 self.value, part, self.expression))
190 raise ThresholdException(
191 "Range {}-{} encountered before unit {} "
192 "in expression '{}'".format(
193 self.min_value, self.max_value,
194 part, self.expression))
197 # Well then, we don't know.
199 raise ThresholdException(
200 "Unknown part '{}' in expression '{}'".format(
201 part, self._expression))
203 if not self.has_range and not self._value:
204 raise ThresholdException(
205 "Value/range missing in expression '{}'".format(
209 # Convert from stated units to default.
210 multiplier = self._multiplier_for_unit(self._unit)
212 self._value = self._value * multiplier
214 self._min_value = self._min_value * multiplier
215 self._max_value = self._max_value * multiplier
217 # Always use the default unit.
218 self._unit = self._default_for_base_unit(self._base_unit)
224 """Returns the original base unit"""
225 return self._base_unit
228 def expression(self):
229 """Returns the original expression"""
230 return self._expression
234 """Returns True if a minimum/maximum value range exists"""
235 return self.min_value and self.max_value
239 """Returns the detected maximum value, if any"""
240 return self._max_value
244 """Returns the detected minimum value, if any"""
245 return self._min_value
249 """Returns the operator"""
250 return self._operator
254 """Returns True if the expression was successfully parsed"""
259 """Returns the expression as a dictionary of parts"""
262 result['operator'] = self.operator
265 'min': self.min_value,
266 'max': self.max_value,
269 result['value'] = self.value
270 result['units'] = self.unit
275 """Returns the units"""
280 """Returns the detected value, if any"""