-
Notifications
You must be signed in to change notification settings - Fork 1
[Feature] Units conversion using parameters #12
base: develop
Are you sure you want to change the base?
Changes from all commits
d56947c
04c8ada
b055cc2
f96b19e
c1d5003
51a01e9
fe6b240
ef5695b
92ff3a1
30cf0d6
f72eaca
06bf779
e1e927b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,92 @@ | ||
| from schematics.models import Model | ||
| from schematics.types import FloatType, GeoPointType, ModelType, StringType | ||
| from schematics.types.serializable import serializable | ||
| from unidecode import unidecode | ||
| from api.utils.metatypes import EnumMeta | ||
|
|
||
| class PhysicalQuantity(Model): | ||
| value = FloatType(required=True) | ||
| unit = StringType(required=True) | ||
|
|
||
|
|
||
| class Temperature(Model): | ||
| value = FloatType(required=True) | ||
|
|
||
| class TemperatureMetrics(StringType, metaclass=EnumMeta): | ||
| CELSIUS = 'CELSIUS' | ||
| FAHRENHEIT = 'FAHRENHEIT' | ||
| KELVIN = 'KELVIN' | ||
|
|
||
| @serializable(type=FloatType, serialized_name='value') | ||
| def get_temperature(self, *args, **kwargs): | ||
| if hasattr(self, 'context'): | ||
| temperature = self.context.get('temperature', 'kelvin') | ||
|
|
||
| if temperature.upper() == self.TemperatureMetrics.CELSIUS: | ||
| value = round(self.value - 273.0, 2) # Converts Kelvin temperature to Celsius | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe instead of rounding to 2 dpp every time you could use a custom type inheriting from
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As a side note, rounding floats is not very reliable. This reference is quite outdated but still relevant. |
||
| elif temperature.upper() == self.TemperatureMetrics.FAHRENHEIT: | ||
| value = round(1.8*(self.value-273.0)+32.0, 2) # Converts Kelvin temperature to Fahrenheit | ||
| else: | ||
| value = round(self.value, 2) # Returns Kelvin temperature | ||
| else: | ||
| value = round(self.value, 2) # Returns Kelvin temperature | ||
| return value | ||
|
|
||
| @serializable(type=StringType, serialized_name='unit') | ||
| def get_unit(self, *args, **kwargs): | ||
| if hasattr(self, 'context'): | ||
| unit = self.context.get('temperature', 'kelvin') | ||
|
|
||
| if unit.upper() == self.TemperatureMetrics.CELSIUS: | ||
| unit = self.TemperatureMetrics.CELSIUS | ||
| elif unit.upper() == self.TemperatureMetrics.FAHRENHEIT: | ||
| unit = self.TemperatureMetrics.FAHRENHEIT | ||
| else: | ||
| unit = self.TemperatureMetrics.KELVIN | ||
| else: | ||
| unit = self.TemperatureMetrics.KELVIN | ||
| return unit.lower() | ||
|
|
||
|
|
||
| class Distance(Model): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe That would add quite a bit of complexity though and would only be justifiable for production code if there were to exist more classes like these. |
||
| value = FloatType(required=True) | ||
|
|
||
| class DistanceMetrics(StringType, metaclass=EnumMeta): | ||
| METERS = 'METERS' | ||
| KILOMETERS = 'KILOMETERS' | ||
| MILES = 'MILES' | ||
|
|
||
| @serializable(type=FloatType, serialized_name='value') | ||
| def get_distance(self, *args, **kwargs): | ||
| if hasattr(self, 'context'): | ||
| distance = self.context.get('distance', 'meters') | ||
|
|
||
| if distance.upper() == self.DistanceMetrics.KILOMETERS: | ||
| value = round(self.value / 1000, 2) # Converts Meters distance to Kilometers | ||
| elif distance.upper() == self.DistanceMetrics.MILES: | ||
| value = round(self.value / 1609.344, 2) # Converts Meters distance to Miles | ||
| else: | ||
| value = round(self.value, 2) # Returns Meters distance | ||
| else: | ||
| value = round(self.value, 2) # Returns Meters distance | ||
| return value | ||
|
|
||
| @serializable(type=StringType, serialized_name='unit') | ||
| def get_unit(self, *args, **kwargs): | ||
| if hasattr(self, 'context'): | ||
| unit = self.context.get('distance', 'meters') | ||
|
|
||
| if unit.upper() == self.DistanceMetrics.KILOMETERS: | ||
| unit = self.DistanceMetrics.KILOMETERS | ||
| elif unit.upper() == self.DistanceMetrics.MILES: | ||
| unit = self.DistanceMetrics.MILES | ||
| else: | ||
| unit = self.DistanceMetrics.METERS | ||
| else: | ||
| unit = self.DistanceMetrics.METERS | ||
| return unit.lower() | ||
|
|
||
|
|
||
| class City(Model): | ||
| name = StringType(required=True) | ||
| country = StringType() | ||
|
|
@@ -27,12 +108,16 @@ class Weather(Model): | |
| city = ModelType(City, required=True) | ||
| description = StringType(required=True) | ||
| long_description = StringType(required=True) | ||
| temperature = ModelType(PhysicalQuantity, required=True) | ||
| feels_like = ModelType(PhysicalQuantity) | ||
| max_temperature = ModelType(PhysicalQuantity) | ||
| min_temperature = ModelType(PhysicalQuantity) | ||
| temperature = ModelType(Temperature, required=True) | ||
| feels_like = ModelType(Temperature) | ||
| max_temperature = ModelType(Temperature) | ||
| min_temperature = ModelType(Temperature) | ||
| wind = ModelType(Wind, required=True) | ||
| visibility = ModelType(PhysicalQuantity) | ||
| visibility = ModelType(Distance) | ||
|
|
||
| class Options: | ||
| serialize_when_none = False | ||
|
|
||
| @serializable(type=StringType, serialized_name='id') | ||
| def id(self): | ||
| return unidecode(f'{self.city.name.upper()}') | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At PassFort's team we use UUID. Please, take a look. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| from schematics.types.base import TypeMeta | ||
|
|
||
| # Inheriting this class will make an enum exhaustive | ||
| class EnumMeta(TypeMeta): | ||
| def __new__(mcs, name, bases, attrs): | ||
| attrs['choices'] = [v for k, v in attrs.items( | ||
| ) if not k.startswith('_') and k.isupper()] | ||
| return TypeMeta.__new__(mcs, name, bases, attrs) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know I've sent this to you, but do you understand what it does? |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| import os, requests | ||
| from api.models.weather import PhysicalQuantity, City, Wind, Weather | ||
| from api.models.weather import PhysicalQuantity, Temperature, Distance, City, Wind, Weather | ||
|
|
||
| class OpenWeatherMap: | ||
|
|
||
|
|
@@ -23,21 +23,17 @@ def format_weather(response): | |
| }), | ||
| 'description': response['weather'][0]['main'].title(), | ||
| 'long_description': response['weather'][0]['description'].title(), | ||
| 'temperature': PhysicalQuantity({ | ||
| 'value': float(response['main']['temp']), | ||
| 'unit': 'celsius degree' | ||
| 'temperature': Temperature({ | ||
| 'value': float(response['main']['temp']) | ||
| }), | ||
| 'feels_like': PhysicalQuantity({ | ||
| 'value': float(response['main']['feels_like']), | ||
| 'unit': 'celsius degree' | ||
| 'feels_like': Temperature({ | ||
| 'value': float(response['main']['feels_like']) | ||
| }) if response['main']['feels_like'] != response['main']['temp'] else None, | ||
| 'max_temperature': PhysicalQuantity({ | ||
| 'value': float(response['main']['temp_max']), | ||
| 'unit': 'celsius degree' | ||
| 'max_temperature': Temperature({ | ||
| 'value': float(response['main']['temp_max']) | ||
| }) if response['main']['temp_max'] != response['main']['temp'] else None, | ||
| 'min_temperature': PhysicalQuantity({ | ||
| 'value': float(response['main']['temp_min']), | ||
| 'unit': 'celsius degree' | ||
| 'min_temperature': Temperature({ | ||
| 'value': float(response['main']['temp_min']) | ||
| }) if response['main']['temp_min'] != response['main']['temp'] else None, | ||
| 'wind': Wind({ | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one should follow the changes of the other fields as well. |
||
| 'speed': PhysicalQuantity({ | ||
|
|
@@ -49,9 +45,8 @@ def format_weather(response): | |
| 'unit': 'degrees' | ||
| }) | ||
| }), | ||
| 'visibility': PhysicalQuantity({ | ||
| 'value': float(response['visibility']), | ||
| 'unit': 'meters' | ||
| 'visibility': Distance({ | ||
| 'value': float(response['visibility']) | ||
| }) | ||
| }) | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, this is quite an improvement from what we've had so far. Congrats.