Work diary revisited

Update 8/21/14
You can pretty much ignore this post unless you want to explore the psychology of how someone can get caught in one way of thinking and miss something as plain as the nose on his face.

My first instinct is to write scripts, and I knew I could write Pythonista scripts to do what I wanted. But I didn’t want to have to launch Pythonista to run the scripts, because that would involve a lot of scrolling and would cost more time than they saved. I knew, though, that I could create home screen shortcuts that would run each script with a single tap. You’ll note that there’s no thought of using Launch Center Pro at this point. Although I bought LCP a while ago, I never got into using it.

After making the home screen shortcuts, I found that running a script that way would leave me in Pythonista. Because there’s no URL scheme for Springboard, I couldn’t script in a way to go back to the home screen where the icons lived. I really didn’t want to have to press the home button to get out of Pythonista.

That was when I hit upon using Launch Center Pro, and I was happy because everything worked. But I should’ve realized that LCP, combined with the Work diary action I’d already built in Drafts, could do what I wanted with very simple actions like this:

drafts://x-callback-url/create?text=Leaving%20home&action=Work%20diary&x-success=launch%3A

Not only is this far, far simpler than the Pythonista scripts described below, it runs faster and is more flexible. Later today, I’ll write a much shorter post about using LCP actions like this to create common diary entries.

In the meantime, read what’s below only if you want to look at a case study in complicated foolishness.

The work diary I outlined in this post last year continues to serve me well, but I’ve been looking for more efficient ways to make common entries. After a few attempts that weren’t as helpful as I’d hoped, I think I’ve hit on a solution using a combination of Pythonista and Launch Center Pro.

To review, my work diary is a set of text files stored in a Dropbox folder named “Diary.” Each file is named according to the date, like 2014-08-20.txt, and contains timestamped entries that look like this:

22:06
Started 200 degree test

22:45
Ended 200 degree test

22:54
Started 300 degree test

These entries are created in Drafts. I type or dictate the text of what I’m doing into a blank draft, invoke the Work diary action,

Drafts diary entry

and a new timestamped entry is added to the day file. The Drafts action that does the work is defined this way,

Drafts work diary action

where the file name is

[[date|%Y-%m-%d]]

and the template for the entry is

[[date|%H:%M]]
[[draft]]

This is a slight change from what I started with last year. Back then, the timestamp used a 12-hour clock with an AM/PM marker. I’ve since changed to a 24-hour clock to make it easier to figure out how much time I spent on each activity.

I don’t have the patience or the discipline to track all my time this way, but I have found this system especially useful on days when I travel. On these days, the entries tend to take on a few common forms:

07:22
Leaving home

08:05
At site

09:13
Leaving site

09:58
At office

Because I usually make these entries in my car with the Bluetooth connection on, the ambient noise and car speakerphone make Siri dictation less than perfect. To avoid dictation errors, I set up some shortcuts using the builtin iOS Settings. I soon learned that while shortcuts work fine when they’re part of a longer batch of text, they leave you hanging when the shortcut is the only thing you type.

Shortcut expansion

The little autocorrect bubble just sits there until you either type something else or tap somewhere in the text field.

TextExpander snippets are better for standalone entries because they expand immediately, with no need for additional typing or tapping. So I made a series of snippets for the entries I found myself making again and again.

lhz  -> Leaving home
lwz  -> Leaving work
lsz  -> Leaving site
lhoz -> Leaving hotel
ahz  -> At home
awz  -> At work
asz  -> At site
ahoz -> At hotel

I used these snippets for months, and they never gave me any trouble. Still, it seemed as though the sequence of

  1. Open Drafts
  2. Type abbreviation
  3. Tap action button
  4. Tap Work diary action

was too much for something as routinized as entering canned text like this, especially when my thumbs mistyped the abbreviation and I had to back up and retype. Autocorrect never wanted to turn “adz” into “asz” for me.

So I started thinking about ways to automate these entries further, and Pythonista seemed like my best bet.1 What I ended up with was a series of eight scripts—one for each of the common entries—that I run from Launch Center Pro.

Launch Center Pro shortcuts

A single tap on one of these icons switches me to Pythonista, runs the appropriate script to create a diary entry, and returns to Launch Center Pro. Counting the tap to start LCP itself, that’s two taps to make a diary entry, each on a large target with no chance for mistyping.

The scripts that do the work rely on the Dropbox module that comes with Pythonista. This module is not quite as easy to use as the one described in the Dropbox Python SDK documentation, and unfortunately the Pythonista documentation for the module contains some mistakes, but with a little trial and error I was able to get a set of scripts that worked.

The first step was to go to Dropbox’s App Console and “create an app.” This generated an app key and app secret that I could use to give my scripts access to my Dropbox files. I did that using this script, which is a slight modification of the authorization script in the Pythonista Dropbox documentation:

python:
 1:  from dropbox import client, rest, session
 2:  import clipboard
 3:  
 4:  # Get your app key and secret from the Dropbox developer website
 5:  APP_KEY = 'aaaaaaaaaaaaaaa'
 6:  APP_SECRET = 'bbbbbbbbbbbbbb'
 7:  
 8:  # ACCESS_TYPE should be 'dropbox' or 'app_folder' as configured for your app
 9:  ACCESS_TYPE = 'dropbox'
10:  
11:  sess = session.DropboxSession(APP_KEY, APP_SECRET, ACCESS_TYPE)
12:  
13:  request_token = sess.obtain_request_token()
14:  
15:  url = sess.build_authorize_url(request_token)
16:  
17:  # Make the user sign in and authorize this token
18:  clipboard.set(url)
19:  print "url:", url
20:  print "This URL is on the clipboard. Please visit the website and press the 'Allow' button, then hit 'Enter' here."
21:  raw_input()
22:  
23:  
24:  access_token = sess.obtain_access_token(request_token)
25:  
26:  print 'key:', access_token.key
27:  print 'secret:', access_token.secret
28:  print
29:  client = client.DropboxClient(sess)
30:  print "linked account:", client.account_info()

The APP_KEY and APP_SECRET in Lines 5 and 6 are the strings I got from the Dropbox App Console. Running this script within Pythonista puts a Dropbox authorization URL on the clipboard and then pauses at Line 21. At this point, I went to Safari, pasted the URL, and authorized my app in much the same way I authorized Tweetbot, Storify, Pinboard, and other apps. Returning to Pythonista and the paused script, I tapped the Return key to allow the script to finish. Lines 26 and 27 printed out the access key and secret that are needed in the next script. With that, this script has done its job and is no longer needed.

By the way, the description of obtain_access_token is one of the places where the Pythonista Dropbox module documentation is wrong. It says the return value is a tuple of the key and the secret when it’s actually a single object with key and secret attributes. That took some time to figure out.

To avoid repeating a big chunk of code eight times, I wrote a module, diary.py, with a function that can be called from all the other scripts:

python:
 1:  import dropbox
 2:  from datetime import datetime
 3:  
 4:  def add_entry(entry):
 5:    "Add a timestamped entry to today's diary file."
 6:  
 7:    # Initialize Dropbox.
 8:    app_key = 'aaaaaaaaaaaaaaa'
 9:    app_secret = 'bbbbbbbbbbbbbbb'
10:    access_key = 'ccccccccccccccc'
11:    access_secret = 'ddddddddddddddd'
12:    sess = dropbox.session.DropboxSession(app_key, app_secret, 'dropbox')
13:    sess.set_token(access_key, access_secret)
14:    client = dropbox.client.DropboxClient(sess)
15:  
16:    # Dates, times, and files.
17:    folder = '/Elements/Diary/'
18:    current = datetime.today()
19:    filename = current.strftime(folder +'%Y-%m-%d.txt')
20:    timestamp = current.strftime('%H:%M')
21:    dfiles = [ f['path'] for f in client.metadata(folder, list=True)['contents'] ]
22:  
23:    # Get the contents of today's diary file if it exists.
24:    if filename in dfiles:
25:      f = client.get_file(filename)
26:      log = f.read()
27:      f.close()
28:    else:
29:      log = ''
30:  
31:    # Add a new entry and save. Creates a new file if it doesn't exist.
32:    log += '%s\n%s\n\n' % (timestamp, entry)
33:    client.put_file(filename, log, overwrite=True)
34:  
35:  if __name__ == '__main__':
36:    add_entry('hello')

The function defined in this module, add_entry, does exactly what you’d expect from its name. Its single argument, entry, is what comes after the timestamp in a diary entry.

Lines 8–13 use the keys and secrets generated earlier to gain access to my Dropbox files. Line 14 initializes a Dropbox client process that handles the reading and writing of the files.

Lines 17–20 define the name and location of the diary file and define the timestamp. You’ll notice that my Diary folder is a subdirectory of a folder named “Elements.” Old-timers might remember that when the Elements app was the hot iOS text editor, it forced you to put all your files into a namesake Dropbox folder. I’ve linked every iOS text editor since then—none of which have been so restrictive—to that same folder.

Line 21 gets the names of all the files in the Diary folder. Line 24 then checks to see if there’s a file in there for today. If there is, it reads all the text from that file and puts it in a variable called log. If there’s no preexisting file for today, log is initialized to the empty string.

Line 31 adds the timestamp and entry to the end of log, and Line 32 writes log out to today’s file. This creates the file if it doesn’t already exist and overwrites the existing file if it does.

With diary.py doing all the hard work, the individual scripts for each common entry are easy. Here’s the one named Leave Home.py:

python:
1:  import diary
2:  import sound
3:  import webbrowser
4:  
5:  diary.add_entry('Leaving home')
6:  sound.play_effect('Coin_2')
7:  webbrowser.open('launch://')

Line 5 writes the entry, Line 6 plays a sound so I know when it’s done, and Line 7 takes me back to Launch Center Pro. The other seven scripts are exactly the same except for the argument to add_entry.

Which leaves just the definitions for the LCP actions. Here’s one:

Leave Home LCP action

The URL is

pythonista://Leave%20Home?action=run

I suppose I shouldn’t have left the space in the script name—that’s the reason for the %20. The .py extension isn’t needed in the URL because Pythonista knows to add it.

Apart from the quick, one-handed entry, the best thing about this solution is that it’ll be easy to extend if I start using other common entries in the future.


  1. I think Editorial would work, too, but I’m not as familiar with it. 


Back to LaunchBar

I gave Alfred a fair shot, I think. A two- to three-week trial in which I used it exclusively on both of my Macs. I even created some workflows to run scripts that I used to run through LaunchBar. And I really wanted Alfred to “stick,” because it uses far less memory than LaunchBar. But I’m back running LaunchBar again.

Two things brought me back. First, I didn’t like how Alfred wanted me to start folder and file searches by hitting either the spacebar or the single quote key. There are ways to have Alfred search files and folders without using this signal, but the preference pane for changing the setting warns against it. I didn’t want to slow Alfred down.

Alfred defaults

Which leads to my second problem: Alfred was, on my iMac at least, distinctly slower than LaunchBar at finding files and folders. I didn’t notice a speed difference on my MacBook Air—probably because its “disk” is all flash storage and only 128 GB—but Alfred’s lag was palpable on the iMac. It drove me crazy.

So what about LaunchBar’s huge appetite for RAM? That was, after all, the reason I gave Alfred a try. I went through LaunchBar’s index settings and pared down both the number and depth of the folders it indexes. I still get the quick results I want—I’m not sure why some of the settings were the way I used to have them—and LaunchBar’s long-term memory footprint is now down to about 100 MB on the MacBook Air and about 150 MB on the iMac. To be sure, this is about an order of magnitude more than what Alfred uses, but it’s a far cry from the 500–900 MB that LaunchBar was using before. And it’s worth it to get the behavior I want.


TextExpander and Numbers

When Apple revived the iWork apps after their four-year coma, users soon found that the newly awakened versions were diminished. Some of the features that were cut have been brought back, but the ability to define named constants in Numbers—which I used in my expense report template—has not reappeared. I’ve learned, though, that I can use TextExpander instead, and I now have a few simple snippets that are even easier to use than named constants.

I use the snippets to enter mileage expenses when I drive my car on business. The IRS rate changes from year to year—currently it’s 56¢ per mile—and I hate having to look it up. As I said, I used to have a constant, named miles, defined in my expense report template to equal the current rate. To enter the mileage expense for a 25-mile trip, for example, I’d type

=25*miles

into the cell and it would do the substitution and the calculation. Simple, but no longer possible. I suppose I could set up a cell in a hidden part of the expenses table and get the same effect, but I don’t like hidden cells. It’s too easy to make mistakes when you can’t see everything.

I set up an Expenses folder in TextExpander. The snippets within it expand after any character and work only in Numbers.

TextExpander Expenses folder

The fundamental snippet in the group is for the mileage rate. You might think I could just define it as 0.56, and that’s what I tried first, but I soon found that Numbers does some odd things with typed input. When I tested the snippet by typing

=25*;mi

into a cell, I heard the TextExpander expansion sound, and the ;mi disappeared, but nothing was substituted in its place. After a bit of tinkering, I found that a snippet with the content =0.56* would always expand, so that’s what I used.

Mileage rate snippet

Now I type ;mi into a cell, it expands to =0.56*, and I type the number of miles as the second term of the multiplication instead of the first. It’s a little different from my old habit, but it was easy to get used to.

Trips to the airport are pretty common, so I defined two other snippets, one for the trip to Midway and another for the trip to O’Hare. They start by invoking the mileage rate snippet and then add the number of miles to the end. If the mileage rate changes next year, I only have to change one snippet.

Midway snippet

I have a tendency to think in terms of complicated AppleScript or shell script snippets, but it’s often the simplest snippets that are the most useful.

In fact, as I was typing this up, I realized that snippets for the tolls I pay on my way to and from the airports would be just as valuable. I’ll be making those as soon as this is published.


A brief construction bestiary

This post was inspired first by Marco Arment and his son, Adam:

Fake Cheerios are better from a backhoe. @ Mont Olympos Restaurant instagram.com/p/rhVYe4w5Ii/
Marco Arment (@marcoarment) Aug 10 2014 9:18 AM

Click the Instagram link to see Adam using a clever spoon substitute. My predictable reaction:

@marcoarment I love the photos of your son and his toys but feel obligated to point out that that’s not a backhoe.
Dr. Drang (@drdrang) Aug 10 2014 11:51 AM

The post was further inspired by Daniel Jalkut, who, like all smart people, isn’t afraid to confess his ignorance on topics outside his comfort zone:

@marcoarment @drdrang Front end loader? Everything I know about trucks comes from those baby picture books.
Daniel Jalkut (@danielpunkass) Aug 10 2014 1:21 PM

What I know about construction equipment comes from growing up with a father who was a design engineer at Caterpillar for 30 years and from my own 20+ years investigating equipment failures. This, far more than the little amateur programming projects I usually write about here, is my comfort zone. So I decided to break out of my usual blogging habits and actually write about something I know.

This is also an exercise in nostalgia. The photo of Adam reminded me of my older son, Joe, who was thoroughly obsessed with construction equipment when he was that age. Nowadays, as a tall, deep-voiced high school senior, he wouldn’t know the difference between a loader and a backhoe, but when he was 3–4 years old, he was an absolute expert. The only thing he liked more that flipping through books with pictures of Big Muskie and the Silver Spade, was going with me to local construction sites and seeing machines at work.

The equipment names I’ll be giving here are what manufacturers use. Construction workers have any number of names for these machines—a small percentage of which don’t even involve profanity—and I’ll include as many of these nonstandard names as I can think of.

Bulldozer

Cat D6T

Photo from Caterpillar.

Dozers are the ur-machine of earthmoving, and the segmented steel tracks they run on are what gave Caterpillar its name.1 Each individual segment is called a shoe, and the spines that stick out of the shoe and dig into the soil for traction are called grousers. The tracks are meant to handle rough terrain while also spreading the load of the machine over a large area of ground contact.

The single wide blade at the front is for grubbing, pushing, and smoothing. Hydraulic cylinders behind the blade adjust its elevation, tilt, and horizontal angle. Dozers are often fitted with a ripper tooth on the back for loosening hard soil and are also used to pull other equipment around on a job site.

Loader

Cat 966M wheel loader

Photo from Caterpillar.

Lots of machines are called loaders, but the most common is the wheel loader, shown above. It’s not called a wheel loader because it loads wheels, it’s called that because it has wheels. It really should be called a wheeled loader, but it’s too late to change now.

Loaders have a wide bucket on the front that can be used for shallow digging but is usually used to scoop up loose material and load it2 into trucks. The position of the bucket has led to these often being called front-end loaders, but the front-end part is redundant. I’ve never seen one with a bucket at the back end.3

Some loaders run on tracks instead of wheels. As you might guess, they’re called track loaders. When Joe was at the peak of his machinery love, he was with me once at a store, looking out the front window at some construction work going on across the street. Another little guy, about a year older, was watching too, and said he liked the bulldozer. “That’s not a bulldozer,” said Joe. “That’s a track loader.” I was never prouder.

The very small loaders you see used by landscapers are officially known as skid steer loaders but are usually called Bobcats for the same reason facial tissues are usually called Kleenex.

Update 8/13/14
Reader Jonas Nilsson sent me a link to this image of a Volvo tractor with a loader attachment at its back end. According to Jonas, the name of this equipment in Swedish translates to back loader or back-end loader. You’ll note that in the linked photo the attachment is equipped with forks, not a bucket, but the mechanism could certainly accomodate a bucket.

Back loader attachment

Photo from Wikimedia.

Jonas says these back loaders used to be popular in northern Europe because they were cheap, but their disadvantages eventually outweighed the cost savings and they’re fairly rare now. I’ve seen plenty of farm tractors in the US with loader attachments on the front, but never anything like this. Thanks, Jonas!

Excavator

Cat 324E excavator

Photo from Caterpillar.

Excavators are for serious digging. They have a long, deep reach, and can switch buckets quickly for different types of digging operations. You’ll often see them outfitted with slings and cables to pick up pipe sections and lower them down into the trenches they just dug.

Many people refer to excavators as backhoes, but this isn’t quite right because the bucket is on the front of the machine, not the back. I’ve also heard them called trackhoes, which makes sense because they’re hoes on tracks,4 but very few people use that name and no manufacturer does.

By the way, there are two fundamentally different types of digging machines, and they get their names from the hand implements they mimic. Shovels dig by scooping down and away, hoes dig by drawing down and back. Hoes are more common, but you probably remember a famous shovel.

Mike Mulligan

Photo from Amazon.

Here’s a little more excavator nomenclature. The two parts of the arm are called the boom and the stick. The boom is the piece closest to the machine; the stick is the piece between the boom and the bucket. The boom, the stick, and the bucket are operated independently.

Backhoe loader

Cat 420F backhoe loader

Photo from Caterpillar.

The backhoe loader (often called the loader backhoe) is a hybrid machine with a loader bucket on the front and an excavator attachment on the back. For this machine, the word backhoe makes sense because the hoe is on the back. The operator, who normally faces forward toward the loader bucket, swings the seat around when it’s time to use the backhoe. The legs off the rear are called outriggers, and they’re brought down to stabilize the machine when digging with the backhoe.

On some machines, the backhoe is truly a separate attachment to a tractor. On this type of equipment, the backhoe has its own little seat.

Bradco backhoe

Photo from Gearmore.

Scraper

Cat 621K scraper

Photo from Caterpillar.

Scrapers are like dozers in that they have a blade that can cut through soil and level the ground. But a scraper doesn’t look anything like a dozer because its blade is at the bottom of a bowl—that’s the low part at the rear—which fills with the soil the blade cuts. A scraper can also be used “in reverse,” dumping soil out of the bowl and leveling it as it’s driven around the site.

I once had a project in Virginia in which the client kept referring to pans being used on his jobsite. It took me a while to realize he was talking about scrapers, substituting pan for bowl and then using that word for the entire machine. Even my dad hadn’t heard that one before.

Soil compactor

Cat 815F soil compactor

Photo from Caterpillar.

Soil compactors are often equipped with dozer blades to push soil around, but their shaped steel wheels are intended to do almost the exact opposite of a dozer’s tracks. The wheels focus the weight of the compactor onto a very small area to make the underlying soil more dense. The denser the soil, the better it is as a base for a roadway, parking lot, or floor slab.

I said brief

Because this is a blog post, and not a Dorling Kindersley book, I’m going to stop here, even though I haven’t even finished with earthmoving equipment. This makes for a good start, though, and should allow you to identify most of the machinery you see despoiling that nice wooded area to put in a strip mall.


  1. There are dozers with wheels, but they’re rare. Dozers are commonly used in areas that need to be smoothed out before wheeled vehicles can enter and work.

    Fun trivia: Caterpillar’s in-house newsletter was, and maybe still is, entitled Tracks and Treads

  2. Hence the name. 

  3. Now I have. See update below. 

  4. There are wheeled excavators, but they’re rare.