python-weather 2.0.0__tar.gz → 2.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python-weather-2.0.0 → python-weather-2.0.1}/MANIFEST.in +2 -1
- {python-weather-2.0.0/python_weather.egg-info → python-weather-2.0.1}/PKG-INFO +1 -1
- {python-weather-2.0.0 → python-weather-2.0.1}/pyproject.toml +1 -1
- {python-weather-2.0.0 → python-weather-2.0.1}/python_weather/__init__.py +11 -7
- {python-weather-2.0.0 → python-weather-2.0.1}/python_weather/base.py +52 -55
- {python-weather-2.0.0 → python-weather-2.0.1}/python_weather/client.py +24 -23
- {python-weather-2.0.0 → python-weather-2.0.1}/python_weather/constants.py +10 -5
- {python-weather-2.0.0 → python-weather-2.0.1}/python_weather/enums.py +81 -65
- {python-weather-2.0.0 → python-weather-2.0.1}/python_weather/errors.py +2 -2
- {python-weather-2.0.0 → python-weather-2.0.1}/python_weather/forecast.py +114 -114
- {python-weather-2.0.0 → python-weather-2.0.1/python_weather.egg-info}/PKG-INFO +1 -1
- {python-weather-2.0.0 → python-weather-2.0.1}/LICENSE +0 -0
- {python-weather-2.0.0 → python-weather-2.0.1}/README.md +0 -0
- {python-weather-2.0.0 → python-weather-2.0.1}/python_weather.egg-info/SOURCES.txt +0 -0
- {python-weather-2.0.0 → python-weather-2.0.1}/python_weather.egg-info/dependency_links.txt +0 -0
- {python-weather-2.0.0 → python-weather-2.0.1}/python_weather.egg-info/requires.txt +0 -0
- {python-weather-2.0.0 → python-weather-2.0.1}/python_weather.egg-info/top_level.txt +0 -0
- {python-weather-2.0.0 → python-weather-2.0.1}/setup.cfg +0 -0
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
"""
|
|
2
|
-
python-weather
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
1
|
from .enums import HeatIndex, Kind, Locale, Phase, UltraViolet, WindDirection
|
|
6
2
|
from .constants import METRIC, IMPERIAL
|
|
7
3
|
from .client import Client
|
|
8
4
|
from .errors import Error
|
|
9
5
|
|
|
10
|
-
__version__ = '2.0.
|
|
6
|
+
__version__ = '2.0.1'
|
|
11
7
|
__all__ = (
|
|
12
|
-
'METRIC',
|
|
13
|
-
'
|
|
8
|
+
'METRIC',
|
|
9
|
+
'IMPERIAL',
|
|
10
|
+
'Client',
|
|
11
|
+
'Error',
|
|
12
|
+
'HeatIndex',
|
|
13
|
+
'Kind',
|
|
14
|
+
'Locale',
|
|
15
|
+
'Phase',
|
|
16
|
+
'UltraViolet',
|
|
17
|
+
'WindDirection',
|
|
14
18
|
)
|
|
@@ -1,140 +1,137 @@
|
|
|
1
|
-
from enum import auto
|
|
1
|
+
from enum import auto
|
|
2
2
|
|
|
3
|
+
from .errors import Error
|
|
3
4
|
from .enums import WindDirection, Kind, Locale, UltraViolet
|
|
4
5
|
from .constants import _Unit
|
|
5
6
|
|
|
7
|
+
|
|
6
8
|
class CustomizableBase:
|
|
7
9
|
__slots__ = ('__unit', '__locale')
|
|
8
|
-
|
|
10
|
+
|
|
9
11
|
def __init__(self, unit: _Unit, locale: Locale):
|
|
10
12
|
self.unit = unit
|
|
11
13
|
self.locale = locale
|
|
12
|
-
|
|
14
|
+
|
|
13
15
|
@property
|
|
14
16
|
def unit(self) -> auto:
|
|
15
17
|
"""The measuring unit used to display information in this object."""
|
|
16
|
-
|
|
18
|
+
|
|
17
19
|
return self.__unit
|
|
18
|
-
|
|
20
|
+
|
|
19
21
|
@unit.setter
|
|
20
22
|
def unit(self, to: _Unit):
|
|
21
23
|
"""
|
|
22
24
|
Sets the default measuring unit used to display information in this object.
|
|
23
|
-
|
|
25
|
+
|
|
24
26
|
:param to: The new default measuring unit to be used to display information in this object. Must be either ``METRIC`` or ``IMPERIAL``.
|
|
25
27
|
:exception Error: If the ``to`` argument is not either ``METRIC`` or ``IMPERIAL``.
|
|
26
28
|
"""
|
|
27
|
-
|
|
29
|
+
|
|
28
30
|
if not isinstance(to, _Unit):
|
|
29
31
|
raise Error('Invalid measuring unit specified!')
|
|
30
|
-
|
|
32
|
+
|
|
31
33
|
self.__unit = to
|
|
32
|
-
|
|
34
|
+
|
|
33
35
|
@property
|
|
34
36
|
def locale(self) -> Locale:
|
|
35
37
|
"""The localization used to display information in this object."""
|
|
36
|
-
|
|
38
|
+
|
|
37
39
|
return self.__locale
|
|
38
|
-
|
|
40
|
+
|
|
39
41
|
@locale.setter
|
|
40
42
|
def locale(self, to: Locale):
|
|
41
43
|
"""
|
|
42
44
|
Sets the default localization used to display information in this object.
|
|
43
|
-
|
|
45
|
+
|
|
44
46
|
:param to: The new :class:`Locale` to be used to display information in this object.
|
|
45
47
|
:type to: Locale
|
|
46
48
|
:exception Error: If the ``to`` argument is not a part of the :class:`Locale` enum.
|
|
47
49
|
"""
|
|
48
|
-
|
|
50
|
+
|
|
49
51
|
if not isinstance(to, Locale):
|
|
50
52
|
raise Error(f'Expected {to!r} to be a Locale enum')
|
|
51
|
-
|
|
53
|
+
|
|
52
54
|
self.__locale = to
|
|
53
55
|
|
|
56
|
+
|
|
54
57
|
class BaseForecast(CustomizableBase):
|
|
55
58
|
__slots__ = ('__inner',)
|
|
56
|
-
|
|
59
|
+
|
|
57
60
|
def __init__(self, json: dict, unit: _Unit, locale: Locale):
|
|
58
61
|
self.__inner = json
|
|
59
|
-
|
|
62
|
+
|
|
60
63
|
super().__init__(unit, locale)
|
|
61
|
-
|
|
64
|
+
|
|
62
65
|
@property
|
|
63
66
|
def ultraviolet(self) -> UltraViolet:
|
|
64
67
|
"""The ultra-violet (UV) index."""
|
|
65
|
-
|
|
68
|
+
|
|
66
69
|
return UltraViolet._new(int(self.__inner['uvIndex']))
|
|
67
|
-
|
|
70
|
+
|
|
68
71
|
@property
|
|
69
72
|
def feels_like(self) -> int:
|
|
70
73
|
"""What it felt like, in Celcius or Fahrenheit."""
|
|
71
|
-
|
|
72
|
-
return int(
|
|
73
|
-
|
|
74
|
-
)
|
|
75
|
-
|
|
74
|
+
|
|
75
|
+
return int(self.__inner[f'FeelsLike{self._CustomizableBase__unit.temperature}'])
|
|
76
|
+
|
|
76
77
|
@property
|
|
77
78
|
def humidity(self) -> int:
|
|
78
79
|
"""The humidity value in percent."""
|
|
79
|
-
|
|
80
|
+
|
|
80
81
|
return int(self.__inner['humidity'])
|
|
81
|
-
|
|
82
|
+
|
|
82
83
|
@property
|
|
83
84
|
def temperature(self) -> int:
|
|
84
85
|
"""The temperature in either Celcius or Fahrenheit."""
|
|
85
|
-
|
|
86
|
+
|
|
86
87
|
return int(self.__inner[f'temp_{self._CustomizableBase__unit.temperature}'])
|
|
87
|
-
|
|
88
|
+
|
|
88
89
|
@property
|
|
89
90
|
def precipitation(self) -> float:
|
|
90
91
|
"""The precipitation in either Millimeters or Inches."""
|
|
91
|
-
|
|
92
|
-
return float(
|
|
93
|
-
|
|
94
|
-
)
|
|
95
|
-
|
|
92
|
+
|
|
93
|
+
return float(self.__inner[f'precip{self._CustomizableBase__unit.precipitation}'])
|
|
94
|
+
|
|
96
95
|
@property
|
|
97
96
|
def pressure(self) -> float:
|
|
98
97
|
"""The pressure in either Pascal or Inches."""
|
|
99
|
-
|
|
100
|
-
return float(
|
|
101
|
-
|
|
102
|
-
)
|
|
103
|
-
|
|
98
|
+
|
|
99
|
+
return float(self.__inner[f'pressure{self._CustomizableBase__unit.pressure}'])
|
|
100
|
+
|
|
104
101
|
@property
|
|
105
102
|
def visibility(self) -> int:
|
|
106
103
|
"""The visibility distance in either Kilometers or Miles."""
|
|
107
|
-
|
|
108
|
-
return int(
|
|
109
|
-
|
|
110
|
-
)
|
|
111
|
-
|
|
104
|
+
|
|
105
|
+
return int(self.__inner[f'visibility{self._CustomizableBase__unit.visibility}'])
|
|
106
|
+
|
|
112
107
|
@property
|
|
113
108
|
def wind_speed(self) -> int:
|
|
114
109
|
"""The wind speeds in either Kilometers per hour or Miles per hour."""
|
|
115
|
-
|
|
116
|
-
return int(
|
|
117
|
-
|
|
118
|
-
)
|
|
119
|
-
|
|
110
|
+
|
|
111
|
+
return int(self.__inner[f'windspeed{self._CustomizableBase__unit.velocity}'])
|
|
112
|
+
|
|
120
113
|
@property
|
|
121
114
|
def wind_direction(self) -> WindDirection:
|
|
122
115
|
"""The wind direction."""
|
|
123
|
-
|
|
116
|
+
|
|
124
117
|
return WindDirection._new(
|
|
125
118
|
self.__inner['winddir16Point'], int(self.__inner['winddirDegree'])
|
|
126
119
|
)
|
|
127
|
-
|
|
120
|
+
|
|
128
121
|
@property
|
|
129
122
|
def description(self) -> str:
|
|
130
123
|
"""The description regarding the forecast. This can be localized in different languages depending on the localization used."""
|
|
131
|
-
|
|
132
|
-
description =
|
|
133
|
-
|
|
124
|
+
|
|
125
|
+
description = (
|
|
126
|
+
self.__inner['weatherDesc'][0]['value']
|
|
127
|
+
if self._CustomizableBase__locale == Locale.ENGLISH
|
|
128
|
+
else self.__inner[f'lang_{self._CustomizableBase__locale.value}'][0]['value']
|
|
129
|
+
)
|
|
130
|
+
|
|
134
131
|
return description.strip()
|
|
135
|
-
|
|
132
|
+
|
|
136
133
|
@property
|
|
137
134
|
def kind(self) -> Kind:
|
|
138
135
|
"""The kind of the forecast."""
|
|
139
|
-
|
|
136
|
+
|
|
140
137
|
return Kind(int(self.__inner['weatherCode']))
|
|
@@ -10,81 +10,82 @@ from .forecast import Forecast
|
|
|
10
10
|
from .errors import Error
|
|
11
11
|
from .enums import Locale
|
|
12
12
|
|
|
13
|
+
|
|
13
14
|
class Client(CustomizableBase):
|
|
14
15
|
"""
|
|
15
16
|
The class that lets you interact with the API.
|
|
16
|
-
|
|
17
|
+
|
|
17
18
|
:param unit: Whether to use the metric or imperial/customary system (``IMPERIAL``). Defaults to ``METRIC``.
|
|
18
19
|
:type unit: Optional[:py:class:`enum.auto`]
|
|
19
20
|
:param locale: Whether to use a different locale/language as the description for the returned forecast. Defaults to ``Locale.ENGLISH``.
|
|
20
21
|
:type locale: Optional[Locale]
|
|
21
22
|
:param session: Whether to use an existing aiohttp client session for requesting or not. Defaults to ``None`` (creates a new one instead)
|
|
22
23
|
:type session: Optional[:class:`aiohttp.ClientSession`]
|
|
23
|
-
|
|
24
|
+
|
|
24
25
|
:raises Error: If the ``unit`` argument is not ``None`` and it's also not ``METRIC`` or ``IMPERIAL``, or if the ``locale`` argument is not ``None`` and it's also not a part of the :class:`Locale` enum.
|
|
25
26
|
"""
|
|
26
|
-
|
|
27
|
+
|
|
27
28
|
__slots__ = ('__session',)
|
|
28
|
-
|
|
29
|
+
|
|
29
30
|
def __init__(
|
|
30
31
|
self,
|
|
31
32
|
*,
|
|
32
33
|
unit: Optional[auto] = METRIC,
|
|
33
34
|
locale: Optional[Locale] = Locale.ENGLISH,
|
|
34
|
-
session: Optional[ClientSession] = None
|
|
35
|
+
session: Optional[ClientSession] = None,
|
|
35
36
|
):
|
|
36
37
|
super().__init__(unit, locale)
|
|
37
|
-
|
|
38
|
+
|
|
38
39
|
self.__session = session or ClientSession(
|
|
39
40
|
timeout=ClientTimeout(total=5000.0),
|
|
40
|
-
connector=TCPConnector(verify_ssl=False)
|
|
41
|
+
connector=TCPConnector(verify_ssl=False),
|
|
41
42
|
)
|
|
42
|
-
|
|
43
|
+
|
|
43
44
|
def __repr__(self) -> str:
|
|
44
45
|
return f'<{self.__class__.__name__} {self.__session!r}>'
|
|
45
|
-
|
|
46
|
+
|
|
46
47
|
async def get(
|
|
47
48
|
self,
|
|
48
49
|
location: str,
|
|
49
50
|
*,
|
|
50
51
|
unit: Optional[auto] = None,
|
|
51
|
-
locale: Optional[Locale] = None
|
|
52
|
+
locale: Optional[Locale] = None,
|
|
52
53
|
) -> Forecast:
|
|
53
54
|
"""
|
|
54
55
|
Fetches a weather forecast for a specific location.
|
|
55
|
-
|
|
56
|
+
|
|
56
57
|
:param location: The requested location name for said weather forecast.
|
|
57
58
|
:type location: str
|
|
58
59
|
:param unit: Overrides the metric or imperial/customary system (``IMPERIAL``) used by the :class:`Client` object. Defaults to ``None`` (uses the one from the :class:`Client`).
|
|
59
60
|
:type unit: Optional[:py:class:`enum.auto`]
|
|
60
61
|
:param locale: Overrides the locale/language used by the :class:`Client` object. Defaults to ``None`` (uses the one from the :class:`Client`).
|
|
61
62
|
:type locale: Optional[Locale]
|
|
62
|
-
|
|
63
|
+
|
|
63
64
|
:exception Error: If the aiohttp client session used by the :class:`Client` object is already closed, if the ``unit`` argument is not ``None`` and it's also not ``METRIC`` or ``IMPERIAL``, if the ``locale`` argument is not ``None`` and it's also not a part of the :class:`Locale` enum, or if the :class:`Client` cannot send a web request to the web server.
|
|
64
|
-
|
|
65
|
+
|
|
65
66
|
:returns: The requested weather forecast.
|
|
66
67
|
:rtype: Forecast
|
|
67
68
|
"""
|
|
68
|
-
|
|
69
|
+
|
|
69
70
|
if (not isinstance(location, str)) or (not location):
|
|
70
71
|
raise Error(f'Expected a proper location str, got {location!r}')
|
|
71
72
|
elif self.__session.closed:
|
|
72
73
|
raise Error('Client is already closed')
|
|
73
|
-
|
|
74
|
+
|
|
74
75
|
if not isinstance(unit, _Unit):
|
|
75
76
|
unit = self._CustomizableBase__unit
|
|
76
|
-
|
|
77
|
+
|
|
77
78
|
if not isinstance(locale, Locale):
|
|
78
79
|
locale = self._CustomizableBase__locale
|
|
79
|
-
|
|
80
|
+
|
|
80
81
|
subdomain = f'{locale.value}.' if locale != Locale.ENGLISH else ''
|
|
81
82
|
delay = 0
|
|
82
|
-
|
|
83
|
+
|
|
83
84
|
while True:
|
|
84
85
|
if delay != 0:
|
|
85
86
|
await sleep(delay)
|
|
86
87
|
delay *= 2
|
|
87
|
-
|
|
88
|
+
|
|
88
89
|
async with self.__session.get(
|
|
89
90
|
f'https://{subdomain}wttr.in/{quote_plus(location)}?format=j1'
|
|
90
91
|
) as resp:
|
|
@@ -95,15 +96,15 @@ class Client(CustomizableBase):
|
|
|
95
96
|
raise e # okay, that's too much requests - just raise the error
|
|
96
97
|
elif delay == 0:
|
|
97
98
|
delay = 0.5
|
|
98
|
-
|
|
99
|
+
|
|
99
100
|
async def close(self):
|
|
100
101
|
"""Closes the :class:`Client` object. Nothing will happen if it's already closed."""
|
|
101
|
-
|
|
102
|
+
|
|
102
103
|
if not self.__session.closed:
|
|
103
104
|
await self.__session.close()
|
|
104
|
-
|
|
105
|
+
|
|
105
106
|
async def __aenter__(self):
|
|
106
107
|
return self
|
|
107
|
-
|
|
108
|
+
|
|
108
109
|
async def __aexit__(self, *_, **__):
|
|
109
110
|
await self.close()
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
from typing import Union
|
|
2
2
|
from re import compile
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
|
|
5
5
|
class _Unit:
|
|
6
6
|
__slots__ = (
|
|
7
|
-
'temperature',
|
|
8
|
-
'
|
|
7
|
+
'temperature',
|
|
8
|
+
'velocity',
|
|
9
|
+
'pressure',
|
|
10
|
+
'precipitation',
|
|
11
|
+
'visibility',
|
|
12
|
+
'cm_divisor',
|
|
9
13
|
)
|
|
10
|
-
|
|
14
|
+
|
|
11
15
|
def __init__(
|
|
12
16
|
self,
|
|
13
17
|
temperature: str,
|
|
@@ -23,10 +27,11 @@ class _Unit:
|
|
|
23
27
|
self.precipitation = precipitation
|
|
24
28
|
self.visibility = visibility
|
|
25
29
|
self.cm_divisor = cm_divisor
|
|
26
|
-
|
|
30
|
+
|
|
27
31
|
def __repr__(self) -> str:
|
|
28
32
|
return f'<Unit [{self.temperature}, {self.velocity}]>'
|
|
29
33
|
|
|
34
|
+
|
|
30
35
|
METRIC = _Unit('C', 'Kmph', '', 'MM', '', 1)
|
|
31
36
|
IMPERIAL = _Unit('F', 'Miles', 'Inches', 'Inches', 'Miles', 2.54)
|
|
32
37
|
|
|
@@ -1,69 +1,71 @@
|
|
|
1
|
-
from enum import Enum
|
|
1
|
+
from enum import Enum
|
|
2
2
|
from typing import Union
|
|
3
3
|
|
|
4
4
|
from .constants import WIND_DIRECTION_EMOJIS
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
|
|
7
7
|
class BasicEnum(Enum):
|
|
8
8
|
__slots__ = ()
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
def __repr__(self) -> str:
|
|
11
11
|
return f'{self.__class__.__name__}.{self.name}'
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
def __str__(self) -> str:
|
|
14
14
|
return self.name.replace('_', ' ').title()
|
|
15
15
|
|
|
16
|
+
|
|
16
17
|
class IndexedEnum(Enum):
|
|
17
18
|
__slots__ = ('__index',)
|
|
18
|
-
|
|
19
|
-
def __lt__(self, other: Union[
|
|
19
|
+
|
|
20
|
+
def __lt__(self, other: Union['IndexedEnum', int, float]) -> bool:
|
|
20
21
|
if isinstance(other, self.__class__):
|
|
21
22
|
return self.__index < other.index
|
|
22
23
|
else:
|
|
23
24
|
return self.__index < other
|
|
24
|
-
|
|
25
|
-
def __eq__(self, other: Union[
|
|
25
|
+
|
|
26
|
+
def __eq__(self, other: Union['IndexedEnum', int, float]) -> bool:
|
|
26
27
|
if isinstance(other, self.__class__):
|
|
27
28
|
return self.__index == other.index
|
|
28
29
|
else:
|
|
29
30
|
return self.__index == other
|
|
30
|
-
|
|
31
|
-
def __gt__(self, other: Union[
|
|
31
|
+
|
|
32
|
+
def __gt__(self, other: Union['IndexedEnum', int, float]) -> bool:
|
|
32
33
|
if isinstance(other, self.__class__):
|
|
33
34
|
return self.__index > other.index
|
|
34
35
|
else:
|
|
35
36
|
return self.__index > other
|
|
36
|
-
|
|
37
|
+
|
|
37
38
|
def __hash__(self) -> int:
|
|
38
39
|
return self.__index
|
|
39
|
-
|
|
40
|
+
|
|
40
41
|
def __int__(self) -> int:
|
|
41
42
|
return self.__index
|
|
42
|
-
|
|
43
|
+
|
|
43
44
|
@property
|
|
44
45
|
def index(self) -> int:
|
|
45
46
|
"""The index value."""
|
|
46
|
-
|
|
47
|
+
|
|
47
48
|
return self.__index
|
|
48
|
-
|
|
49
|
+
|
|
49
50
|
@index.setter
|
|
50
51
|
def index(self, new_index: int) -> int:
|
|
51
52
|
self.__index = new_index
|
|
52
53
|
|
|
54
|
+
|
|
53
55
|
class HeatIndex(IndexedEnum):
|
|
54
56
|
"""Represents a heat index."""
|
|
55
|
-
|
|
57
|
+
|
|
56
58
|
CAUTION = None
|
|
57
59
|
EXTREME_CAUTION = None
|
|
58
60
|
DANGER = None
|
|
59
61
|
EXTREME_DANGER = None
|
|
60
|
-
|
|
62
|
+
|
|
61
63
|
def _new(celcius_index: int, true_index: int):
|
|
62
64
|
enum = HeatIndex(celcius_index)
|
|
63
65
|
enum.index = true_index
|
|
64
|
-
|
|
66
|
+
|
|
65
67
|
return enum
|
|
66
|
-
|
|
68
|
+
|
|
67
69
|
@classmethod
|
|
68
70
|
def _missing_(self, celcius_index: int):
|
|
69
71
|
if celcius_index <= 32:
|
|
@@ -75,21 +77,22 @@ class HeatIndex(IndexedEnum):
|
|
|
75
77
|
else:
|
|
76
78
|
return self.EXTREME_DANGER
|
|
77
79
|
|
|
80
|
+
|
|
78
81
|
class UltraViolet(BasicEnum, IndexedEnum):
|
|
79
82
|
"""Represents ultra-violet (UV) index."""
|
|
80
|
-
|
|
83
|
+
|
|
81
84
|
LOW = None
|
|
82
85
|
MODERATE = None
|
|
83
86
|
HIGH = None
|
|
84
87
|
VERY_HIGH = None
|
|
85
88
|
EXTREME = None
|
|
86
|
-
|
|
89
|
+
|
|
87
90
|
def _new(index: int):
|
|
88
91
|
enum = UltraViolet(index)
|
|
89
92
|
enum.index = index
|
|
90
|
-
|
|
93
|
+
|
|
91
94
|
return enum
|
|
92
|
-
|
|
95
|
+
|
|
93
96
|
@classmethod
|
|
94
97
|
def _missing_(self, index: int):
|
|
95
98
|
if index <= 2:
|
|
@@ -103,38 +106,39 @@ class UltraViolet(BasicEnum, IndexedEnum):
|
|
|
103
106
|
else:
|
|
104
107
|
return self.EXTREME
|
|
105
108
|
|
|
109
|
+
|
|
106
110
|
class WindDirection(BasicEnum):
|
|
107
111
|
"""Represents a wind direction."""
|
|
108
|
-
|
|
112
|
+
|
|
109
113
|
__slots__ = ('__degrees',)
|
|
110
|
-
|
|
111
|
-
NORTH =
|
|
112
|
-
NORTH_NORTHEAST =
|
|
113
|
-
NORTHEAST =
|
|
114
|
-
EAST_NORTHEAST =
|
|
115
|
-
EAST =
|
|
116
|
-
EAST_SOUTHEAST =
|
|
117
|
-
SOUTHEAST =
|
|
118
|
-
SOUTH_SOUTHEAST =
|
|
119
|
-
SOUTH =
|
|
120
|
-
SOUTH_SOUTHWEST =
|
|
121
|
-
SOUTHWEST =
|
|
122
|
-
WEST_SOUTHWEST =
|
|
123
|
-
WEST =
|
|
124
|
-
WEST_NORTHWEST =
|
|
125
|
-
NORTHWEST =
|
|
126
|
-
NORTH_NORTHWEST =
|
|
127
|
-
|
|
114
|
+
|
|
115
|
+
NORTH = 'N'
|
|
116
|
+
NORTH_NORTHEAST = 'NNE'
|
|
117
|
+
NORTHEAST = 'NE'
|
|
118
|
+
EAST_NORTHEAST = 'ENE'
|
|
119
|
+
EAST = 'E'
|
|
120
|
+
EAST_SOUTHEAST = 'ESE'
|
|
121
|
+
SOUTHEAST = 'SE'
|
|
122
|
+
SOUTH_SOUTHEAST = 'SSE'
|
|
123
|
+
SOUTH = 'S'
|
|
124
|
+
SOUTH_SOUTHWEST = 'SSW'
|
|
125
|
+
SOUTHWEST = 'SW'
|
|
126
|
+
WEST_SOUTHWEST = 'WSW'
|
|
127
|
+
WEST = 'W'
|
|
128
|
+
WEST_NORTHWEST = 'WNW'
|
|
129
|
+
NORTHWEST = 'NW'
|
|
130
|
+
NORTH_NORTHWEST = 'NNW'
|
|
131
|
+
|
|
128
132
|
def _new(value: str, degrees: float):
|
|
129
133
|
enum = WindDirection(value)
|
|
130
134
|
enum.__degrees = degrees
|
|
131
|
-
|
|
135
|
+
|
|
132
136
|
return enum
|
|
133
|
-
|
|
134
|
-
def __contains__(self, other: Union[
|
|
137
|
+
|
|
138
|
+
def __contains__(self, other: Union['WindDirection', float, int]) -> bool:
|
|
135
139
|
if isinstance(other, self.__class__):
|
|
136
140
|
other = other.degrees
|
|
137
|
-
|
|
141
|
+
|
|
138
142
|
if self is self.NORTH:
|
|
139
143
|
return other > 348.75 or other <= 11.25
|
|
140
144
|
elif self is self.NORTH_NORTHEAST:
|
|
@@ -167,27 +171,28 @@ class WindDirection(BasicEnum):
|
|
|
167
171
|
return 303.75 < other <= 326.25
|
|
168
172
|
else:
|
|
169
173
|
return 326.25 < other <= 348.75
|
|
170
|
-
|
|
174
|
+
|
|
171
175
|
def __float__(self) -> float:
|
|
172
176
|
return self.__degrees
|
|
173
|
-
|
|
177
|
+
|
|
174
178
|
@property
|
|
175
179
|
def degrees(self) -> int:
|
|
176
180
|
"""The wind direction's value in degrees."""
|
|
177
|
-
|
|
181
|
+
|
|
178
182
|
return self.__degrees
|
|
179
|
-
|
|
183
|
+
|
|
180
184
|
@property
|
|
181
185
|
def emoji(self) -> str:
|
|
182
186
|
"""The emoji representing this enum."""
|
|
183
|
-
|
|
187
|
+
|
|
184
188
|
return WIND_DIRECTION_EMOJIS[int(((self.__degrees + 22.5) % 360) / 45.0)]
|
|
185
189
|
|
|
190
|
+
|
|
186
191
|
class Locale(Enum):
|
|
187
192
|
"""Represents the list of supported locales/languages by this library."""
|
|
188
|
-
|
|
193
|
+
|
|
189
194
|
__slots__ = ()
|
|
190
|
-
|
|
195
|
+
|
|
191
196
|
AFRIKAANS = 'af'
|
|
192
197
|
AMHARIC = 'am'
|
|
193
198
|
ARABIC = 'ar'
|
|
@@ -260,19 +265,20 @@ class Locale(Enum):
|
|
|
260
265
|
VIETNAMESE = 'vi'
|
|
261
266
|
WELSH = 'cy'
|
|
262
267
|
ZULU = 'zu'
|
|
263
|
-
|
|
268
|
+
|
|
264
269
|
def __repr__(self) -> str:
|
|
265
270
|
return f'{self.__class__.__name__}.{self.name}'
|
|
266
|
-
|
|
271
|
+
|
|
267
272
|
def __str__(self) -> str:
|
|
268
273
|
arr = self.name.title().split('_')
|
|
269
274
|
return f'{arr[:-1].join(" ")} ({arr[-1]})' if len(arr) != 1 else arr[0]
|
|
270
275
|
|
|
276
|
+
|
|
271
277
|
class Kind(BasicEnum):
|
|
272
278
|
"""Represents a weather forecast kind."""
|
|
273
|
-
|
|
279
|
+
|
|
274
280
|
__slots__ = ()
|
|
275
|
-
|
|
281
|
+
|
|
276
282
|
SUNNY = 113
|
|
277
283
|
PARTLY_CLOUDY = 116
|
|
278
284
|
CLOUDY = 119
|
|
@@ -291,7 +297,7 @@ class Kind(BasicEnum):
|
|
|
291
297
|
HEAVY_SNOW_SHOWERS = 335
|
|
292
298
|
THUNDERY_HEAVY_RAIN = 389
|
|
293
299
|
THUNDERY_SNOW_SHOWERS = 392
|
|
294
|
-
|
|
300
|
+
|
|
295
301
|
@classmethod
|
|
296
302
|
def _missing_(self, value: int):
|
|
297
303
|
if value == 248 or value == 260:
|
|
@@ -300,7 +306,16 @@ class Kind(BasicEnum):
|
|
|
300
306
|
return self.LIGHT_SHOWERS
|
|
301
307
|
elif value == 362 or value == 365 or value == 374:
|
|
302
308
|
return self.LIGHT_SLEET_SHOWERS
|
|
303
|
-
elif
|
|
309
|
+
elif (
|
|
310
|
+
value == 185
|
|
311
|
+
or value == 281
|
|
312
|
+
or value == 284
|
|
313
|
+
or value == 311
|
|
314
|
+
or value == 314
|
|
315
|
+
or value == 317
|
|
316
|
+
or value == 350
|
|
317
|
+
or value == 377
|
|
318
|
+
):
|
|
304
319
|
return self.LIGHT_SLEET
|
|
305
320
|
elif value == 386:
|
|
306
321
|
return self.THUNDERY_SHOWERS
|
|
@@ -318,11 +333,11 @@ class Kind(BasicEnum):
|
|
|
318
333
|
return self.LIGHT_SNOW_SHOWERS
|
|
319
334
|
elif value == 371 or value == 395:
|
|
320
335
|
return self.HEAVY_SNOW_SHOWERS
|
|
321
|
-
|
|
336
|
+
|
|
322
337
|
@property
|
|
323
338
|
def emoji(self) -> str:
|
|
324
339
|
"""The emoji representing this enum."""
|
|
325
|
-
|
|
340
|
+
|
|
326
341
|
if self is self.CLOUDY:
|
|
327
342
|
return '☁️'
|
|
328
343
|
elif self is self.FOG:
|
|
@@ -362,11 +377,12 @@ class Kind(BasicEnum):
|
|
|
362
377
|
else:
|
|
363
378
|
return '✨'
|
|
364
379
|
|
|
380
|
+
|
|
365
381
|
class Phase(BasicEnum):
|
|
366
382
|
"""Represents a moon phase."""
|
|
367
|
-
|
|
383
|
+
|
|
368
384
|
__slots__ = ()
|
|
369
|
-
|
|
385
|
+
|
|
370
386
|
NEW_MOON = 'New Moon'
|
|
371
387
|
WAXING_CRESCENT = 'Waxing Crescent'
|
|
372
388
|
FIRST_QUARTER = 'First Quarter'
|
|
@@ -375,11 +391,11 @@ class Phase(BasicEnum):
|
|
|
375
391
|
WANING_GIBBOUS = 'Waning Gibbous'
|
|
376
392
|
LAST_QUARTER = 'Last Quarter'
|
|
377
393
|
WANING_CRESCENT = 'Waning Crescent'
|
|
378
|
-
|
|
394
|
+
|
|
379
395
|
@property
|
|
380
396
|
def emoji(self) -> str:
|
|
381
397
|
"""The stylized name for this enum."""
|
|
382
|
-
|
|
398
|
+
|
|
383
399
|
if self is self.NEW_MOON:
|
|
384
400
|
return '🌑'
|
|
385
401
|
elif self is self.WAXING_CRESCENT:
|
|
@@ -1,315 +1,315 @@
|
|
|
1
1
|
from typing import Iterable, Optional, Tuple
|
|
2
2
|
from datetime import datetime, date, time
|
|
3
|
-
from enum import auto
|
|
4
3
|
|
|
5
4
|
from .base import BaseForecast, CustomizableBase
|
|
6
5
|
from .enums import Phase, Locale, HeatIndex
|
|
7
6
|
from .constants import _Unit, LATLON_REGEX
|
|
8
7
|
|
|
8
|
+
|
|
9
9
|
class HourlyForecast(BaseForecast):
|
|
10
10
|
"""Represents a weather forecast of a specific hour."""
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
__slots__ = ()
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
def __init__(self, json: dict, unit: _Unit, locale: Locale):
|
|
15
15
|
# for inheritance purposes
|
|
16
16
|
if 'temp_C' not in json:
|
|
17
17
|
json['temp_C'] = json.pop('tempC')
|
|
18
18
|
if 'temp_F' not in json:
|
|
19
19
|
json['temp_F'] = json.pop('tempF')
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
super().__init__(json, unit, locale)
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
def __repr__(self) -> str:
|
|
24
24
|
return f'<{self.__class__.__name__} time={self.time!r} temperature={self.temperature!r} description={self.description!r} kind={self.kind!r}>'
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
@property
|
|
27
27
|
def dew_point(self) -> int:
|
|
28
28
|
"""The dew point in either Celcius or Fahrenheit."""
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
return int(
|
|
31
31
|
self._BaseForecast__inner[f'DewPoint{self._CustomizableBase__unit.temperature}']
|
|
32
|
-
)
|
|
33
|
-
|
|
32
|
+
)
|
|
33
|
+
|
|
34
34
|
@property
|
|
35
35
|
def heat_index(self) -> HeatIndex:
|
|
36
36
|
"""The heat index in either Celcius or Fahrenheit."""
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
celcius_index = int(self._BaseForecast__inner['HeatIndexC'])
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
return HeatIndex._new(
|
|
41
41
|
celcius_index,
|
|
42
|
-
int(
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
int(
|
|
43
|
+
self._BaseForecast__inner[
|
|
44
|
+
f'HeatIndex{self._CustomizableBase__unit.temperature}'
|
|
45
|
+
]
|
|
46
|
+
),
|
|
47
|
+
)
|
|
48
|
+
|
|
45
49
|
@property
|
|
46
50
|
def wind_chill(self) -> int:
|
|
47
51
|
"""The wind chill value in either Celcius or Fahrenheit."""
|
|
48
|
-
|
|
52
|
+
|
|
49
53
|
return int(
|
|
50
54
|
self._BaseForecast__inner[f'WindChill{self._CustomizableBase__unit.temperature}']
|
|
51
|
-
)
|
|
52
|
-
|
|
55
|
+
)
|
|
56
|
+
|
|
53
57
|
@property
|
|
54
58
|
def wind_gust(self) -> int:
|
|
55
59
|
"""The wind gust value in either Kilometers per hour or Miles per hour."""
|
|
56
|
-
|
|
60
|
+
|
|
57
61
|
key = f'WindGust{self._CustomizableBase__unit.velocity}'
|
|
58
62
|
return int(self._BaseForecast__inner[key])
|
|
59
|
-
|
|
63
|
+
|
|
60
64
|
@property
|
|
61
65
|
def chances_of_fog(self) -> int:
|
|
62
66
|
"""Chances of a fog in percent."""
|
|
63
|
-
|
|
67
|
+
|
|
64
68
|
return int(self._BaseForecast__inner['chanceoffog'])
|
|
65
|
-
|
|
69
|
+
|
|
66
70
|
@property
|
|
67
71
|
def chances_of_frost(self) -> int:
|
|
68
72
|
"""Chances of a frost in percent."""
|
|
69
|
-
|
|
73
|
+
|
|
70
74
|
return int(self._BaseForecast__inner['chanceoffrost'])
|
|
71
|
-
|
|
75
|
+
|
|
72
76
|
@property
|
|
73
77
|
def chances_of_high_temperature(self) -> int:
|
|
74
78
|
"""Chances of a high temperature in percent."""
|
|
75
|
-
|
|
79
|
+
|
|
76
80
|
return int(self._BaseForecast__inner['chanceofhightemp'])
|
|
77
|
-
|
|
81
|
+
|
|
78
82
|
@property
|
|
79
83
|
def chances_of_overcast(self) -> int:
|
|
80
84
|
"""Chances of an overcast in percent."""
|
|
81
|
-
|
|
85
|
+
|
|
82
86
|
return int(self._BaseForecast__inner['chanceofovercast'])
|
|
83
|
-
|
|
87
|
+
|
|
84
88
|
@property
|
|
85
89
|
def chances_of_rain(self) -> int:
|
|
86
90
|
"""Chances of a rain in percent."""
|
|
87
|
-
|
|
91
|
+
|
|
88
92
|
return int(self._BaseForecast__inner['chanceofrain'])
|
|
89
|
-
|
|
93
|
+
|
|
90
94
|
@property
|
|
91
95
|
def chances_of_remaining_dry(self) -> int:
|
|
92
96
|
"""Chances of remaining dry in percent."""
|
|
93
|
-
|
|
97
|
+
|
|
94
98
|
return int(self._BaseForecast__inner['chanceofremdry'])
|
|
95
|
-
|
|
99
|
+
|
|
96
100
|
@property
|
|
97
101
|
def chances_of_snow(self) -> int:
|
|
98
102
|
"""Chances of a snow in percent."""
|
|
99
|
-
|
|
103
|
+
|
|
100
104
|
return int(self._BaseForecast__inner['chanceofsnow'])
|
|
101
|
-
|
|
105
|
+
|
|
102
106
|
@property
|
|
103
107
|
def chances_of_sunshine(self) -> int:
|
|
104
108
|
"""Chances of a sunshine in percent."""
|
|
105
|
-
|
|
109
|
+
|
|
106
110
|
return int(self._BaseForecast__inner['chanceofsunshine'])
|
|
107
|
-
|
|
111
|
+
|
|
108
112
|
@property
|
|
109
113
|
def chances_of_thunder(self) -> int:
|
|
110
114
|
"""Chances of a thunder in percent."""
|
|
111
|
-
|
|
115
|
+
|
|
112
116
|
return int(self._BaseForecast__inner['chanceofthunder'])
|
|
113
|
-
|
|
117
|
+
|
|
114
118
|
@property
|
|
115
119
|
def chances_of_windy(self) -> int:
|
|
116
120
|
"""Chances of windy in percent."""
|
|
117
|
-
|
|
121
|
+
|
|
118
122
|
return int(self._BaseForecast__inner['chanceofwindy'])
|
|
119
|
-
|
|
123
|
+
|
|
120
124
|
@property
|
|
121
125
|
def cloud_cover(self) -> int:
|
|
122
126
|
"""The cloud cover value in percent."""
|
|
123
|
-
|
|
127
|
+
|
|
124
128
|
return int(self._BaseForecast__inner['cloudcover'])
|
|
125
|
-
|
|
129
|
+
|
|
126
130
|
@property
|
|
127
131
|
def time(self) -> time:
|
|
128
132
|
"""The local time in hours and minutes."""
|
|
129
|
-
|
|
130
|
-
return
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
time()
|
|
136
|
+
if len(self._BaseForecast__inner['time']) < 3
|
|
137
|
+
else datetime.strptime(self._BaseForecast__inner['time'], '%H%M').time()
|
|
138
|
+
)
|
|
139
|
+
|
|
131
140
|
|
|
132
141
|
class DailyForecast(CustomizableBase):
|
|
133
142
|
__slots__ = ('__inner', '__astronomy')
|
|
134
|
-
|
|
143
|
+
|
|
135
144
|
def __init__(self, json: dict, unit: _Unit, locale: Locale):
|
|
136
145
|
self.__astronomy = json.pop('astronomy')[0]
|
|
137
146
|
self.__inner = json
|
|
138
|
-
|
|
147
|
+
|
|
139
148
|
super().__init__(unit, locale)
|
|
140
|
-
|
|
149
|
+
|
|
141
150
|
def __repr__(self) -> str:
|
|
142
|
-
return
|
|
143
|
-
|
|
151
|
+
return (
|
|
152
|
+
f'<{self.__class__.__name__} date={self.date!r} temperature={self.temperature!r}>'
|
|
153
|
+
)
|
|
154
|
+
|
|
144
155
|
@property
|
|
145
156
|
def moon_illumination(self) -> int:
|
|
146
157
|
"""The percentage of the moon illuminated."""
|
|
147
|
-
|
|
158
|
+
|
|
148
159
|
return int(self.__astronomy['moon_illumination'])
|
|
149
|
-
|
|
160
|
+
|
|
150
161
|
@property
|
|
151
162
|
def moon_phase(self) -> Phase:
|
|
152
163
|
"""The moon's phase."""
|
|
153
|
-
|
|
164
|
+
|
|
154
165
|
return Phase(self.__astronomy['moon_phase'])
|
|
155
|
-
|
|
166
|
+
|
|
156
167
|
@property
|
|
157
168
|
def moonrise(self) -> Optional[time]:
|
|
158
169
|
"""The local time when the moon rises. This can be ``None``."""
|
|
159
|
-
|
|
170
|
+
|
|
160
171
|
try:
|
|
161
172
|
return datetime.strptime(self.__astronomy['moonrise'], '%I:%M %p').time()
|
|
162
173
|
except ValueError:
|
|
163
174
|
...
|
|
164
|
-
|
|
175
|
+
|
|
165
176
|
@property
|
|
166
177
|
def moonset(self) -> Optional[time]:
|
|
167
178
|
"""The local time when the moon sets. This can be ``None``."""
|
|
168
|
-
|
|
179
|
+
|
|
169
180
|
try:
|
|
170
181
|
return datetime.strptime(self.__astronomy['moonset'], '%I:%M %p').time()
|
|
171
182
|
except ValueError:
|
|
172
183
|
...
|
|
173
|
-
|
|
184
|
+
|
|
174
185
|
@property
|
|
175
186
|
def sunrise(self) -> Optional[time]:
|
|
176
187
|
"""The local time when the sun rises. This can be ``None``."""
|
|
177
|
-
|
|
188
|
+
|
|
178
189
|
try:
|
|
179
190
|
return datetime.strptime(self.__astronomy['sunrise'], '%I:%M %p').time()
|
|
180
191
|
except ValueError:
|
|
181
192
|
...
|
|
182
|
-
|
|
193
|
+
|
|
183
194
|
@property
|
|
184
195
|
def sunset(self) -> Optional[time]:
|
|
185
196
|
"""The local time when the sun sets. This can be ``None``."""
|
|
186
|
-
|
|
197
|
+
|
|
187
198
|
try:
|
|
188
199
|
return datetime.strptime(self.__astronomy['sunset'], '%I:%M %p').time()
|
|
189
200
|
except ValueError:
|
|
190
201
|
...
|
|
191
|
-
|
|
202
|
+
|
|
192
203
|
@property
|
|
193
204
|
def date(self) -> date:
|
|
194
205
|
"""The local date of this forecast."""
|
|
195
|
-
|
|
206
|
+
|
|
196
207
|
return datetime.strptime(self.__inner['date'], '%Y-%m-%d').date()
|
|
197
|
-
|
|
208
|
+
|
|
198
209
|
@property
|
|
199
210
|
def lowest_temperature(self) -> int:
|
|
200
211
|
"""The lowest temperature in either Celcius or Fahrenheit."""
|
|
201
|
-
|
|
202
|
-
return int(
|
|
203
|
-
|
|
204
|
-
) # yapf: disable
|
|
205
|
-
|
|
212
|
+
|
|
213
|
+
return int(self.__inner[f'mintemp{self._CustomizableBase__unit.temperature}'])
|
|
214
|
+
|
|
206
215
|
@property
|
|
207
216
|
def highest_temperature(self) -> int:
|
|
208
217
|
"""The highest temperature in either Celcius or Fahrenheit."""
|
|
209
|
-
|
|
210
|
-
return int(
|
|
211
|
-
|
|
212
|
-
) # yapf: disable
|
|
213
|
-
|
|
218
|
+
|
|
219
|
+
return int(self.__inner[f'maxtemp{self._CustomizableBase__unit.temperature}'])
|
|
220
|
+
|
|
214
221
|
@property
|
|
215
222
|
def temperature(self) -> int:
|
|
216
223
|
"""The average temperature in either Celcius or Fahrenheit."""
|
|
217
|
-
|
|
218
|
-
return int(
|
|
219
|
-
|
|
220
|
-
) # yapf: disable
|
|
221
|
-
|
|
224
|
+
|
|
225
|
+
return int(self.__inner[f'avgtemp{self._CustomizableBase__unit.temperature}'])
|
|
226
|
+
|
|
222
227
|
@property
|
|
223
228
|
def sunlight(self) -> float:
|
|
224
229
|
"""Hours of sunlight."""
|
|
225
|
-
|
|
230
|
+
|
|
226
231
|
return float(self.__inner['sunHour'])
|
|
227
|
-
|
|
232
|
+
|
|
228
233
|
@property
|
|
229
234
|
def snowfall(self) -> float:
|
|
230
235
|
"""Total snowfall in either Centimeters or Inches."""
|
|
231
|
-
|
|
232
|
-
return float(
|
|
233
|
-
|
|
234
|
-
) / self._CustomizableBase__unit.cm_divisor
|
|
235
|
-
|
|
236
|
+
|
|
237
|
+
return float(self.__inner['totalSnow_cm']) / self._CustomizableBase__unit.cm_divisor
|
|
238
|
+
|
|
236
239
|
@property
|
|
237
240
|
def hourly_forecasts(self) -> Iterable[HourlyForecast]:
|
|
238
241
|
"""The hourly forecasts of this day."""
|
|
239
|
-
|
|
242
|
+
|
|
240
243
|
return (
|
|
241
|
-
HourlyForecast(
|
|
242
|
-
|
|
243
|
-
) for elem in self.__inner['hourly']
|
|
244
|
+
HourlyForecast(elem, self._CustomizableBase__unit, self._CustomizableBase__locale)
|
|
245
|
+
for elem in self.__inner['hourly']
|
|
244
246
|
)
|
|
245
247
|
|
|
248
|
+
|
|
246
249
|
class Forecast(BaseForecast):
|
|
247
250
|
"""Represents today's weather forecast, alongside daily and hourly weather forecasts."""
|
|
248
|
-
|
|
251
|
+
|
|
249
252
|
__slots__ = ('__inner', '__nearest')
|
|
250
|
-
|
|
253
|
+
|
|
251
254
|
def __init__(self, json: dict, unit: _Unit, locale: Locale):
|
|
252
255
|
current = json['current_condition'][0]
|
|
253
256
|
self.__nearest = json.pop('nearest_area')[0]
|
|
254
257
|
self.__inner = json
|
|
255
|
-
|
|
258
|
+
|
|
256
259
|
super().__init__(current, unit, locale)
|
|
257
|
-
|
|
260
|
+
|
|
258
261
|
def __repr__(self) -> str:
|
|
259
262
|
return f'<{self.__class__.__name__} location={self.location!r} datetime={self.datetime!r} temperature={self.temperature!r}>'
|
|
260
|
-
|
|
263
|
+
|
|
261
264
|
@property
|
|
262
265
|
def local_population(self) -> int:
|
|
263
266
|
"""The local population count."""
|
|
264
|
-
|
|
267
|
+
|
|
265
268
|
return int(self.__nearest['population'])
|
|
266
|
-
|
|
269
|
+
|
|
267
270
|
@property
|
|
268
271
|
def region(self) -> str:
|
|
269
272
|
"""The local region's name."""
|
|
270
|
-
|
|
273
|
+
|
|
271
274
|
return self.__nearest['region'][0]['value']
|
|
272
|
-
|
|
275
|
+
|
|
273
276
|
@property
|
|
274
277
|
def location(self) -> str:
|
|
275
278
|
"""The location's name."""
|
|
276
|
-
|
|
279
|
+
|
|
277
280
|
return self.__nearest['areaName'][0]['value']
|
|
278
|
-
|
|
281
|
+
|
|
279
282
|
@property
|
|
280
283
|
def country(self) -> str:
|
|
281
284
|
"""The local country's name."""
|
|
282
|
-
|
|
285
|
+
|
|
283
286
|
return self.__nearest['country'][0]['value']
|
|
284
|
-
|
|
287
|
+
|
|
285
288
|
@property
|
|
286
289
|
def datetime(self) -> datetime:
|
|
287
290
|
"""The local date and time of this weather forecast."""
|
|
288
|
-
|
|
291
|
+
|
|
289
292
|
return datetime.strptime(
|
|
290
293
|
self._BaseForecast__inner['localObsDateTime'], '%Y-%m-%d %I:%M %p'
|
|
291
294
|
)
|
|
292
|
-
|
|
295
|
+
|
|
293
296
|
@property
|
|
294
297
|
def daily_forecasts(self) -> Iterable[DailyForecast]:
|
|
295
298
|
"""Daily weather forecasts in this location."""
|
|
296
|
-
|
|
299
|
+
|
|
297
300
|
return (
|
|
298
|
-
DailyForecast(
|
|
299
|
-
|
|
300
|
-
) for elem in self.__inner['weather']
|
|
301
|
+
DailyForecast(elem, self._CustomizableBase__unit, self._CustomizableBase__locale)
|
|
302
|
+
for elem in self.__inner['weather']
|
|
301
303
|
)
|
|
302
|
-
|
|
304
|
+
|
|
303
305
|
@property
|
|
304
306
|
def coordinates(self) -> Tuple[float, float]:
|
|
305
307
|
"""A tuple of this forecast's latitude and longitude."""
|
|
306
|
-
|
|
308
|
+
|
|
307
309
|
try:
|
|
308
|
-
for req in filter(
|
|
309
|
-
lambda x: x['type'] == 'LatLon', self.__inner['request']
|
|
310
|
-
):
|
|
310
|
+
for req in filter(lambda x: x['type'] == 'LatLon', self.__inner['request']):
|
|
311
311
|
lat, lon = LATLON_REGEX.findall(req['query'])[0]
|
|
312
|
-
|
|
312
|
+
|
|
313
313
|
return float(lat), float(lon)
|
|
314
314
|
except:
|
|
315
|
-
return float(self.__nearest['latitude']), float(self.__nearest['longitude'])
|
|
315
|
+
return float(self.__nearest['latitude']), float(self.__nearest['longitude'])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|