Monday, February 28, 2011

Shop Stats open for business!

Shop Stats is out of closed beta and now ready for open signups!

After I made a couple of paid Android apps available on Market, I quickly found out how painful it is to keep track of my sales through Google Checkout.  I was spending more time refreshing and counting than coding.  Thus, Shop Stats was born.

With this web service, you can:

  • leave the overview page open and it'll auto-refresh with the latest numbers throughout the day
  • see your sales broken out by state in case your state makes you pay sales tax for digital goods
  • sanity check your numbers when you fill out your tax forms
  • see the effect of app update timing
  • lots more coming...

Saturday, November 6, 2010

Adding a single static file

To add a single static file to your App Engine application, let's say your robots.txt file, add the following to the handlers section of your app.yaml:


- url: /robots.txt
  static_files: static/robots.txt
  upload: static/robots.txt

This assumes your robots.txt file lives in the directory static and that you want to access it from http:///robots.txt

My first try after looking at the docs missed the upload parameter.  Totally zoomed in on static_files and figured I was good to go.

Mac helpers

Some Mac-related things that have helped with the day-to-day:

  • UTC clock widget - detailed instructions here.  UTC is way easier to work with as a base for date/time normalization, but it's a pain to constantly have to convert in your head (especially if you travel).  Modifying the World Clock widget was simple.  Remember to look in /Library not /Library and make a backup just in case...
  • iTunes control - you control: tunes.  I use all of my Spaces and with this free utility I get iTunes music control no matter which Space I'm in (adds controls to your menu bar).

Tuesday, November 2, 2010

Google App Engine and Cron

  1. Stagger your cron tasks such that they don't resonate
  2. Limit cron time limits to 2 digits
Looking at the requests/second graph on my GAE dashboard recently, I noticed a pattern of medium peaks at one frequency and a pattern of large peaks at about half that frequency.  These were the result of the way I had set up my cron tasks.

Self-inflicted usage spikes have to be avoided when you need to live within per minute quotas.  Because of a natural inclination towards time anchors like whole and half hours, multiple cron tasks were often starting up at the same time even though they all had different intervals.  The large spikes were from when they would all start up at the same time.  Oops.

Changed the intervals to ones that didn't overlap so frequently and ran in the the error below: 

Error parsing yaml file:
Unable to assign value 'every 157 minutes' to attribute 'schedule':
schedule 'every 157 minutes' failed to parse: line 1:8 mismatched input u'7' expecting set None

Turns out that 'every n ...' is ok and 'every nn ...' is ok, but 'every nnn ...' is not.

Monday, July 19, 2010

Google App Engine and pytz

Here are some notes on my experience with making the Python date/time package pytz available to an application on Google App Engine (GAE).

The default pytz package is generally accepted to be unsuitable for GAE because of the load time.  Rodrigo Moraes has created a version called gaepytz that is more suitable to GAE's distributed architecture.


I downloaded the zip version from the Python site thinking that I could use zipimport as described at this GAE article: http://code.google.com/appengine/articles/django10_zipimport.html

GAE complained about not being able to find my reference to gaepytz.  A little tinkering around revealed that the only files necessary are the ones in the pytz folder and that either GAE or zipimport doesn't like hyphens in the zip.  So I unzipped the gaepytz zip, then repackage it with:

zip -r gaepytz.zip pytz

With this, the error saying that it couldn't find the gaepytz file went away and was replaced with the following:

IOError: [Errno 20] Not a directory: 
'.../gaepytz.zip/pytz/zoneinfo.zip'

Turns out that zipimport doesn't like nested zip files.  The next step was to unzip the gaepytz.zip file and just use the pytz folder directly.  This meant going from 1 file at 512kB to 6 files at 569kB which isn't the end of the world.

The next bit was to dig into the comments of the source code to figure out how to use the package because import pytz clearly wasn't the answer.  In the comments of one of the files is the helpful note:

import sys
sys.path.insert(0, 'gaepytz')
from pytz.gae import pytz

You only need the last line since the pytz folder is added directly and no longer in the zip file. A little more work than I was originally counting on, but glad to have it working.