{"id":103,"date":"2020-06-14T02:12:54","date_gmt":"2020-06-14T02:12:54","guid":{"rendered":"http:\/\/www.ridgeline-analytics.com\/?p=103"},"modified":"2020-09-30T02:54:25","modified_gmt":"2020-09-30T02:54:25","slug":"build-a-web-app-with-python-flask-and-aws-elastic-beanstalk","status":"publish","type":"post","link":"https:\/\/www.ridgeline-analytics.com\/index.php\/2020\/06\/14\/build-a-web-app-with-python-flask-and-aws-elastic-beanstalk\/","title":{"rendered":"Build a Web App with Python, Flask and AWS Elastic Beanstalk"},"content":{"rendered":"\n<p><a href=\"https:\/\/flask.palletsprojects.com\/en\/1.1.x\/\">Flask<\/a> is a popular framework for building lightweight server-side web applications with Python. As an antidote to the more complex and fully-featured siblings such as Django, Flask excels with a minimal footprint, intuitive MVC-style routing and view controls, and a host of thoughtful plugins for things like SQLAlchemy. <\/p>\n\n\n\n<p>In this article we&#8217;ll review my experience building a single-page Flask app and deploying it to AWS Elastic Beanstalk (EB), Amazon&#8217;s single-click scale-out application server. AWS EB orchestrates everything from spinning up an EC2 instance to setting up load balancing to configuring alerts and monitoring, all from a single command.<br><br>If you wish to take a look at the source code you can find it here: <a href=\"https:\/\/github.com\/Sassberto\/MP-GPX\/blob\/master\/MPAPI_GPX_classes.py\">https:\/\/github.com\/Sassberto\/MP-GPX\/<\/a> <br><br>The example application can be found here: <a href=\"http:\/\/gpx.ridgeline-analytics.com\/\">http:\/\/gpx.ridgeline-analytics.com\/<\/a><\/p>\n\n\n\n<p>As with all good programming projects I had an itch to scratch. &nbsp;In my spare time I enjoy rock climbing and mountaineering, and I use a site called <a href=\"http:\/\/mountainproject.com\/\">MountainProject.com<\/a>&nbsp;to identify, select and &nbsp;research potential climbing routes. &nbsp;The site has a&nbsp;handy&nbsp;feature which allows me to add routes to a todo list, where I can easily find them later. &nbsp;This route data has all sorts of stuff, but most important is&nbsp;the latitude and longitude of the climb itself. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" loading=\"lazy\" width=\"1024\" height=\"294\" src=\"http:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-1024x294.png\" alt=\"\" class=\"wp-image-104\" srcset=\"https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-1024x294.png 1024w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-300x86.png 300w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-768x220.png 768w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-1536x441.png 1536w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-2048x588.png 2048w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><figcaption>My todo list on MountainProject.   Doesn&#8217;t do me much good if I&#8217;m not online&#8230;<\/figcaption><\/figure>\n\n\n\n<p>I needed&nbsp;to get this location data off the web site and onto a GPS&nbsp;device, or smartphone GPS app, so I could use it offline when I\u2019m in the field and out of cell reception. &nbsp;<br>There\u2019s no really good way to do this with the web site, so enter the Mountain Project Data API: &nbsp;<a href=\"https:\/\/www.mountainproject.com\/data\">https:\/\/www.mountainproject.com\/data<\/a>.vvI figured I could fetch my todo list, get all the routes on the list and their GPS data, and create a GPX file that could uploaded to my GPS device and would work offline.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" loading=\"lazy\" width=\"1024\" height=\"713\" src=\"https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/Screen-Shot-2020-06-13-at-8.06.35-PM-1024x713.png\" alt=\"\" class=\"wp-image-127\" srcset=\"https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/Screen-Shot-2020-06-13-at-8.06.35-PM-1024x713.png 1024w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/Screen-Shot-2020-06-13-at-8.06.35-PM-300x209.png 300w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/Screen-Shot-2020-06-13-at-8.06.35-PM-768x535.png 768w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/Screen-Shot-2020-06-13-at-8.06.35-PM-1536x1070.png 1536w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/Screen-Shot-2020-06-13-at-8.06.35-PM.png 1686w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><figcaption><em>Example of the GPX file loaded into Google Earth &#8211; Red Rock Canyon, Las Vegas NV<\/em><\/figcaption><\/figure>\n\n\n\n<p>Thankfully there is a Python library called gpxpy <a href=\"https:\/\/pypi.org\/project\/gpxpy\/\">https:\/\/pypi.org\/project\/gpxpy\/<\/a>&nbsp;which seemed to be the ticket. &nbsp;This library&nbsp;would allow me to create a GPX file with tracks and waypoint data. &nbsp;By mapping the API fields&nbsp;from MountainProject to the gpxpy object, I could send a GPX file to the end user\u2019s device, which could now work offline.<br><br>The first step is to set up the development environment. &nbsp;This is important to get right if you are going to use Elastic Beanstalk or want to&nbsp;package your app for automated deployment on a WSGI server. &nbsp;Later we are&nbsp;going to look at the EB command&nbsp;line&nbsp;utility, but for now, set up your application something like this, with your flask app in the application.py file.&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img decoding=\"async\" loading=\"lazy\" src=\"http:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-3.png\" alt=\"\" class=\"wp-image-110\" width=\"496\" height=\"236\" srcset=\"https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-3.png 746w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-3-300x143.png 300w\" sizes=\"(max-width: 496px) 100vw, 496px\" \/><\/figure>\n\n\n\n<p>Install Flask and gpxpy using <a href=\"https:\/\/pip.pypa.io\/en\/stable\/\">pip<\/a>,  then use <code>pip freeze &gt; requirements.txt<\/code> to hold the external dependencies list for EB. &nbsp;You\u2019ll also want to use git as the EB CLI allows you to deploy directly from a git repo,  package to a zip, upload, and deploy all in one go.  <\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"alignright size-large is-resized\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-4.png\" alt=\"\" class=\"wp-image-112\" width=\"175\" height=\"165\" srcset=\"https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-4.png 330w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-4-300x282.png 300w\" sizes=\"(max-width: 175px) 100vw, 175px\" \/><figcaption>Example of requirements.txt<\/figcaption><\/figure><\/div>\n\n\n\n<p>Creating the basic Flask app shell using the excellent <a href=\"https:\/\/flask.palletsprojects.com\/en\/1.1.x\/tutorial\/\">tutorial<\/a> is enough to get started.&nbsp;Rigging routes to the controller and passing request variables is really handled very nicely. I figured I needed a form and a results page. &nbsp;In my&nbsp;first&nbsp;iteration I just&nbsp;served the file on form submit, but that didn\u2019t give the end user&nbsp;much feedback, so I switched to two views, a form view and a results view.&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" loading=\"lazy\" width=\"1024\" height=\"422\" src=\"https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-5-1024x422.png\" alt=\"\" class=\"wp-image-113\" srcset=\"https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-5-1024x422.png 1024w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-5-300x124.png 300w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-5-768x317.png 768w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-5-1536x633.png 1536w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-5.png 1712w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><figcaption>application.py contains all the view logic and application routing.<\/figcaption><\/figure>\n\n\n\n<p>The <a href=\"http:\/\/mountainproject.com\/\">MountainProject.com<\/a>&nbsp;API was pretty simple, it returns JSON which I passed&nbsp;mostly back as strings or lists. &nbsp;It\u2019s pretty easy to map this to gxpy as you see below. &nbsp;I elected to send the XML as an attachment to the request vs. write it to a temp file, it just seemed cleaner with no temp files to delete. &nbsp; I used very little of Bootstrap to implement some styling and the built-in javascript validation.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" loading=\"lazy\" width=\"1024\" height=\"796\" src=\"https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-6-1024x796.png\" alt=\"\" class=\"wp-image-114\" srcset=\"https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-6-1024x796.png 1024w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-6-300x233.png 300w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-6-768x597.png 768w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-6-1536x1193.png 1536w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-6.png 1578w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><figcaption>The main function that gets Lat \/ Long from the API and makes GPS waypoints.<\/figcaption><\/figure>\n\n\n\n<p>Jinja2 templating is clean and and allows for nested templates and layouts. &nbsp;I just used a&nbsp;single template with minimal&nbsp;show &amp; hide UI logic. &nbsp;It worked well with Flask&#8217;s message-flashing feature that allows for snappy UI responses. &nbsp; You can also use the built-in development server for Flask, which will auto-restart when it detects a code change &#8211; very nice indeed.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" loading=\"lazy\" width=\"1024\" height=\"431\" src=\"https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-7-1024x431.png\" alt=\"\" class=\"wp-image-115\" srcset=\"https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-7-1024x431.png 1024w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-7-300x126.png 300w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-7-768x323.png 768w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-7.png 1174w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><figcaption>Jinja2 template block for showing info and error messages.<\/figcaption><\/figure>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img decoding=\"async\" loading=\"lazy\" src=\"http:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-10-982x1024.png\" alt=\"\" class=\"wp-image-118\" width=\"600\" height=\"624\"\/><figcaption>The GPS Generator in action, showing flashed messages and dynamic content.<\/figcaption><\/figure><\/div>\n\n\n\n<p>Finally it was time to deploy this thing on EB. &nbsp; EB is essentially a series of scrips that manage a&nbsp;virtual environment. &nbsp;These environments can put&nbsp;up a scale-out web server infrastructure in just a few minutes. &nbsp;As you can imagine they are very sensitive&nbsp;to configuration so you&#8217;ll want to get familiar with finding the logs in the AWS console.  I&nbsp;won\u2019t &nbsp;get&nbsp;into the details, but you can find comprehensive info here:<a href=\"https:\/\/docs.aws.amazon.com\/elasticbeanstalk\/latest\/dg\/create-deploy-python-flask.html\">https:\/\/docs.aws.amazon.com\/elasticbeanstalk\/latest\/dg\/create-deploy-python-flask.html<\/a><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" loading=\"lazy\" width=\"1024\" height=\"249\" src=\"https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-8-1024x249.png\" alt=\"\" class=\"wp-image-116\" srcset=\"https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-8-1024x249.png 1024w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-8-300x73.png 300w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-8-768x186.png 768w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-8.png 1318w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><figcaption>Just commit your code and run eb deploy &#8211; the rest is done for you.<\/figcaption><\/figure>\n\n\n\n<p>Once up and running managing the EB instance is very simple, and you can configure monitoring and alerts. &nbsp;Setting up DNS and SSL would be a logical next step and requires a bit more technical work. &nbsp; If millions of people somehow wanted to download their todo lists as GPS files, this would be a fantastic set up for a cloud-based application that needs scalability.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" loading=\"lazy\" width=\"1024\" height=\"738\" src=\"https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-9-1024x738.png\" alt=\"\" class=\"wp-image-117\" srcset=\"https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-9-1024x738.png 1024w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-9-300x216.png 300w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-9-768x553.png 768w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-9-1536x1107.png 1536w, https:\/\/www.ridgeline-analytics.com\/wp-content\/uploads\/2020\/06\/image-9.png 1890w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><figcaption>AWS Cloudwatch metrics up and running automatically.  You can add lots more.<\/figcaption><\/figure>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Flask is a popular framework for building lightweight server-side web applications with Python. As an antidote to the more complex and fully-featured siblings such as Django, Flask excels with a minimal footprint, intuitive MVC-style routing and view controls, and a host of thoughtful plugins for things like SQLAlchemy. In this article we&#8217;ll review my experience &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/www.ridgeline-analytics.com\/index.php\/2020\/06\/14\/build-a-web-app-with-python-flask-and-aws-elastic-beanstalk\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Build a Web App with Python, Flask and AWS Elastic Beanstalk&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[6],"tags":[5,4],"_links":{"self":[{"href":"https:\/\/www.ridgeline-analytics.com\/index.php\/wp-json\/wp\/v2\/posts\/103"}],"collection":[{"href":"https:\/\/www.ridgeline-analytics.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ridgeline-analytics.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ridgeline-analytics.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ridgeline-analytics.com\/index.php\/wp-json\/wp\/v2\/comments?post=103"}],"version-history":[{"count":12,"href":"https:\/\/www.ridgeline-analytics.com\/index.php\/wp-json\/wp\/v2\/posts\/103\/revisions"}],"predecessor-version":[{"id":132,"href":"https:\/\/www.ridgeline-analytics.com\/index.php\/wp-json\/wp\/v2\/posts\/103\/revisions\/132"}],"wp:attachment":[{"href":"https:\/\/www.ridgeline-analytics.com\/index.php\/wp-json\/wp\/v2\/media?parent=103"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ridgeline-analytics.com\/index.php\/wp-json\/wp\/v2\/categories?post=103"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ridgeline-analytics.com\/index.php\/wp-json\/wp\/v2\/tags?post=103"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}