72,000 visitors in 2008

Well I didn’t make 100,000 visits as I hoped but I got close.  As of yesterday’s stats I had 98,517 visits from 71,701 visitors according to Google Analytics, plus today will be 300 or so.  Other stats: busiest day (4 March) 403 visits from 348 people, quietest day (28 December) 101 visits from a mere 88 people.

The average visit is 4 pages in about 4 minutes.  An ad gets clicked on 2% of the time it’s shown, so roughly 8% or one visitor in 12 clicks on an ad, which indicates they’re pretty well targeted.  Just under half the people (46%) leave in under 10 seconds, but some of these might very well be people who immediately clicked on the site they were looking for.  So I’m going to count some of them as satisfied customers (Google rather disparagingly calls this the ‘Bounce Rate’).  
You can see Internet Explorer’s market share slipping before your eyes: in the first three months of 2008 it was 55% IE, 33% Firefox, in the last three months it was 49% IE, 38% Firefox, while Safari and Opera had about 5% each, and Chrome, 2%.  And I’m pleased to see the Macintosh market share increase over the same period, from 7.1% to 8.9%.  Even a few iPhones in there, including mine.
Goals for 2009:
  1. 100,000 visitors
  2. 5 minutes and 5 pages per visit
  3. Less than 40% bounce rate
These are ambitious goals but I’ll try to make them happen.  I made a start this September when I redid the site design and colors.  I hadn’t changed anything since 2002 and it was showing – for the first time, I had fewer same-month visits, by 13%, than the previous year. 

After  making the changes, over the past month I have 6% more visitors than the same period last year.
Hopefully this indicates I’m on the right track.

Wider Pages, Immediately Filled

I’ve widened the page layout to 920 pixels, from the previous 728 pixels which is the width of a Google ad block (seemed as good a width as any to choose).  However some pages, particularly the ‘Programs’ page, were looking squashed.  I looked at a few high volume sites (New York Times, Facebook, EBay) and they seem to have settled on 1000 pixels as the default width – any less than that and you get horizontal scroll bars.  My website stats tell me that 98% of visitors have a horizontal screen resolution of greater than 1000 (though 20% are just over, at 1024 x 768, boy it’s been a while since I saw a screen that size).

So 920 is the new width, though using the awesome ‘Inspect Element’ tag in Google Chrome, I can see that some pages appear to spill over a little.

(In the course of this, I read how to simulate the missing ‘delayed capture’ feature in the otherwise great Vista ‘Snipping Tool’.  Open Snipping Tool, press Esc, compose your picture, press Ctrl-PrintScreen to freeze as-is, drag over the now-frozen screen to capture).

Now that I have additional width, I immediately filled half the new space with icons.  I’ve resisted using them to now, partly to get more information in per line, and partly because I think that a textual interface is more efficient.  But I have to admit that well-designed sites that use icons well, do look better (and now I’m half-way there, in that I use icons.  Now I just have to get a good design).  I used the beautiful ‘Silk’ icons from Mark James’s famfamfam.com, a lovely set of exactly 1,000 icons in 16×16 pixel PNG format.  They are, as he says, “strokably soft”.
Not every idea I wanted to convey had an exactly-matching icon, so I chose those I thought best fitting.  So for program interfaces now I have image.png for ‘GUI’, application_osx_terminal.png for ‘Command Line’, and application_edit.png for ‘API/Programming’.  Hopefully they get the idea across, and I added tool tips for clarification.  They are very pretty, but do take up some of my precious horizontal width.  
The other icons I use are arrow_right.png for ‘Link to site’, error_go.png for ‘Site link down’ (though it still will link through), add.png for ‘Add monitor’ (email notification), and error_add.png for ‘Unable to add monitor’.  The last is for websites where there is no revision number or release date listed, so I’m unable to provide update information.  This affects about 20% of the projects, though usually the less-active one. 
I like to get the program interface icons to line up nicely in columns (‘column up’?  Is there a verb meaning ‘align in columns’?).  I used to do this with an internal HTML table within the TD cell for each entry.  This is pretty wasteful and slow – the browser has to set up a table for each line, calculate its width, and so on.  So I made up composite icons of the eight permutations of the three individual icons each in their ‘on’ and ‘off’ states.  For this as with most of my graphical editing needs I use the amazing ImageMagick command line tools.  The ‘montage’ command allows you to combine multiple pictures into one, in a definable grid geometry, and furthermore can create blank spacing elements, and a transparent background.  Simple.
I want to add icons to denote the platform or operating system of each program, which would be very useful though using up still more width.  The problem has been finding the icons for Mac, Windows, and Linux in a suitable size (16×16).  You’d think the Web would be swimming in them, and you’d be wrong.  Probably due to copyright, none of the dozens of icon collections I squinted through had them.  The hunt continues.

Javascript and Apache

I’m testing out Walter Zorn’s very cool Javascript Tooltips for possible inclusion.  I want to use tooltips on the dropdown elements of the ‘Search’ page, since I am finally rationalizing the search categories and I’d like to be able to add some descriptive text to each element.

I run everything from Perl, so I made a little test program and included a call to his Javascript libraries:
print “<script type=’text/javascript’ src=’wz_tooltip.js’></script>n”;
No go, and I get error messages in the local httpd log files like this:
Permission denied: exec of ‘/Users/ahc/public_html/cgi-bin/wz_tooltip.js’ failed
Hmmm I think, that’s funny, I didn’t know Javascript files needed to be executable.  But it’s late and I’m not thinking too clearly, so I make the js file executable 755.  Now I get a different error message:
Exec format error: exec of ‘/Users/ahc/public_html/cgi-bin/wz_tooltip.js’ failed
OK someone’s trying to tell me something about how I shouldn’t be executing that file.  I resort to reading the manual.  Apache httpd Dynamic Content FAQ number 1, sentence number 1 begins:

“Apache recognizes all files in a directory named as a ScriptAlias  as being eligible for execution rather than processing as normal documents. This applies regardless of the file name…”

A dim light comes on.  httpd has been trying to execute the js file, because I told it to, with the ScriptAlias directive in my httpd.conf file, which says that everything in cgi-bin is a script:

   ScriptAlias /cgi-bin/ “/Users/ahc/public_html/cgi-bin/”

Dummy.  I moved the js file out of my cgi-bin directory and into the http document root, and changed the line to specify the file is in the root:
print “<script type=’text/javascript’ src=’/wz_tooltip.js’></script>n”;
Probably a well-known trap but at least I found it eventually.

Sorting dynamic studies in DICOM

I work with dynamic, or multi-frame, PET scans, so in essence 4-D datasets.  Nuclear medicine scans have frequently used file formats that are either proprietary or  used largely or wholly by NM scans.  These formats tend to support dynamic studies natively.  Newer PET scanners come with native DICOM capabilities, but older scanners and some research scanners use different file formats.  I’ve been trying to get everything into DICOM format, so it can all go into a PACS for storage and distribution.

One of the tricky steps is, getting your software to recognize the concept of a timeframe, or volume.  There are several DICOM header elements that could be used to group the slice files into frames, and different software interprets the headers differently.  I’m building DICOM headers from scratch for a scanner that creates ECAT format files, and this step is proving quite interesting, shall we say.

I started by looking at how a commercial system groups dynamic PET studies.  The example I’m using is a GE PET scanner which is acquiring 30 frames of 35 slices each, so it creates 1,050 DICOM files.  I notice that while Vinci shows the 30×35 arrangement, the Osirix viewer and DCM4CHEE PACS server both show the study as a single frame of 1050 files.

So how are they being grouped?  I have a small perl utility program I use to show differing fields between two DICOM files.  I rename the DICOM files when I receive them from the PACS, since we use a wide range of research software, very little of which is PACS- or even DICOM-aware.  So for much of our research needs, we just treat the DICOM files as image files, and number them from 1 to 1050.

First, I compare two adjacent slices from the same frame.

[widget dynamicpet] 115% dicomdiff.pl 0001.dcm 0002.dcm | grep -v UID
0008 0013  InstanceCreationTime: 122044.000               122045.000
0020 0013           ImageNumber: 1                        2
0020 0032  ImagePositionPatient: 128128                1281284.25
0020 1041         SliceLocation: 0.00000000000000         4.25000000000000
0028 1053          RescaleSlope: 0.20659                  0.106116
0054 1330            ImageIndex: 1                        2
Nothing unexpected here, the image number and index are sequential and the image position increments by the thickness of one slice.  I do note though that InstanceCreationTime is the time of reconstruction, so is not going to be useful to tell one frame from another.  RescaleSlope differs from slice to slice – with the very wide range of values in a PET scan, it’s customary to scale each slice separately.

Next, I look at the difference between the same slice in two consecutive frames: files 001 and 036 hold slice 1 from frames 1 and 2.

[Note: I’m numbering the frames and slices from 1.  My background is CS so all experience and intuition says I should count from 0, and some software does just this, but I find that counting from 1 confuses fewer people than counting from 0.]

[widget dynamicpet] 117% dicomdiff.pl 0001.dcm 0036.dcm | grep -v UID
0008 0013  InstanceCreationTime: 122044.000          122059.000
0008 0033             ImageTime: 110638.00           110419.00
0020 0013           ImageNumber: 1                   36
0028 1053          RescaleSlope: 0.20659             1.62858
0054 1300    FrameReferenceTime: 0                   15000
0054 1321           DecayFactor: 1.00427             1.01288
0054 1324        DeadTimeFactor: 1.61667             2.54626
0054 1330            ImageIndex: 1                   36

Well we have another time field differing, ImageTime, but it’s not related to the frame time because this scan started at 08:34.  ImageNumber and ImageIndex both are counting the file number rather than slice number.  The key field that differs here is (0054,1300) FrameReferenceTime, which is listing the frame start offset in milliseconds.  This field is part of the NM multi-frame module so is present only in NM scans.

Microsoft IIS 7

IIS 7 is Microsoft’s latest web server, if such a complex and comprehensive package can be reduced to two words.  I’ve been working with IIS for the first time, and it sure as heck isn’t Apache.

The ClearCanvas PACS installation has been about getting three items installed and communicating – ClearCanvas itself, IIS, and SQL Server. I found a very good resource for debugging IIS 7 errors: Mark Volodarsky’s Troubleshoot IIS Errors Like A Pro.    

I followed his instructions for turning on Failed Request Tracing Rules.  This does largely what it says, building a log file of failed requests.  This in turn led me to another error 500.19 Internal Server Error: “This configuration section cannot be used at this path” in my web.config file located within the ClearCanvas Server web directory.

Pressing on…I’m out of my depth here.  I followed these instructions regarding editing my applicationHost.config file located in Windowssystem32inetsrv.  Hey I’ve come this far, why not start messing with system files I don’t understand.  I turned on overrideModeDefault to “allow” for several modules.  While I was at it, I went back to the IIS settings in the Windows Features, and checked pretty much every feature I could find for IIS.  I’m going to need about a terabyte of RAM to run all this.

I’m not sure if I’ve made progress.  Now when I try to access the local CC Server web site I get a Server Error stating, amongst other things, “WRN: Assembly binding logging is turned OFF.  To enable assembly bind failure logging, set the registry value [HKLMSoftwareMicrosoftFusion!EnableLog] (DWORD) to 1.”.   Into the Registry Editor I go, what the heck.  I discover there is no key called Fusion!EnableLog…and I’m out of ideas.  I’m not going to start hacking the registry blind.


Installing ClearCanvas Server

I’ve written previously about installing Microsoft SQL Server 2008 Express to support ClearCanvas Server.  Once I got it running, I moved on to the server itself.  I’m making things a little hard for myself by running Vista, with IIS 7 and SQL Server 2008, instead of the example configuration of XP with IIS 6 and SQL Server 2005.

The first hurdle was getting SQL Server authentication, which is a step in the ClearCanvas Server installation program.  There are a few permutations of username and database server formats that I experimented with until I got a connection to succeed.  The CC installer just gave a ‘pass/fail’ message, but I checked in the SQL server log for a descriptive messages.  My problem was that I’d been using an SQL login that I’d created, rather than the ‘SA’ login that the CC Server installation process had created.  Note that you do have to go in and activate the SA account, it’s disabled by default. The installation process does not halt if a successful connection is established, but to the wrong account. So I got pretty good at uninstalling and reinstalling, going by either the install log file or the lack of a database named ‘ImageServer’ in SQL Server.

Most of my other knowledge was gained by not reading the manual, and trying to install the packages myself. So the best advice I can give is, follow the instructions closely, particularly the section about SQL Server Express.

The first thing I did wrong was to have SQL Server set up to use Windows authentication only.  The CC Server install guide clearly states I have to have SQL Server authentication mode enabled, whereas I, not having RTM, had the default which was to have Windows authentication mode only.  This is a setup option in SQL Server but thankfully can also be changed in the SQL Server Management Studio under ‘Security’ in the server properties.  This is something else I learned from belatedly reading the CC install manual.
Some more of learning from my mistakes.  I initially tried a Username of the form ‘andypc2ahc’ which is the domain-style name format (I’m running Vista, which uses Windows Server-style accounts).  I created this account in SQL Server Management Console and the domain (machine name) is prefixed upon clicking ‘Check Names’.  Perhaps this is from the habit of using Windows domain-style logins.  It caused a problem here though, for one thing the backslash was being escaped Unix-style and I was getting SQL error messages about an account ‘andypc2ahc’ not being found.  I tried the Unix solution of ‘andypc2ahc’ but didn’t hold much hope of success, and I wasn’t (or was) disappointed.  Finally I created an account under SQL Server with just my login ‘ahc’ and no machine/domain name prefix.  This is what led me to believe I had an SQL connection, but in fact it was to the wrong account (not SA).

Here’s what the CC Server install log file showed when I was not using the SA account:

> ... etc etc
> GO
Error executing commands:
CREATE DATABASE permission denied in database 'master'.
[SqlException Number 262, Class 14, State 1, Line 2]

The other sticking point (also my mistake) was the ‘DB Server’ field.  The SQL Server Management Console refers to the server entity as ‘andypc2SQLEXPRESS’ and this was ultimately worked.  Entering either ‘SQLEXPRESS’ or ‘’ lead to no messages in the error log, meaning that the database was not found at all.  This was because I’d failed to follow instructions and allow TCP connections to SQL Server Express…

Once I had that sorted, things were easier.  I accepted the default settings for the database name (‘ImageServer’) and the CC Server Filesystem.  This defaulted to C:FS, and I don’t like to fill up my C: drive top level with directories, but during the first install of programs I tend to accept the defaults until I understand the program.  For the same reason, I accepted SQL Server installing its database files under ‘C:Program FilesMicrosoft SQL Server’ whereas I’d prefer these potentially very large directories to be somewhere other than my C drive and certainly not mixed in with my program files.  In my next install, I’ll move both these directories off to another drive and in a directory I know to contain only data.

Next, I changed the Partition AE Title to ‘CCSERVER’, and the port number from the default of 104 to an arbitrary port of 11100.  I test and run multiple PACS servers, and I can’t have them fighting over port 104 (currently I have K-PACS using that port).  On this machine I’m also running the DCM4CHEE PACS server which defaults to port 11112 (the ‘second-choice’ PACS port after 104), so I chose something close to it. This keeps them grouped in the list of exempted ports in Windows Firewall.

Clicking ‘Next’ and off we go.  These Microsoft installs are so complex that I just hope they work – there is no source-code hacking or library-linking here. 

I did have to make one change, due to running IIS 7 (I have Vista SP1 installed).  The installation stopped with the error message “Error 424 trying to create IIS virtual directory”.  I eventually found from this site that I had to enable “IIS 6 Management Compatibility” under “Windows Features” in the Programs and Features control panel.  This message came right at the end so I had to uninstall CC Server, enable the IIS feature, and reinstall.  This process doesn’t take very long.

Perl Varargs

I’ve often wanted to have more than one optional argument to a Perl subroutine.  For instance I have a utility function printHash() to which I pass a pointer to a hash (associative array).  It prints the hash contents in a formatted box, with an optional description at the top.

my %hash = (‘one’ => 1,
            ‘two’ => 2,
            ‘three’ => 3);
printHash(%hash, “My Comment”);

sub printHash {
  my ($hashptr, $comment) = @_;
  # … Test for existence of comment, and use it if necessary.
  # … Then print the hash in a tidy format.
  # … Default is to print the keys in alphabetical order.
[andy tmp] 116% ./printhash.pl
  | My Comment |
  | one   : 1  |
  | three : 3  |
  | two   : 2  |
Which is all very nice.  Frequently, though, the hash has a lot of elements – for example, all the header elements of an image file.  I don’t always want all 300 or so lines of a DICOM header.  Also, I’d like to be able to control the order of the elements.  I changed the function so  I can pass to the function an array of the keys I want passed, and the order I want them in. I use this array (if it is passed) as the hash keys, instead of a sorted list of all hash keys.
However, I already have one optional argument to the function (the comment).  Just as in C, you can’t put anything after an optional argument because of course you don’t know if it will be there or not.
For years I’ve been too lazy to do anything about this and I had either an optional comment, or an optional key-ordering array, but not both.  Recently I did the obvious thing and changed to passing a hash of variable arguments.  This way I can have as many varargs as I like.
my @keys = (qw(three one));
my %opts = (
   ‘keyptr’  => @keys,
            ‘comment’ => ‘My Other Hash Comment’,
printHash(%hash, %opts);

  | My Other Hash Comment |
  | three : 3             |
  | one   : 1             |
This does what I want.  But now what to do with the dozens of times I’ve written old-style printHash(%hash, “comment”) calls into my code?  Well I could be thorough, and edit every function call so that I pass a hash
every time.  But t
hat is Actual Work, and anyway it’s tedious to create a single-element hash whenever I just want to dump a full hash with a comment.  Usually, this is an image file header and the file name as a comment.  So I changed printHash() to overload the second argument: if it’s a hash, use it as a hash, if it’s a scalar, use it as a comment.  This is questionable programming practice but oh well.
my %opts = (
    ‘keyptr’  => @keys,
            ‘comment’ => ‘My Other Hash Comment’,
printHash(%hash, %opts);

sub printHash {
  my ($hashptr, $opts) = @_;

  my ($comment, $keyptr) = (”, ”);
  if (ref($opts) eq ‘HASH’) {
     ($comment, $keyptr) = @{$opts}{(qw(comment keyptr))};
  } else {

    $comment = $opts;
  my @keys = (ref($keyptr)) ? @$keyptr : sort keys %hash;

  # … Now proceed to use $comment and @keys.
  # … $comment will be my comment, or blank, @keys will be my keys, or all keys.
I was gratified to see that Damian Conway in ‘Perl Best Practices’ espouses the use of a hash of named arguments for nay subroutine that has more than three parameters.  I use a similar idea but for optional arguments.

Microsoft SQL Server 2008 Express

This program takes even longer to install than to say.  However it’s free so I will use it.  It’s a huge and complex program, as far as I can tell it’s a full SQL Server application but limited to 1 GB RAM and 1 CPU.

I’m installing it to support ClearCanvas PACS Server, which I’d like to run and test.  Microsoft products are not really my field so I’m getting used to their way of doing things.  The SS2K8E install also kicks off a number of other large products, particularly .net 3.5 sp1.  A few times the install didn’t run through and I wasn’t given a clear reason why, the only recourse at that point was to go back, check the prereqs, and in one instance uninstall and reinstall.
There was one real catch.  There is a graphical management tool, SQL Server Management Studio Express, with a link from the SS2K8E page. The first issue I ran in to was, how to get it.  The SQL Server 2008 Express download page gives you the choice of three versions: “SS2K8E with Tools”, “SS2K8E with Advanced Services”, and “SS2K8E Runtime Only”.  My first installation, I accidentally downloaded and installed ‘Runtime Only’, which took a while.  I then discovered that it did not include the Management Studio, and I could not find a way to download it separately.  So I uninstalled it, and downloaded and installed the ‘With Tools’ version.  This came with the management console…so far so good.  I am used to MySQL, and phpMyAdmin, and this database is a step up in complexity.