Creating a Simple Weather App with Python and Flask
Tags: python, webdev, flask, codenewbie
Published: 2022-04-04 00:21:15

Are you learning Python? Practicing Flask? And tired of the usual Hello World?

Well! Then this post is for you.

In this post we will build a weather app in Python using Flask.

## Project Overview :cloud:

**Flask** is a framework for building **web applications** with Python. We will develop a web application that provides the weather forecast for 5 days.

Flask makes the task of creating a web application relatively simple. Our weather app will require no more than **22 lines** of Python code and **48 lines** of HTML.

## An example :sunny:

We need a web server to host our application. 

An example of how our app will work:
1. The user will open a browser and navigate to http://localhost:5000/forecast?city=London (like "Tell me the weather forecast for London").
2. The web server will receive the request and call our web app to retrieve the weather forecast. 
3. Finally, the web server will send an HTML page containing the weather forecast back to the user.

The user will receive the HTML page and display the forecast for London in the browser.

There are many different web servers like NGINX or Apache HTTP Server. We will use the Flask's built-in server.

## Accessing weather forecast :snowflake:

You may be wondering: where will our weather app get the forecast data from?

Well, there are many different weather services available. We will use **OpenWeatherMap** service. It supports different subscription plans. We will use the free plan that supports up to 60 calls/minute and 1,000,000 calls/month which are sufficient for the purpose of this tutorial.

## Building the weather app :zap:

### Prerequisites

Before starting to code, we need to install **Flask**. To install Flask, we open a terminal and execute the following command:

```shell
pip install flask
```

The installation requires few seconds.

### Project Structure

We structure the project as follows:

```
.
├── static
│   └── icons
|       ├── 01d.png
|       ├── 01n.png
|       ├── 02d.png
|       └── ...
├── templates
│   └── index.html
├── weather.py
└── README.md
```

In `weather.py` we will put the Flask application.

The folder `static/icons` will contain all the icons used to represent the weather conditions. For example, we will have an icon for Clouds, another icon for Sunny, and so on.

`templates` is a folder containing a single file `index.html`. This HTML file only contains an empty table where we will put the weather data.

### Sign up to OpenWeatherMap

Before moving on, we need to sign up to OpenWeatherMap.

Visit [https://openweathermap.org/api](https://openweathermap.org/api) and click on the **Subscribe** button under the **5 Day / 3 Hour Forecast** section:

![5 Day / 3 Hour Forecast](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/exnk1abm04gqn5c03vxp.png)

Click on **Get API key** and complete the registration:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4qgu4egrqsle8rwcn9df.png)

Then, log into OpenWeatherMap website with your credentials and click on **My API keys**. Follow the instrutions to generate a new API key.

### Implementing the Weather App

In `weather.py` we put the logic that receives user requests and provides weather forecast. 

We need to create a function that retrieves the weather data from OpenWeatherMap API. This function is invoked when the user sends an HTTP GET request to our web app (for example http://localhost:5000/forecast?city=London) and returns an HTML page containing the weather forecast.

From `flask` package, we need to import the Flask class and other functions that we will use later:

```python
from flask import Flask, render_template, request, abort, Response
```

Then we initialize the Flask application:

```python
app = Flask(__name__)
```

We create a function called `get_weather()` that will be executed when the user sends an HTTP GET request to the weather app. 
1. This function will extract the city name from the request of the user.
2. It will retrieve the weather for the requested city from OpenWeatherMap.
3. Finally, an HTML page containing the weather is returned to the user.

The browser of the user will show the HTML page received from the web server. This HTML page contains a table showing the weather conditions for the requested city.

Let's create the `get_weather()` function step by step.

We define a function `get_weather()` that takes no arguments:

```python
@app.route('/forecast', methods=['GET'])
def get_weather():
```

`@app.route('/forecast', methods=['GET'])` is a decorator. It is a directive that routes the HTTP GET requests made to the http://localhost/forecast URL to the `get_weather()` function. In this way, when an user navigates to http://localhost/forecast our Flask web server will execute the `get_weather()` function.

We want that the user specifies the city as an argument of the HTTP GET request. Therefore, we expect that the HTTP request contains an argument named _city_. We can extract the city from the HTTP request as follows:

```python
    city = request.args.get('city')
```

It is important to validate the parameters extracted from the HTTP request. In our case, city is a mandatory argument. We need to ensure that the user provided a city. If the request does not contain a city (i.e. `city is None`), we return a status code "400" (BAD REQUEST) and we provide a description that tells the user that the city argument is missing:

```python
    if city is None:
        abort(400, 'Missing argument city')
```

If a city has been provided in the HTTP request, we prepare a request for OpenWeatherMap to get the weather forecast. The request will contain three parameters: `q`, `appid` and `units`.

`q` is the name of the city for which we want the forecasts. `appid` is your OpenWeatherMap API key.

```python
    data = {}
    data['q'] = request.args.get('city')
    data['appid'] = 'XXXXXXXXX'
    data['units'] = 'metric'
```

We use urllib python library to perform the retrieve the forecast from OpenWeatherMap:

```python
    url_values = urllib.parse.urlencode(data)
    url = 'http://api.openweathermap.org/data/2.5/forecast'
    full_url = url + '?' + url_values
    data = urllib.request.urlopen(full_url)
```

Finally, we use `render_template` to generate the HTML page containing the results. Basically `render_template` takes the template HTML file, fills it with the weather data received from OpenWeatherMap and return the generated HTML page to the user. Finally, the user will display the weather results in the browser:

```python
    resp = Response(data)
    resp.status_code = 200
    return render_template('index.html', title='Weather App', data=json.loads(data.read().decode('utf8')))
```



### HTML Template file

The HTML template file is reported below:

```html
<html>
    <head>
        <title>{{ title }}</title>
        <style>
            table {
            font-family: arial, sans-serif;
            border-collapse: collapse;
            width: 100%;
            }
            
            td, th {
            border: 1px solid #dddddd;
            text-align: left;
            padding: 8px;
            }
            
            tr:nth-child(even) {
            background-color: #dddddd;
            }

            #weather {
                display: flex;
                flex-direction: row;
                align-items: center;
            }
        </style>
    </head>
    <body>
        <table>
            <tr>
                <th>Time</th>
                <th>Temperature</th>
                <th>Humidity</th>
                <th>Weather</th>
                <th>Wind</th>
            </tr>
            {% for item in data["list"] %}
                <tr>
                    <td>{{ item["dt_txt"] }}</td>
                    <td>{{ item["main"]["temp"] }}°C</td>
                    <td>{{ item["main"]["humidity"] }}%</td>
                    <td><div id="weather"><img src="/static/icons/{{ item["weather"][0]["icon"] }}.png"></img> {{ item["weather"][0]["main"] }}</div></td>
                    <td>{{ '%.2f' % (item["wind"]["speed"]*3.6) }} km/h ({{ item["wind"]["deg"] }} deg)</td>
                </tr>
            {% endfor %}
        </table>
    </body>
</html>
```


Here is the final result:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rdrx3t3dgtc5znwcelvi.png)

## Conclusion

In this blog post we developed a simple weather app using Python and Flask. This is a great exercise for Python and Flask beginners.

If you like this post, consider following me on Twitter (https://twitter.com/cscarpitta94).
 


Credits for the images:
<a href="https://www.flaticon.com/free-icons/weather" title="weather icons">Weather icons created by iconixar - Flaticon</a>