django-boardinghouse

I wrote a heap of code last April, under the name Multi-tenanted Django. It was fairly complete, but not especially well documented, and not really that well tested.

Recently, I’ve been having to write some reporting code at work that dealt with objects that are generated by django-reversion. If I was using tenancy-based partitioning, it would be really easy for me to just fetch the changes that were made to data from a given company: instead I need to do heaps of queries, and lots of filtering.

Which got me enthused on django-multi-schema, which has since been renamed to django-boardinghouse. And, it now has it’s own documentation, and an example project.

I’m still a bit cagey about releasing it to pypi, as the example project is pretty simple, and I’d like to build that (or another project) up a bit to see if I’ve made any more bad decisions: I’ve already changed it to opt-in to seperate schema to opt-out, and added in a configurable SCHEMA_MODEL.

It currently passes all tests under django 1.4 - 1.6, and has some functionality under django 1.7, but the migration handling code is not well tested just yet.

Generating Coverage Badges

Drone.io is a pretty neat (free for open-source projects) continuous integration server. The best feature from my perspective is that it works with BitBucket repositories.

It’s pretty nice having status badges indicating if a build is passing or failing, but even better is also getting a coverage report.

I’ve been using django-coverage for this, and ages ago manually created a set of drone.io style badges, and added a patch to copy the relevant file across. But then, shortly after, drone.io changed their status badge format. I never got around to redoing my badges, as it was pretty time consuming.

Enter Pillow.

from PIL import Image, ImageDraw, ImageFont

SIZE = (95, 18)

BACKGROUND = hex_colour('#4A4A4A')
SUCCESS = hex_colour('#94B944')
WARNING = hex_colour('#E4A83C')
ERROR = hex_colour('#B10610')

# You may need a different font filename if you aren't on a Mac
FONT = ImageFont.truetype(size=10, filename="/Library/Fonts/Arial.ttf")
FONT_SHADOW = hex_colour('#525252')

PADDING_TOP = 3

def build_image(percentage, colour):
    # Create a brand-new Image object, with the background
    # as the main badge colour.
    image = Image.new('RGB', SIZE, color=BACKGROUND)
    drawing = ImageDraw.Draw(image)
    
    # Write the word 'coverage' in our specified font.
    # Fake a text-shadow by drawing the text twice.
    # TODO: Make the text-shadow better.
    drawing.text((8, PADDING_TOP+1), 'coverage', font=FONT, fill=FONT_SHADOW)
    drawing.text((7, PADDING_TOP), 'coverage', font=FONT)
    
    # Do the percentage text.
    # TODO: Make the text-shadow better.
    # TODO: Make the text centred in the coloured box.
    drawing.rectangle([(55, 0), SIZE], colour, colour)
    drawing.text((63, PADDING_TOP+1), '%s%%' % percentage, font=FONT, fill=FONT_SHADOW)
    drawing.text((62, PADDING_TOP), '%s%%' % percentage, font=FONT)

Creating the required RGB tuple from a hex colour is also fairly easy:

def hex_colour(hex):
    if hex[0] == '#':
        hex = hex[1:]
    return int(hex[:2], 16), int(hex[2:4], 16), int(hex[4:6], 16)

Finally, you can just generate an image for every percentage point, and save them:

SUCCESS_CUTOFF = 85
WARNING_CUTOFF = 45

# range(101) -> [0, 1, 2, ..., 99, 100]
for i in range(101):
    file = open('%i.png' % i, 'wb')
    
    if i < WARNING_CUTOFF:
        build_image(i, ERROR).save(file)
    elif i < SUCCESS_CUTOFF:
        build_image(i, WARNING).save(file)
    else:
        build_image(i, SUCCESS).save(file)

It’s not quite perfect: that isn’t quite the font they use, but it will do for now.

Dear ezyreg

I went to register with EzyReg to set up monthly direct debit for my car registration, and to register I was required to enter a secret question or two.

These secret questions were all essentially publicly available information, or things that were inane enough that I probably would not remember them. Thus, they are useless from both sides of the fence: I would not be able to use the meaningless ones to reset a password (not really that much of a problem), but if I used one of the “one true answer” ones, someone could easily discover, for instance, what my Mother’s maiden name was.

I could enter a random value, but I decided to get on my high horse, and supply feedback.

Then, after writing for a few minutes: “You may only enter 1000 characters in feedback”.

What. The. Fuck.

So, after splitting it into two comments, here is the entirety of my comment to the braindead fuckwads who wrote the registration system:

I want to use monthly direct debit to pay my car registration, but with the current requirement to have an ezyreg account, and the requirement that said account is “secured” with secret questions means that I cannot in good faith complete the registration process.

There are well documented flaws with secret questions as a second-level of security, or that can be used to reset or change a password. This becomes an active attack vector that, in the case of someone who uses good password hygiene, partially defeats the processes I have in place to protect access to my accounts.

This is made even worse in the case of your security questions, of which there are very few, and I am unable to create my own.

Some examples of the arguments against security questions, and why they are a security risk, not an improvement:

http://www.oneid.com/blog/passwords-are-bad-but-security-questions-are-worse/

https://www.schneier.com/blog/archives/2005/02/the_curse_of_th.html

http://www.intego.com/mac-security-blog/your-secret-question-may-not-be-so-secret-easy-to-guess-password-retrieval-questions-you-should-avoid-and-why/

Please consider removing this requirement from your account registration process.

pbgrep: grep your clipboard history

I’ve used ClipMenu as my clipboard history manager for several years now: it’s unobtrusive, and does almost exactly what I need.

Except, you can’t search the clipboard history.

I keep thousands of items in my clipboard history, and today I was trying to find a specific item, that I know was in there. And I couldn’t find it after about a minute of scanning through submenus.

Now, ClipMenu can persist it’s history to disk, in ~/Library/Application Support/ClipMenu/clips.data. Which is a binary plist file.

We can view it using plutil:

$ plutil -p ~/Library/Application\ Support/ClipMenu/clips.data -o -

I made the decision to limit searching for single-line clips: this means I can grep for lines that contain:

<string>.*(QUERY).*</string>

Doing single-line matches means I can use grep (or, as I discovered later, ack), which should be faster than firing up a python interpreter.

My first iteration was:

$ plutil -convert xml1 ~/Library/Application\ Support/ClipMenu/clips.data -o - \
  | grep "<string>.*test.*</string>" -o

This works quite well, but includes the XML string tags. I did strip them out using sed, but this is an extra command. It turns out that grep’s regular expressions can’t handle positive lookahead/behind assertions, and Mac OS X’s grep does not support --perl-mode, so I reached for ack:

function pbgrep() {
  plutil -convert xml1 ~/Library/Application\ Support/ClipMenu/clips.data -o - \
    | ack "(?<=<string>).*$1.*(?=</string>)" -o
}

That now takes pride of place in my .bashrc, and I can pbgrep foo to my hearts content.

I guess I could (if there was only one match), put the value back into the clipboard. That might be kind-of nice.