Created
September 4, 2013 17:52
-
-
Save dsuch/6440366 to your computer and use it in GitHub Desktop.
Services accompanying an LJ article
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
# stdlib | |
from datetime import datetime | |
# dateutil | |
from dateutil.parser import parse | |
# lxml | |
from lxml import etree | |
# Zato | |
from zato.common.util import grouper | |
from zato.server.service import Service | |
# Interesting namespaces treasury.gov uses for rates | |
NAMESPACES = { | |
'd':'http://schemas.microsoft.com/ado/2007/08/dataservices', | |
'm':'http://schemas.microsoft.com/ado/2007/08/dataservices/metadata' | |
} | |
# Pattern for a Redis key to store the data under | |
REDIS_KEY_PATTERN = 'linuxjournal:rates:{}:{}:{}' | |
def get_date(input): | |
now = datetime.utcnow() | |
year = input.get('year') or now.year | |
month = input.get('month') or now.month | |
day = input.get('day') or now.day | |
return year, month, day | |
# ############################################################################## | |
class UpdateCache(Service): | |
""" Fetches complex XML from treasury.gov and updates the application's Redis cache. | |
""" | |
class SimpleIO: | |
input_optional = ('year', 'month') | |
def handle(self): | |
# Grab month and year from user-provided input or use defaults, i.e. current date, | |
# note that day is not needed so it's discarded | |
year, month, _ = get_date(self.request.input) | |
# Fetch connection by its name | |
out = self.outgoing.plain_http.get('treasury.gov') | |
# Build a query string the backend data source expects | |
query_string = { | |
'$filter': 'month(QUOTE_DATE) eq {} and year(QUOTE_DATE) eq {}'.format(month, year) | |
} | |
# Invoke the backend with query string, fetch the response as a UTF-8 string | |
# and turn it into an XML object | |
response = out.conn.get(self.cid, query_string) | |
response = response.text.encode('utf-8') | |
xml = etree.fromstring(response) | |
# Look up all XML elements needed (date and rate) using XPath | |
elements = xml.xpath('//m:properties/d:*/text()', namespaces=NAMESPACES) | |
# elements is a flat list that needs to be turned into pairs using the 'grouper' | |
# function before iterating over | |
elements = grouper(2, elements) | |
for date, rate in elements: | |
# Create a date object out of string | |
date = parse(date) | |
# Build a key for Redis and store the data under it | |
key = REDIS_KEY_PATTERN.format(date.year, str(date.month).zfill(2), str(date.day).zfill(2)) | |
self.kvdb.conn.set(key, rate) | |
# Leave a trace of our activity | |
self.logger.info('Key %s set to %s', key, rate) | |
# ############################################################################## | |
class GetRate(Service): | |
""" Returns the real long-term rate for a given date (defaults to today if no date is given). | |
""" | |
class SimpleIO: | |
input_optional = ('year', 'month', 'day') | |
output_optional = ('rate',) | |
def handle(self): | |
# Get date needed either from input or current day | |
year, month, day = get_date(self.request.input) | |
# Build the key the data is cached under | |
key = REDIS_KEY_PATTERN.format(year, month, day) | |
# Assign the result from cache directly to response | |
self.response.payload.rate = self.kvdb.conn.get(key) | |
# ############################################################################## |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment