Package dk

I spent a little time getting familiar with the life-cycle steps of publishing a Python package…

  • working with code on github (https://github.com/thebjorn/dk)  Hint:  use their windows application, it makes everything very streamlined.
  • figuring out setup.py so that the package is uploaded to PyPI and others can install it with `pip install dk` (https://pypi.python.org/pypi/dk/0.7.4).   Hint: if you don’t increase the version number, nothing good happens.
  • Creating a home page using GitHub Pages (http://thebjorn.github.io/dk/).  Hint: editing it after you’ve created it is the same process as creating it for the first time (don’t worry  you’re not going to clobber everything you did the first time).
  • Extracting docstrings using autodoc into Sphinxdoc documentation and integrating it with Read the Docs (http://dk.readthedocs.org/en/latest/). Hint: if using autodoc you’ll need to check the box for building in a virtualenv.
  • Setting up a trigger so the documentation is automatically re-built every time I push to github.

Everything was pretty straight forward, and I especially like the webhook to re-build the documentation (since developers + manual steps = fail).

The remaining steps are a bit more hazy…,  the remaining steps being the running of the test suite and reporting test failures + coverage.  Perhaps tox is the answer…?

Posted in Python | 1 Comment

django :: list_display can’t sort on attribute of foreign key field…

I’m not the only one surprised by the fact that you can’t use the double-underscore-foreignkey-attribute-accessor syntax in list_display:

class MyModelOptions(admin.ModelAdmin):
    list_display = ['fk_field__fk_attribute']  # ILLEGAL

There has been extensive discussions on the tracker (https://code.djangoproject.com/ticket/5863) and on the mailing list (http://groups.google.com/group/django-developers/browse_thread/thread/790484bfbe1b421f). It seems unlikely that this will be possible to do before hell freezes over (although someone commented that it works in Django 1.2 here: http://stackoverflow.com/questions/163823/can-list-display-in-a-django-modeladmin-display-attributes-of-foreignkey-field).

Luke Plant has suggested a solution using callables, which I personally find ugly, but YMMW:

def foreign_field(field_name):
    def accessor(obj):
        val = obj
        for part in field_name.split('__'):
            val = getattr(val, part)
        return val
    accessor.__name__ = field_name
    return accessor

ff = foreign_field

class MyAdmin(ModelAdmin):

    list_display = [ff('foreign_key__related_fieldname1'),
                    ff('foreign_key__related_fieldname2')]

The code from @lukeplant does all the work in the ModelAdmin, which keeps the Model class free of extra methods for the admin. This is generally a good idea, however I just had a use case where adding accessors made working with the model much easier.

The model in question looks like this:

class DailyEmployeeProjectHours(models.Model):
    employee = models.ForeignKey(Employee)
    empday = models.ForeignKey(EmployeeDay)
    project = models.ForeignKey(Project)
    seconds_worked = models.IntegerField(default=0)

The code was littered with “deph.employee.user.username”, “deph.empday.date”, and “deph.project.name”…

We needed something that could define an accessor/property, that would automagically be sortable in Django’s admin interface… which sounds like the job for a descriptor:

class FkeyLookup(object):
    def __init__(self, fkeydecl, short_description=None, admin_order_field=None):
        self.fk, fkattrs = fkeydecl.split('__', 1)
        self.fkattrs = fkattrs.split('__')

        self.short_description = short_description or self.fkattrs[-1]
        self.admin_order_field = admin_order_field or fkeydecl

    def __get__(self, obj, klass):
        if obj is None:
            return self  # hack required to make Django validate (if obj is None, then we're a class, and classes are callable <wink>)

        item = getattr(obj, self.fk)
        for attr in self.fkattrs:
            item = getattr(item, attr)
        return item

Usage:

class DailyEmployeeProjectHours(models.Model):
    employee = models.ForeignKey(Employee)
    username = FkeyLookup("employee__user__username")

    empday = models.ForeignKey(EmployeeDay)
    date = FkeyLookup("empday__date")

    project = models.ForeignKey(Project)
    name = FkeyLookup("project__name")

    seconds_worked = models.IntegerField(default=0)

and in admin.py::

class DailyEmployeeProjectHoursOptions(admin.ModelAdmin):
    list_display = "username date name hours".split()

    def hours(self, obj):
        return '%.2f' % round(obj.seconds_worked / 3600.0, 2)
    hours.admin_order_field = 'seconds_worked'

The admin list for EmployeeProjectHours now has columns named “Username”, “Date”, “Name”, and “Hours” — and all of them will be sortable! (if you’re on an ancient version of Django, you’ll need to apply r9212 from Django trunk, only the changes in contrib/admin/views/main.py are needed if you’re lazy).

In addition the DailyEmployeeProjectHours class now has a number of new properties…

deph = DailyEmployeeProjectHours.objects.get(...)
assert deph.username == deph.employee.user.username
assert deph.date == deph.empday.date
assert deph.name == deph.project.name

It’s cute and it uses descriptors… :-)

Posted in django | Tagged | 2 Comments

python :: Calculating the distance between two locations…

How far is it from point a to point b? There are complicated ways to answer this question, that e.g. takes into account whether you’re walking or driving etc. However, if you only need the approximate distance “as the crow flies”, some simple math is sufficient.

I’m assuming you’ve used Google’s geo-coding, or geonames.org’s postal code search, etc., and now have locations with lat and lng attributes.

The Earth isn’t a perfect sphere, among many non-spherical properties, it’s actually fatter around the equator than between the poles: The algorithm doesn’t take any of this into account, and instead uses a single value for the Earth’s radius. Assuming you’re not going very far (i.e. “halfway around the globe”, the result will probably not be too far off, YMMV of course).

Module haversine.py:

import math

def cosrad(n):
    "Return the cosine of ``n`` degrees in radians."
    return math.cos(math.radians(n))
    
def haversine((lat1, long1), (lat2, long2)):
    """Calculate the distance between two points on earth.
    """
    earth_radius = 6371  # km
    dLat = math.radians(lat2 - lat1)
    dLong = math.radians(long2 - long1)

    a = (math.sin(dLat / 2) ** 2 +
         cosrad(lat1) * cosrad(lat2) * math.sin(dLong / 2) ** 2)
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
    d = earth_radius * c
    return d

def distance(a, b):
    "Return the distance between two points that have .lat and .lng members."
    return haversine(
        (float(a.lat), float(a.lng)),
        (float(b.lat), float(b.lng)))
Posted in Python | Leave a comment

sphinx :: adding a new theme

Sphinx (http://sphinx.pocoo.org/) has made our internal documentation effort go a lot smoother, and the autodoc extension has provided much needed motivation for writing useful docstrings.

For those unfamiliar with autodoc, a simple declaration in your documentation file:

.. automodule:: test.sphinxtest
   :members:
   :undoc-members:

will automatically document your module, pulling in docstrings automatically:

Sphinx with autodoc output (default theme).

At first glance that’s nice, but after having used it for a while it becomes clear that your eyes are drawn away from the content and onto the “packaging”, like the headers/TOC/etc. It is also difficult to tell which level you’re at since all headers start flush left.

The choice of fonts for describing classes also seems a bit unusual — at least for first time readers:

Sphinx classdef (default theme)

There are other themes to choose from, but if you’re going to customize you might as well create your own theme — especially since it’s really easy.

I’ll use our dktheme as an example, the finished product looks like this:

Sphinx with dktheme

To start, create a folder (dktheme) to hold your theme files. I chose to put it in the documentation folder. Change your conf.py file to reflect the new folder:

html_theme = 'dktheme'
html_theme_path = ['.']

Create a theme.conf file in the folder and provide a few basic configuration settings:

[theme]
inherit = default
stylesheet = dktheme.css
pygments_style = sphinx

inherit declares which theme you want to use as a base (they exist in site-packages/sphinx/themes). pygments_style defines which syntax highlighting color scheme to use.

Create a directory dktheme/static, and put a file named dktheme.css_t in it. Notice the _t suffix. This file corresponds to the stylesheet setting in the dktheme/theme.conf file.

Define your own css rules inside dktheme.css_t, but begin by importing the css file of the theme you’re inheriting from:

@import url("default.css");

Firebug/Webdeveloper/etc. are useful for finding the correct selectors to override:


@import url("default.css");

html, body, h1, h2, h3, h4, h5, h6 {
  font-family: 'Ubuntu', 'Trebuchet MS', sans-serif !important;
}

html,
.bodywrapper,
.documentwrapper,
.footer,
.footer a,
.related,
.related ul,
.related ul li a  {
    background-color: #c0c0cf !important;
    color:#808080 !important;
}

.related ul {
  max-width:900px; margin-left:250px !important; font-size:smaller
}
.related ul a {  background-color: transparent !important;}

.body {
  -moz-box-shadow:0px 0px 12px #666;
  -webkit-box-shadow:0px 0px 12px #666;
  box-shadow:0px 0px 12px #666;
  margin:15px 25px 15px 0px;
  margin:0px 25px 0px 0px;
  border:9px solid white;
  -moz-border-radius:14px;
  -webkit-border-radius:14px;
  border-radius:14px;
  max-width:900px;
}

.sphinxsidebar a,
.sphinxsidebar h3,
.sphinxsidebar h4,
.sphinxsidebar h5 { color:#555 !important }

.sphinxsidebar .searchtip { color:black; }

.body h1,
.body h2,
.body h3,
.body h4,
.body h5,
.body h6 {
  margin-left: 0px !important;
  margin-right: 0px !important;
  padding-left:0 !important;
  background-color: white !important;
}

.body h1 { padding-top:17px !important; }

.section .section { margin-left:14px }

.class big { font-size:85%; }
.class em { font-size:85%; }

.class .property,
.class .descclassname,
.class .descname { font-size:100%; line-height:100%; }

.class .property {
   font-style:normal;
   font-weight:bold;
   color:#1D599F;
}

.descclassname { font-size:100% !important; font-family: Tahoma; }
.descname { color:#041F3F; font-size:100% !important; font-family: Tahoma; }

dl.method::before {
    content:"def";
    float:left; margin-right:0.5ex;
    font-weight:bold; color:#222;
}

dl.attribute::before {
    content:"@property";
    font-size:85%;
    float:left; margin-right:0.5ex;
    font-style:italic;
}

dd > p { font-size:85%; }

I’ve used ::before rules to e.g. insert the word def before method definitions.

At the top I’ve listed the preferred font as being Ubuntu, which is a very clean font that Google has made available as a webfont (www.google.com/webfonts).

To use the Ubuntu web font, you’ll need to add a link to its css file in the <head> section of your html.  To accomplish this, you’ll need to add a final file to your theme, dktheme/layout.html, with the following content:

{% extends "default/layout.html" %}

{% block extrahead %}
<link href="http://fonts.googleapis.com/css?family=Ubuntu:300,300italic,regular,italic,500,500italic,bold,bolditalic" rel="stylesheet" type="text/css">
{% endblock %}

What this does should be obvious to anyone familiar with Django templates (although these are Jinja templates).

I’m not a graphical designer, but to my development eyes the documentation is much easier to use with the new theme :-)

Posted in Uncategorized | Tagged , , | Leave a comment