{"id":1740,"date":"2019-02-25T03:33:53","date_gmt":"2019-02-25T03:33:53","guid":{"rendered":"https:\/\/www.codeastar.com\/?p=1740"},"modified":"2019-03-05T16:49:51","modified_gmt":"2019-03-05T16:49:51","slug":"flask-backend-react-frontend-weather-1","status":"publish","type":"post","link":"https:\/\/www.codeastar.com\/flask-backend-react-frontend-weather-1\/","title":{"rendered":"Flask Backend and React Frontend for Weather Forecast – Part 1"},"content":{"rendered":"\n
We built a weather forecast web app with Flask<\/a> in past. That was an old fashioned way which we handled all frontend and backend stuff under Flask<\/a> framework. The best practice for web applications nowadays is separating frontend and backend into 2 modules. So we can utilize both modules to their max capability. <\/p>\n\n\n\n\n\n\n\n Let’s think about we are playing a fantasy role playing game. As a hero in early stage, we have a bard companion who can do magic and sword fighting. And we have no problem fighting monsters in our early journey. But when we go deeper and face bigger challenges, we realize we need to re-arrange our strategy. We need more and higher level of magics and fighting skills. Thus we find our new companions, a mage who focuses on casting magic at the back and a sword swinger who deals with trouble at the front. And in this post, we are going to talk about our magic user at the back — the backend module.<\/p>\n\n\n\n What is a RESTful backend? It is a backend module that aims to run web services in a fast and reliable way. Those web services use standard and predefined textual representations (e.g. XML or JSON) for information exchange operations (GET \/ POST \/ PUT \/ DELETE) . Thus it is easy for others (frontend modules) to send request to and receive response from backend module. We have built the EZ Weather Forecast web app (EZW) with Flask before. And Flask is a microframework of Python to build a web app, Flask RESTful<\/a> is its RESTful version to build a RESTful backend, which also focuses on small and fast in development cycle. How fast the development cycle is in Flask RESTful? It is no better way to try it out ourselves, so let’s roll!<\/p>\n\n\n\n First of all, let’s create our development folder. <\/p>\n\n\n\n Then use pipenv<\/a> to create our development environment and install required libraries.<\/p>\n\n\n\n As we have built the EZW with Flask before, we can reuse most of it on our new Flask RESTful backend. Let’s download or fork our EZW source from GitHub<\/a>. From the ezw <\/em>folder, we can keep the weather report model file, ezw_model.py<\/em>. Before we go further, let’s think about the request interface when frontend modules try to make a call to us. We require them to send us date range and location information in JSON format. So we have:<\/p>\n\n\n\n Then we work on the EZW’s core logic file, ezw_controller.py<\/em>. First, we rename it to ezw_restful_controller.py<\/em> to identify the difference. Of course, renaming the file to restful won’t make our service restful :]] . What we do is changing our output to JSON object, in order to make a restful response. On our ezw_restful_controller.py <\/em>file, we change our code to: <\/p>\n\n\n\n You may notice that our “new restful” <\/em>file just looks like our good old controller file. Yes, but we do modify 3 little parts from the file to make it simpler:<\/p>\n\n\n\n Now we are going to make our Flask RESTful api file, let’s call it api.py<\/em> then. The goal of this file is to receive POST request from frontend and send back the response. Let’s copy following content to the file:<\/p>\n\n\n\n We only do 2 things in this file:<\/p>\n\n\n\n All files are done, yes, the coding is even simpler and shorter than our original Flask app. Let’s review our file system, we should have following files under our ezw_restful<\/em> folder:<\/p>\n\n\n\n When all files are present, it is the time to run our RESTful backend server. But before we run the server, please make sure you have Dark Sky API<\/a> Key set in your environment. If you do not have it, go to apply it, it’s free! To set the Dark Sky Key in your environment, you can:<\/p>\n\n\n\n When all the stuff is ready, we can type following command to start our server:<\/p>\n\n\n\n You should see similar messages from your console:<\/p>\n\n\n\n Okay, our server is started, let’s call our api on a browser by typing “http:\/\/127.0.0.1:5000\/ezw_api” and we have:<\/p>\n\n\n\n Wait, don’t panic, this is our expected result. As we create a POST operation and no GET operation at all. At least, we know our server is started. :]]<\/p>\n\n\n\n We can test our RESTful backend using command line tool curl<\/em> with JSON data. But it is always good to have a GUI on handling such task. So, let’s download Postman<\/a> then. This app is widely used by developers on RESTful API testing. When you run the program, we should see following interface:<\/p>\n\n\n\n Since we are testing the POST operation, select “POST” method on spot (1), then enter our API endpoint “http:\/\/127.0.0.1:5000\/ezw_api” on spot (2).<\/p>\n\n\n\n We click “Body” on spot (3) as input type, select “raw” on spot (4) and “JSON” on spot (5) as input options. After that we type following input content on spot (6): (it is Mountain View, CA, USA geographic location actually) <\/p>\n\n\n\n And click “Send”, then a list of weather reports in JSON format should be returned: <\/p>\n\n\n\n Done! We have created our working RESTful backend successfully.<\/p>\n\n\n\n Our next step is crafting a frontend module, a React frontend module. So stay tuned for Part 2<\/a>!<\/p>\n\n\n\n (the complete source can be found at GitHub<\/strong>: https:\/\/github.com\/codeastar\/ez_weather_forecast_flask_backend<\/a> or GitLab<\/strong>: https:\/\/gitlab.com\/codeastar\/ez_weather_forecast_flask_backend<\/a>)<\/p>\n","protected":false},"excerpt":{"rendered":" We built a weather forecast web app with Flask in past. That was an old fashioned way which we handled all frontend and backend stuff under Flask framework. The best practice for web applications nowadays is separating frontend and backend into 2 modules. So we can utilize both modules to their max capability.<\/p>\n","protected":false},"author":1,"featured_media":1758,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"site-sidebar-layout":"default","site-content-layout":"default","ast-site-content-layout":"","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","theme-transparent-header-meta":"default","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false},"version":2}},"categories":[2],"tags":[15,132,71,60,133,8,130,32],"class_list":["post-1740","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-we-code-therefore-we-are","tag-api","tag-backend","tag-ezw","tag-flask","tag-json","tag-python","tag-restful","tag-weather-forecast"],"jetpack_publicize_connections":[],"yoast_head":"\nRESTful Backend Module<\/h3>\n\n\n\n
Flask RESTful backend in action<\/h3>\n\n\n\n
$mkdir ezw_restful\n$cd ezw_restful<\/code><\/pre>\n\n\n\n
$pipenv --three\n$pipenv install flask\n$pipenv install flask-cors\n$pipenv install flask-restful\n$pipenv install requests<\/code><\/pre>\n\n\n\n
{\n \"latitude\": <0.0000>, \n \"longitude\": <0.0000>, \n \"start_date\": <YYYY-MM-DD>,\n \"end_date\": <YYYY-MM-DD> \n}<\/code><\/pre>\n\n\n\n
from ezw_model import WeatherReport\nfrom datetime import datetime,timedelta\nimport requests, os, json\n\nDARK_SKY_API_KEY = os.environ['DARK_SKY_KEY']\noption_list = \"exclude=currently,minutely,hourly,alerts&units=si\"\n\nclass EZWRController:\n \n def getWeatherReports(self, date_from, date_to, \n latitude, longitude):\n d_from_date = datetime.strptime(date_from , '%Y-%m-%d')\n d_to_date = datetime.strptime(date_to , '%Y-%m-%d')\n delta = d_to_date - d_from_date\n\n latitude = str(latitude)\n longitude = str(longitude)\n\n weather_reports = []\n\n for i in range(delta.days+1):\n new_date = (d_from_date + timedelta(days=i)).strftime('%Y-%m-%d')\n search_date = new_date+\"T00:00:00\"\n response = requests.get(\"https:\/\/api.darksky.net\/forecast\/\"+DARK_SKY_API_KEY+\"\/\"+latitude+\",\"+longitude+\",\"+search_date+\"?\"+option_list)\n json_res = response.json()\n report_date = (d_from_date + timedelta(days=i)).strftime('%Y-%m-%d %A')\n unit_type = '\u00b0F' if json_res['flags']['units'] == 'us' else '\u00b0C'\n min_temperature = str(json_res['daily']['data'][0]['apparentTemperatureMin'])+unit_type\n max_temperature = str(json_res['daily']['data'][0]['apparentTemperatureMax'])+unit_type\n summary = json_res['daily']['data'][0]['summary']\n icon = json_res['daily']['data'][0]['icon']\n precip_type = None\n precip_prob = None\n raining_chance = None\n if'precipProbability' in json_res['daily']['data'][0] and 'precipType' in json_res['daily']['data'][0]:\n precip_type = json_res['daily']['data'][0]['precipType']\n precip_prob = json_res['daily']['data'][0]['precipProbability']\n if (precip_type == 'rain' and precip_prob != None):\n precip_prob *= 100\n raining_chance = \"%.2f%%\" % (precip_prob)\n\n ezw_wr = WeatherReport(report_date, max_temperature,min_temperature, \n summary, raining_chance, icon) \n\n weather_reports.append(ezw_wr)\n json_reports = json.dumps([weather_report.__dict__ for weather_report in weather_reports])\n\n return json_reports\n<\/pre>\n\n\n\n
from flask import Flask, request\nfrom flask_restful import Resource, Api\nfrom flask_cors import CORS\nfrom ezw_restful_controller import EZWRController\napp = Flask(__name__)\napi = Api(app)\nCORS(app)\n\nclass Ezw_API(Resource):\n def post(self):\n json_data = request.get_json()\n latitude = json_data['latitude']\n longitude = json_data['longitude']\n start_date = json_data['start_date']\n end_date = json_data['end_date']\n print(json_data)\n ezw = EZWRController()\n ezw_reports = ezw.getWeatherReports(start_date, end_date, latitude, \n longitude)\n return {'reports': ezw_reports}\n\napi.add_resource(Ezw_API, '\/ezw_api')\nif __name__ == '__main__':\n app.run(debug=False)\n<\/pre>\n\n\n\n
Running our RESTful backend server<\/h3>\n\n\n\n
\/ezw_restful\n |-- api.py\n |-- ezw_model.py\n |-- ezw_restful_controller.py<\/code><\/pre>\n\n\n\n
$export DARK_SKY_KEY=<your Dark Sky Key> \/\/for MacOS\n>SET DARK_SKY_KEY=<your Dark Sky Key> \/\/for Windows<\/code><\/pre>\n\n\n\n
$python api.py<\/code><\/pre>\n\n\n\n
* Environment: development\n * Running on http:\/\/127.0.0.1:5000\/ (Press CTRL+C to quit)<\/code><\/pre>\n\n\n\n
<\/figure>\n\n\n\n
Test the RESTful backend<\/h3>\n\n\n\n
<\/figure>\n\n\n\n
<\/figure>\n\n\n\n
{\n\t\"latitude\": 37.386051,\n\t\"longitude\": -122.083855, \n\t\"start_date\": \"2019-03-01\", \n\t\"end_date\": \"2019-03-03\"\n}<\/code><\/pre>\n\n\n\n
<\/figure>\n\n\n\n
What have we learnt in this post?<\/h3>\n\n\n\n