pax_global_header00006660000000000000000000000064134621426020014512gustar00rootroot0000000000000052 comment=0104aed770b8ddc39d2c62173a42b5065cad339d malexer-meteocalc-0104aed/000077500000000000000000000000001346214260200155015ustar00rootroot00000000000000malexer-meteocalc-0104aed/.gitignore000066400000000000000000000014441346214260200174740ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # pyenv .python-version # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *,cover .hypothesis/ .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ # PyBuilder target/ #Ipython Notebook .ipynb_checkpoints malexer-meteocalc-0104aed/.travis.yml000066400000000000000000000003371346214260200176150ustar00rootroot00000000000000language: python matrix: include: - python: 2.7 - python: 3.2 - python: 3.3 - python: 3.4 - python: 3.5 - python: 3.6 - python: 3.7 dist: xenial # command to run tests script: nosetests malexer-meteocalc-0104aed/LICENSE000066400000000000000000000021001346214260200164770ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2016 Oleksii (Alex) Markov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. malexer-meteocalc-0104aed/README.rst000066400000000000000000000043351346214260200171750ustar00rootroot00000000000000meteocalc ========= .. image:: https://travis-ci.org/malexer/meteocalc.svg?branch=master :target: https://travis-ci.org/malexer/meteocalc Several functions for calculation of meteorological variables. Calculations were implemented based on publicly available formulas. Implemented calculations: 1. **Dew Point** is the temperature at which dew forms. 2. **Heat Index** is an index that combines air temperature and relative humidity in an attempt to determine the human-perceived equivalent temperature. 3. **Wind Chill** is the lowering of body temperature due to the passing-flow of lower-temperature air. 4. **Feels Like temperature** or Apparent temperature is the temperature equivalent perceived by humans, caused by the combined effects of air temperature, relative humidity and wind speed. Also **Temp** class is available to convert temperature between Celsius, Fahrenheit and Kelvin. It is also can be mixed with floats for basic math operations. Requirements ------------ * Python 2.7 or 3.2+ Install ------- .. code-block:: shell $ pip install meteocalc Usage ----- ..note: Any input Temperature value can be provided in different units: ``Temp(20, 'c') # c - celsius, f - fahrenheit, k - kelvin`` .. code-block:: python from meteocalc import Temp, dew_point, heat_index, wind_chill, feels_like # create input temperature in different units t = Temp(20, 'c') # c - celsius, f - fahrenheit, k - kelvin t2 = Temp(60, 'f') # calculate Dew Point dp = dew_point(temperature=t, humidity=56) # calculate Heat Index hi = heat_index(temperature=t2, humidity=42) print('Dew Point in celsius:', dp.c) print('Dew Point in fahrenheit:', dp.f) print('Heat Index in kelvin:', hi.k) # calculate Wind Chill wc = wind_chill(temperature=15, wind_speed=25) print('Wind Chill in fahrenheit:', wc.f) # calculate Feels Like temperature fl = feels_like(temperature=40, humidity=40, wind_speed=5) print('Feels Like in fahrenheit:', fl.f) History ======= v 1.1.0 - 2019-04-30 -------------------- Added: ~~~~~~ * Wind Chill and Feels Like temperature (thanks to @Currywurst) v 1.0.0 - 2016-04-03 -------------------- Added: ~~~~~~ * First version malexer-meteocalc-0104aed/meteocalc/000077500000000000000000000000001346214260200174355ustar00rootroot00000000000000malexer-meteocalc-0104aed/meteocalc/__init__.py000066400000000000000000000004531346214260200215500ustar00rootroot00000000000000from .dewpoint import dew_point from .heatindex import heat_index from .windchill import wind_chill from .feelslike_temperature import feels_like from .temperature import Temp, C, F, K __all__ = [ 'dew_point', 'heat_index', 'wind_chill', 'feels_like', 'Temp', 'C', 'F', 'K', ] malexer-meteocalc-0104aed/meteocalc/classutils.py000066400000000000000000000034251346214260200222010ustar00rootroot00000000000000import sys from functools import wraps import operator PYTHON2 = sys.version_info.major == 2 class FloatCompatible(type): """Metaclass to make Temp class compatible with float for basic math. This will allow to mix Temp class with floats in basic math expressions and return Temp instance in result of the same unit. """ math_methods = [ '__add__', '__sub__', '__mul__', '__truediv__', '__pos__', '__neg__', ] math_rmethods = ['__radd__', '__rsub__', '__rmul__', '__rtruediv__'] def __new__(cls, name, bases, namespace): if PYTHON2: cls.math_methods.append('__div__') cls.math_rmethods.append('__rdiv__') for method in cls.math_methods: namespace[method] = cls.math_method(method) for rmethod in cls.math_rmethods: method = rmethod.replace('__r', '__') namespace[rmethod] = cls.math_method(method, right_operator=True) return super(FloatCompatible, cls).__new__(cls, name, bases, namespace) @classmethod def math_method(cls, name, right_operator=False): """Generate method for math operation by name. :param name: name of method. i.e. '__add__' :param right_operator: is it a "right" operation as '__radd__' :type right_operator: bool """ math_func = getattr(operator, name) @wraps(math_func) def wrapper(*args): # [self, other] - binary operators, [self] - unary args = list(args) self = args[0] args[0] = self.value if right_operator: args = args[::-1] # args: self, other -> other, self result = math_func(*args) return type(self)(result, unit=self.unit) return wrapper malexer-meteocalc-0104aed/meteocalc/dewpoint.py000066400000000000000000000026021346214260200216400ustar00rootroot00000000000000"""Module for calculation of Dew Point. The dew point is the temperature at which dew forms and is a measure of atmospheric moisture. It is the temperature to which air must be cooled at constant pressure and water content to reach saturation. Check wikipedia for more info: https://en.wikipedia.org/wiki/Dew_point """ import math from .temperature import Temp, C def dew_point(temperature, humidity): """Calculate Dew Point temperature. Two set of constants are used provided by Arden Buck: for positive and negative temperature ranges. :param temperature: temperature value in Celsius or Temp instance. :type temperature: int, float, Temp :param humidity: relative humidity in % (1-100) :type humidity: int, float :returns: Dew Point temperature :rtype: Temp """ CONSTANTS = dict( positive=dict(b=17.368, c=238.88), negative=dict(b=17.966, c=247.15), ) if humidity < 1 or humidity > 100: msg = 'Incorrect value for humidity: "{}". Correct range 1-100.' raise ValueError(msg.format(humidity)) T = temperature.c if isinstance(temperature, Temp) else temperature RH = humidity const = CONSTANTS['positive'] if T > 0 else CONSTANTS['negative'] pa = RH / 100. * math.exp(const['b'] * T / (const['c'] + T)) dp = const['c'] * math.log(pa) / (const['b'] - math.log(pa)) return Temp(dp, C) malexer-meteocalc-0104aed/meteocalc/feelslike_temperature.py000066400000000000000000000024611346214260200243720ustar00rootroot00000000000000"""Module to simplify calculation of Wind chill and heat_index.""" from .temperature import Temp, F from .windchill import wind_chill from .heatindex import heat_index def feels_like(temperature, humidity, wind_speed): """Calculate Feels Like temperature based on NOAA. Logic: * Wind Chill: temperature <= 50 F and wind > 3 mph * Heat Index: temperature >= 80 F * Temperature as is: all other cases Default unit for resulting Temp value is Fahrenheit and it will be used in case of casting to int/float. Use Temp properties to convert result to Celsius (Temp.c) or Kelvin (Temp.k). :param temperature: temperature value in Fahrenheit or Temp instance. :type temperature: int, float, Temp :param humidity: relative humidity in % (1-100) :type humidity: int, float :param wind_speed: wind speed in mph :type wind_speed: int, float :returns: Feels Like value :rtype: Temp """ T = temperature.f if isinstance(temperature, Temp) else temperature if T <= 50 and wind_speed > 3: # Wind Chill for low temp cases (and wind) FEELS_LIKE = wind_chill(T, wind_speed) elif T >= 80: # Heat Index for High temp cases FEELS_LIKE = heat_index(T, humidity) else: FEELS_LIKE = T return Temp(FEELS_LIKE, unit=F) malexer-meteocalc-0104aed/meteocalc/heatindex.py000066400000000000000000000034351346214260200217650ustar00rootroot00000000000000"""Module for calculation of Heat Index. Heat Index or humiture or "feels like temperature" is an index thatcombines air temperature and relative humidity in an attempt to determine the human-perceived equivalent temperature. Check wikipedia for more info: https://en.wikipedia.org/wiki/Heat_index Formula details: http://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml """ import math from .temperature import Temp, F def heat_index(temperature, humidity): """Calculate Heat Index (feels like temperature) based on NOAA equation. HI is useful only when the temperature is minimally 80 F with a relative humidity of >= 40%. Default unit for resulting Temp value is Fahrenheit and it will be used in case of casting to int/float. Use Temp properties to convert result to Celsius (Temp.c) or Kelvin (Temp.k). :param temperature: temperature value in Fahrenheit or Temp instance. :type temperature: int, float, Temp :param humidity: relative humidity in % (1-100) :type humidity: int, float :returns: Heat Index value :rtype: Temp """ c1 = -42.379 c2 = 2.04901523 c3 = 10.14333127 c4 = -0.22475541 c5 = -6.83783e-3 c6 = -5.481717e-2 c7 = 1.22874e-3 c8 = 8.5282e-4 c9 = -1.99e-6 T = temperature.f if isinstance(temperature, Temp) else temperature RH = humidity # try simplified formula first (used for HI < 80) HI = 0.5 * (T + 61. + (T - 68.) * 1.2 + RH * 0.094) if HI >= 80: # use Rothfusz regression HI = math.fsum([ c1, c2 * T, c3 * RH, c4 * T * RH, c5 * T**2, c6 * RH**2, c7 * T**2 * RH, c8 * T * RH**2, c9 * T**2 * RH**2, ]) return Temp(HI, unit=F) malexer-meteocalc-0104aed/meteocalc/temperature.py000066400000000000000000000061671346214260200223560ustar00rootroot00000000000000"""Temperature conversion routines.""" from .classutils import FloatCompatible C = 'c' # Celsius F = 'f' # Fahrenheit K = 'k' # Kelvin # support metaclass both in Python 2 and 3 AbstractTemp = FloatCompatible('AbstractTemp', (object, ), {}) class Temp(AbstractTemp): """Temperature value. Temp instance can be created in any unit by specifying `unit` attribute. Can be converted to any unit by using properties: .c. .f, .k Currently supported units: C - Celsius F - Fahrenheit K - Kelvin """ _allowed_units = (C, F, K) _conversions = dict( # Fahrenheit c2f=lambda t: t * 9 / 5. + 32, f2c=lambda t: (t - 32) * 5 / 9., # Kelvin c2k=lambda t: t + 273.15, k2c=lambda t: t - 273.15, ) def __init__(self, temperature, unit='C'): """Create new temperature value. :param temperature: temperature value in selected units. :type temperature: int, float :param unit: temperature unit, allowed values: C, F, K. :type unit: str """ self.unit = unit.lower() self.value = float(temperature) if self.unit not in self._allowed_units: allowed_units = ', '.join( map(lambda u: '"%s"' % u.upper(), self._allowed_units) ) msg = 'Unsupported unit "{}". Currently supported units: {}.' raise ValueError(msg.format(unit, allowed_units)) @classmethod def convert(cls, value, from_units, to_units): """Convert temperature value between any supported units. Conversion is performed using Celsius as a base unit. i.e. Fahrenheit -> Kelvin will be converted in two steps: F -> C -> K :param value: temperature value :type value: int, float :param from_units: source units ('C', 'F', 'K') :param to_units: target units ('C', 'F', 'K') :rtype: float """ from_units = from_units.lower() to_units = to_units.lower() if from_units == to_units: return value if from_units != C: func_name = '{}2{}'.format(from_units, C) f = cls._conversions[func_name] value = f(value) if to_units == C: return value func_name = '{}2{}'.format(C, to_units) f = cls._conversions[func_name] return f(value) def _convert_to(self, unit): return self.convert(self.value, from_units=self.unit, to_units=unit) @property def c(self): """Temperature in Celsius.""" return self._convert_to(C) @property def f(self): """Temperature in Fahrenheit.""" return self._convert_to(F) @property def k(self): """Temperature in Kelvin.""" return self._convert_to(K) def __float__(self): return self.value def __int__(self): return int(self.value) def __round__(self, n=0): return round(self.value, n) def __str__(self): return str(self.value) def __repr__(self): return 'Temp({}, unit="{}")'.format(self.value, self.unit.upper()) malexer-meteocalc-0104aed/meteocalc/windchill.py000066400000000000000000000031311346214260200217620ustar00rootroot00000000000000"""Module for calculation of Wind chill. Wind-chill or windchill (popularly wind chill factor) is the lowering of body temperature due to the passing-flow of lower-temperature air. Wind chill numbers are always lower than the air temperature for values where the formula is valid. When the apparent temperature is higher than the air temperature, the heat index is used instead. Check wikipedia for more info: https://en.wikipedia.org/wiki/Wind_chill Formula details: https://www.wpc.ncep.noaa.gov/html/windchill.shtml """ from .temperature import Temp, F def wind_chill(temperature, wind_speed): """Calculate Wind Chill (feels like temperature) based on NOAA. Default unit for resulting Temp value is Fahrenheit and it will be used in case of casting to int/float. Use Temp properties to convert result to Celsius (Temp.c) or Kelvin (Temp.k). Wind Chill Temperature is only defined for temperatures at or below 50 F and wind speeds above 3 mph. :param temperature: temperature value in Fahrenheit or Temp instance. :type temperature: int, float, Temp :param wind_speed: wind speed in mph :type wind_speed: int, float :returns: Wind chill value :rtype: Temp """ T = temperature.f if isinstance(temperature, Temp) else temperature V = wind_speed if T > 50 or V <= 3: raise ValueError( "Wind Chill Temperature is only defined for temperatures at" " or below 50 F and wind speeds above 3 mph.") WINDCHILL = 35.74 + (0.6215 * T) - 35.75 * V**0.16 + 0.4275 * T * V**0.16 return Temp(WINDCHILL, unit=F) malexer-meteocalc-0104aed/setup.py000066400000000000000000000026751346214260200172250ustar00rootroot00000000000000from setuptools import setup from codecs import open from os import path here = path.abspath(path.dirname(__file__)) with open(path.join(here, 'README.rst'), encoding='utf-8') as f: long_description = f.read() setup( name='meteocalc', version='1.1.0', description='Functions for calculation of meteorological variables.', long_description=long_description, url='https://github.com/malexer/meteocalc', author='Alex (Oleksii) Markov', author_email='alex@markovs.me', license='MIT', classifiers=[ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'Intended Audience :: Science/Research', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Scientific/Engineering :: Atmospheric Science', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', ], keywords=( 'meteorology meteo dew heat heatindex humiture humidex feels like ' 'temp temperature wind chill'), packages=['meteocalc'], ) malexer-meteocalc-0104aed/tests/000077500000000000000000000000001346214260200166435ustar00rootroot00000000000000malexer-meteocalc-0104aed/tests/__init__.py000066400000000000000000000000001346214260200207420ustar00rootroot00000000000000malexer-meteocalc-0104aed/tests/context.py000066400000000000000000000003011346214260200206730ustar00rootroot00000000000000import os import sys here = os.path.dirname(__file__) sys.path.insert(0, os.path.abspath(os.path.join(here, '..'))) from meteocalc import dew_point, feels_like, heat_index, wind_chill, Temp malexer-meteocalc-0104aed/tests/test_dewpoint.py000066400000000000000000000022121346214260200221020ustar00rootroot00000000000000import unittest from tests.context import dew_point, Temp dew_point_table = { (-20, 76): -23, (-20, 52): -27, (-20, 20): -37, (-20, 9): -45, (-4, 90): -5, (-4, 52): -12, (-4, 32): -18, (-4, 11): -30, (0, 92): -1, (0, 60): -7, (0, 28): -16, (8, 93): 7, (8, 56): 0, (8, 26): -10, (19, 82): 16, (19, 39): 5, (19, 14): -9, (32, 90): 30, (32, 66): 25, (32, 35): 15, (32, 9): -5, (46, 89): 44, (46, 52): 34, (46, 22): 19, } class DewPointTest(unittest.TestCase): def test_dew_point_for_100_humidity(self): for t in range(-40, 60, 5): self.assertEqual(round(dew_point(t, 100)), t) def test_return_type(self): self.assertIsInstance(dew_point(-20, 40), Temp) def test_dew_point_by_table(self): for t_rh, dp in dew_point_table.items(): t, rh = t_rh self.assertEqual(round(dew_point(t, rh)), dp) def test_invalid_range(self): self.assertRaises(ValueError, dew_point, -20, 0) self.assertRaises(ValueError, dew_point, -20, 100.1) if __name__ == '__main__': unittest.main() malexer-meteocalc-0104aed/tests/test_feels_like_temp.py000066400000000000000000000040751346214260200234110ustar00rootroot00000000000000import unittest from tests.context import feels_like, Temp feels_like_table = { # Wind Chill # (F, Relative_Humidity, Wind_Speed_MPH): F (-45, 50, 5): -63, (-45, 50, 35): -89, (-45, 50, 60): -98, (-30, 50, 5): -46, (-30, 50, 35): -69, (-30, 50, 60): -76, (-15, 50, 35): -48, (-15, 50, 60): -55, (0, 50, 5): -11, (0, 50, 30): -26, (0, 50, 60): -33, (15, 50, 5): 7, (15, 50, 25): -4, (15, 50, 40): -8, (15, 50, 60): -11, (40, 50, 5): 36, (40, 50, 30): 28, (40, 50, 60): 25, # Just Temperature (no Wind Chill or Heat Index effect) (-45, 50, 0): -45, (-15, 50, 0): -15, (0, 50, 0): 0, (40, 50, 0): 40, (51, 50, 0): 51, (50, 50, 3): 50, (51, 50, 5): 51, (51, 50, 50): 51, (60, 50, 50): 60, (60, 50, 0): 60, (70, 50, 50): 70, (79, 50, 50): 79, # Heat Index # (F, Relative_Humidity, Wind_Speed_MPH): F (80, 40, 20): 80, (88, 40, 20): 88, (90, 40, 20): 91, (96, 40, 20): 101, (98, 40, 20): 105, (106, 40, 20): 124, (108, 40, 20): 130, (110, 40, 20): 136, (80, 65, 20): 82, (84, 65, 20): 89, (86, 65, 20): 93, (96, 65, 20): 121, (100, 65, 20): 136, (80, 100, 20): 87, (82, 100, 20): 95, (86, 100, 20): 112, (90, 100, 20): 132, } class FeelsLikeTest(unittest.TestCase): def test_feels_like_by_table(self): for temp_rh_wind, feels_like_f in feels_like_table.items(): temp_f, rel_humidity, wind_speed = temp_rh_wind self.assertEqual( round(feels_like(temp_f, rel_humidity, wind_speed)), feels_like_f) def test_return_type(self): self.assertIsInstance(feels_like(90, 50, 5), Temp) self.assertIsInstance(feels_like(-45, 50, 60), Temp) self.assertIsInstance(feels_like(70, 50, 60), Temp) def test_input_temp_class(self): wc = feels_like(Temp(-15, unit='c'), 50, 10) self.assertIsInstance(wc, Temp) self.assertEqual(round(wc.c), -23) if __name__ == '__main__': unittest.main() malexer-meteocalc-0104aed/tests/test_heatindex.py000066400000000000000000000026271346214260200222340ustar00rootroot00000000000000import unittest from tests.context import heat_index, Temp # format: # (F, Relative_Humidity): F heat_index_noaa_table = { (80, 40): 80, (88, 40): 88, (90, 40): 91, (96, 40): 101, (98, 40): 105, (106, 40): 124, (108, 40): 130, (110, 40): 136, (80, 65): 82, (84, 65): 89, (86, 65): 93, (96, 65): 121, (100, 65): 136, (80, 100): 87, (82, 100): 95, (86, 100): 112, (90, 100): 132, } heat_index_below_80 = { (42, 80): 39.66, (50, 30): 46.11, (50, 90): 48.93, (60, 80): 59.46, (70, 100): 71.4, } class HeatIndexTest(unittest.TestCase): def test_heat_index_by_noaa_table(self): for t_rh, hi in heat_index_noaa_table.items(): temp, rel_humidity = t_rh self.assertEqual(round(heat_index(temp, rel_humidity)), hi) def test_return_type(self): self.assertIsInstance(heat_index(80, 40), Temp) self.assertIsInstance(heat_index(52, 56), Temp) def test_input_temp_class(self): hi = heat_index(Temp(30, unit='c'), 70) self.assertIsInstance(hi, Temp) self.assertEqual(round(hi.c), 35) self.assertEqual(round(hi, 1), 95.1) def test_heat_index_values_below_80(self): for t_rh, hi in heat_index_below_80.items(): t, rh = t_rh self.assertEqual(round(heat_index(t, rh), 2), hi) if __name__ == '__main__': unittest.main() malexer-meteocalc-0104aed/tests/test_temperature.py000066400000000000000000000070221346214260200226120ustar00rootroot00000000000000import unittest from tests.context import Temp temperature_values = ( dict(c=-273.15, f=-459.67, k=0), dict(c=-100, f=-148, k=173.15), dict(c=-30, f=-22, k=243.15), dict(c=-20, f=-4, k=253.15), dict(c=-10, f=14, k=263.15), dict(c=0, f=32, k=273.15), dict(c=10, f=50, k=283.15), dict(c=20, f=68, k=293.15), dict(c=100, f=212, k=373.15), dict(c=200, f=392, k=473.15), dict(c=300, f=572, k=573.15), ) class TempTest(unittest.TestCase): def test_celsius_to_celsius(self): for t in temperature_values: temp = Temp(t['c'], unit='C') self.assertEqual(temp.c, t['c']) def test_fahrenheit_to_fahrenheit(self): for t in temperature_values: temp = Temp(t['f'], unit='F') self.assertEqual(temp.f, t['f']) def test_kelvin_to_kelvin(self): for t in temperature_values: temp = Temp(t['k'], unit='K') self.assertEqual(temp.k, t['k']) def test_celsius_to_fahrenheit(self): for t in temperature_values: temp = Temp(t['c'], unit='C') self.assertEqual(round(temp.f, 2), t['f']) def test_celsius_to_kelvin(self): for t in temperature_values: temp = Temp(t['c'], unit='C') self.assertEqual(round(temp.k, 2), t['k']) def test_fahrenheit_to_celsius(self): for t in temperature_values: temp = Temp(t['f'], unit='F') self.assertEqual(round(temp.c, 2), t['c']) def test_fahrenheit_to_kelvin(self): for t in temperature_values: temp = Temp(t['f'], unit='F') self.assertEqual(round(temp.k, 2), t['k']) def test_kelvin_to_celsius(self): for t in temperature_values: temp = Temp(t['k'], unit='K') self.assertEqual(round(temp.c, 2), t['c']) def test_kelvin_to_fahrenheit(self): for t in temperature_values: temp = Temp(t['k'], unit='K') self.assertEqual(round(temp.f, 2), t['f']) def test_add_with_float(self): t = Temp(243.15, unit='K') t2 = 40 + t self.assertEqual(float(t2), 283.15) self.assertEqual(t2.c, 10) self.assertEqual(t2.f, 50) self.assertEqual(t2.k, 283.15) self.assertIsInstance(t2, Temp) def test_sub_with_float(self): t = Temp(68, unit='F') t2 = t - 36 self.assertEqual(int(t2), 32) self.assertEqual(t2.c, 0) self.assertEqual(t2.f, 32) self.assertEqual(t2.k, 273.15) self.assertIsInstance(t2, Temp) def test_mul_with_float(self): t = Temp(100, unit='C') t2 = t * 2 self.assertEqual(int(t2), 200) self.assertEqual(t2.c, 200) self.assertEqual(t2.f, 392) self.assertEqual(t2.k, 473.15) self.assertIsInstance(t2, Temp) def test_truediv_with_float(self): t = Temp(573, unit='K') t2 = t / 2 self.assertEqual(t2.k, 286.5) self.assertEqual(round(t2.c, 2), 13.35) self.assertEqual(round(t2.f, 2), 56.03) self.assertEqual(t2.k, 286.5) self.assertIsInstance(t2, Temp) def test_neg_with_float(self): t = Temp(-22, unit='F') t2 = -t self.assertEqual(int(t2), 22) self.assertEqual(round(t2.c, 2), -5.56) self.assertEqual(t2.f, 22) self.assertEqual(round(t2.k, 2), 267.59) self.assertIsInstance(t2, Temp) def test_unsupported_units(self): self.assertRaises(ValueError, Temp, 20, 'd') if __name__ == '__main__': unittest.main() malexer-meteocalc-0104aed/tests/test_windchill.py000066400000000000000000000031541346214260200222340ustar00rootroot00000000000000import unittest from tests.context import wind_chill, Temp # format: # (F, MPH): F wind_chill_table = { (40, 5): 36, (40, 30): 28, (40, 60): 25, (15, 5): 7, (15, 25): -4, (15, 40): -8, (15, 60): -11, (0, 5): -11, (0, 30): -26, (0, 60): -33, (-15, 35): -48, (-15, 60): -55, (-30, 5): -46, (-30, 35): -69, (-30, 60): -76, (-45, 5): -63, (-45, 35): -89, (-45, 60): -98, } class WindChillTest(unittest.TestCase): def test_wind_chill_by_table(self): for temp_wind, wind_chill_f in wind_chill_table.items(): temp_f, wind_speed = temp_wind self.assertEqual( round(wind_chill(temp_f, wind_speed)), wind_chill_f) def test_return_type(self): self.assertIsInstance(wind_chill(40, 5), Temp) self.assertIsInstance(wind_chill(-45, 60), Temp) def test_input_temp_class(self): wc = wind_chill(Temp(-15, unit='c'), 10) self.assertIsInstance(wc, Temp) self.assertEqual(round(wc.c), -23) def test_temp50_wind4(self): self.assertEqual(round(wind_chill(50, 4)), 49) def test_temp50_wind3(self): self.assertRaises(ValueError, wind_chill, 50, 3) def test_temp51_wind3(self): self.assertRaises(ValueError, wind_chill, 51, 3) def test_temp51_wind4(self): self.assertRaises(ValueError, wind_chill, 51, 4) def test_temp100_wind60(self): self.assertRaises(ValueError, wind_chill, 100, 60) def test_temp0_wind0(self): self.assertRaises(ValueError, wind_chill, 0, 0) if __name__ == '__main__': unittest.main()