Solving a New Event Bug Present in iCal When Scripting

25 01 2008

While trying to solve a user’s problem with an mGTD script, I came across a subtle issue that demonstrates some issues that arise when violating a programming philosophy, tackling bugs in other people’s code, and general uncertainty whenever coding in AppleScript.

Working with AppleScript is generally considered iffy, because a lot seems ambiguous and so much is dependent on how the dialect is interpreted and how scriptable apps handle some of the application events which scripting is dependent on. I’ve written scripts before, some I’m pretty awed by (that it works, actually, but also what it does), but I’m still hesitant to tackle some scripting issues. In addition, being a GTD greenhorn, and an mGTD noob made trying to respond to this issue authoritatively very questionable.

Thankfully, BMEGuy, mGTD’s author and all-around community nice guy, tackled the question with a quick solution. But the updated script was still problematic, and so I felt I really needed to participate in coming up with a solution.

Again, being an mGTD noob and all, it took me at least half an hour to figure out how the plugin worked, and the script on top of that. Then, after I was able to get the script to run, it worked for me. Hmm.

But that’s because I was testing with an entry with a date due of today. Once I switched it to later in the week, the entry was still showing up for today. Isn’t that odd? It seemed I had inadvertently left in the date line from the original script. When I removed it, I witnessed the same problem.

Artifacts of the shy scripted event

It turns out there’s a bug in MOX 10.4.11’s iCal 2.0.5 (I’m guessing it’s present in earlier versions as well) where it doesn’t properly update the calendar display for new events made by the script. You won’t see it in the monthly view. However, you might notice a little oddness in the weekly view.

You can see the event if you add ’show theEvent’ after the script makes a new display alarm for the event (between the 2nd and 3rd ‘end tell’ up from the bottom). This will display it’s properties in the info drawer, but you won’t see the event anywhere on the calendar (in either week or month view) until iCal is restarted.

Although the event doesn

Running the script in monthly view doesn’t show any artifact in the calendar, but the data is shown in the info drawer.

icalbugmonthshow.png

You could also run the script in the weekly view and then switch to the monthly view, in which case you get this:

icalbugmonthswitched.png

So now that the question of the event’s presence in the calendar was settled in my mind, I had to figure out why my faulty script displayed the event, but not the proper one; and how to coax iCal to display it.

Being unfamiliar with mGTD still, I tried to figure out the difference between the attribute name “dateDue” and due date. due date is one of the standard properties for entries in a Mori document. attribute name “dateDue” is a user column added in the example mGTD notebook. You can view them all the user columns by selecting the menu item Edit > Edit Notebook Columns…

Okay, good so far, but why would one cause iCal to display properly and not the other? After moving the due date line about for a while, I checked Script Editor’s Event Log, and saw

icalbugmissingvalue.png

The event reply for the due date had a missing value! Mori wasn’t returning a value for the due date property because it wasn’t set (and wouldn’t be in the example notebook). Now I had to find a way to use one of those missing values to make theEvent visible without setting it to the wrong date. And the problem with that is most of the properties used in Mori’s entries aren’t appropriate for an iCal event.

I eventually thought about re-ordering the messages to iCal instead of being so fixated on a change in the messages to Mori or playing with the properties being set in creating the event. What I came up with was a plan to use the messed up missing value date as before to make the event visible first, and then set the date correctly. The code turned out like this:

tell application "Mori"
	tell current entry
		set theDate to (get attribute name "dateDue")
		set faultyDate to due date
		set theName to name
		set theNote to note
	end tell
end tell
tell application "iCal"
	tell calendar "Scramble" -- the user should specify the name of the target calendar here
		set theEvent to make new event at end with properties {description:theNote, summary:theName, start date:faultyDate, allday event:true}
		tell theEvent
			make new display alarm at end with properties {trigger date:theDate}
		end tell
		-- show theEvent
		set theEvent's start date to theDate
	end tell
end tell

And to my surprise, it worked! So as I began gathering the materials together for my reply to the issue, I noticed something in the event’s info drawer that had escaped my attention before:

The previous script set up appointments for H. G. Wells.

iCal, that’s just crazy talk! But at least it would explain why it would display traces of an event, if anything at all; and why it wasn’t noticeable earlier: iCal would correct the event data when reading it in when it started (”iCal database, that’s just crazy talk!”). But somebody forgot to add a sanity check when creating a new event from the properties passed to it by our script. (This is an example of why the Once and Only Once principle should be heeded. If there’s only one place where events are synthesized from pre-recorded values, whether those values are from a stored file, a script or the UI, then all those code paths will benefit from any sanity checks added to event creation.)

Knowing this, here’s another means of working around this bug, by sending iCal info that won’t confuse it:

tell application "Mori"
	tell current entry
		set theDate to (get attribute name "dateDue")
		-- set faultyDate to due date
		set theName to name
		set theNote to note
	end tell
end tell
tell application "iCal"
	tell calendar "Scramble" -- the user should specify the name of the target calendar here
		set theEvent to make new event at end with properties {description:theNote, summary:theName, start date:theDate, end date:(theDate + 1), allday event:true}
		tell theEvent
			make new display alarm at end with properties {trigger date:theDate}
		end tell
		-- show theEvent
		-- set theEvent's start date to theDate
	end tell
end tell

Thinking about these two solutions it’s clear that picking the latter one, with well-formed properties, is the safest choice to make. Here’s additional proof: the first solution, the one which plays with the start date to make the event appear, will indeed make the event appear. But if there’s less than 24 hours until the event begins, it will appear on the wrong date and still require iCal to be restarted to appear in the proper location!

It just goes to show you, while you might be able to get away with just the barest minimum, and someone else might normally clean up after you, it’s best if you did the job correctly from the start in case your safety net disappears from under you.



A Superficial Review of TaskPaper (Cuz superficial is all my backlog allows)

14 12 2007

It doesn’t matter how much we complain about time, we’re not being realistic or honest about our use of it. Everyone gets the same amount every day; that is, if we’re getting the whole day. But asking for more time to do things has to strike someone as having too many things to do, and being grown up enough to abandon or postpone those things for which there isn’t time.

There are plenty of Time Management systems out there. Gettings Things Done (GTD) is a recent one with a lot of buzz and adopters. There are plenty of GTD software packages for those who prefer computer-based systems to paper, including two versions for Mori: Jeff Fisher’s mGTD plugin and Jim Harrison’s MoriGTD scripted system.

Now, I’ve had the audiobook for GTD for years but I’ve never listened to it. (I did at least listen to Merlin Mann’s David Allen interviews when I was on the road.) And now that I’m developing (and using) Mori I’ve even installed the mGTD plugin, but that was more for testing purposes than actually knowing what GTD was about (or how to use it).

So when Jesse introduced his own GTD app, TaskPaper, as a simpler way to getting things done with “paper-like simplicity” I was of course concerned that it would cannibalize Mori sales. But TaskPaper only manages tasks, not notes like Mori does. So unless your notes are one-liners used only to manage your activities, you’ll still need to grab a copy of Mori. ;)

But the real reason not to be concerned is TaskPaper is an awesome app in its own right. Jesse prodded me a couple times to try it, and even provided me with a license. So the next time I needed to jot down a task reminder, instead of using Mori, I opened up TaskPaper and gave it a try.

Now I have to admit, being both a GTD noob and a programmer, TaskPaper’s interface threw me at first. Let’s face it, any device with less than five buttons on it leaves me scratching my head. But I do know how to use an outliner. So with that interface in mind I set out to discover just how it was earning such wonderful reviews.

But TP isn’t exactly an outliner. It just uses an outline-like interface, and I emphasize that point. You’ll be productive very quickly, but I also got my list complicated very quickly by adding sub-sub-headings to my project. (But you have to expect that from a GTD noob and a programmer.) The beauty is that by using a very simple text editor/outliner interface, TP doesn’t complicate the ability to change your organization of tasks and projects. Nor does it complicate your ability to add extra info about the task (its metadata). Avoiding special columns and a form interface, TP instead uses the following special text characters to signify meaning: “:” for projects, “-” for tasks, “[tab]” to nest tasks within other tasks (which are themselves nested within a project), any non-reserved character for notes, and “@” for the interesting context.

Contexts are GTD’s way of giving you a way of lumping similar activities together. They aren’t necessarily part of the same project, but just things which are done at the same place (e.g., @home, @mall), the same type of activity (@computer, @phone), and so on. TP refers to them as tags, and uses the normal GTD syntax (I guess) to refer to them.

TP’s interface is very sparse, which means less clutter and distractions. It’s nonetheless powerful, letting you filter to a specific project or context. The point is to spend more time doing your activities than actually organizing what they are. And TaskPaper handles that beautifully (and elegantly). I now understand what the Hog Bay Notebook users have been clamoring for!

I also have a better understanding of how GTD works, even more so after I sat through the mGTD screencast put up by Jeff, which in turn helped me make better use of TP and GTD.

There are definitely some features I’d like to see added, though:

The UI signifies task completion by changing the text style to strike-through, but the only way to mark the task as completed textually is by adding the context @done or the keyboard shortcut cmd-D. (You can also mark it completed by clicking on the open circle in the left margin or the menu item “Project > Mark as Done”, but that requires lifting your hands from the keyboard to use the mouse. I would prefer some other delimiter such as “x”, or a checkmark to signify completion, just for continuity.

I’d like to see true tags supported, perhaps using Chris Messina’s “#” tag delimiter.

So while there are some features I’d like to see added for more sophisticated capabilities, for GTD noobs like myself (still) and those who just want to work and not manage yet another system, TaskPaper should be the first stop to GTD (and priority) mastery!

In time, I might become profficient enough at GTD to be able to use mGTD or MoriGTD. But for now I just need to start the “filtering the inbasket thing” to help things along. That, and redefine my understanding of the word “superficial”.



Mori: v1.6.7 Post-Mortem and Upcoming Changes

27 11 2007

Well, it looks like the latest toolbar fix has finally stuck. There were actually a few, very subtle interrelated items, and some procedural issues that cascaded into others. Code was shifted around hither and yon, resulting in elimination of two of a main class’ instance variables and simplification of logic in several methods.

Speaking of which, I’d really love to try out Xcode3’s new refactoring tools, but that would mean

  1. spending more time in Leopard, which freezes on me,
  2. and spending more time in Xcode3, whose text editing I already loathe.

I will need to investigate how well refactoring works in TextMate (which I already own and use from time to time when bumping against some other Xcode2 limitation.)

The pressure cooker development level of the past couple of weeks really put the development process here through the wringer. Some things held up. Some things fell apart. So more ‘behind the scenes’ development is going to take place here to minimize the interference caused by the mismatch between what needs to be done and what the tools require me to do next.

    Some observations of the release’s development:

  1. The primary, agile-based, processes were abandoned in the panic over the continued toolbar problems and Leopard incompatibilities.
    There was no established process for handling emergencies, so the chaotic edit-compile-test behavior resurfaced. This is not to be repeated.

    Corrective Measures: A plan of action for dealing with emergencies must be established. Exercises and drills prepared and performed so the proper outcome occurs next time.

  2. Users seemed to be unaware, or unconvinced, of my plans for Leopard.
    Although I had made statements regarding the planned support for Leopard, it was only in response to questioning by users. I had failed to provide information ahead of time and prominently.

    Corrective Measures: While users refer to the forum when there are questions or they have some issue to resolve, they refer to the blog constantly. It’s best to have already disclosed plans so users have time to assimilate needed info and prepare accordingly. Blog. Blog. Blog. Forum posting: link to blog. Then blog.

  3. Unit testing got skipped.
    It was unavoidable given the current process because tests didn’t cover the critical issue being developed: the user interface (i.e., toolbar items and empty windows).

    Corrective Measures: Mock objects are inadequate to sufficiently test the UI. For all my blustering that the UI is testable, it’s clearly time I seek or develop the necessary tools and put them to use.

  4. Subversion is a win, but it’s an ugly win.
    Being able to restore files, or the project, of a particular beta or release build, or of a particular date and time, is great. Being able to make wholesale changes to the project, then abandoning them, or keeping them along a separate branch to continue experiments at a later date without juggling project directories here and there, is great. Being able to merge or contrast multiple working project directories from separate environments (Tiger, Leopard, laptop, and the v1.6.3 release) quickly and easily, is great.

    Nibs saved as a smattering of files in the repository: not so great. Confusing subversion with its own metadata when copying/adding directories: not so great. Performing an add/delete when moving or copying directories and files instead of adding “move” or “copy” semantics to the system: not so great. Poor integration mechanism for multiple offline revisions: not so great.

    SCM is great. I’ve used CVS for years. I’ve even set up another project or two using Subversion before I bought Mori and Clockwork from Jesse. But I don’t see where it’s an obviously better package than CVS, but perhaps that’s because I’ve learned to work around CVS’ shortcomings. (Reviewing the feature list at http://subversion.tigris.org, the only practical feature I see that means anything to someone actually using Subversion is command-level manipulation of directories, although the handling of renames is poor. Hrmm. And svn+ssh access to the repository, with Xcode integration even!)

    Corrective Measures: For the time being, the messiness of the metadata in copies and moves will have to remain unsettled. As long as the structure ends up correct, and it doesn’t take long to get it to that state, it remains tolerable in the face of bigger issues. A procedure must be established to handle the circumstance where developers are offline and must “commit” in some manner which is transferrable to the repository when they are back online.

  5. Debugging is severely inadequate for professional-grade development.
    Can you tell Apple’s team has a severe case of he-man, mouse-haters club insecurities? (Either that, or they’re operating in crunch mode so much, they don’t have time to develop the appropriate tools. Nah!) The debugging facilities they provide for developers operate primarily through the use of environment variables, console output and functions and output which require gdb to access.

    ZeroLink didn’t work well and was abandoned; Fix and Continue is still problematic; and the vaunted debug libraries (to help you catch errors in the parameters passed to the AppKit and Foundation libraries) hasn’t worked right since 10.4.3.

    My own debugging facilities are rudimentary and lacking in depth. More of the failures in the field should be communicated back to me (remember Safari’s ‘bug’ button?). The crash reporting mechanism should work when the crash occurs, not the next time Mori runs. Also, exceptions are logged to the console, but otherwise go unnoticed by Mori and the user.

    Corrective Measures: Extend the runtime monitoring and browsing tools. Rewrite the crash reporting mechanism to activate when the application terminates. Add an exception handler which sends reports back. Develop proper high-level debugging tools for Objective-C, the UI, CoreData and Bindings. Coding takes place at a higher level. Debugging should be at least at that same level. -fobjc-gc exists for a reason. Take advantage of it!

  6. The build process is inconsistent, confused and unstable.
    The various plugins’ build settings aren’t consistent. Extraneous resources (images, sounds, etc.) are stored in the nibs. At times the build directory must be purged for a functioning executable to be built.

    Corrective Measures: A Build Settings Table has already been prepared and Mori and its plugins have had their settings documented. The settings for the Blocks plugins has yet to be documented. The effect of some settings has to be determined, which can then be propagated to the targets as appropriate. The project file should be purged of duplicated actions, unnecessary references, etc.

  7. Debugging Mori when it’s used for normal work results in too much human activity thrashing.
    Because the debug and test versions use the same preferences/file settings, the release version used for normal work had to be exited to avert data corruption in the notebooks.

    Corrective Measures: This issue was resolved with the Oneill branch. However, that uses a separate target to achieve its distinction. Some means of specifying a special bundle id for the debug and test builds must be developed to accomplish a similar effect, perhaps through preprocessing the Info.plist.

  8. Checklists are great
    Being able to state that processes are being followed, builds are complete, and updates were released correctly, is great.

    Corrective Measures: Checklists are great, but scripted procedures are better. Automate as much of every process as possible.

  9. To-Do lists, Getting Things Done (GTD), or whatever Time Management activity that is put into practice definitely helps.
    I’ve got the audio book. Mori’s got the plugin. I’ve got Taskpaper, too. I’ve got a Handspring Visor Edge and its Palm Desktop software. Something, anything, that helps track tasks that need to be done so nothing falls between the cracks is a plus. They are effective at keeping things moving forward, but I’m not efficient at it and a lot of discipline and effort must be used to keep moving things forward.
    Corrective Measure: I used TaskPaper to handle the tracking during this hectic period. I’ve got a blog entry in preparation, but basically, I found it a great way to get into the GTD system due to its simple interface. I must continue developing my understanding of this system to manage my activities, and see about getting the various software/devices integrated better.

Next Batch of Changes

I’ve already begun work on v1.6.8: Improved checking and repair functionality for notebooks. Correct Italian localization, thanks to Mario Pettenghi. The code for the Blocks framework will be tweaked so it compiles without triggering warnings (e.g., unused parameters, missing prototype, etc.). Additional unit testing for the UI, and refactoring of UI code.

These improvements to the code will set up the continuation of progress for v1.7, which should then be ready during the holidays. At least that’s the current goal.