Friday, June 27, 2014

Some fun with Bluemix, Cloud Foundry, Python, JSON, and the Weather

Bluemix: Bring Your Own Community
Today, I wanted to try out running a Python application in IBM Bluemix. And I wanted to check out weather reports for some possible weekend destinations. Why not combine it?

Because IBM Bluemix is based on Cloud Foundry, I am using the Cloud Foundry-related tools (mainly "cf") to interact with Bluemix. A first challenge is that a Python runtime is not built into Bluemix/Cloud Foundry and I have to tell it to prepare the runtime for me. It is called "Bring Your Own Community". When you click that icon, some helpful description comes up, pointing you to how to get started.
Bluemix: Get started with own buildpacks

The trick is use some scripting to package framework and runtime support and it is called buildpack. For Python there are several buildpacks available and I am going to use this one. It is specified whenever pushing the application from my local machine to Bluemix:

cf push weatherFN -b https://github.com/ephoning/heroku-buildpack-python.git

As a starter, I downloaded a sample Python-based Web application that is deployable to Bluemix/Cloud Foundry. It comes with 5 files, one of them a small "hello.py", the actual application. The other files are:
  • runtime.txt - it specifies the Python version
  • requirements.txt to list the dependencies on other Python modules
  • Procfile - tells the runtime what and how to start
  • README.md with some basic introductions
With the starter snippet ready, I looked for a source for weather data. Data for airports worldwide is available, e.g., at the US National Weather Service. I could fetch the current weather for Friedrichshafen airport like this encoded as METAR:

2014/06/27 11:20
EDNY 271120Z 22007KT 180V240 CAVOK 24/06 Q1017

However, it would require decoding (for untrained people like myself). So I looked further and I found OpenWeatherMap. They offer the weather data in various formats, including JSON. The data for Friedrichshafen can easily be fetched like this and look like in this sample:

{"coord":{"lon":9.48,"lat":47.65},"sys":{"message":0.0037,"country":"DE","sunrise":1403839575,"sunset":1403897051}, "weather":[{"id":800,"main":"Clear","description":"Sky is Clear","icon":"01d"}], "base":"cmc stations","main":{"temp":297.46,"humidity":41,"pressure":1010.562,"temp_min":297.15,"temp_max":297.95}, "wind":{"speed":1,"gust":4,"deg":61}, "clouds":{"all":0},"dt":1403869524,"id":2924585,"name":"Friedrichshafen","cod":200}

Python has a built-in JSON module and it is easy to load and dump the data:
    wdata = json.load(urllib.urlopen(url))
    print json.dumps(wdata, indent=2)


A single field can be accessed using array notation (description of current weather condition):
  wdata["weather"][0]["description"]

After some local testing I pushed the code to Bluemix as shown earlier. My Web app is called "weatherFN" as in "weather in Friedrichshafen". When the index page is requested the app redirects to the weather for Friedrichshafen:
http://weatherfn.mybluemix.net/weather/Friedrichshafen
Weather in Friedrichshafen
The same can be done for London or Recife (Brazil), the soccer match USA vs. Germany was held there yesterday:
Weather in London
Weather in Recife

Weather looks nice, I learned more about IBM Bluemix and how to use Python today. Now it is almost time for the weekend.

Last but not least, here is the entire Python code that I used:
 import os  
 from flask import Flask,redirect  
 import urllib  
 import json  
 BASE_URL = "http://api.openweathermap.org/data/2.5/weather?q="  
 app = Flask(__name__)  
 @app.route('/')  
 def index():  
   return redirect('/weather/Friedrichshafen')  
 @app.route('/weather/<city>')  
 def weather(city):  
   url = "%s/%s" % (BASE_URL, city)  
   wdata = json.load(urllib.urlopen(url))  
   print json.dumps(wdata, indent=2)  
   page='<title>current weather for '+wdata["name"]+'</title>'  
   page +='<h1>Current weather for '+wdata["name"]+' ('+wdata["sys"]["country"]+')</h1>'  
   page += '<br/>Min Temp. '+str(wdata["main"]["temp_min"]-273.15)+'<br/>'  
   page += '<br/>Max Temp. '+str(wdata["main"]["temp_max"]-273.15)+'<br/>'  
   page += '<br/>Current Temp. '+str(wdata["main"]["temp"]-273.15)+'<br/>'  
   page += '<br/>Weather: '+wdata["weather"][0]["description"]+'<br/>'  
   return page  
 port = os.getenv('VCAP_APP_PORT', '5000')  
 if __name__ == "__main__":  
      app.run(host='0.0.0.0', port=int(port))  

There are follow-up articles to this one describing how I enabled a custom domain for my Bluemix application and how I added a Cloudant / couchDB to my Python application on Bluemix.