July 2006 Archives

XHTML Strict Compliance

As of a few minutes ago, prayerletters.us is now XHTML compliant, not with the transitional rules, which are fairly similar to HTML, but with the strict rules, which aren’t very forgiving. I needed something to do on my vacation. :-)

Lessons learned:

  • It’s possible to get XHTML Strict to work cross-browser in much the same way as XHTML Transitional does. It might even be easier. I think the only difference I saw between transitional and strict was that the margin between the logo and the navigation bar increased by a couple of pixels. (This is excluding some changes I made on purpose.)

  • Yahoo’s User Interface Library is very handy, and open source (with a BSD license, which is considerably nicer than the viral GPL). I didn’t end up copying anything verbatim, but I made good use of their grid and font CSS documentation and examples. Beware their “reset” file!

  • Firefox and the Web Developer Toolbar are indispensible. If you don’t believe me, install them, load up a web page, and click “Outline…Block Level Elements”, or “CSS…Disable Styles”, or “Disable…JavaScript” (umm, just remember to turn it back on afterward…). It also has handy keystrokes for sending your web page to the W3C’s HTML validator.

  • Updating your pages to be XHTML compliant is a good way to track down errors in your tags, particularly for missing closing tags, which don’t always cause obvious errors, but can cause some extremely annoying subtle ones.

  • Using em’s instead of pixels can indeed work (though not without some tweaking from Yahoo’s CSS files, for some reason — possibly due to something I changed). It also makes things work a lot better when you mess with the font size in the browser (Ctrl-Equal and Ctrl-Minus in Firefox, or “View…Text Size” in IE).

  • When validating, use the same rules as for debugging — unless the errors are obvious, just fix the first one, then revalidate. The earlier errors may be confusing the validator and keeping it from processing the rest of the file properly.

Most of the public pages should now be more accessibility-friendly, as well — that’s not a required side-effect of XHTML-strict-compliance, but I tried to build it in while I was at it. I’m not sure how many blind missionaries there are out there who want help sending their prayer letters, but I want to be there for them too!

It also helps for people with poor eyesight (e.g. the web site now handles very large font sizes nicely), or for people who don’t use mice. I still have a way to go in that area, but it’s a start.

I like the results so far — try turning off style sheets and/or images, or using a text browser like lynx.

Check the power cable...

I just spent about half an hour trying to figure out why some very basic JavaScript wasn’t working (click on image results in click on nearby radiobox). I rewrote the HTML at least a couple of times, spent quite a bit of time in the DOM Inspector, and eventually discovered:

I’d turned JavaScript off.

Sigh.

Great Quote

“I was with book, as a woman is with child.”
— Orual, in Till We Have Faces, by C.S. Lewis

The Temple Tax

MLF brought up the story of the temple tax this morning at church. It’s a short one, tucked in Matthew between the transfiguration and the argument between the disciples about who’s the greatest, so it’s easy to gloss over. I love the story, though:

When they came to Capernaum, the collectors of the half-shekel tax went up to Peter and said, “Does your teacher not pay the tax?” He said, “Yes.” And when he came into the house, Jesus spoke to him first, saying, “What do you think, Simon? From whom do kings of the earth take toll or tax? From their sons or from others?” And when he said, “From others,” Jesus said to him, “Then the sons are free. However, not to give offense to them, go to the sea and cast a hook and take the first fish that comes up, and when you open its mouth you will find a shekel. Take that and give it to them for me and for yourself.” — Matthew 17:24-27

It’s packed with good stuff. I don’t think the setup was necessarily a test like the question of paying taxes to Caesar — we’re not told that it was a setup, and Jesus doesn’t seem to respond as if it were (when that happens, he usually stymies them).

Nevertheless, Peter answers without thinking, saying that of course Jesus pays the tax. He’s had a rough couple of chapters.

And when he (Peter) came into the house, Jesus spoke to him first, saying, “What do you think, Simon? From whom do kings of the earth take toll or tax? From their sons or from others?” And when he said, “From others,” Jesus said to him, “Then the sons are free.”

We get a little prophet moment — “Jesus spoke to him first.” Just a little nudge. Maybe Jesus was in earshot, or even next to Peter when the tax collector asked his question. It doesn’t really matter, and we’re not told; read it as you will. I’m more inclined to believe the former, since they didn’t pay the tax right away, which you’d expect to happen if he was present.

In understanding Jesus’ question, it’s important to note that this was the temple tax, not Roman taxes. Does a king tax his own children in his kingdom, or does he tax others? Others, of course. Why would you tax yourself?

Likewise with the temple. In such a subtle way (especially when compared with the transfiguration, which happened at the beginning of this chapter), Jesus has just made yet another claim to divinity.

“However, not to give offense to them, go to the sea and cast a hook and take the first fish that comes up, and when you open its mouth you will find a shekel. Take that and give it to them for me and for yourself.”

This is the main reason I don’t think the tax collector’s question was meant as a test. Jesus spent quite a lot of time offending people by healing the blind and lame, raising the dead, and casting out demons. But here he doesn’t want to cause offense.

But rather than pay a half-shekel from such money as they had (another of the gospels says that they had money), he tells Peter to do something absurd — similar to when he appeared to the disciples when they were out fishing after his resurrection. In that case, they were out in a fishing boat, and were catching nothing. Jesus calls out to them from the shore and suggests that they cast their net on the other side of the boat. Just picture with me all of the disciples, scratching their heads, looking over one side of the boat. On the other side are 153 fish, heads poking out of the water, making googly-eyed fish faces at the disciples. That’s more or less what would have been necessary for his suggestion to work.

Anyway, in this case, imagine what Peter must have thought as he was going out to catch the fish. He’s declared Jesus to be the son of the living God, and just saw him in some semblance of his divine appearance at the transfiguration. Now, not only does Jesus teach that he’s exempt from the temple tax as the son of the God of the temple, but without being asked, and without any fanfare, he gives Peter another sign from heaven of his divinity, just after the religious folks so loudly demanded one and were turned down.

And, as an added touch, the fish with a taste for shiny metal would have exactly enough for both of them. Peter wouldn’t have to fend for himself.

A wonderful story.

Small Business Stories: Fluffy Booties

One of my value-added features is that I offer to receive mail for missionaries who do an “ask-and-run” campaign — in other words, sending out a financial “ask” letter immediately before leaving on a missions trip. In those cases, they’re often not able to receive their mail (and therefore the money that’s helping pay for their trip), so it comes to me instead, and I forward it along to their missions organization.

This has led to two recent pieces of mail that were rather unexpected:

1) Someone used the response envelope to send me a check and response card for a different missionary organization. Oops.

2) Someone used the address to send me a package containing a pair of pink fluffy baby booties for the missionary’s newborn. I guess I’ll hold on to them until the missionaries get back.

Successes in Optimization

I’m waiting for my address database to do a global update before going home for the night, so now seems like a good time to post the evening’s results.

A little while ago, I added the ability for my business software to handle bulk mail. A significant part of that is sorting the addresses properly so that the order they come out of the printer is the order in which they go into trays. In my case, I decided to add the support to print one tray at a time, since that looks like it’ll be the easiest way to handle everything — treating each tray as its own job, more or less.

The code worked, but I had it mostly commented out (unless I added “&bulk=1” to the URL) since it was SLOOOOOOOOOOOW. To give you an idea how slow, I rewrote it earlier this evening, and brought it down to 18.8 seconds, which was a significant improvement.

That’s still way too slow, though, since this gets calculated every time I view the details for a particular mailing (ideally, in any case). I changed the code to only sort the list for applicable mailings (i.e. anything with at least 200 recipients), but it wasn’t the small cases that were the biggest nuisances.

My goal was five seconds for the page load. A little bit of profiling showed that over 15 out of the 18.8 seconds were spent doing the sorting, so that was going to be the focus:

Here are the results:

Page Load Time Total Savings Incremental Savings Description
18.79s         Starting Point
16.68s 2.11s 11.2% 2.11s 11.2% Remove two grep statements, moving them into the main for loop
5.85s 12.93s 68.9% 10.82s 64.9% Use DBIx::SimpleQuery to prefetch the relevant data for all recipients using a massive “SELECT … FROM contacts WHERE contact_id IN (#, #, #, #, #…)” instead of getting the data from Class::DBI (which would involve a separate query for each recipient)
4.00s 14.79s 78.7% 1.85s 31.7% The mailing details page no longer needs a list of recipients for anything, so don’t bother retrieving it.
1.52s 17.26s 91.9% 2.48s 61.9% Cache the list of (163) USPS Automated Area Distribution Centers instead of looking them up as needed.
1.25s 17.54s 93.3% 0.27s 18.0% Eliminate one of two map statements used to generate the list needed by SimpleQuery above.
2.55s 16.24s 86.5% -1.30s -103.6% Oops: The use of DBIx::SimpleQuery introduced a bug that I didn’t catch until here — the query wasn’t necessarily sorting the list properly.
1.37s 17.42s 92.7% 1.18s 46.2% Give the presorter a mailing number rather than a list of recipients, which eliminates the massive IN(#, #, #…) part of the DBIx::SimpleQuery call above.
1.34s 17.44s 92.8% 0.03s 1.9% Eliminate an unnecessary sort (which was being handled in the database rather than code, so it didn’t really make a difference here — it opens up a possibility to knock a good second and a half off of other areas of code, though)

After getting under five seconds on the third try, I decided to see if I could get under one second, and hit the “more or less instantaneous” milestone.

I came pretty close, but hit a wall at around 1.4 seconds — changes after that point were only giving marginal improvements with heavy costs in code readability. I gave up after a couple of hours. 1.3 seconds is still quite good, especially since this was a larger test mailing than I expect to face on a regular basis.

Definitely be sure to use some sort of profiling tool when optimizing, even if it’s just printing out timestamps (which is what I did, with Time::HiRes to give a finer granularity than one second). You’ll save yourself a lot of shame when you avoid spending hours optimizing code that wasn’t actually a bottleneck (a lesson I learned a couple of years ago the hard way; oops!).

Anyway, the database update is done, and everything seems to be working, so I’m going home!

emacs `cvs -nq up | grep -e '^[MA]' | awk '{ print $2 }'`

From bash (may or may not work in other shells), run emacs, opening all locally-modified or added-but-not-committed files (e.g. so that cvs diff can be run on them, or to allow last-minute changes before committing them directly from within emacs, rather than the shell.

You could also include new files that haven’t yet been added to CVS by changing [MA] to [MA\?] in the grep. Be sure to note that everything after “emacs” is enclosed in backticks (the key to the left of the “1” key on most keyboards), not apostrophes.

(I’m sure this could be converted to vi and/or Subversion pretty easily, as well.)

Search term highlights from June

Looking over the server logs for June, I think most people found what they were looking for when visiting this site (I’d still like to beef up the sound system section, since that accounts for a lot of visits). There are always a few search terms that make me smile, though, and these three win for last month:

  • “aid for deef people” (we’re long past the possibility of being helped…)
  • “happy envelope” (especially amusing given that it isn’t one of my other sites)
  • “daily earthquakes” (an unfortunate side effect of living near a construction site)

Incidentally, the daily earthquakes have stopped, and we now have an industrial-strength woodpecker as they’re putting up the walls, beams, and such, but since I moved into an office 10 minutes away, it hasn’t been a problem.

That city in Florida that I’m not mentioning in the hopes that it’ll eventually fall of the caches of every search engine ever is still four of the top ten search strings…

Oh, and who’s visiting this site from Newfoundland on at least a halfway regular basis? Identify yourself! Especially if I know you!

(You can tell that this hasn’t been as busy a month for prayer letter printing as May…)

Somewhere, in an article I was reading over the past week or two on productivity, the suggestion was made that we could save quite a bit of time (cumulatively, at least) by binding “Move current message to folder x” to a keystroke, so that we don’t need to use the mouse, or memorize the sequence “Menu-key M Right Enter O Enter” or “Menu-key M Right Enter L L Enter”, which was what I was doing on the PC (the Mac is less keyboard-friendly, and I couldn’t find an equivalent*).

I thought: there has to be a way to do this in Thunderbird (my E-Mail client of choice — I tried Mail.app, but it’s missing some very fundamental features, like multiple identities within one mail account, which I use all the time**).

Recent Entries

Who's on first?
I’m going through the settings of a hard-core E-Mail program, and came across this lovely setting: Copy To Address to…
USPS Extreme Cost-Saving Measures
In the July 2010 edition of the PCC Insider (a USPS publication) is found the following statement: There are 26,000…
At 1:15am this morning
At a hotel, after a day of eventful travel and 3.5 hours of sleep the night before, it’s (almost) needless…
My Amazon.com Wish List