Archive for the ‘internet’ Category
Weather icons
June 11th, 2008
Yes, I’ve written a lot about weather forecasts lately. I like to think that my interest in weather forecasts comes from a combination of this year’s rainy and cold spring and my desire to ride my bike to work most days of the week. Getting an accurate forecast helps me dress for the weather and, in some cases, time my rides to avoid the heaviest rain.
Or maybe I’m just developing that peculiar interest in weather that comes with advancing years.
Either way, I’ve noticed that the online weather services play up the possibility of storms just as much as TV weatherfolks do. For example, over the last few days the National Weather Service, AccuWeather, and the Weather Underground have been predicting a 30-40% chance of thunderstorms in Naperville tomorrow and Friday—a reasonably high percentage, but not even to the level of a coin flip. Despite the less-than-even odds, all three sites have decorated their forecasts with formidable icons of thunderclouds, lightning bolts, and slanting rain. (To be fair, the Weather Underground’s icon incorporates a question mark in the cloud. But the question mark is pretty subtle—I didn’t notice it when looking at the reduced-size icon on my iPhone.)
The icons make you think the chance of bad weather is much higher than what’s actually predicted. Several times I’ve found myself planning to drive to work to avoid getting caught in a thunderstorm only to read the fine print and decide to ride my bike. I know I’ll get caught eventually, but so far my rides have been pretty dry.
Consider this my top tip for bicycle commuting: pack rain gear as if the thunderstorm icon is right, but make your decision to ride as if it’s wrong. You’ll do a lot more riding and won’t get nearly as wet as you think.
Another “best” weather webapp for the iPhone
June 3rd, 2008
Before writing this post about Weather Underground’s iPhone-specific site, I looked around on AccuWeather’s site to see if it had an iPhone page. I didn’t find one then, but I did today. It’s at http://apple.accuweather.com/widget/iphone1/iphone.html, and despite some user interface problems, it’s pretty good. Weather Underground gives more detail in current conditions; AccuWeather gives more detail in today’s forecast.
When you first load the AccuWeather page, you’ll probably see something like this:

The strip at the top of the page has icon that summarizes the current conditions (sunny, cloudy, rainy, etc.), and gives the time, temperature and location. At the upper right are two circular icons that act as buttons to control what’s displayed in center of the page (more about that later).
Along the bottom are rectangular buttons for quick access to three default locations. You can set these locations by first clicking on the button you want to set, then clicking the script “i” in the lower right corner. This brings up a form where you can enter the city name or zip code.

The central section of the page is where most of the information is delivered. It’s split into an upper part, which is controlled by the leftmost of the two circular buttons, and a lower part, which is controlled by the rightmost of the two circular buttons. In the image above, the top part is showing a 15-day forecast calendar and the bottom part is showing a more detailed 4-day forecast calendar.
Clicking on the left circular button changes the upper part to an animated radar map.

Clicking the left circular button again changes the upper part to a hour-by-hour forecast for the rest of the day.

Clicking the left circular button again returns you to the 15-day forecast calendar.
Clicking the right circular button toggles the lower part between the 4-day forecast calendar shown above and a 5-hour forecast calendar.

The site loads pretty quickly, even over the EDGE connection; more quickly, it seems, than the Weather Underground site. It’s certainly not as well designed as the Weather Underground site, where the buttons look like buttons and have obvious labels like “Radar” and “Forecast.” In fact, I wrote these directions because I found the AccuWeather site confusing, with cryptic icon buttons. And don’t get me started on the ugly color choices.
Despite its user interface problems, I like the AccuWeather page because it’s more detailed in giving today’s forecast and because its radar images are time stamped so you have a sense of how fast a weather system is moving. I have buttons for both it and the Weather Underground page on my iPhone home screen.
Syncing contacts with Google and the “Sync” menu
May 29th, 2008
You’ve probably heard that the 10.5.3 update to Leopard allows you to sync your Address Book contacts with your GMail (or Google Apps) contacts. This is officially supported only if you have an iPhone or iPod Touch, but can apparently be done with a bit of plist editing even if you don’t own one.
I’m sure most people did their first sync with no trouble, but I made the mistake of looking at Address Book’s help page on the topic and didn’t read carefully. Step 5 of the “Synchronizing with Google Contacts” instructions is
Click the Sync menu in the menu bar and choose Sync Now.
My eyes went up to the Address Book menu bar and saw no menu named Sync and no menu item named Sync Now. Maybe Sync is a submenu? No. Maybe I read the instructions wrong? No. Maybe I didn’t read the next paragraph? Yes.
To add the Sync menu to the menu bar, open iSync, choose iSync > Preferences, and then select “Show status in menu bar.”
Oh, the Sync menu isn’t really named Sync, and it isn’t part of Address Book. It’s the Ouroboros-like iSync status menu over at the right side of the menu bar. The one I chose to keep hidden long ago because it’s practically useless.

In fact, I never did use the Sync Now command. I decided to just put my iPhone in the Dock and see what happened; it’s been my impression that iSync kind of runs in the background when my iPhone syncs. Success! Google syncing went ahead and I had to resolve a few minor conflicts.
Oddly enough, after my first iPhone/Google sync, the iSync menu appeared (which is how I got the screenshot above). I had to open iSync and uncheck the status menu preference to get it back to the way I like.

Update
Today (5/30) tried using the Sync Now command instead of just connecting my iPhone. I had cleaned up my Address Book and decided to delete all of my Google Contacts and start with a fresh sync. Because I was at home, and my iPhone is synced to my work computer, I couldn’t put the phone in its cradle. So I used Leopard’s screen sharing to access my work machine from my iBook, activated the iSync status menu, and selected Sync Now. Nothing. The arrowed circle spun a bit, but my list of contacts at Google stayed empty. Maybe Sync Now only works with .Mac?
Further update: Goodbye, Google syncing
After just having almost all my contacts deleted from Address Book and my iPhone during a “sync” with Google Contacts, I’ve turned off syncing with Google and don’t expect to turn it back on again. There was no warning from iSync, I just opened Address Book and found only 6 or 7 entries instead of the 700 or so that should have been there. I’d be really pissed if restoring from my backup weren’t so easy.
Praise for Library Elf
May 21st, 2008
I’ve been using Library Elf to keep track of my families library loans for a couple of years now, and it’s been one of those things that integrate so well with your life that you forget what it was when you didn’t have it. Recently I’ve learned that the people behind Library Elf are not only clever for coming up with a useful tool, they’re also dedicated to keeping their users happy.
First, some background: Library Elf is a web/email/rss service that polls your local library’s web site to keep track of the books (and CDs and movies) you’ve checked out and those you’ve put on hold. You can keep track by logging on to your account at libraryelf.com, by having emails sent to you, or by subscribing to an RSS feed. Library Elf presents the information in a very nice way, with a calendar showing upcoming due dates

and a set of tables showing your borrowings and holds in various categories (click for full size).
The best thing about the Elf for families is that you can keep track of several cards on one Elf account. These can be
- cards for one person issued by several libraries,
- cards for several people issued by one library, or
- cards for several people issued by several libraries.
This is especially useful for families. The status of Mom’s, Dad’s, and the kids’ items are all listed in one spot and everything that’s due soon can be rounded up to return on the next trip.
You can choose from among several notification schedules—weekly, daily, x days before an item is due, etc. From our one account, my wife and I get an email notice every day; we always know what’s coming up due and can consolidate our returns, making fewer trips.
So that’s why the Elf works so well for us. As I said, it quickly became a normal part of our routine, unnoticed until our local library revamped its web presence last month. The new URLs for the library’s holdings screwed up the Elf’s queries. I wrote an email to support@libraryelf.com (which was a guess, there’s no support address listed on the site), letting them know of the problem. Within a few days they had adjusted to the new system and we were back in business.
Late last week I discovered that holds ready for pickup were being placed in the Holds Not Ready Yet category. Another email went out to the Elf and the fix was delivered today. Great response!
Library Elf is free, supported by ads from Google and Amazon. If your library is on their supported list, you should look into setting up an account.
Best weather webapp for the iPhone
May 5th, 2008
A few days ago, I wrote a post describing a little webapp I’d written that scraped a National Weather Service page to deliver a detailed text forecast to the iPhone. The next morning, I saw that @dsandler had Twittered that night about a Weather Underground service that did the same thing. And more.
The iPhone-optimized page is i.wund.com, and it provides
- Current conditions: temperature, wind, humidity, visibility
- Sun and moon rise and set times
- A radar image of your area
- Forecasts for the next several days
- Severe weather warnings
- A search field to get the weather for another city
Some of this is information that could be scraped from the NWS, and I had been thinking about improving my webapp by adding the current conditions and a search field. But there’s no reason for me to go any further; the Weather Underground page gives me everything I want in a compact, fast-loading package. I’ve bookmarked the page for Naperville and added an icon for it to my home screen. Had I known of i.wund.com earlier, I never would have bothered to develop my webapp. At least I learned a bit about Beautiful Soup.
You might wonder why I didn’t learn about the Weather Underground webapp before starting to develop my own. It is, after all, on Apple’s page for popular weather applications. Well, the sad fact is that I’ve been disappointed by so many of the webapps listed on Apple’s iPhone pages that I’ve gotten out of the habit of looking there first. I guess I should get back into the habit.
Better weather forecasts for the iPhone
May 2nd, 2008
I’ve come to hate the weather application built into the iPhone. The graphics are cute, but the information is limited: current temperature, highs and lows for today and the next five days, and a little icon that tries to describe the cloudiness/sunniness/raininess/snowiness/fogginess for each day. It’s the icon that gets to me. A spring day in the Midwest can seldom be described with just one picture, because the weather changes too quickly. What I want is something more granular—a forecast for this afternoon, overnight, tomorrow, and tomorrow night. Predictions beyond that are OK, but I don’t really believe them.
So I set about making a web app, geared to the iPhone, that would give me that information. Here’s what it looks like:

It screen-scrapes a National Weather Service page that gives a detailed text forecast for the location of your choice. The full page looks like this:
I suppose I could have grabbed the part of the page that has the icons, but I like having the wind speed and other information that’s in the text forecast.
For my first go-round, I’ve hard-coded my home town into the CGI script, which I’ve called nws-naperville.cgi. It’s a Python program that uses the very cool Beautiful Soup library for scraping the NWS page.
1: #!/usr/bin/python
2:
3: from BeautifulSoup import BeautifulSoup
4: import urllib
5:
6: # Query the NWS site and get the page of results for Naperville.
7: f=urllib.urlopen('http://forecast.weather.gov/zipcity.php', urllib.urlencode({'inputstring' : '60565'}))
8: page = f.read()
9:
10: # Parse the HTML.
11: soup = BeautifulSoup(page)
12:
13: # Pluck the forecast from below the "Detailed 7-day Forecast" banner.
14: forecastItem = repr(soup.find('td', { 'width' : '50%', 'align' : 'left', 'valign' : 'top'}))
15: start = forecastItem.find('<b>')
16: forecast = forecastItem[start:-17]
17:
18: # Construct the page.
19: print "Content-type: text/html\n"
20:
21: print ''' <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html>
22: <html>
23: <head>
24: <title>Naperville Forecast</title>
25: <meta name="viewport" content="width = device-width" />
26: <style type="text/css">
27: body {
28: font-family: Sans-Serif;
29: font-size: large;
30: }
31: h1 {
32: font-size: x-large;
33: }
34: </style>
35: </head>
36: <body>
37: <h1>Naperville Forecast</h1>'''
38:
39: print forecast
40:
41: print '''</body>
42: </html>
43: '''
Lines 7 feeds one of Naperville’s zip codes to the PHP script that generates the NWS forecast page, and Line 8 gathers up the contents of that page. Beautiful Soup then parses the page on Line 11, and the detailed text forecast is pulled out of the page in Lines 14-16. From that point on, it’s just a matter of generating an HTML output page with the forecast embedded in it. Very simple after Beautiful Soup does the heavy lifting.
I’ve put the script in my cgi-bin directory, and I’ve added a link to it to my iPhone bookmarks. Maybe I’ll get around to creating an icon for the iPhone homepage later.
An obvious improvement to the script would be to generalize it to handle other cities. Fortunately, the NWS’s zipcity.php script can accept “City, State” input as well as zipcodes, which makes searching much easier with no additional work from me. Unfortunately, zipcity.php seems to call different servers depending on where the requested city is. If the requested city is in Arizona, California, Idaho, Montana, Nevada, New Mexico, Oregon, Utah, or Washington, the search is handled by the Western Region Headquarters server (wrh.noaa.gov), which seems to handle redirects and ambiguous city names in ways that are different (and, in my opinion, stupider) than the way the other regional servers work. I had to spend way more time learning about these differences and figuring out how to deal with them than I should have.
Here’s the HTML for simple web page that asks the user to enter a zipcode or city and state. I hope it’s clear from Line 22 that the city and state have to be separated by a comma and the state should be given as a two-letter abbreviation.
1: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html>
2: <html>
3: <head>
4: <title>Get Forecast</title>
5: <meta name="viewport" content="width = device-width" />
6: <style type="text/css">
7: body {
8: font-family: Sans-Serif;
9: font-size: large;
10: }
11: h1 {
12: font-size: x-large;
13: }
14: input {
15: font-size: large;
16: }
17: </style>
18: </head>
19: <body>
20: <h1>Get Forecast</h1>
21: <form method="get" action="http://www.leancrew.com/cgi-bin/nws.cgi">
22: Zip code or City, ST:
23: <input type="text" name="inputstring" value="" /><br />
24: <input type="submit">
25: </form>
26: </body>
27: </html>
I call this page forecast.html and have bookmarked this link to it on my iPhone. As you can see on Line 21, it calls a CGI script called nws.cgi:
1: #!/usr/bin/python
2:
3: from BeautifulSoup import BeautifulSoup
4: import urllib, os
5:
6: def followPage(origPage):
7: "Recursively follow JavaScript redirects to the final page."
8: pos = origPage.find('<script>document.location.replace')
9: if pos == -1:
10: return origPage
11: else:
12: start = origPage.find("('", pos) + 2
13: stop = origPage.find("')", start)
14: newurl = 'http://www.wrh.noaa.gov' + origPage[start:stop]
15: f = urllib.urlopen(newurl)
16: newPage = f.read()
17: f.close()
18: return followPage(newPage)
19:
20: # Start the script.
21:
22: # Where are we getting the forecast from?
23: place = urllib.unquote_plus(os.environ['QUERY_STRING'].split('=')[1])
24:
25: # Query the NWS site and get the page of results.
26: f=urllib.urlopen('http://forecast.weather.gov/zipcity.php', urllib.urlencode({'inputstring' : place}))
27: page = f.read()
28: f.close()
29:
30: # If the above URL returns a JavaScript redirect instead of a forecast page,
31: # follow the redirect.
32: page = followPage(page)
33:
34: # If the search string is ambiguous, choose one of the possiblities presented.
35: if page.find('More than one match was found') != -1:
36: soup = BeautifulSoup(page)
37: # We'll use the first link if we can't find a better fit.
38: link = soup.h3.nextSibling['href']
39: linkTags = soup.h3.parent.findAll('a', href=True)
40: # print linkTags
41: # Look for an exact match of the city name.
42: for tag in linkTags:
43: if tag.string.split(',')[0].lower() == place.split(',')[0].lower():
44: link = tag['href']
45: # print tag.string
46: newurl = 'http://www.wrh.noaa.gov' + link
47: f = urllib.urlopen(newurl)
48: page = f.read()
49: f.close()
50: # If it's a JavaScript redirect, follow the redirect to get the forecast.
51: page = followPage(page)
52:
53:
54: # Parse the HTML.
55: soup = BeautifulSoup(page)
56:
57: # Pluck the city and state from the "Point Forcast" line.
58: cityItem = repr(soup.find('td', {'align' : 'left', 'valign' : 'center'}))
59: start = cityItem.find('Point Forecast:</b>') + 19
60: stop = cityItem.find('<br', start)
61: city = cityItem[start:stop].strip()
62:
63: # Pluck the forecast from below the "Detailed 7-day Forecast" banner.
64: forecastItem = repr(soup.find('td', { 'width' : '50%', 'align' : 'left', 'valign' : 'top'}))
65: start = forecastItem.find('<b>')
66: forecast = forecastItem[start:-17]
67:
68: # Construct the page.
69: print "Content-type: text/html\n"
70:
71: print '''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html>
72: <html>
73: <head>
74: <title>%s Forecast</title>
75: <meta name="viewport" content="width = device-width" />
76: <style type="text/css">
77: body {
78: font-family: Sans-Serif;
79: font-size: large;
80: }
81: h1 {
82: font-size: x-large;
83: }
84: </style>
85: </head>
86: <body>
87: <h1>%s Forecast</h1>''' % (city, city)
88:
89: print forecast
90:
91: print '''</body>
92: </html>
93: '''
As you can see, it’s about twice as long as nws-naperville.cgi, and virtually all of the additional lines are the idiosyncrasies of the Western Region’s server. The followPage function on Lines 6-18 deals with the (sometimes repeated) JavaScript redirects, and Lines 35-51 deal with the stupid way that server handles ambiguous city names.
[Aside and rant: Here’s an example of the stupidity. If you ask for the forecast for “Phoenix, AZ,” you don’t get the forecast for Phoenix; you’re asked to be more specific. Do you want
- Phoenix, AZ;
- Phoenix Acres Trailer Park, AZ;
- Phoenix Mobile Home Park, AZ;
- Phoenix West Mobile Home Park, AZ;
- South Phoenix, AZ; or
- The Phoenix-Scottsdale Mobile Home Park, AZ
This result is, of course, a sad commentary on the Phoenix area, but it’s an even sadder commentary on whoever programmed the Western Region’s server. The other regional servers handle this ambiguity—which isn’t really an ambiguity, since you can’t get any more specific than “Phoenix,” but we’ll let that go—the right way. If you ask for “Chicago, IL,” for example, the Central Region’s server will give you (wait for it) the forecast for Chicago. Yes, it will give you the opportunity to change to Chicago Heights or Chicago Ridge, but only after it’s given you the forecast you asked for, which is almost certainly what you wanted. Really, if you wanted the forecast for South Phoenix, why the hell would you ask for Phoenix?]
I like having both bookmarks on my iPhone. Most of the time I’ll want the Naperville forecast, and it’s much more efficient to go there directly than through a query page. But when I’m out of town, or planning to go out of town, the query page is ready to give me a forecast for anywhere.
Feel free to adapt these scripts for your own use. If you live anywhere but in the Western Region, you should be able to customize the nws-naperville.cgi script by replacing the zipcode in Line 7. If you live in the Western Region and want a script that goes directly to your town’s forecast, you’ll have to customize nws.cgi by changing Line 23 to set place to either your zipcode or your city and state. For example, changing it to
23: place = '98039'
would be appropriate for Bill Gates.
Update
The Weather Underground has a great webapp for the iPhone at i.wund.com. It does everything my app does and more. I wrote a short description of it a few days after this post.
MIT courseware on the iPhone
April 13th, 2008
On Friday, I started downloading video lectures from MIT’s Open Courseware project, expecting to put them on my iPhone at the next sync. Today I learned that the videos were tagged as Movies instead of TV Shows, which made the iPhone syncing process a little goofy. So I wrote a quick little AppleScript to fix the tagging which I thought others might find useful. Unless you plan to download this exact course, you’ll have to make some changes to the AppleScript, but I think those changes will be obvious.
The course I chose to start with is Physics I, Classical Mechanics1. The MIT Open Courseware has a section in the iTunes Store, and that’s where I went to download the lectures. They came in their own little playlist and automatically generated an enclosing “MIT” folder in my iTunes sidebar.

Unfortunately, as I mentioned, the lecture videos were classified as Movies rather than TV Shows, so they had to be individually selected to be sync’d with my iPhone. I thought that a series of videos like this is more naturally thought of as a TV Show with many individual episodes. Recategorizing them as a TV Show would:
- Allow me to sync them with the iPhone with a single click.
- Automatically remove the lectures I had already watched (assuming my TV Show syncing is set to “all unwatched”).
I could, of course, recategorize all 36 videos by going to the Video tab of Info window and making the appropriate changes.
But I’d have to do it 37 times for this lecture series and then about as many times again for every other lecture series I download. Better to make up a quick script that will work for this course and can be easily edited to work for other courses.
1: set ep to 0
2: tell application "iTunes"
3: repeat with lecture in the selection
4: set video kind of lecture to TV show
5: set show of lecture to "MIT Physics I: Classical Mechanics"
6: set season number of lecture to 1999
7:
8: -- strip the "Lecture" prefix from the name
9: set epname to name of lecture
10: set colon to offset of ":" in epname
11: if colon > 0 then
12: set lastChar to length of epname
13: set epname to (characters (colon + 2) thru lastChar of epname) as text
14: end if
15:
16: set episode ID of lecture to epname
17: set episode number of lecture to ep
18: set ep to ep + 1
19: end repeat
20: end tell
I call the script “Convert Lectures,” and it’s saved in ~/Library/Scripts/Applications/iTunes, which is where FastScripts can find it and make it readily available to me when iTunes is the active application. (Since iTunes has its own AppleScript menu, I could have saved it in ~/Library/iTunes/Scripts, which is where iTunes looks for scripts. I’m just more in the FastScripts habit now.)
The script expects all the lecture videos to be downloaded, in order, and selected. It then goes through all the selected videos and makes the appropriate changes. Line 4 is the most important—it turns the video from a Movie to a TV Show. Line 5 gives the notional TV show a name, and Line 6 sets the season number—which for a real TV show would be 1 or 2 or whatever—to the year in which the lectured were recorded.
Lines 8-16 give each lecture a “episode” name. Each video in the lecture series comes with a name, and except for the introduction, each name is of the form “Lecture nn: Topics.” Lines 9-14 strip away the “Lecture nn: ” prefix—if it’s there—and Line 16 puts the result in the “episode ID” property of the track.
The “ep” variable keeps a running count of the lectures and puts it into the “episode number” property. The count starts at zero because first video, the course introduction, is only a few minutes long and isn’t considered a real lecture. Apparently, iTunes doesn’t consider zero to be a legitimate episode number, because after running the script, the episode number of the course introduction was still blank.
If you want to use this script as a template for your own script:
- You’ll need to change Line 5.
- You’ll need to change Line 6 unless your series was also recorded in 1999.
- You’ll need to change Lines 8-16 unless your track names follow the same format as mine.
- You’ll need to change the starting value of “ep” on Line 1 unless your series also starts with a short introduction.
Although the title of this post mentions the iPhone, you could, of course, transfer the lectures to any video-enabled iPod. It just so happens that the only experience I have with avideo-enabled iPod is with my iPhone. The 36 videos from this course take up about 3.6 GB, which may or may not a big deal depending on the capacity of your iPod and how much other stuff you carry on it. Right now, I have the entire course on my iPhone, but if I were short on space, I’d use the little checkboxes next to the tracks and the “Sync only checked songs and videos” option in the iPhone summary view to control how much of the course would get uploaded.
-
This is a topic with which I am quite familiar; I’m expecting to learn more about the usefulness of this form of education than I am about the topic itself. If I enjoy the form and see it as a nice way of learning, I’ll move on to topics I know less about. ↩
A CGI script with Emacs Lisp
April 10th, 2008
I fiddled with the code in this post to make a CGI script in Elisp (Emacs Lisp) that would take the date provided on web page’s form and spit out a new page with that date converted to several different calendars.
The HTML for the page with the input form is about as stripped-down as you can get:
1: <html>
2: <head>
3: <title>Convert a Date</title>
4: </head>
5: <body>
6: <h1>Convert a Date</h1>
7: <form method="get" action="cgi-bin/date-convert.cgi">
8: Month: <input type="text" name="month" value="6"><br />
9: Day: <input type="text" name="day" value="6"><br />
10: Year: <input type="text" name="year" value="1945"><br />
11: <input type="submit" Value="Convert">
12: </form>
13: </body>
14: </html>
The important things are
- to get the correct location of the CGI script in Line 7;
- to have the form submitted as a “get;” and
- to have the three input elements of the form in month, day, year order.
The CGI script, “date-convert.cgi,” looks like this:
1: #!/usr/bin/emacs --script
2:
3: (require 'calendar)
4: (setq query (getenv "QUERY_STRING"))
5:
6: (setq query-list (mapcar (lambda (x) (split-string x "=")) (split-string query "&")))
7: (setq my-date (mapcar (lambda (x) (string-to-number (cadr x))) query-list))
8:
9: (princ
10: (concat
11: "Content-type: text/html\n\n"
12: "<html>\n"
13: "<head>\n<title>Converted Date</title>\n</head>"
14: "<body>\n"
15: "<h1>Converted Date</h1>\n"
16: "<table>\n"
17: "<tr><td align=right>Gregorian:</td><td>" (calendar-date-string my-date)
18: "</td></tr>\n"
19: "<tr><td align=right>ISO:</td><td>" (calendar-iso-date-string my-date)
20: "</td></tr>\n"
21: "<tr><td align=right>Julian:</td><td>" (calendar-julian-date-string my-date)
22: "</td></tr>\n"
23: "<tr><td align=right>Hebrew:</td><td>" (calendar-hebrew-date-string my-date)
24: "</td></tr>\n"
25: "<tr><td align=right>Islamic:</td><td>" (calendar-islamic-date-string my-date)
26: "</td></tr>\n"
27: "<tr><td align=right>Chinese:</td><td>" (calendar-chinese-date-string my-date)
28: "</td></tr>\n"
29: "<tr><td align=right>Mayan:</td><td>" (calendar-mayan-date-string my-date)
30: "</td></tr>\n"
31: "</table>\n"
32: "</body>\n"
33: "</html>\n" ))
Most of this is the same as my previous date conversion script, but with a bunch of HTML tags added. The interesting stuff is in Lines 4-7. Because the form uses the “get” method to submit its data, the script gets called with a URL that looks like
http://localhost/~drang/Sites/cgi-bin/date-convert.cgi?month=6&day=6&year=1945
The stuff before the question mark is the address of the CGI script; mine happens to be in the cgi-bin folder in the Sites folder of my home folder on my Mac. Everything after the question mark is put in the environment variable named QUERY_STRING. Line 4 uses the Elisp “getenv” function to get that environment variable.
Line 6 splits the query string, first on the ampersands, then on the equals signs. The result is a list of lists that looks like this
(("month", "6") ("day", "6") ("year", 1945))
Line 7 plucks the second item (the “cadr” in Lisp terms) from each of the nested lists and converts it to a number. The result is a list like this
(6 6 1945)
which is the date form used by the various conversion functions in the Emacs calendar library. The items in the list are in the same order as the input elements in the form, which is why I said it was important to put them in month, day, year order. This list, in the “my-date” variable, is the argument to all the calendar functions in Lines 17-29.
The power of Lines 6 and 7 come from the use of the “mapcar” function, which applies a function to each item in a list, and the “lambda” construct, which allows anonymous functions to be created on the fly. Lispers will recognize that Lines 4-7 could have been reduced to a single statement, eliminating the need for the “query” and “query-list” variables.
The result is a nice little table with the converted dates. As I said above, I have these two files installed in the Apache “user directory” on my Mac and they work just fine. Unfortunately, the older version of Emacs on the leancrew.com web host doesn’t support the shebang line, so I couldn’t make a publicly-available version for anyone to run. If I find a way to work around that, I’ll post an update.
Train schedule for iPhone
March 27th, 2008
A commuter train schedule for my iPhone seemed like a good idea, so I made an iPhone-ready web page and uploaded it here. It’s highly specialized for my use: its for the Metra train that runs between my home town of Naperville and Chicago Union Station. Here’s a screenshot of it in a very narrow Safari window, a reasonable approximation of what it looks like on the iPhone.

As you might expect, Metra has a set of web pages with its schedule, but they include all the stations on the line (of course) and aren’t laid out for easy iPhone viewing. Look at the weekday inbound page:
The font is pretty lightweight (afternoon trains are shown in bold, which is more readable) and the choice of color gives little contrast. As you might guess from the monospace font, this isn’t an HTML table, it’s a table from the old typewriter days that’s been wrapped in <pre></pre> tags. Looking at this page on the iPhone requires a lot of zooming and scrolling.
What I wanted was a page that was quick to load, easy to navigate, and with text I could read without zooming. From the Metra pages, I extracted only the trains that stop at the Naperville station and only the times for Naperville and Union Station. I plugged these times into a table template and fiddled with the formatting until it looked OK.
Here’s the top of the HTML file:
1: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2: <html>
3: <head>
4: <title>Metra Schedule</title>
5: <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6: <meta name="viewport" content="width = device-width" />
7: <link href="rr-158.png" rel="apple-touch-icon" />
8: <link rel="stylesheet" type="text/css" media="all" href="metra.css" />
9: </head>
10: <body>
11: <h1>Metra Schedule</h1>
12: <h2>Monday–Friday</h2>
13:
14: <div>
15: <a name="eastbound"></a>
16: <div class="schedlink"><a href="#westbound">to Westbound</a></div>
17: <h3>Eastbound</h3>
18: </div>
19: <table>
20: <thead>
21: <tr>
22: <th>Naperville</th>
23: <th>Union Sta</th>
24: <th></th>
25: </tr>
26: </thead>
27: <tbody>
28: <tr class="am">
29: <td class="time">4:43</td>
30: <td class="time">5:32</td>
31: <td class="x"> </td>
32: </tr>
The <tr></tr> stanza in Lines 28-32 is repeated again and again but with different times and, for afternoon and evening trains, a class of “pm.” The eastbound table ends like this:
164: <tr class="pm">
165: <td class="time">11:33</td>
166: <td class="time">12:29</td>
167: <td class="x"> </td>
168: </tr>
169: </tbody>
170: </table>
After the eastbound table is a westbound table with the same layout. The third column in the tables is either blank or has an “x” in it to denote express trains that have few if any stops between the two stations.
But for two lines, this is pretty generic HTML. Line 6 sets the viewport width to the width of the iPhone. Without this line, the iPhone thinks it’s looking at a page that’s 980 pixels wide and will start in a zoomed-out view that’s almost as hard to read as the Metra pages. Line 6 has no effect on other browsers.
For one-touch access to the schedule, I’ve added this page to my home screen. Line 7 sets the icon to this clip-art image:

There are two schools of thought on the size of the “apple-touch-icon”: Apple official docs say to use a 57x57 pixel PNG image, but others say to use a higher-resolution image to get a better looking icon when the iPhone adjusts it for a 3D look. Since Apple’s own icon is bigger than 57x57, I decided to go with a bigger icon, too. I’m happy with the result.
The CSS file sets the font sizes, borders, alignments, and background colors. It’s nothing special. I use a yellow background for the morning trains and a light purple for the afternoon and evening trains.
There’s no programming here, so this is is certainly not a “webapp.” In fact, I put it on the internet only because the iPhone currently doesn’t support local HTML files. But it’s the kind of information that’s very handy to have in my pocket, and I don’t have to remember to carry a paper train schedule with me.
Update
I now have three pages with Metra schedules, weekdays (updated), Saturdays, and Sundays. The CSS file has also been updated.
Twittering
March 17th, 2008
Until a week ago, I had used Twitterrific for all my Twittering because everyone said it was the Twitter client. So, apart from signup and adjusting the list of people I’m following, I’d never spent any time with the Twitter web app. But I think those days are over. It’s not that Twitterrific isn’t a fine program—it is, and I understand why so many people sing its praises. But some of its design choices just don’t work for me.
First, Twitterrific has a black background displays the stream of tweets in either white text (for the selected tweet)or gray text (for all the other tweets). This is in keeping with Twitterrific’s “heads up display” aesthetic, but I don’t like reading white-on-black, and gray-on-black is a strain on my old eyes.
Second, Twitterrific doesn’t give me the opportunity to increase the font size to overcome the low-contrast problem. Yes, there’s a preference for using a larger font, but the larger font is barely big enough and it only applies to the list of tweets, not to the text entry box.
What to do? I could, of course, just dedicate a browser tab to Twitter, something I suspect a lot of Twitter users do. But I do like the idea of a dedicated client, and the Twitter stream doesn’t require a full browser window. So I used Fluid to create a site-specific browser (SSB) dedicated to Twitter and grabbed seyDoggy’s Twitter icon from the Fluid Flickr pool. Now I have a reasonably small window with a reasonably large font that I can keep tucked away on a secondary Spaces desktop. And because it’s a real browser, I can use it to do all the administrative things that can’t be done in Twitterrific.

I’ve been using this setup for about a week now and it’s worked out well. I’ve been pleased with how nice the Twitter web app is and how well it runs in the SSB. The SSB does tend to use more memory than Twitterrific; they start out at about the same level but the SSB’s share of memory increases at a faster rate. Not a dealbreaker.







