Skip to content

Commit 57b536e

Browse files
authored
update to use new weather api
since DarkSky is going away, switching to VisualCrossing
1 parent c14226c commit 57b536e

File tree

9 files changed

+271
-419
lines changed

9 files changed

+271
-419
lines changed

Makefile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ project-requirements:
1818

1919
requirements: project-requirements
2020
pip-compile
21-
pip-sync
2221

2322
check-updates:
2423
pip-compile -nU | diff requirements.txt -

hotasballs/config.example.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ region: ap-northeast-1
33
function_name: hotasballs
44
handler: service.handler
55
description: Is your city hot as balls?
6-
runtime: python3.7
6+
runtime: python3.9
77
# role: lambda_basic_execution
88

99
# S3 upload requires appropriate role with s3:PutObject permission
@@ -25,6 +25,7 @@ memory_size: 128
2525
# Experimental Environment variables
2626
environment_variables:
2727
DARKSKY_SECRET_KEY:
28+
WEATHER_SECRET_KEY:
2829
HOT_THRESHOLD:
2930
COLD_THRESHOLD:
3031
TWITTER_CONSUMER_KEY:

hotasballs/requirements.txt

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
#
2-
# This file is autogenerated by pip-compile
2+
# This file is autogenerated by pip-compile with python 3.10
33
# To update, run:
44
#
55
# pip-compile --no-annotate
66
#
7-
certifi==2019.3.9
8-
chardet==3.0.4
9-
idna==2.8
10-
oauthlib==3.0.1
11-
pysocks==1.7.0
12-
requests-oauthlib==1.2.0
13-
requests==2.22.0
14-
six==1.12.0
15-
tweepy==3.7.0
16-
urllib3==1.25.3
7+
certifi==2022.6.15
8+
charset-normalizer==2.0.12
9+
idna==3.3
10+
oauthlib==3.2.0
11+
requests==2.28.0
12+
requests-oauthlib==1.3.1
13+
tweepy==4.10.0
14+
urllib3==1.26.9

hotasballs/service.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,37 @@
55
import requests
66
import tweepy
77

8-
DARKSKY_SECRET_KEY: str = os.environ.get('DARKSKY_SECRET_KEY')
8+
WEATHER_SECRET_KEY: str = os.environ.get('WEATHER_SECRET_KEY')
99
HOT_THRESHOLD: int = int(os.environ.get('HOT_THRESHOLD', 35))
1010
COLD_THRESHOLD: int = int(os.environ.get('COLD_THRESHOLD', 28))
1111
TWITTER_CONSUMER_KEY: str = os.environ.get('TWITTER_CONSUMER_KEY')
1212
TWITTER_CONSUMER_SECRET: str = os.environ.get('TWITTER_CONSUMER_SECRET')
1313

1414

1515
class Weather:
16-
'''Get data we're interested in from Darsky forecast json and format it.'''
16+
'''Get data we're interested in from weather forecast json and format it.'''
1717
temperatureHigh: float
1818
apparentTemperatureHigh: float
1919
humidity: int
2020

2121
def __init__(self, data: dict) -> None:
22-
self.temperatureHigh = round(data['temperatureHigh'], 1)
23-
self.apparentTemperatureHigh = round(data['apparentTemperatureHigh'], 1)
24-
self.humidity = round(data['humidity'] * 100)
22+
self.temperatureHigh = round(data['tempmax'], 1)
23+
self.apparentTemperatureHigh = round(data['feelslikemax'], 1)
24+
self.humidity = round(data['humidity'])
2525

2626

27-
def get_weather(lat: float, lon: float) -> object:
28-
'''Call Darksky API and return the weather data we need.'''
29-
DARKSKY_URL = f'https://api.darksky.net/forecast/{DARKSKY_SECRET_KEY}/{lat},{lon}'
27+
def get_weather(lat: float, lon: float, city: str) -> object:
28+
'''Call Weather API and return the weather data we need.'''
29+
WEATHER_URL = f'https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/{city}'
3030
payload = {
31-
'units': 'si',
32-
'exclude': 'hourly',
31+
'unitGroup': 'metric',
32+
'key': WEATHER_SECRET_KEY,
33+
'contentType': 'json',
3334
}
34-
forecast = requests.get(DARKSKY_URL, params=payload).json()
35+
forecast = requests.get(WEATHER_URL, params=payload).json()
3536

3637
# Only interested in today's forecast
37-
today = forecast['daily']['data'][0]
38+
today = forecast['days'][0]
3839

3940
return Weather(today)
4041

@@ -80,7 +81,7 @@ def handler(event, context):
8081
access_token: str = event.get('access_token')
8182
access_token_secret: str = event.get('access_token_secret')
8283

83-
weather = get_weather(lat, lon)
84+
weather = get_weather(lat, lon, city)
8485

8586
message = generate_message(weather, city)
8687

requirements.in

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
-r hotasballs/requirements.in
2-
python-lambda
2+
pip-tools
3+
git+https://github.com/Lowess/python-lambda@master#egg=python-lambda
34
pylint
45
autopep8
56
python-dotenv

requirements.txt

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,20 @@ astroid==2.11.6
88
# via pylint
99
autopep8==1.6.0
1010
# via -r requirements.in
11-
boto3==1.24.14
11+
boto3==1.24.15
1212
# via python-lambda
13-
botocore==1.27.14
13+
botocore==1.27.15
1414
# via
1515
# boto3
1616
# s3transfer
1717
certifi==2022.6.15
1818
# via requests
1919
charset-normalizer==2.0.12
2020
# via requests
21-
click==6.6
22-
# via python-lambda
21+
click==7.1.2
22+
# via
23+
# pip-tools
24+
# python-lambda
2325
colorama==0.4.5
2426
# via green
2527
coverage==6.4.1
@@ -52,6 +54,10 @@ oauthlib==3.2.0
5254
# via
5355
# requests-oauthlib
5456
# tweepy
57+
pep517==0.12.0
58+
# via pip-tools
59+
pip-tools==6.6.2
60+
# via -r requirements.in
5561
platformdirs==2.5.2
5662
# via pylint
5763
pycodestyle==2.8.0
@@ -62,7 +68,7 @@ python-dateutil==2.8.2
6268
# via botocore
6369
python-dotenv==0.20.0
6470
# via -r requirements.in
65-
python-lambda==11.8.0
71+
python-lambda @ git+https://github.com/Lowess/python-lambda@master
6672
# via -r requirements.in
6773
pyyaml==5.1
6874
# via python-lambda
@@ -86,7 +92,9 @@ six==1.16.0
8692
toml==0.10.2
8793
# via autopep8
8894
tomli==2.0.1
89-
# via pylint
95+
# via
96+
# pep517
97+
# pylint
9098
tomlkit==0.11.0
9199
# via pylint
92100
tweepy==4.10.0
@@ -97,8 +105,11 @@ urllib3==1.26.9
97105
# via
98106
# botocore
99107
# requests
108+
wheel==0.37.1
109+
# via pip-tools
100110
wrapt==1.14.1
101111
# via astroid
102112

103113
# The following packages are considered to be unsafe in a requirements file:
114+
# pip
104115
# setuptools

test/.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Fake environment variable for testing
2-
DARKSKY_SECRET_KEY=darkskysecret
2+
WEATHER_SECRET_KEY=secretkey
33
HOT_THRESHOLD=35
44
COLD_THRESHOLD=28
55
TWITTER_CONSUMER_KEY=consumerkey

test/test_service.py

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,48 +17,49 @@ def setUp(self):
1717
open(Path(__file__).parent / 'weather_data.json', 'r'))
1818
self.lat = 35.640432
1919
self.lon = 139.728833
20+
self.city = 'Tokyo'
2021
event_input = {
2122
'lat': self.lat,
2223
'lon': self.lon,
23-
'city': 'Tokyo',
24+
'city': self.city,
2425
'access_token': 'token',
2526
'access_token_secret': 'secret',
2627
}
2728
event_template = json.load(
28-
open(Path(__file__).parent / 'weather_data.json', 'r'))
29+
open(Path(__file__).parent / 'cloudwatch_event.json', 'r'))
2930
self.event = {**event_template, **event_input}
3031

3132
def testTestEnv(self):
3233
'''Test that we have proper environment variables setup.'''
3334
self.assertEqual(service.HOT_THRESHOLD, 35)
34-
self.assertEqual(service.DARKSKY_SECRET_KEY, 'darkskysecret')
35+
self.assertEqual(service.WEATHER_SECRET_KEY, 'secretkey')
3536

3637
def testWeatherGet(self):
37-
'''Test that we can grab data from Darksky and format it.'''
38+
'''Test that we can grab data from weather API and format it.'''
3839
with requests_mock.Mocker() as mock:
3940
mock.get(
40-
f"https://api.darksky.net/forecast/{os.getenv('DARKSKY_SECRET_KEY')}/{self.lat},{self.lon}?units=si&exclude=hourly",
41+
f"https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/{self.city}?unitGroup=metric&key={os.getenv('WEATHER_SECRET_KEY')}&contentType=json",
4142
json=self.weather_data
4243
)
43-
weather = service.get_weather(self.lat, self.lon)
44-
self.assertEqual(weather.temperatureHigh, 26.4)
45-
self.assertEqual(weather.apparentTemperatureHigh, 28.0)
46-
self.assertEqual(weather.humidity, 85)
44+
weather = service.get_weather(self.lat, self.lon, self.city)
45+
self.assertEqual(weather.temperatureHigh, 28.1)
46+
self.assertEqual(weather.apparentTemperatureHigh, 29.0)
47+
self.assertEqual(weather.humidity, 78)
4748

4849
def testColdMessage(self):
4950
'''Test message creation when too cold.'''
5051
w = service.Weather({
51-
'temperatureHigh': 23.4,
52-
'apparentTemperatureHigh': 24.0,
52+
'tempmax': 23.4,
53+
'feelslikemax': 24.0,
5354
'humidity': 85,
5455
})
5556
self.assertEqual(service.generate_message(w, 'Tokyo'), None)
5657

5758
def testWarmMessage(self):
5859
'''Test message creation when just warm.'''
5960
w = service.Weather({
60-
'temperatureHigh': 23.4,
61-
'apparentTemperatureHigh': 29.3,
61+
'tempmax': 23.4,
62+
'feelslikemax': 29.3,
6263
'humidity': 85,
6364
})
6465
message = service.generate_message(w, 'Tokyo')
@@ -68,8 +69,8 @@ def testWarmMessage(self):
6869
def testHotMessage(self):
6970
'''Test message creation when it's hot as balls.'''
7071
w = service.Weather({
71-
'temperatureHigh': 35.8,
72-
'apparentTemperatureHigh': 36.3,
72+
'tempmax': 35.8,
73+
'feelslikemax': 36.3,
7374
'humidity': 85,
7475
})
7576
message = service.generate_message(w, 'Tokyo')
@@ -80,8 +81,8 @@ def testHotMessage(self):
8081
def testHotFeltMessage(self):
8182
'''Test message creation when it's hot as balls and apparent temp gap.'''
8283
w = service.Weather({
83-
'temperatureHigh': 29.4,
84-
'apparentTemperatureHigh': 36.3,
84+
'tempmax': 29.4,
85+
'feelslikemax': 36.3,
8586
'humidity': 85,
8687
})
8788
message = service.generate_message(w, 'Tokyo')
@@ -94,7 +95,7 @@ def testFullHandler(self, mock_update_status):
9495
'''Test the full handler including twitter.'''
9596
with requests_mock.Mocker() as mock:
9697
mock.get(
97-
f"https://api.darksky.net/forecast/{os.getenv('DARKSKY_SECRET_KEY')}/{self.lat},{self.lon}?units=si&exclude=hourly",
98+
f"https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/{self.city}?unitGroup=metric&key={os.getenv('WEATHER_SECRET_KEY')}&contentType=json",
9899
json=self.weather_data
99100
)
100101
output = service.handler(self.event, None)

0 commit comments

Comments
 (0)