import wsgiref.handlers import cgi import urllib import re import datetime from google.appengine.ext import webapp from google.appengine.ext import db from google.appengine.api import urlfetch from google.appengine.api import users from google.appengine.api import memcache class Kitco: ''' Makes use of the following google.appengine.api tools: urlfetch - to scrape kitco data memcache - to be friendly to kitco and not scrape so much ''' cacheTimeDelta = datetime.timedelta(0, 300) kitcoUrl = 'http://genesiss.kitco.com/scripts/cgi-bin/texten.pl' kitcoMetals = {'Gold': (r'\s{3}Gold\s{8,9}([0-9]{3,4}\.[0-9]{2})\s{4,5}([0-9]{3,4}\.[0-9]{2})', ''), 'Silver': (r'\s{3}Silver\s{7,9}([0-9]{1,3}\.[0-9]{2})\s{5,7}([0-9]{1,3}\.[0-9]{2})', ''), 'Platinum': (r'\s{3}Platinum\s{3,5}([0-9]{3,5}\.[0-9]{2})\s{3,5}([0-9]{3,5}\.[0-9]{2})', '')} def __init__(self): # memcache is buggy, must try stuff try: if not memcache.get('previousFetch'): self._fetchKitcoPrices() except: self._fetchKitcoPrices() def getKitcoPrice(self, metalName): # if the last fetch happened more than cacheTimeDelta ago fetch again try: if memcache.get('previousFetch') + self.cacheTimeDelta < datetime.datetime.utcnow(): self._fetchKitcoPrices() except: self._fetchKitcoPrices() return memcache.get(metalName) def getLastKitcoUpdate(self): try: return memcache.get('previousFetch') except: self._fetchKitcoPrices() return memcache.get('previousFetch') def _fetchKitcoPrices(self): memcache.flush_all() memcache.set('previousFetch', datetime.datetime.utcnow()) self.kitco_page = urlfetch.fetch(self.kitcoUrl).content for metalName, metalStuff in self.kitcoMetals.iteritems(): re_obj = re.compile(metalStuff[0]) re_match = re_obj.findall(self.kitco_page) memcache.set(metalName, (float(re_match[0][0]) + float(re_match[0][1])) / 2.0) class PersonalStash(db.Model): owner = db.UserProperty() metalsOwned = db.ListProperty(float, verbose_name=None, default=[0.0, 0.0, 0.0]) dbMap = ['Gold', 'Silver', 'Platinum'] def getMetalAmount(self, metalName): if metalName in self.dbMap: return self.metalsOwned[self.dbMap.index(metalName)] return None def setMetalAmount(self, metalName, metalAmount): if metalName in self.dbMap: self.metalsOwned[self.dbMap.index(metalName)] = metalAmount class MainPage(webapp.RequestHandler): ''' The main page. A request handler extending google.appengine.ext.webapp.RequestHandler Posts to itself - get displays the form, post displays the result ''' metals = ['Gold', 'Silver', 'Platinum'] kitco = Kitco() user = None # updated with self.refresh() stashes = None # updated with self.refresh() header = ''' Bullion Value Calculator by JesseBickel.com ''' footer = '''
App by jessebickel.com
''' def get(self): self.refresh() self.response.out.write(''.join((self.header, self.getFormBody(), self.getFooter()))) def post(self): self.refresh() try: stash = self.stashes[0] except IndexError, Error: stash = PersonalStash() stash.owner = self.user for metalName in self.metals: try: stash.setMetalAmount(metalName, float(cgi.escape(self.request.get(metalName)))) except ValueError: pass if self.user: stash.put() self.response.out.write(''.join((self.header, self.getBody(stash), self.getFooter()))) def getBody(self, personalStash): body = '' total = 0.0 for metalName in self.metals: if personalStash.getMetalAmount(metalName) > 0.0: thisMetal = personalStash.getMetalAmount(metalName) * self.kitco.getKitcoPrice(metalName) body = ''.join((body, '

', str(thisMetal), ' Your estimated ', metalName, ' USD value.

')) body = ''.join((body, '
', self.kitco.kitcoMetals[metalName][1], '
')) total += thisMetal body = ''.join((body, '

Amounts current as of ', str(self.kitco.getLastKitcoUpdate()), ' UTC

')) body = ''.join(('

YOUR TOTAL (USD): ', str(total), '

', body)) return body def getFormBody(self): self.refresh() body = '''

Enter the troy weight of each metal

''' try: for metalName in self.metals: body = ''.join((body, '
', metalName, ' troy ounces
')) except IndexError: for metalName in self.metals: body = ''.join((body, '
', metalName, ' troy ounces
')) body = ''.join((body, '''
''')) return body def getFooter(self): if self.user: footer = ''.join(('

Welcome, ', str(self.user.nickname()), '. (Sign out)

')) else: footer = ''.join(('

Welcome, guest. (Sign in)

')) return ''.join((footer, self.footer)) def refresh(self): self.user = users.get_current_user() self.stashes = PersonalStash.gql('WHERE owner = :1 LIMIT 1', self.user) def main(): application = webapp.WSGIApplication([('/', MainPage)], debug=True) wsgiref.handlers.CGIHandler().run(application) if __name__ == "__main__": main()