| home | about me | feed RSS feed

I knew it would happen.
It was just a matter of time.

My wife became insanely jealous of my web application, and had to have her own.

This prompted me to adapt the ugly code from my own app for her purposes.

Her primary concern: how does the weather look here in Los Angeles and back home. Perhaps she wants to convince herself that moving away from her family makes at least meteorological sense.

I thought the code to do this was kind of fun in a kludgy, non-DRY way. Full source is here.

Summary:

  1. fetch some XML from the kind folks at Yahoo!,
  2. parse aforementioned XML,
  3. cache results, and
  4. display from cache if present; fetch if not.

Part 1, Fetch:

def fetch_forecast(url, loc_param, units='c'):
    ''' url must point to some xml page
        loc_param can be either zip code or 
        yahoo-specific string
        units default to celsius
    '''
    params = urllib.urlencode({'p': loc_param, 'u': units,})
    full_uri = '?'.join([url, params,])

    result = urlfetch.fetch(full_uri)
    if result.status_code == 200:
        return minidom.parseString(result.content)
    else:
        return result.status_code

Parts 2 and 3, Save and Cache:

def xml_parse(dom, fc_id, weather_ns):
    ''' parse xml weather forecast from 
        http://xml.weather.yahoo.com/forecastrss?p=[zip code here]&u=c
    '''
    try:
        if len(dom) == 3:
            status_code = dom
            return {
                'forecast': "can't be fetched now %s" % status_code,
            }
    except AttributeError:
        pass # this means we got the xml ok!

    forecast = {}
    forecast['cd'], forecast['dt'] = [], []
    forecast['lo'], forecast['hi'] = [], []

    for node in dom.getElementsByTagNameNS(weather_ns, 'forecast'):
        forecast['cd'].append(node.getAttribute('text'))
        forecast['dt'].append(node.getAttribute('date'))
        forecast['lo'].append(node.getAttribute('low'))
        forecast['hi'].append(node.getAttribute('high'))

    for node in dom.getElementsByTagNameNS(weather_ns, 'atmosphere'):
        forecast['humidity'] = node.getAttribute('humidity')

    # not currently using Yahoo!-supplied title
    forecast['title'] = dom.getElementsByTagName('title')[0].firstChild.data
    # PST
    forecast['cached'] = datetime.now() - timedelta(hours=7)

    # 3600 seconds == 1 hour
    # 21600 seconds == 6 hours
    # 86400 seconds == 24 hours
    memcache.set('%s_forecast' % fc_id, forecast, time=21600)
    return forecast

Part 4, Conditionally Display:

class IndexHandler(BaseHandler):
    ''' Main handler for this simple app.

        Supports returning all blog posts (page_size == 0),  
        or a page_size number at a time.

        First checks for cached results.
        Fetches from xml.weather.yahoo.com if none available.
        Cache expires in one (1) hour.
    '''
    def get(self):
        us_fc_id = '90024'
        cr_fc_id = 'EZXX0014'

        # first check for cached info
        us_fc = memcache.get('%s_forecast' % us_fc_id)
        cr_fc = memcache.get('%s_forecast' % cr_fc_id)

        forecast_list = []
        forecast_list.append(us_fc)
        forecast_list.append(cr_fc)

        # if we're missing something go get it!
        if not all(forecast_list):
            forecast_list = [] # reset

            weather_url = read_config_file(CFG_FILE, 'settings', 'url')
            weather_ns = read_config_file(CFG_FILE, 'settings', 'ns')

            us_fc_id = '90024'
            cr_fc_id = 'EZXX0014'

            us_fc_dom = fetch_forecast(weather_url, us_fc_id)
            cr_fc_dom = fetch_forecast(weather_url, cr_fc_id)

            us_fc = xml_parse(us_fc_dom, us_fc_id, weather_ns)
            cr_fc = xml_parse(cr_fc_dom, cr_fc_id, weather_ns)

            forecast_list.append(us_fc)
            forecast_list.append(cr_fc)

        self.render('index.html', 
            {   'forecast_list': forecast_list, 
                'title': 'Doma',
            })

To see the thing in action go here: http://miruska-amalka.appspot.com/

Happy coding!

created: 2010-04-22 | updated: 2010-06-07


comments:
on 2010-04-22 Wife wrote:

I'm really impressed. After six years of a lovely married life you would think I know you. But no, I'm still learning who you are every day. Can you make for me one more? The one that will look for all of the 'how to k^%l someone so it looks like a suicide?' Lots of love

on 2010-04-22 Wife wrote:

Sikulka, dekuji :-))))

on 2010-05-20 Greg wrote:

Glad to see the Czechs still care about the weather back home. VĂ½bornĂ½!

on 2010-05-20 adam wrote:

Greg, your Czech is amazing.

Comments are now closed for this post.