Damnit, Django: has_usable_password()

Let’s say you have an account that you’ve marked as having an unusable password. The built-in Django password-change forms won’t let you just set an initial password on that account. You have to write a new view to handle the idea that someone who has an alternative authentication mechanism, might also want to set a password on their account if they want to login using a username + password combo locally.

Update: Ahh, there it is: “class SetPasswordForm, A form that lets a user change his/her password without entering the old password.” It’s in the docs after all, but not exactly the principle of least surprise at work here.

Busy Thursday Project: Power Meter Fix

A few years ago, in a rush of curiosity, I bought myself one of those plug-in power meters to see how much energy all the various bits of equipment were using in the apartment. It revealed some interesting things, like the fact that my washing machine would chew up 10W while “off”. Keep in mind that 10W is enough to light up 3 relatively-bright LED spotlights, brighter than the crappy halogen bulbs I keep using, because no one’s gotten around to building a 3W GU4-socketed LED bulb. It’s a good reminder why low-power standby modes can keep your electricity meter spinning.

In any case, the meter was an interesting novelty which lasted about six months before the two button-cell batteries ran out of juice. Which then made me wonder who designed the meter, because it has sat on the shelf in my apartment for the past four years. One could argue for operator error, that it’s really my responsibility to change the batteries. But let’s be realistic: Button cell batteries are supremely annoying, especially when you have to use two or three at a time. I just discovered another piece of electronics I own that uses 3 of them, and again, I haven’t used it in several years.

I don’t know why people design a device that connects to the mains and then decide, for cost reasons, to leave out the solid state switching power supply and passive components that would allow you to use the thing w/o having to constantly provide extra batteries to run the digital electronics. It’s a major pain in the ass, and you just end up throwing away lithium batteries, which is a waste of resources. Also, is this the best we can do, in terms of battery technology?

So I added a replacement battery lead to a 3-AA battery pack. The nominal digital electronics voltage on the meter’s back panel is 3V, but I figure the electronics can handle a little more. 3 fully-charged NiMH batteries in series will put out 3.6 – 4V and then slowly run down to 3V. And they store about 10x more usable energy than a button-cell battery (>1500mAh vs. 150mAh, you do the math).

And so now the meter works and I can recharge it when necessary:

fixing-power-meter-01

fixing-power-meter-02

fixing-power-meter-03

fixing-power-meter-04

fixing-power-meter-05

The 4V over-voltage does lead to the LCD display being characteristically darker, but now I know that my MacBook Pro uses around 40-50 watts under normal use, which I suppose is useful.

Lazy Sunday Project: Fixing My Bike Light

For the past (I don’t know how many) months, my bike light has had a defect that would cause it to shut off whenever I ran over the smallest of obstructions. Cobblestones, curbs, even your standard bumpy path would cause the light to just turn off. It made sensible use of the light impossible. Whoever designed the light didn’t quite get it right, and the inconvenience had me consider sending the light back to the manufacturer. Then winter came, and none of this happened.

So I sat down on Sunday and yesterday evening to crack open the light and rewire it to skip all of the fancy electronics. I had to get some extra switches from Conrad Electronics, the German chain of hobby shops that carry all manner of tools and components that tinkerers use. Amazingly, I’ve run into at least one of these shops in most of the mid-sized towns I’ve been in: Freiburg has Omega Elektronik, Ulm has Mükra. These places remind me of the old days, when I’d wander around the aisles of the industrial-surplus behemoth known as Mendelson’s. It is a great sign that the society can support these kinds of shops and that enough people still know their way around a soldering bench. And of course, places like Mendelson’s always made me think of the kinds of place one would want to raid for parts, at the end of the world.

First things first, I did continuity checks to see how the whole thing was being supplied with power. The four batteries are strung in series end to end, providing 6V at 2400mAh. When hooked directly to the LED, with no resistance in series, an ammeter test showed 1A flowing through the circuit, which is crazy high, and would probably burn out the LED pretty quickly. At that rate, the batteries would be dead in little over 2 hours, and that would be driving 6W through the LED, which seems pretty high. It did look pretty damn good though.

Unfortunately, I didn’t have any specs on the LED, so I’m not sure what its nominal current should be. But the numbers I could find indicate a typical 4V forward voltage and 300 – 350mA current for white-light LEDs. So I threw a 10 ohm resistor in line, which dropped the current to around 220mA. 1.3W through the LED seems pretty reasonable, though certainly not as bright. It would be interesting to do a test with a photodiode to see what the light output curve looks like for the applied power.

But 6V across a 7 + 10 Ohm load is not good enough either, the LED takes a measured voltage drop of 2.7V, but then the 10 Ohm resistor takes 3.3V, and is then dissipating just as much power as the LED. So I added a small bit of wiring to the light, and reordered the way the batteries were inserted, so that I could get a better 3V, 4800mAh battery pack. The bad thing is that the amperage dropped as well. The LED now draws only 170mA of current, and is noticeably dimmer.

So I changed the circuit again, reverting to the original 6V battery order and adding a second 10 Ohm resistor in parallel, which gives a 7 + 5 Ohm load, which means more of the power is going through the LED, with current measured around 300mA. I added a second switch to control whether the current flows through the resistor at all, meaning that when I really want to burn things out, I can just dump all the current through the LED. It’s like having low and high beams on a car. Problem solved, but man does it look fugly:

bike-light-01

bike-light-03

bike-light-04

bike-light-05

bike-light-07

bike-light-08

bike-light-09

bike-light-10

Localization / Translation Using Google Spreadsheets

So I spent a little time over the weekend hacking together a piece of code to help export usable localization / translation files from a simple spreadsheet in Google Docs. With a little extra effort setting up Protected Ranges and giving other users editing permissions, team translation using the same spreadsheet should be pretty easy. Currently, it can export Django-style gettext .po files and jquery.localize-compatible .json translation files.

This was an experiment in learning how to use Google Spreadsheets in combination with Google App Scripts to create a localization table that can be accessed via JSON/JSONP. This is useful because there’s still no useful built-in JSON publishing option for individual spreadsheets.

So here is the localization spreadsheet URL that I’m using as my source (spreadsheet key highlighted):
https://docs.google.com/spreadsheet/ccc?key=0AqrUvD5TZZs3dF9ULUh5X1JlakVJRGFHaWRZQmFuZEE

It looks like this:

And here is the Google App Script URL that will generate localization string tables from that spreadsheet:
https://script.google.com/macros/s/AKfycbxLnEUyElPtL01qHnL7pD2hmTmaO7Tc1yLhjJzQpitpuBfxxBU/exec

If you put the two together, with the querystring sheet_id set to the spreadsheet key and sheet_name set to an appropriate sheet name inside that spreadsheet, you get the following:
https://script.google.com/macros/s/AKfycbxLnEUyElPtL01qHnL7pD2hmTmaO7Tc1yLhjJzQpitpuBfxxBU/exec?sheet_id=0AqrUvD5TZZs3dF9ULUh5X1JlakVJRGFHaWRZQmFuZEE&sheet_name=Main

And when you retrieve that link, it generates the following (folded for brevity):

{
    "de": {
        "string_with_quotes": "Mit \"\"", 
        "potato": "Kartoffel", 
        "language_code": "de", 
        "hello": "Guten Tag!", 
        "chanterelle_mushroom": "Pfifferlinge", 
        "how_are_you": "Wie geht's?", 
        "string_with_comma": "Mit, Komma", 
        "language_name": "Deutsch", 
        "string_with_colon": "With:", 
        "text_direction": "ltr", 
        "string_with_newlines": "Mit \n\n"
    }, 
    "zh-Hant": {},
    "zh-Hans": {},
    "de-AT": {
        "string_with_quotes": "Mit \"\"", 
        "potato": "Erdapfel", 
        "language_code": "de-AT", 
        "hello": "Guten Tag!", 
        "chanterelle_mushroom": "Eierschwammerl", 
        "how_are_you": "Wie geht's?", 
        "string_with_comma": "Mit, Komma", 
        "language_name": "Deutsch (\u00d6sterreich)", 
        "string_with_colon": "With:", 
        "text_direction": "ltr", 
        "string_with_newlines": "Mit \n\n"
    }, 
    "fr": {},
    "en": {
        "string_with_quotes": "With \"\"", 
        "potato": "potato", 
        "language_code": "en", 
        "hello": "Hello!", 
        "chanterelle_mushroom": "chanterelle mushroom", 
        "how_are_you": "How are you?", 
        "string_with_comma": "With, comma", 
        "language_name": "English", 
        "string_with_colon": "With:", 
        "text_direction": "ltr", 
        "string_with_newlines": "With\n\n"
    }, 
    "ja": {
        "hello": "こんにちは!",
        "language_code: "ja",
        "text_direction: "ltr",
        "string_with_comma: "With, comma",
        "string_with_newlines: "With \n\n",
        "how_are_you: "お元気ですか?",
        "string_with_quotes": "With \"\"",
        "string_with_colon": "With:",
        "potato": "potato",
        "language_name": "日本語",
        "chanterelle_mushroom": "chanterelle mushroom"
    }
}

The spreadsheet format follows a few conventions: By convention, the left-most column (Column 0) is the keystring, which you use to access the translation value later. The next column to the right (Column 1) is the source language or default-language column, in this case English, which should contain all of the original strings you need to localize. By convention, the language_code row is the top-most row (Row 0). Note that the export script will replace untranslated strings in a target language first with the translations from the closest base-language language_code, so in the case of Austrian German “de-AT”, it pulls in the translations from the generic German “de” language_code column; then, for anything missing in that column, it pulls from the default-language column.

I’ve set up a github repository to capture further development, and have been looking at the various file formats supported by Transifex, to see if it would be possible to generate output from the spreadsheet for some of them. Unfortunately, Google App Scripts doesn’t let you generate and immediately return a ZIP file, for which I filed a bug. To be exact, you can generate the ZIP file, but Google provides no good way to return the raw bytes and it’s unclear whether they ever will. Nonetheless, there are two Python scripts in to github repo that generate useful gettext and JSON files. I think the most important (which I’ll add eventually unless someone beats me to it) formats that need support would be Android, Windows, and OSX/iOS string resource files.

Helper Scripts: jquery.localize

Looking at the github repository, there’s one script under the “jquery.localize” folder called “generate-language-pack.py”, which generates files that can be used with the jquery.localize plugin:

translation-de-AT.json
translation-de.json
translation-en.json
translation-fr.json
translation-ja.json

The file contents look like:

{
    "string_with_quotes": "Mit \"\"",
    "potato": "Kartoffel",
    "language_code": "de",
    "hello": "Guten Tag!",
    "chanterelle_mushroom": "Pfifferlinge",
    "how_are_you": "Wie geht's?",
    "string_with_comma": "Mit, Komma",
    "language_name": "Deutsch",
    "string_with_colon": "Mit:",
    "text_direction": "ltr",
    "string_with_newlines": "Mit \n\n"
}

I’ve set up an example website using jquery.localize and a simple $.click() handler, to show the translations in action.

Helper Scripts: gettext

Under the “gettext” subdirectory in the github repository, there’s a file called “localize-django-app.py”, which, when run inside a Django app directory, prints out the following:

Markdown unavailable.
Creating locale/de/LC_MESSAGES/django.po
Creating locale/de_AT/LC_MESSAGES/django.po
Creating locale/en/LC_MESSAGES/django.po
Creating locale/fr/LC_MESSAGES/django.po
Creating locale/ja/LC_MESSAGES/django.po
Creating locale/zh_Hans/LC_MESSAGES/django.po
Creating locale/zh_Hant/LC_MESSAGES/django.po

And generates gettext catalogs that look like:

If you have Markdown installed, the script will run markdown.markdown() on the msgstr translation values before outputting them.

Google App Script Source

I’ve also set the export script to be viewable via the following link: https://script.google.com/d/167d-d6YtX74ZOZ0auMlPdd6emqusmWy5wUqhruKo9uu8AQoaoc3yvZsP/edit?usp=sharing, but I think you might have to log in to view it. It doesn’t seem like you can just see the script w/o having a Google Account. If you’d like to collaborate on making this better, let me know and I can grant edit access.