Python Bibliotheca
Python resources for teachers and students.

A little more advanced CGI

You saw in the Basic CGI Scripting tip how to create an HTML form and connect it to a Python script. That Python program would then process some part of the form input and return some HTML that the Web server sends back to the browser. It's not necessary to separate the HTML form from the Python program, however. Sometimes it may make things simpler to combine them.

Here's a CGI version of our Weather Grabber program. Notice that the programming logic is unchanged from the original weather program. Like the previous example, the major difference is changing the lines that print to the screen to lines that create a new string that will be substituted into the HTML.

The script

#!/usr/bin/python

import urllib2, string, cgi

header = "Content-Type: text/html\n\n"

reshtml = """<html>
 <head><title>%s Weather Conditions</title></head>

 <body>
  <h1>Current weather conditions for %s</h1>
  %s
 </body>
</html>"""

errorhtml  = """<html>
 <head><title>Weather retrieval error for %s</title></head>

 <body>
  <h1>No data available</h1>
  <p>There doesn't appear to be a station named %s.</p>
 </body>
</html>"""

formhtml = """<html>

 <head><title>METAR Weather conditions</title></head>
 <body>
  <h1>Current weather conditions</h1>
  <form action="weather2.py">

   Enter a METAR station ID 
   <input name="station" width="5" size="5">
   <input type="submit">
   <input type="reset">
  </form>
 </body>
</html>"""

def getData(stationID):
    """
    Return a list of METAR data.

    Argument
    station -- four-letter METAR station code
    """
    noaa_url = "ftp://weather.noaa.gov/data/observations/metar/stations/"
    url = noaa_url + stationID + ".TXT"
    f = urllib2.urlopen(url).read()
    data = string.split(f)
    return data

def CtoF(Ctemp):
    """
    Convert a Celsius temperature to Fahrenheit
    """
    return (9.0/5)*Ctemp+32

def FtoC(Ftemp):
    return (5.0/9)*(Ftemp-32)

def parseTemps(s):
    temp, dewPt = string.split(s, "/")
    if temp[0] == 'M':
        temp = -1*int(temp[1:])
    else:
        temp = int(temp)
    if dewPt[0] == 'M':
        dewPt = -1*int(dewPt[1:])
    else:
        dewPt = int(dewPt)
    return temp, dewPt

def getWindDir(s):
    if s == 'VRB':
        return s
    else:
        dir = int(s)
        if 348.75 < dir < 360 or 0 < dir < 11.25: return "N"
        elif 11.25 <= dir < 33.75: return "NNE"
        elif 33.75 <= dir < 56.25: return "NE"
        elif 56.25 <= dir < 78.75: return "ENE"
        elif 78.75 <= dir < 101.25: return "E"
        elif 101.25 <= dir < 123.75: return "ESE"
        elif 123.75 <= dir < 146.25: return "SE"
        elif 146.25 <= dir <168.75: return "SSE"
        elif 168.75 <= dir < 191.25: return "S"
        elif 191.25 <= dir < 213.75: return "SSW"
        elif 213.75 <= dir < 236.25: return "SW"
        elif 236.25 <= dir < 258.75: return "WSW"
        elif 258.75 <= dir < 281.25: return "W"
        elif 281.25 <= dir < 303.75: return "WNW"
        elif 303.75 <= dir < 326.25: return "NW"
        else: return "NNW"
    
def parseWind(s):
    windDir = getWindDir(s[:3])
    windSpd = s[3:-2]
    windGust = 0
    if "G" in windSpd:
        windSpd, windGust = string.split(windSpd, "G")
    return windDir, int(windSpd), int(windGust)

def calcWindChill(temp, windSpd):
    T = CtoF(temp)
    V = windSpd * 1.15 # convert to mph
    windChill = 35.75 + 0.6215*T - 35.75*V**0.16 + .4275*T*V**0.16
    return windChill

def makeTable(data):
    """
    Generate HTML table containing the last reported weather conditions.

    Argument
    data -- list of METAR codes
    """

    s = ''
    date = data[0]
    time = data[1]
    station = data[2]
    data = data[3:] # slice off first four items since they're always the same
    for item in data:
        if "/" in item and item[-2:] != 'SM':
            temp, dewPt = parseTemps(item)
        elif item[-2:] == 'KT':
            windDir, windSpd, windGust = parseWind(item)
        elif item[0] == 'A' and item != 'AUTO':
            pressure = float(item[1:])/100
        elif item == 'RMK':
            break
    # Print weather conditions
    s += "<table border='1' cellpadding='5'>\n"
    s += "<tr><th>Last observation</th> \
           <td>%s at %s GMT</td></tr>\n" % (date, time)
    s += "<tr><th>Temperature</th> \
           <td>%s °C (%.0f °F)</td></tr>\n" % (temp, CtoF(temp))
    s += "<tr><th>Dew point</th> \
           <td>%s °C (%.0f °F)</td></tr>\n" % (dewPt, CtoF(dewPt))
    if windSpd == 0:
        s += "<tr><th>Wind</th><td>calm</td></tr>\n"
    else:
        if windDir == "VRB":
            s += "<tr><th>Wind</th><td>variable"
        else:
            s += "<tr><th>Wind</th><td>%s " % windDir
        if windGust == 0:
            s += "at %s mph (%.0f knots)</td></tr>\n" % (windSpd, windSpd*1.15)
        else:
            s += "at %s mph (%.0f knots) gusting to \
%s mph (%.0f knots)</td></tr>\n" \
                  % (windSpd, windSpd*1.15, windGust, windGust*1.15)
        if windSpd*1.15 >= 5 and CtoF(temp) <= 40:
            windChill = calcWindChill(temp, windSpd)
            s += "<tr><th>Wind chill</th> \
	           <td>%.0f °C (%.0f °F)</td></tr>\n" \
		   % (FtoC(windChill), windChill)
    s += "<tr><th>Pressure</th><td>%.2f in. of Hg</td></tr>\n" % pressure
    s += "</table>\n"
    return s

def main():
    form = cgi.FieldStorage()
    if form.has_key('station'):
        station = string.upper(form['station'].value)
        try:
            conditions = getData(station)
            print header + reshtml % (station, station, makeTable(conditions))
        except IOError:
            print header + errorhtml % (station, station)
    else:
        print header + formhtml
        
if __name__ == '__main__':
    main()

[ Copyright 2005, Tim Wilson ]

Comments, questions, or suggestions? Email the webmaster