-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
119 lines (104 loc) · 3.84 KB
/
app.py
File metadata and controls
119 lines (104 loc) · 3.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
from flask import Flask, request, jsonify
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
import os, requests, redis, json
from dotenv import load_dotenv
app = Flask(__name__)
'''
App routing means mapping the urls to a specific function that will handle the logic for that URL
'''
redis_client = redis.Redis(host='localhost', port=6379, db=0)
@app.route("/weather")
def weather():
load_dotenv(dotenv_path=".env")
api_key = os.getenv("API_KEY")
city = request.args.get('city')
if not city:
return jsonify({"error": "Please provide a city name"}), 400
city = city.strip().lower()
redis_key = f"weather:{city}"
cached_weather = redis_client.get(redis_key)
if cached_weather is None:
print(f"Could not retrieve {city} weather information, from IEX cloud API")
url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}"
try:
response = requests.get(url, timeout=10)
response.raise_for_status()
except requests.exceptions.HTTPError as http_err:
return jsonify({"error":f"HTTP error: {http_err}"}), 503
except requests.exceptions.RequestException as req_err:
return jsonify({"error": "Network error or request failed"}),503
except requests.exceptions.ReadTimeout as time_err:
return jsonify({"error":"Request timed out"}),503
data = response.json()
if data["cod"] == 200:
result = weather_info(data)
emoji = get_weather_emoji(result['weather_id'])
weather_data = ({
"city": city,
"temperature": f"{result['temperature']:.0f}°C",
"emoji": emoji,
"description": result['description']
})
redis_client.set(redis_key, json.dumps(weather_data),ex=43200)
return jsonify(weather_data)
else:
return jsonify({"error":"City not found"}),404
else:
print(f"Found {city} weather information in cache, serving from redis")
cached_weather = json.loads(cached_weather)
return jsonify(cached_weather)
def weather_info(data):
temperature_k = data["main"]["temp"]
temperature_c = temperature_k - 273.15
temperature_f = (temperature_k * 9/5) - 459.67
weather_id = data["weather"][0]["id"]
weather_description = data["weather"][0]["description"]
return{
"temperature": temperature_c,
"weather_id": weather_id,
"description": weather_description
}
def get_weather_emoji(weather_id):
if 200 <= weather_id <= 232:
return "⛈"
elif 300 <= weather_id <= 321:
return "🌦"
elif 500 <= weather_id <= 531:
return "🌧"
elif 600 <= weather_id <= 622:
return "❄"
elif 701 <= weather_id <= 741:
return "🌫"
elif weather_id == 762:
return "🌋"
elif weather_id == 771:
return "💨"
elif weather_id == 781:
return "🌪"
elif weather_id == 800:
return "☀"
elif 801 <= weather_id <= 804:
return "☁"
else:
return ""
limiter = Limiter(key_func=get_remote_address,app=app,default_limits=["20 per minute"])
'''
Rate limiting by the remote_address of the request
A default rate limit of 20 request per minute applied to all routes.
The slow route having an explicit rate limit decorator will bypass the default rate limit and only allow 1 request per minute.
The medium route inherits the default limits and adds on a decorated limit of 1 request per second.
'''
@app.route("/slow")
@limiter.limit("1 per minute")
def slow():
return ":("
@app.route("/medium")
@limiter.limit("1 per second", override_defaults=False)
def medium():
return ":|"
@app.route("/fast")
def fast():
return ":)"
if __name__ == "__main__":
app.run(debug=True)