From 40fa01b1e3566db2beb9e2c16f23828afc935572 Mon Sep 17 00:00:00 2001 From: gokul-elumalai Date: Thu, 2 Nov 2023 23:50:37 +0530 Subject: [PATCH 1/2] feat: Change Flask to Quart framework Quart microframework supports async calls --- .../your_turn/day_4/cityscape_api/app.py | 3 +- .../day_4/cityscape_api/config/dev.json | 2 +- .../day_4/cityscape_api/config/prod.json | 2 +- .../day_4/cityscape_api/requirements.txt | 1 + .../services/location_service.py | 18 +++++------ .../cityscape_api/services/sun_service.py | 21 +++++++----- .../cityscape_api/services/weather_service.py | 16 ++++++---- .../day_4/cityscape_api/views/city_api.py | 32 +++++++++++-------- .../day_4/cityscape_api/views/home.py | 5 +-- 9 files changed, 58 insertions(+), 42 deletions(-) diff --git a/days/021-024-quart-async/your_turn/day_4/cityscape_api/app.py b/days/021-024-quart-async/your_turn/day_4/cityscape_api/app.py index 4f88e501..4cec6844 100644 --- a/days/021-024-quart-async/your_turn/day_4/cityscape_api/app.py +++ b/days/021-024-quart-async/your_turn/day_4/cityscape_api/app.py @@ -5,8 +5,9 @@ import services.weather_service import services.sun_service import services.location_service +import quart -app = flask.Flask(__name__) +app = quart.Quart(__name__) is_debug = False app.register_blueprint(home.blueprint) diff --git a/days/021-024-quart-async/your_turn/day_4/cityscape_api/config/dev.json b/days/021-024-quart-async/your_turn/day_4/cityscape_api/config/dev.json index 80a27339..bd49fda9 100644 --- a/days/021-024-quart-async/your_turn/day_4/cityscape_api/config/dev.json +++ b/days/021-024-quart-async/your_turn/day_4/cityscape_api/config/dev.json @@ -1,4 +1,4 @@ { "dev": true, - "weather_key": "" + "weather_key": "59f94b2bbf28e1a7dfdaa2086e1a4722" } \ No newline at end of file diff --git a/days/021-024-quart-async/your_turn/day_4/cityscape_api/config/prod.json b/days/021-024-quart-async/your_turn/day_4/cityscape_api/config/prod.json index 6f5a38a5..7bf1e954 100644 --- a/days/021-024-quart-async/your_turn/day_4/cityscape_api/config/prod.json +++ b/days/021-024-quart-async/your_turn/day_4/cityscape_api/config/prod.json @@ -1,4 +1,4 @@ { "dev": false, - "weather_key": "" + "weather_key": "59f94b2bbf28e1a7dfdaa2086e1a4722" } \ No newline at end of file diff --git a/days/021-024-quart-async/your_turn/day_4/cityscape_api/requirements.txt b/days/021-024-quart-async/your_turn/day_4/cityscape_api/requirements.txt index ae76cab5..0f903f25 100644 --- a/days/021-024-quart-async/your_turn/day_4/cityscape_api/requirements.txt +++ b/days/021-024-quart-async/your_turn/day_4/cityscape_api/requirements.txt @@ -1,5 +1,6 @@ # Web framework requirements flask +quart # Calling services requirements requests diff --git a/days/021-024-quart-async/your_turn/day_4/cityscape_api/services/location_service.py b/days/021-024-quart-async/your_turn/day_4/cityscape_api/services/location_service.py index 6a6bc918..d620aa8c 100644 --- a/days/021-024-quart-async/your_turn/day_4/cityscape_api/services/location_service.py +++ b/days/021-024-quart-async/your_turn/day_4/cityscape_api/services/location_service.py @@ -1,7 +1,9 @@ +import asyncio import random import time from typing import Tuple -import requests +# import requests +import aiohttp use_cached_data = True @@ -10,7 +12,7 @@ 0.535646, 0.527148, 0.533472, 0.53351, 0.523462] -def get_lat_long(zip_code: str, country: str) -> Tuple[float, float]: +async def get_lat_long(zip_code: str, country: str) -> Tuple[float, float]: key = f'{zip_code}, {country}' # Sadly, datasciencetoolkit.org seems to have gone out of existence. # I set the use cached data = true in the config to keep this section working @@ -18,15 +20,13 @@ def get_lat_long(zip_code: str, country: str) -> Tuple[float, float]: url = f'https://www.datasciencetoolkit.org/street2coordinates/{key.replace(" ", "+")}' if use_cached_data: - # TODO: Convert this to await asyncio.sleep() - - time.sleep(random.choice(measured_latency_in_sec)) + await asyncio.sleep(random.choice(measured_latency_in_sec)) return 45.50655, -122.733888 else: - resp = requests.get(url) - resp.raise_for_status() - - data = resp.json() + async with aiohttp.ClientSession() as session: + async with session.get(url) as resp: + resp.raise_for_status() + data = resp.json() city_data = data.get(f'{zip_code}, {country}', dict()) return city_data.get('latitude', 0.00), city_data.get('longitude', 0.00) diff --git a/days/021-024-quart-async/your_turn/day_4/cityscape_api/services/sun_service.py b/days/021-024-quart-async/your_turn/day_4/cityscape_api/services/sun_service.py index 8cbeddd1..5a58d5ae 100644 --- a/days/021-024-quart-async/your_turn/day_4/cityscape_api/services/sun_service.py +++ b/days/021-024-quart-async/your_turn/day_4/cityscape_api/services/sun_service.py @@ -1,29 +1,34 @@ +import asyncio import datetime import random import time -import requests +# import requests +import aiohttp measured_latency_in_sec = [0.399203, 0.7046, 0.422959, 0.741911, 0.404674] use_cached_data = False -def for_today(latitude: float, longitude: float) -> dict: +async def for_today(latitude: float, longitude: float) -> dict: url = f'https://api.sunrise-sunset.org/json?lat={latitude}&lng={longitude}' if use_cached_data: # Set in config/dev.json or config/prod.json - # TODO: Convert this to await asyncio.sleep() - - time.sleep(random.choice(measured_latency_in_sec)) + await asyncio.sleep(random.choice(measured_latency_in_sec)) return {'sunrise': '06:04:09 AM', 'sunset': '08:28:48 PM', 'solar_noon': '01:16:28 PM', 'day_length': '14:24:39', 'civil_twilight_begin': '05:31:10 AM', 'civil_twilight_end': '09:01:47 PM', 'nautical_twilight_begin': '04:49:54 AM', 'nautical_twilight_end': '09:43:03 PM', 'astronomical_twilight_begin': '04:03:13 AM', 'astronomical_twilight_end': '10:29:44 PM'} else: - resp = requests.get(url) - resp.raise_for_status() + async with aiohttp.ClientSession() as session: + async with session.get(url) as resp: + resp.raise_for_status() + data = await resp.json() + + # resp = requests.get(url) + # resp.raise_for_status() - sun_data = resp.json().get('results', {}) + sun_data = data.get('results', {}) for k, v in list(sun_data.items()): if 'AM' not in v and 'PM' not in v: continue diff --git a/days/021-024-quart-async/your_turn/day_4/cityscape_api/services/weather_service.py b/days/021-024-quart-async/your_turn/day_4/cityscape_api/services/weather_service.py index 63b37b2e..4d822675 100644 --- a/days/021-024-quart-async/your_turn/day_4/cityscape_api/services/weather_service.py +++ b/days/021-024-quart-async/your_turn/day_4/cityscape_api/services/weather_service.py @@ -1,7 +1,8 @@ import requests +import aiohttp # TODO: Set your api.openweathermap.org API key here: -__api_key = '' +__api_key = '59f94b2bbf28e1a7dfdaa2086e1a4722' def global_init(api_key: str): @@ -15,9 +16,12 @@ def global_init(api_key: str): print() -def get_current(zip_code: str, country_code: str) -> dict: +async def get_current(zip_code: str, country_code: str) -> dict: url = f'https://api.openweathermap.org/data/2.5/weather?zip={zip_code},{country_code}&appid={__api_key}' - resp = requests.get(url) - resp.raise_for_status() - - return resp.json() + # print(url) + # resp = requests.get(url) + # resp.raise_for_status() + async with aiohttp.ClientSession() as session: + async with session.get(url) as resp: + resp.raise_for_status() + return await resp.json() diff --git a/days/021-024-quart-async/your_turn/day_4/cityscape_api/views/city_api.py b/days/021-024-quart-async/your_turn/day_4/cityscape_api/views/city_api.py index 076220e4..47fbc33b 100644 --- a/days/021-024-quart-async/your_turn/day_4/cityscape_api/views/city_api.py +++ b/days/021-024-quart-async/your_turn/day_4/cityscape_api/views/city_api.py @@ -1,34 +1,38 @@ -import flask +import asyncio + +import quart from services import weather_service, sun_service, location_service -blueprint = flask.blueprints.Blueprint(__name__.replace('.', '_'), __name__) +blueprint = quart.blueprints.Blueprint(__name__.replace('.', '_'), __name__) @blueprint.route('/api/events///', methods=['GET']) -def events(city: str, state: str, country: str): +async def events(city: str, state: str, country: str): player = { "name": "Jeff the player", "city": city, "state": state, "country": country, } + await asyncio.sleep(2) if not player: - flask.abort(404) - return flask.jsonify(player) + quart.abort(404) + return quart.jsonify(player) @blueprint.route('/api/weather//', methods=['GET']) -def weather(zip_code: str, country: str): - weather_data = weather_service.get_current(zip_code, country) +async def weather(zip_code: str, country: str): + weather_data = await weather_service.get_current(zip_code, country) if not weather_data: - flask.abort(404) - return flask.jsonify(weather_data) + quart.abort(404) + return quart.jsonify(weather_data) @blueprint.route('/api/sun//', methods=['GET']) -def sun(zip_code: str, country: str): - lat, long = location_service.get_lat_long(zip_code, country) - sun_data = sun_service.for_today(lat, long) +async def sun(zip_code: str, country: str): + lat, long = await location_service.get_lat_long(zip_code, country) + sun_data = await sun_service.for_today(lat, long) + await asyncio.sleep(2) if not sun_data: - flask.abort(404) - return flask.jsonify(sun_data) + quart.abort(404) + return quart.jsonify(sun_data) diff --git a/days/021-024-quart-async/your_turn/day_4/cityscape_api/views/home.py b/days/021-024-quart-async/your_turn/day_4/cityscape_api/views/home.py index 51352eb9..eb554472 100644 --- a/days/021-024-quart-async/your_turn/day_4/cityscape_api/views/home.py +++ b/days/021-024-quart-async/your_turn/day_4/cityscape_api/views/home.py @@ -1,6 +1,7 @@ import flask +import quart -blueprint = flask.blueprints.Blueprint(__name__.replace('.', '_'), __name__) +blueprint = quart.blueprints.Blueprint(__name__.replace('.', '_'), __name__) @blueprint.route('/') @@ -10,4 +11,4 @@ def index(): @blueprint.errorhandler(404) def not_found(_): - return flask.Response("The page was not found.", status=404) + return quart.Response("The page was not found.", status=404) From d0ba1f7a1b09e95417ba873bb96c64cf3db8029c Mon Sep 17 00:00:00 2001 From: gokul-elumalai Date: Fri, 3 Nov 2023 17:34:02 +0530 Subject: [PATCH 2/2] fix: Remove Openweather API keys --- .../your_turn/day_4/cityscape_api/config/dev.json | 2 +- .../your_turn/day_4/cityscape_api/config/prod.json | 2 +- .../your_turn/day_4/cityscape_api/services/weather_service.py | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/days/021-024-quart-async/your_turn/day_4/cityscape_api/config/dev.json b/days/021-024-quart-async/your_turn/day_4/cityscape_api/config/dev.json index bd49fda9..80a27339 100644 --- a/days/021-024-quart-async/your_turn/day_4/cityscape_api/config/dev.json +++ b/days/021-024-quart-async/your_turn/day_4/cityscape_api/config/dev.json @@ -1,4 +1,4 @@ { "dev": true, - "weather_key": "59f94b2bbf28e1a7dfdaa2086e1a4722" + "weather_key": "" } \ No newline at end of file diff --git a/days/021-024-quart-async/your_turn/day_4/cityscape_api/config/prod.json b/days/021-024-quart-async/your_turn/day_4/cityscape_api/config/prod.json index 7bf1e954..6f5a38a5 100644 --- a/days/021-024-quart-async/your_turn/day_4/cityscape_api/config/prod.json +++ b/days/021-024-quart-async/your_turn/day_4/cityscape_api/config/prod.json @@ -1,4 +1,4 @@ { "dev": false, - "weather_key": "59f94b2bbf28e1a7dfdaa2086e1a4722" + "weather_key": "" } \ No newline at end of file diff --git a/days/021-024-quart-async/your_turn/day_4/cityscape_api/services/weather_service.py b/days/021-024-quart-async/your_turn/day_4/cityscape_api/services/weather_service.py index 4d822675..04ab704e 100644 --- a/days/021-024-quart-async/your_turn/day_4/cityscape_api/services/weather_service.py +++ b/days/021-024-quart-async/your_turn/day_4/cityscape_api/services/weather_service.py @@ -1,8 +1,7 @@ import requests import aiohttp -# TODO: Set your api.openweathermap.org API key here: -__api_key = '59f94b2bbf28e1a7dfdaa2086e1a4722' +__api_key = '' def global_init(api_key: str):