The Daily Parker

Politics, Weather, Photography, and the Dog

More on the Kindle

Usability expert Jakob Nielsen takes a look at Kindle 2 usability in his column today:

[T]he device is best for reading long linear material, such as novels and some non-fiction. Kindle's best user interface feature is turning the page; the reading experience you design should require no other interactions.

Writing linear books simply requires a skill that all good authors already possess: the ability to keep readers immersed in the plot.

Kindle also works well for the long, narrative articles common in certain literary magazines and Sunday newspaper supplements. No surprise that The New Yorker is currently the best-selling magazine for the device.

... [But] it's awkward to interact with the device through its 5-way controller. Also, after every selection, you're doomed to wait for a sluggish response. And, once you finally get something, you get very little because of the small screen. Setting aside the header and footer areas, Kindle 2's content area is 525x650 pixels, or 341 kilo-pixels. In contrast, a mid-sized PC screen is 1280x1024, offering 999 KP of content, or the equivalent of three Kindles.

Given these constraints, navigating non-linear content on Kindle feels much like navigating websites on a mobile phone. Kindle content designers should therefore follow mobile usability guidelines for many user interface issues, including the presentation of article pages.

Two on technology

The first, from the Poynter Institute, concerns how Michigan Governor Jennifer Granholm's staff made Twitter into journalism:

I tuned in an Internet broadcast of ... Granholm's annual state of the state speech because it was expected to be laden with energy and environment issues. On impulse I logged into Twitter and asked my followers if there had been a hashtag established for the speech. There was: MiSOTS (Mich. State of the State).

To my amazement, the hashtag had been established by the governor's staff—who were tweeting major points of Granholm's speech as she made them.

Meanwhile, many, many, many other people used this hashtag to challenge points, support points, do some partisan sniping, question assumptions, add perspective, speculate about what was going on, and provide links to supporting information—including a transcript of the speech and the opposite (Republican) party's response.

(Emphasis in original.)

The second, Mark Morford musing about technology in general:

To paraphrase a renowned philosopher, we just keep making the pie higher. This is the nature of us. It is, in turns, both wonderful and terrifying.

It seems there are only two real options, two end results of our civilization's grand experiment. Either the stack becomes so high -- with our sense of wonder and integrity rising right along with it -- that it finally lifts us off the ground and transports us to some new realm of understanding and evolution, or it ultimately topples over, crashes and mauls everything that came before, because we just didn't care enough to stop and smell the astonishment.

You have but to remember: How many ancient, advanced civilizations have collapsed under the weight of their own unchecked growth, their own technological advances, their own inability to stay nimble and attuned to the crushing marvel of it all? Answer: all of them.

Both are worth reading in full.

Annoying software design (professional edition)

Developers generally don't like third-party UI controls because they're generally frustrating to use. Case in point: in the Infragistics Windows Forms controls package, the UltraGridColumn has sucked a substantial portion of my day away.

If you don't write software, you still appreciate it when it works simply and intuitively. You want to search for something, you go to Google and type in a search term. Brilliant. When you go to some company's website because you want to call the company, you look for something called "contact us" and click it. If you don't get the address and phone number of the company after clicking that link, you get irritated: the simple, intuitive thing didn't work. Jakob Nielsen is all over that stuff.

So. I have a simple problem, which is how to make a column in a grid grey out so my users don't inadvertently edit something they shouldn't. What I expect to write is something like this—or I would, if the member existed:

theReadOnlyColumn.Enabled = false;

Sadly, there is no "Enabled" member. So how about using a member that actually does exist?

theReadOnlyColumn.IsReadOnly = true;

Interesting. That member doesn't allow you to change its value. In fairness, the particle "Is" suggested it was a read-only member (ironic, that), but still, it looked like the right thing to do.

But no, here's the intuitive, simple, gosh-how-didn't-I-see-that-right-away thing to do:

theReadOnlyColumn.CellActivation = Activation.Disabled;

<rant>

This sort of thing happens when developers create software based on how it works, rather than what it does. It's sloppy, it reflects an inability to think like the person using the product, and it's compounded by a criminal lack of clear "how-to" documentation. (The Infragistics documentation site appears to have no way to search for concepts, requiring you to figure out how Infragistics developers organize things on your own.) This really, really annoys me, and is why I avoid using their products.

</rant>

Unbelievable cold in Alaska

The weather has cooled off a bit in the interior of Alaska:

Friday marked day six of the worst cold snap to hit Fairbanks in several years and there is no relief in sight for residents who live in Alaska’s second-largest city — or the business owners they call to bail them out when their cars, pipes and septic tanks freeze.

The temperature in North Pole dipped to 55 degrees below zero on Wednesday night, the lowest temperature recorded in the greater Fairbanks area during what has been six days of severe cold. It was “only” 46 below at 4 p.m. Friday in North Pole, but the temperature was “dropping by the hour,” meteorologist Austin Cross at the National Weather Service in Fairbanks said.

Friday marked the fifth day in the last six the temperature at Fairbanks International Airport hit 40 below or colder; it was only 38 below at the airport on New Year’s Day.

Forecasters expect temperatures in Fairbanks this weekend will likely touch 50 below and there is no indication the cold wave will dissipate anytime soon.

Since I don't read Alaskan newspapers often, and I'm used to seeing cold Alaskan temperatures on the Weather Now extremes page, I actually first heard this when ten people emailed me to complain about a bug in Weather Now. It turns out, the news story above linked to Weather Now and drove 2,400 unique visitors to the site in six hours.

I should know better. Fortunately my servers easily handle 10,000 page views per hour, but still, seeing a traffic spike like that caught me a little off-guard.

Turning Japanese

I've given up for the evening attempting to configure a recalcitrant firewall. To cheer up, I turn to Paul Krugman, who reminds us that a 0% Fed rate means we're in trouble:

ZIRP!

That's zero interest rate policy. And it has arrived. America has turned Japanese.

This is the thing I’ve been afraid of ever since I realized that Japan really was in the dreaded, possibly mythical liquidity trap. ...

Seriously, we are in very deep trouble. Getting out of this will require a lot of creativity, and maybe some luck too.

At least I think I've figured out where I mis-configured the firewall....

LINQ to FogBugz fun

Most Daily Parker readers can skip this (long) post about software. But if you're interested in C# 3.0, LINQ, or FogBugz, read on.

I use FogBugz's time tracking tool to provide tracability in my billing. If I bill a client 2.75 hours for work on a bug, I want the client to see the exact times and dates I worked on the bug along with all the other details. And because I track non-billable time as well, and I often work in coffee shops or places like the Duke of Perth, I wind up with lots of tiny time intervals that I have to aggregate to produce a bill.

My time sheet today, for example, looks like this:

Start End Case Title
7:11 AM 7:23 AM 901 Walking the dog (November)
8:18 AM 9:32 AM 950 FogBugz to LINQ project
9:32 AM Stop Work 902 Blogging (November)

But what I need for QuickBooks looks like this:

Case Hours
901: Walking the dog (November) 0.20
950: FogBugz to LINQ project 1.23
902: Blogging (November)  

(The last bit has no time because I'm still working on it.)

This is the perfect kind of thing to waste a few hours on while learning some new programming tricks. (Code here.)

First: the entity

To use LINQ to SQL in its pure form, you first have to create entity classes that pretty much exactly mirror the tables you're interested in. My target, the FogBugz timeintervals table, yields an entity that looks like this:

[Table(Name="timeinterval")]
class TimeInterval
{
	[Column(Name="ixInterval",IsPrimaryKey=true)]
	public int Identity;

	[Column(Name = "ixPerson")]
	public int PersonId;

	[Column(Name = "ixBug")]
	public int CaseId;

	[Column(Name = "dtStart")]
	public DateTime StartDate;

	[Column(Name = "dtEnd")]
	public DateTime EndDate;
}

Because I'm interested in the aggregate time spent on each case, I also created a simple structure to hold that info:

struct AggregateInterval
{
	public int CaseId;
	public double TotalHours;
}

Second: the console app

To use LINQ to SQL, you need to include a reference to the System.Data.Linq assembly, and import the appropriate namespaces:

#region Copyright ©2008 Inner Drive Technology

using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Linq;

#endregion

namespace InnerDrive.Research.FogBugz
{
	class Program
	{
		static void Main(string[] args)
		{

Next, set up the data context:

DataContext context = new DataContext(" (your connection string) ");
context.ObjectTrackingEnabled = false;
Table<TimeInterval> table = context.GetTable<TimeInterval>();

(I turned off object tracking because this is a read-only application. Setting ObjectTrackingEnabled to false improves performance, but the data context will throw an exception if you call DataContext.SubmitChanges().)

I actually need two queries, one to get the table rows and another to aggregate them. The reason for this is that my aggregation depends on getting the total hours each interval represents; LINQ to SQL won't do that. Here's the first query:

// FogBugz stores time as UTC; I want the time for today in Chicago, not London
DateTime startDate = DateTime.Today.ToUniversalTime();

var intervals =
	from interval in table
	where interval.EndDate <= startDate.AddDays(1) &
		interval.StartDate >= startDate
	group interval by interval.CaseId
	into grouping
	select grouping;

The second query does the aggregation, transforming the first query into an IEnumerable<T> and returning AggregateInterval structs:

IEnumerable<AggregateInterval> aggregation =
	from grouping in intervals.AsEnumerable()
	select new AggregateInterval
	{
		CaseId = grouping.First().CaseId,
		TotalHours = grouping.Sum(t => t.EndDate.Subtract(t.StartDate).TotalHours)
	};

Neither query has fired yet, by the way. That's another cool thing about LINQ. Both queries fire when I output the data, which is trivially simple:

foreach(var item in aggregation)
{
	Console.WriteLine(string.Format("Case {0} = {1:0.00}", item.CaseId, item.TotalHours));
}
Console.ReadLine();

That's it. Share and enjoy.

Down the memory hole

Every so often, one must wipe and reinstall his main computer. This is not fun. Even Parker finds it boring, and he sleeps all day.

Still, my main box (a Dell D620) now runs so much faster it's making me cry. So, several hours of boring work will save me several dozen hours waiting for the damn computer.

Heading home

Ah, family. I'm glad I got a chance to unwind with the Ps after my conference. But I do miss my dog.

Tomorrow: or, rather, tonight after 7pm CDT: check out Weather Now for, well, something appropriate to the season.