While it’s possible to argue that Mitch Podolak’s greatest invention is the Winnipeg Folk Festival, an equally compelling argument can be made that it’s actually Home Routes, the Canadian “house concert circuit” that he co-founded.

Here in Charlottetown one of the stops on the Home Routes trail is the Elmwood Heritage Inn on North River Road. By chance I ended up on the mailing list for their series when my old friend Eve played there two years ago and I’ve had the pleasure of returning to Elmwood 3 or 4 times since. It is a wonderful tiny venue for music, and Carol and Jay are warm and generous hosts. It makes seeing music in larger venues seem cold and barbaric by comparison.

Yesterday was the opening of the 4th season of Home Routes concerts at Elmwood and it was a wonderful afternoon: Red Moon Road from Winnipeg, fresh off the ferry from Cape Breton, set themselves up in the living room and played to an enthusiastic crowd of about 15 people from the neighbourhood.

They had me from the opening line of their tune Come Home:

Little Wilhelmina was abandoned,
in circumstances imagined to be tragic,
maybe magic,
was involved,
Like her parents met an evil witch,
who took the baby and made a switch,
for cabbage,
and so managed to escape.

The song is, despite the wit of the opening, a rather tragic one. But so well told, and with such musicianship:

It’s difficult to convey in words how much joy effuses from this trio; they may be the happiest band I’ve ever seen play; they genuinely love playing and it shows. Each is an accomplished musician and storyteller and each brings something to the mix; they are a trio in the truest sense of the notion, and to watch them interact is a thrill.

Red Moon Road at Elmwood

You’ll have two more chances to see Red Moon Road on Prince Edward Island this fall: they play a Home Routes concert in Summerside on September 24 (email tim@homeroutes.ca for details) and then the Trailside in Mount Stewart on October 3rd.

You really should make an effort to see them.

I’ve extended my code that interacts with StudentsAchieve to allow the harvesting of attendance data for your child. There are now methods in the class to support grabbing attendance for a given date or range of dates and I’ve built a sample script, get-attendance-ical.php, that will grab attendance data for a range of dates and output an iCalendar file suitable for import into an application like iCal or Google Calendar.

Fortunately for me (but alas not for him), [[Oliver]]’s back-to-school cold caught up with him on Thursday, so he was absent, giving me useful data to test my regular expressions for parsing status data out of the StudentsAchieve HTML. So here’s the resulting calendar for him, loaded into iCal, showing attendance (taken twice a day) for the last week:

When I spoke about Applications vs. Capabilities back in 2009 at the Access Conference, it was this kind of thing that I was talking about: I want the entities in my life to participate in an open data ecosystem, not create “destination portals.” Don’t make me login to proprietary systems: instead, allow me to exchange data with you, using open standards, so that I can integrate you into whatever collection of tools I use to support my own particular information management needs.

In other words, build capabilities, not applications.

I’ve been hearing about the StudentsAchieve system used by Prince Edward Island schools since I first started working with the PEI Home and School Federation. In elementary grades StudentsAchieve is only used for tracking attendance, and there’s no provision for parent access to the system, so although I’ve been actively engaged in advocating for improved access to and use of StudentsAchieve it wasn’t until today that I actually got to use it myself.

Oliver’s in intermediate school now, and starting at grade 7 the system is used to record marks and share information about homework in additional to tracking attendance, and there’s provision for online access by parents through a web system. We got our username and password for Birchwood’s StudentAchieve system this afternoon, and so, at long last, I got to take it out for a ride.

The system was first implemented on PEI in 2006, and its design and usability make it clear that it has its roots in an era in web history not particularly concerned with the niceties of either. It works, yes, but its ugly and confusing and makes me feel sad.

Fortunately all is not lost: the web is open, and web systems, by their very nature, are open to reinterpretation through code. The data, in other words, is there: if I want it to be beautiful, I can do so myself.

By way of starting this process, I’ve started to code a PHP class for automated interaction with StudentsAchieve. All this code does is to mimic the same actions that a parent takes using a web browser – logging in, clicking on things, etc. – it just does it all automatically and takes the information it finds and makes it available in “machine readable” form that can then be reimagined.

The code is primitive right now: all it does is “login” to StudentsAchieve using your parent username and password and retrieve a list of classes, teachers and email addresses (and even then it only works, as far as I know, for households with a single student).

But the heavy lifting of getting a script to login to StudentsAchieve is done, and as a sort of “proof of concept” I used the code to create a PHP script to create a vCard file of all of Oliver’s teachers. Using the script I can do this:

php get-teacher-vcard.php username password

(substituting my actual parent username and password) and then this happens:

Logging in to StudentsAchieve...
Making a vCard file...
Saved a vCard file called 'BirchwoodIntermediateSchool.vcf'.
Load this into your address book.

And, sure enough, when I open that BirchwoodIntermediateSchool.vcf file in the Contacts app on my Mac and then search for “Teacher”, I see all of Oliver’s teachers:

Contacts App Screen Shot

Given the hullabaloo last year about unauthorized student access to StudentsAchieve it’s important to note that this code doesn’t take advantage of any capability of StudentsAchieve not exposed to the web: you still have to be an authorized parent with a username and password to use it, and you only have access to the same information about your child that you have in an interactive web browser.

I am deeply, passionately, monogamously, long-term lovingly committed to The New Yorker magazine. It’s the only magazine I subscribe to, a subscription that, against my character, I simply set to auto-renew in perpetuity.

I adore absolutely everything about the magazine (except the poetry and, with some rare exceptions, the fiction). If you told me I’d be sent to a desert island but could take a stack of New Yorkers with me (and, probably, some food), I’d be happy. Lonely, but happy.

So when The New Yorker redesigns parts of itself it’s a big deal in my life. And thus when I watched this video announcing some of the changes I held my breath and felt both giddy anticipation and a sense of foreboding: what if they get it wrong, I thought.

It’s not that the magazine need never change (although it’s glacial pace of change is one of its most endearing qualities), and I’ve enjoyed some of the small unannounced design touches that have been introduced in recent years. But. I. Still. Hold. My. Breath.

The September 23, 2013 issue, where the “Goings On About Town” changes will be introduced, won’t arrive in Charlottetown until sometime next week (the magazine’s cover date is the date it goes off sale, and it’s a rare week that I receive an issue before that date). I took a peek online to see what’s in store (something I almost never do: The New Yorker is about print for me, and reading it online makes it seem soulless). I don’t dislike it.

The New Yorker Goings On About Down Redesign

My friend Claude has a much better camera than the abysmal one on my phone; he was visiting my office on auction business this morning and generously snapped a photo of the completed Youngfolk & The Kettle Black “Cameron Block Blend” coffee bag in all its two-colour splendour.

Cameron Block Blend

One of the nice things about this photo is that it lets you see the physical structure of the bag that influenced some of the design decisions: the creases that run horizontally, for example, forced me to print things closer to the “top” than I would have otherwise (although I see now, looking at this again, that the design would have benefitted from 2 or 3 additional points of leading between “Cameron Block Blend” and the body text, leading that wouldn’t have caused conflict with the creases).

Here’s the red, printed as a second run after the black had a chance to dry for a while. I really gotta get myself a better camera. Available filled with coffee as soon as they can figure out what “Cameron Block Blend” is.

Untitled

Oliver and I were sitting out on a bench on Victoria Row one day this summer when he noticed “Cameron Block” at the crest of the block of buildings that include Youngfolk & The Kettle Black. Which got me thinking: who was Cameron?

Cameron, it turns out, was Ewen Cameron. Among other things he was Speaker of the House of Assembly, insurance agent, realtor, shipbuilder, and teacher. He died an untimely death, at the age of 43, in 1831, drowning off what is now Victoria Park. During his legislative career Cameron was active in the consideration of bills to extend the vote to Roman Catholics and to end slavery.

The Cameron Block wasn’t completed until 54 years after Ewen Cameron’s death – it was developed by his son-in-law Horace Haszard and designed by William Critchlow Harris – but it sits on the site where the family had long roots.

To help memorialize Ewen Cameron – in an email to me, Boyde Beck called him “one of the most most tantalizing ‘might-have-beens’ in that era of politics – and the building that came after him, I’m printing up coffee bags for a “Cameron Block Blend.”  They’ll be two colour; what you see below is the black, which contains a (very) short biography of Cameron (I hope it’s enough to prompt the coffee drinker to dig deeper). Red to follow this afternoon.

Cameron Block Blend

I want to build systems around the information that’s stored about me and the materials I’ve borrowed in the Provincial Library Service’s online system, but the SirsiDynix used for this doesn’t have any hooks to allow me to do this, so I’ll have to build my own.

By way of doing this, I need to understand to “login” to my library account in SirsiDynix from a script. Here’s what I’ve learned.

How SirsiDynix Authentication Works

The link SirsiDynix system from the front page of the Provincial Library Service’s website goes to:

http://library.pe.ca/catalogue-en

This URI is a 302 redirect to:

http://24.224.240.218/

which itself, in turn, gets 302 redirected to:

http://24.224.240.218/uhtbin/cgisirsi.exe/x/x/0/49/

This is the last URI in the journey that doesn’t contain parameter related to my “session”: this URI sends my browser a session_security cookie with a value of 259310010 and then 302 redirects me to a URI that has this value embedded in the ps parameter:

http://24.224.240.218/uhtbin/cgisirsi.exe/?ps=3SoTIEJTW9/PLS/259310010/38/1/X

which sends me a session_number cookie with the same value of 259310010 and then 302 redirects me to:

http://24.224.240.218/uhtbin/cgisirsi.exe/?ps=wyIB2k9pHm/PLS/259310010/60/1180/X

Entering my library card barcode and PIN in the header and clicking “Login” does an HTTP POST to:

http://24.224.240.218/uhtbin/cgisirsi.exe/?ps=8N5QS6vOI2/PLS/259310010/303

sending the card number as the user_id parameter and the PIN as the password parameter.

The response to this POST sets cookies of the same name (user_id and password) and updates the session_security and session_number cookies with a new value.

In the above URIs, the initial part of the ps parameter – 8N5QS6vOI2 for example – appears to be random and meaningless and I found that it could be changed or the same value used all the time without ill effect.

Automating SirsiDynix Authentication

With the above reverse-engineering, we have enough to automate the login process using PHP and cURL.

First, let’s set some variables that we’ll use later:

$user_id = "XXXXXXXXXXXXX"; // replace with your card number
$pin = "YYYY"; // replace with your PIN
$baseurl = "http://24.224.240.218"; // root URI of SirsiDynix server

The first task is to start a SirsiDynix session, and to grab the value of the session_security cookie that we’re sent back; we do this with cURL:

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $baseurl . "/uhtbin/cgisirsi.exe/x/x/0/49/");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_COOKIESESSION, true);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_COOKIEFILE, "/tmp/sirsidynix-cookies.txt");
curl_setopt($ch, CURLOPT_COOKIEJAR, "/tmp/sirsidynix-cookies.txt");
$result = curl_exec($ch);

preg_match('/^Set-Cookie:\s*([^;]*)/mi', $result, $m);
parse_str($m[1], $cookies);
$session_security_cookie = $cookies['session_security'];

The key ingredients here are the setting of the CURLOPT_HEADER open to true (so that we can parse the cookie out of the headers), and setting the CURLOPT_COOKIEFILE and CURLOPT_COOKIEJAR options so that subsequent requests will pass back the same cookies.

With the value of $session_security_cookie set, we can now login by doing an HTTP POST:

$loginurl = $baseurl . "/uhtbin/cgisirsi.exe/?ps=8N5QS6vOI2/PLS/" . $session_security_cookie . "/303";
$ch = curl_init(); 
curl_setopt($ch, CURLOPT_URL, $loginurl); 
curl_setopt($ch, CURLOPT_POSTFIELDS, "user_id=$user_id&password=$pin"); 
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
curl_setopt($ch, CURLOPT_COOKIEFILE, "/tmp/sirsidynix-cookies.txt"); 
curl_setopt($ch, CURLOPT_COOKIEJAR, "/tmp/sirsidynix-cookies.txt"); 
$result = curl_exec($ch);

That’s it: we’re now “logged in” to SirsiDynix, and we can use this session to interact with the system as though we were using a web browser.

Retrieving Checked Out Items

One thing we might want to do now that we’re “logged in” is retrieve a list of the items we have checked out. We can retrieve a list of the paths for all of our checked out items like this:

$itemsurl = $baseurl . "/uhtbin/cgisirsi.exe/?ps=07P15KtNyC/CHA/" . $session_security_cookie . "/30#";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $itemsurl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_COOKIEFILE, "/tmp/sirsidynix-cookies.txt");
curl_setopt($ch, CURLOPT_COOKIEJAR, "/tmp/sirsidynix-cookies.txt");
$result = curl_exec($ch);

$regex = '/\\Details\\<\/a\\>/';
preg_match_all($regex, $result, $matches);
$checked_out_items = $matches[1];

This results in a $checked_out_items array that looks like this:

Array
(
    [0] => /uhtbin/cgisirsi.exe/?ps=obViAQ0yfH/CHA/152430023/5/3?searchdata1=293432{CKEY}&searchfield1=GENERAL^SUBJECT^GENERAL^^
    [1] => /uhtbin/cgisirsi.exe/?ps=TGF3wAfAQt/CHA/152430023/5/3?searchdata1=104284{CKEY}&searchfield1=GENERAL^SUBJECT^GENERAL^^
)

We can now iterate over those paths and parse out the title, author, ISBN and date due:

foreach($checked_out_items as $key => $url) {
    $item = $baseurl . $url;
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $item);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_COOKIEFILE, "/tmp/sirsidynix-cookies.txt");
    curl_setopt($ch, CURLOPT_COOKIEJAR, "/tmp/sirsidynix-cookies.txt");
    $result = curl_exec($ch);

    $regex = '/(?:(?Us)\\(.{0,})\\<\/dd\\>)/';
    preg_match($regex, $result, $matches);
    $items[$key]['title'] = trim(html_entity_decode($matches[1]));

    $regex = '/(?:(?Us)\\\\n(.{0,}) .{0,}\\<\/dd\\>)/';
    preg_match($regex, $result, $matches);
    $items[$key]['author'] = trim(html_entity_decode($matches[1]));

    $regex = '/(?:(?Us)\\(.{0,})\\<\/dd\\>)/';
    preg_match($regex, $result, $matches);
    $items[$key]['isbn'] = trim(html_entity_decode($matches[1]));

    $regex = '/(?:(?Us)\\Due:(.{0,})\\<\/td\\>)/';
    preg_match($regex, $result, $matches);
    $items[$key]['datedue'] = trim(html_entity_decode($matches[1]));
    $items[$key]['datedue_unixtime'] = strtotime($items[$key]['datedue']);
}

Using regular expressions (put together and tested using the excellent Debuggex tool), the metadata for each item is parsed out; the result, after all items have been iterated over, is an $items array that looks like this:

Array
(
    [0] => Array
        (
            [title] => Reporting : writings from The New Yorker
            [author] => Remnick, David.
            [isbn] => 0307263584
            [datedue] => 6 Oct 2013
            [datedue_unixtime] => 1381028400
        )

    [1] => Array
        (
            [title] => Start & run a coffee bar
            [author] => Matzen, Thomas, 1963-
            [isbn] => 1551803542
            [datedue] => 6 Oct 2013
            [datedue_unixtime] => 1381028400
        )

)

Created iCalendar Events for Due Dates

Now that we’ve pulled out all the checked out items, we can create iCalendar events for each one; rather than using some sort of heavyweight iCalendar class, we just manually stitch together the iCalendar files:

foreach($items as $key => $item) {

    $ical = "";
    $ical .= "BEGIN:VCALENDAR\n";
    $ical .= "CALSCALE:GREGORIAN\n";
    $ical .= "PRODID:-//Date Due Processor //DateDue 1.1//EN\n";
    $ical .= "VERSION:2.0\n";
    $ical .= "METHOD:PUBLISH\n";
    $ical .= "BEGIN:VEVENT\n";
    $ical .= "TRANSP:TRANSPARENT\n";
    $ical .= "STATUS:CONFIRMED\n";
    $ical .= "SUMMARY:" . $item['title'] . "\n";
    $ical .= "DTSTART;VALUE=DATE:" . strftime("%Y%m%d", $item['datedue_unixtime']) . "\n";
    $ical .= "DTEND;VALUE=DATE:" . strftime("%Y%m%d", $item['datedue_unixtime']) . "\n";
    $ical .= "END:VEVENT\n";
    $ical .= "END:VCALENDAR\n";
    
    $fp = fopen("icalendar-" . $key . ".ics","w");
    fwrite($fp,$ical);
    fclose($fp);
}

When the resulting .ics files get loaded up into iCal on my Mac, they look like this:

iCal showing book dates due on calendar

Grab the Code

You can grab the code outlined above as sirsidynix-to-ical.php from GitHub; to use it in your particular SirsiDynix setup will require that you change the base URL and might require that you modify the URLs for logging in, retrieving items, and so on.

In theory you should be able to automate any action that a patron can otherwise perform interactively using a browser, from renewing loans to placing and reporting on holds. I welcome reports of the use of this code elsewhere.

My friend Catherine Hennessey is turning 80 years old this September. Catherine and I share an interesting age-related mathematical pattern: she was born in 1933, I was born in 1966, so when I was 33 years old, she was 66 years old. And when I was 44 years old, she was 77 years old. In 8 years, when she turns 88, I will turn 55. And in 19 years, when she turns 99, I will turn 66.

In any case, her 80th birthday is, among other things, an occasion to downsize, and as part of that effort she is holding a silent auction: it starts online right now, and continues on September 21, 2013 at her birthday party. I’ve just finished loading up all of the auction items and, if I don’t say so myself, there are some great bargains to be had.

I’m particularly fond of the Dominos and Rummoli collection, the Souris school desk, the old Stanley plane and the board of nautical knots. And, of course given my numerical fascinations, the PEI license plate from 1966.

Send all your friends to auction.catherinehennessey.com.

Catherine Hennessey Auction Screen Shot

Twenty years ago in my career as a professional typist I started to feel the effects of my work on my wrists. I sought the advice of my doctor, who referred me to the Queen Elizabeth Hospital here in Charlottetown where the excellent technicians in the Occupational Therapy department fabricated a custom wrist brace for me.

I’ve been using this brace ever since; I suspect nobody ever thought it would last this long. And, indeed, when it started to show its wear three years ago I sought out a replacement which, though well-made and custom-tailored, has never quite worked as well as the original. So I’ve continued to squeeze use out of old faithful even as various cracks and tears have signalled that its end is near.

Until today.

I realized that some judicious application of Sugru – the miracle “you can fix anything with this stuff” material from the U.K. – I could breath some new life into the old brace and keep it going. Yesterday I formed some yellow-coloured Sugru over the cracks and tears in the brace and today, now that the Sugru has set, it’s almost as good as new:

Wrist Brace + Sugru

About This Blog

Photo of Peter RukavinaI am . I am a writer, letterpress printer, and a curious person.

To learn more about me, read my /nowlook at my bio, read presentations and speeches I’ve written, or get in touch (peter@rukavina.net is the quickest way). You can subscribe to an RSS feed of posts, an RSS feed of comments, or receive a daily digests of posts by email.

Search