Archive for January, 2009

yourmuni now has instant stop lookup

Wednesday, January 28th, 2009

After another successful hack session on the shuttle ride home, yourmuni now lets you just look up any given stop on the spot without creating a bookmark. Now you can has the best of both worlds all on one site. Up next is adding other regions/agencies covered by nextbus (easy, probably done by the end of this week while I ride the shuttle) and an API.

Site is at http://yourmuni.appspot.com

Latest code is at Github

Ternary in Python: The Cover Beats the Original

Saturday, January 24th, 2009

Like most newcomers to Python, I lamented the absence of a ternary operator. Some say the operator is hard to read, but I say those people need better reading glasses. If I want to assign a value based on a condition, I don’t think there’s anything clearer than an operator triad that exists solely for that purpose.

In any case, Python doesn’t have the standard = ? : ternary. It has a similar shorthand if-else construct, which I used begrudgingly. It looks like this:

result = value1 if condition else value2
THAT is hard to read. Code highlighting helps, but it’s still far from optimal.

This Kung Fu Is Weak!

Then I stumbled upon this random page while looking up exactly what the syntax was. The trick is to build a tuple on the fly and immediately select one of its elements using the condition as the index. The above code ends up looking like this:

result = (value1, value2)[condition]
How fucking awesomely elegant is that? As the post title suggests, I actually like this better than the original ternary.

As if to compliment this trick, the bool type in Python actually evaluates to a numeric 0 or 1. This makes the classic “Set the value to itself if it’s already set; set it to X otherwise” case incredibly easy:

value = (value, X)[bool(value)]
A frequent use case is one where you have a function argument that you want to default to a certain value calculated using another function. It can’t just be set in the function signature; you end up setting it to None in the signature (which, IMO, you should have done to begin with), then assigning it whatever the default value is if the caller doesn’t pass anything.

Here’s a real example: an implementation of Binary Search in Python. (I’ve been going through Introduction to Algorithms as part of my New Years resolution to become a better programmer; I hate writing pseudo code, so I’ve been writing Python for all the exercises)

UDPATE

Found an even better alternative for this case:

value = value or X
Example updated:
def binSearch(needle, haystack, start=None, end=None):
    start = start or 0
    end = end or len(haystack)-1

    midpt = start+int(math.floor((end-start)/2))     median = haystack[midpt]     if (needle > median):         return binSearch(needle, haystack, midpt, end)     elif (needle < median):         return binSearch(needle, haystack, start, midpt)     else:         return midpt

Enjoy.

yourmuni makes commuting easier

Saturday, January 17th, 2009
what PH's "to work" bookmark would look like

I am proud to present my latest app yourmuni. It is a cross between momuni.com and Paul Hammond’s minimuni. Its purpose is to make it easier for people to get to and from places they frequent, such as jobs, gyms, favorite spots, and bootycalls. yourmuni lets you define bookmarks which represent collections of transit stops, and then view the bus/train arrival information for each bookmark on a single page. For example, if Paul didn’t already have his highly personalized mimimuni app, he could log onto yourmuni, define a “To Work” bookmark, and assign to it the same stops that he currently scrapes. See the screenshot on the right for an example.

While it’s obviously not a “disruptive” innovation, I think it’s a nice incremental improvement on what most people do, which is look up multiple routes using momuni or nextbus.com while walking out the door. I know I’ve been using it, and it has saved me a tremendous amount of time/clicking around on my iPhone, looking like an idiot.

Though yourmuni was developed with my iPhone in mind, it appears to work just fine on most phones.

Still on the burner:

  • Instant stop lookup (ala momuni)
  • using other agencies that nextbus covers (including ones outside of NorCal)
  • deleting stops from bookmarks
  • better instructions while setting up bookmarks
  • cleaning up some code

yourmuni was demoed at the January Django Meetup, and everyone seemed to like it. I was very flattered by the positive feedback, since it’s a rather simple app.

Technical Details

yourmuni is written using the latest Django at the time of the start of the project, which was r9768. My previous post about getting the latest Django to work on the Google App Engine was the result of me setting yourmuni up on said App Engine, which is where it now lives. The source is on github. It’s far from perfect, as it was my first real Django/Python project, and I am aware of several precise places in the code that could use a minor rewrite. However, here are the parts that I put lots of thought into, and that I think might be useful to others.

App Engine userRequired Decorator

Since the login_required decorator from django.contrib is useless when using the App Engine, I wrote my own, which checks to see if the user is logged in and, if not,  redirects them to the Google Accounts login page, while saving the URL they were trying to access as the callback URL. Here’s the source for all to enjoy (gist here):

def userRequired(fn):
    """decorator for forcing a login"""
    def new(args, **kws):
        user = users.get_current_user()
        if not (user):
            r = args[0]
            return HttpResponseRedirect(users.create_login_url(
                                            r.build_absolute_uri()))
        else:
            return fn(args, **kws)
    return new

Encapsulating Slug Generation in Form Code

Since I only ask for one field (“Description”) when creating a bookmark and place no restrictions on that field (i want it to look like whatever the user wants to see in the interface), I need some way to generate an identifier for the bookmark. I could just give it a numeric or hash identifier, but then it would be useless to the user in terms of seeing it in their browser history (I want to allow the user to jump straight to the bookmark they want if their browser shows it as an option after they type ‘y’). I needed to create a slug. I could accept the “Description” field and then process it in my addBmark view, but instead I defined the form to have two fields, one optional, and used the contents of the description field to automatically populate the “name” field using Django’s built in slugify method available in the template API (thanks to the folks at the Django Meetup who pointed this out). This allows me to encapsulate the validation within the form, so my view code looks very clean – I just have to call the is_valid() method on the form, and the form then has two properties that give me everything I need to create the bookmark. Here’s the code (full source here).

class AddBmarkForm(forms.Form):
    name = forms.CharField(max_length=50, required=False)
    description = forms.CharField(max_length=255, required=True)

def clean_description(self):
    desc = self.cleaned_data['description']
    name = slugify(desc).decode()
    q = db.Query(Bmark)
    q.filter('name =', name)
    q.filter('user =', users.get_current_user())
    if (q.get()):
        raise forms.ValidationError(_("A bookmark with that \
                    name exists already"))
    else:
        self.cleaned_data['name'] = name
        return desc</pre>

Scraping Nextbus

For an unclear reason, nextbus does not have a clean, public API. My assumption is that they want to sell their data, but that's sort of pointless since they provide a free, publicly accessible website everywhere they provide service. It just sucks. So in order to make something better, I basically had to scrape that same publicly accessible website. It wasn't easy, as apparently nextbus hired a live bear to write their markup. Though all of their pages look almost identical, each has its own qurky combination of li, a, nobr, and font tags. I still managed to write a single scrape function to handle all of them, but it ended up being a bit more complex than it needed to be. Thank the powers that be for the BeautifulSoup library. The scrape code is here.

Misc Stuff

As I had mentioned before, I used the latest Django avaialble to me at the start of development. Though I don't get to play with the cool ORM stuff that's been added recently, I did get to use some of the new template tags, such as the {% empty %} tag to specify the behavior in the event of an empty {% for %} loop (docs here, used here).

All in all, I hope this helps people get to and from wherever it is they're going easier. This is the first project I've actually launched in a very long time, and certainly the most useful one.

Django SVN on Google App Engine

Sunday, January 4th, 2009

The Google How-To for using a version of Django other than the built in 0.96 appears to be a bit out of date, as signal handling has changed since the writing. Here’s what needs to be changed:

NOTE: These changes apply to this document dated April 2008. If the date at the top of the document is different, the how-to may have been updated since the writing of this post. Django Rev 9699 is what I’m using.

The main.py portion of the how-to says that two signal handlers need to be changed. Here’s the original source:

# Log errors.
django.dispatch.dispatcher.connect(
   log_exception, django.core.signals.got_request_exception)

# Unregister the rollback event handler. django.dispatch.dispatcher.disconnect(     django.db._rollback_on_exception,     django.core.signals.got_request_exception)

Since the writing of the how-to, the connect and disconnect methods have been moved to the Signal object itself; the legacy functions have been removed (see diff). The code SHOULD be:
# Log errors.
django.core.signals.got_request_exception.connect(log_exception)

Unregister the rollback event handler.

django.core.signals.got_request_exception.disconnect(     django.db._rollback_on_exception)

TADA! Your shit should work on at least revision 9699 of Django.