How to use DTP with ToDo items?

I do not yet own DTP, but I’m very interested in it because of its ability to reveal relationships between documents. But, I also need to:

  • keep track of to-do items,
  • relate them to other documents in a database,
  • have these to-do documents sort properly (in ascending and descending date order) in a list, and
  • have their due dates readily visible,
  • in one app,

all without having to manually paw through the to-do documents themselves.

As far as I can tell, DTP does not have any date field that can be used for assigning a Due Date to a document. (I think I read somewhere that one could reset the Date Created, but this will not work for my purpose, and besides, is not enough to uniquely identify a to-do item. I could, I suppose, add “ToDo” in the text of a document, but having to change two attributes makes this scheme fail if I forget to change one.)

So, in lieu of a Due Date attribute for documents, I am left to putting the due date in the subject of a document, and using a Smart Group to filter these documents out of the collection. But to do this, I must identify the to-do documents uniquely (which would be a snap if there were a “Due Date” attribute and support for it in Smart Groups, but I repeat myself…).

So, OK. In a document’s title, I can insert a unique character and a properly-formatted “date” string that sorts properly (“YYYY-MM-DD”). But, this is a pain, and the scheme fails if I make a typing error and don’t catch it.

So, OK. I can use AppleScript. But, AppleScript has no date picker control, and its input facilities are primitive and there’s no way to create forms, so I can’t build my own date picker. And, I’m an AppleScript novice, and always bang my head bloody against the wall when trying to use it.

But, OK. I worked all day to create the below AppleScript. It puts up a list of dates, and if one is selected, copies a string to the clipboard, of the format:

YYYY - MM - DD, DOW

where:

  • is a unique character that a Smart Group can use to detect a dated to-do item, followed by a space,
  • “YYYY - MM - DD” is a string composed of the 4-digit year, " - " (space, hyphen, space), the 2-digit (zero-padded) month, " - " (space, hyphen, space), and the 2-digit (zero-padded) day of the month,
  • “, DOW” is a comma, a space, and the first three letters of the day of the week of the date represented in the string, and
  • “” are two tab characters.

(I use space, hyphen, space in the string to aid readability. I zero-pad the month and day and use tabs at the end of the string to provide a fake form of justification, so that things hopefully line up in the titles of documents returned by a Smart Group’s action. I expect that a future version of the script could set the string directly into the beginning of a DTP document’s title, preserving anything that’s already in the title.)

There are some problems with this approach, one of the more important for me is: preparing the list can be time-consuming, so I need to start on some date, and calculate for some defined duration. Preparing a list of the dates for a year takes roughly a second on my 2.8GHz Core II Duo. This is acceptable, and I expect that most of my due dates will fall within this range.

So, OK. But, some life events take place farther in the future than one year from the current date. I can’t generate an infinite list of dates, nor can I use some sort of “More…” button to conveniently and rapidly fetch another year’s worth of dates. (Oh, for a scriptable date picker…in lieu of a DTP “Due Date” attribute…)

So, OK. To avoid these problems, before preparing a list of dates to choose from, the script first asks for a range:

  • between now and 1 year from now
  • between 1 - 5 years from now
  • between 5 - 10 years from now
  • between 10 - 20 years from now

Given that I expect that most of my to-do’s will be in the upcoming year from the current date, I used larger periods in the durations of the farther-away ranges, so that the inconvenience of waiting will be endured not so frequently.

Then, having set the start date and duration for the range, the script chews away and creates a list, which AppleScript unfriend-ily tosses up onto the screen in a miserable-looking, screen-high list box format, and I can choose a date, or cancel. If I select a date, the script then reformats the date to make it (what I think will be) friendly to the eyes in a DTP list generated by a Smart Group that looks for the unique character in documents’ titles.

So, OK, mission (sorta) accomplished. But, geez, what a pain this is. It’s just one compromise after another, to overcome limitation after limitation. This would be soooo simple if DTP had a “Due Date” attribute for documents.

What am I missing? Is there an easier way to accomplish in this sophisticated AI-enabled program what Journler can do with ease (using its “Due Date” attribute)? I’d really like to use DTP to meet other requirements, but dated to-do’s are important, and my tasks and way of working require supporting documentation with my to-do items. (Project management systems, and putting to-do items into a calendar with an alarm, do not work for me. I need my to-do’s and supporting documentation contained within one app.)

I cannot accept that assigning a due date to a DTP document, which can be filtered through Smart Groups, sorts properly in ascending and descending date order, and is readily visible and changeable, is outside DTP’s scope.

Please tell me that my approach is ridiculously overkill, and show me an easier way of meeting these requirements, and ideally, that DTP does in fact have a “Due Date” attribute for documents, which I’ve somehow stupidly overlooked. My head really hurts. Thanks very much everyone, for your advice.

(The board does not allow attaching .scpt, .txt, or .rtf files, so I could not attach the script as a file.)

-- ==================================================
-- Date Picker
-- display consecutive dates in a list box, and return the date selected in an easy-to-read format that sorts properly in text controls.
-- method:
--   1. because preparing a list of dates can be time-consuming, prompt first for a time period to select from, i.e.,
--      between the current date and 1 year from the current date,
--      between 1 - 5 years from the current date,
--      between 5 - 10 years from the current date,
--      between 10 - 20 years from the current date.
--   2. list the dates in the period selected in the format: "mm/dd/yyyy, DOW" e.g., "09/05/2011, Mon"
--   3. return the date selected from a call to "choose from list" (if none selected, or Cancel button clicked, quit)
--   3a. because a string is returned, not a date, prepare it in yyyy-mm-dd, dow format, so that these date strings will sort in proper ascending & descending order in text controls.
--   3b. also, format the string so that it is easy to read in text controls such as lists, and
--   3c. add a distinctive prefix to the selected date string, throw a couple of tabs onto the end to provide a fake form of justification, and copy it to the clipboard.
-- revision history:
--      2011-09-04: original code
-- ==================================================
set magic to "›"

set curdate to current date
set datelist to {}

set padding to tab & tab & "-" & tab
-- promptlist, startat, and duration are parallel lists. their elements are accessed through a common index.
set promptlist to {¬
	"Now" & tab & padding & "1 year from now", ¬
	"1 year" & padding & "5 years from now", ¬
	"5 years" & padding & "10 years from now", ¬
	"10 years" & padding & "20 years from now"}
-- when, in months after the current date, to start counting the interval
set startat to {0, 12, 60, 120}
-- how many months to include in the interval
set duration to {12, 48, 60, 120}

-- ==================================================
--   1. because preparing a list of dates can be time-consuming, prompt first for a time period to select from, i.e.,
--      between the current date and 1 year from the current date,
--      between 1 year from the current date and 5 years from then,
--      between 5 years form the current date and 10 years from then, and
--      between 10 years from the current date and 20 years from then.

set response to choose from list promptlist with prompt "Due Date between:" default items "Now" & tab & padding & "1 year from now" OK button name "Choose"
if response is equal to false then
	return
end if

-- not finding a match should only happen during development & testing, but trap it.
set ctr to finditeminlist(response, promptlist)
if ctr is equal to 0 then
	display dialog "Could not find " & "'" & response & "'" & " in the list." & return & return & "Click Cancel to exit." buttons "Cancel" default button "Cancel"
	return
end if

-- ----------------------------------------------------------------------
-- set the start and end dates for the list.
-- both the start and end dates will use the current day number.
set startday to day of curdate as integer

-- i want the list to (at least roughly) reflect months, because it's a familiar concept: "2 months from now," etc.
-- the problem is that applescript does not natively know how to increment months.
-- applescript does natively understand how to increment weeks, but dealing with weeks is not intuitive -- is 2 months from now 8 weeks? sometimes, and sometimes not. it depends.
-- so, the script extracts the month from the start date, and adds the required number of months to that to get the end date.
-- calculate the start date: add the proper number of months to today to get the month to start from.

set startmonth to (month of curdate as integer) + (item ctr of startat) mod 12
-- add the proper number of months to today to get the year to start from.
set startyear to (year of curdate as integer) + ((item ctr of startat) / 12) as integer
-- convert these values to a date object for the start date.
set thestr to ((startmonth as string) & "/" & startday as string) & "/" & startyear as string
set startdate to date thestr

-- calculate the end date:
-- how many whole years must be added to today's date to get the year to end in?

set yearstoadd to ((item ctr of duration) / 12) as integer
-- how many months must be added to today's month to get the month to end in?
set monthstoadd to (item ctr of duration) mod 12
set endmonth to startmonth + monthstoadd
set endyear to startyear + yearstoadd
-- correct for year and month for months greater than 12.
if endmonth is greater than 12 then
	set endyear to endyear + 1
	set endmonth to endmonth - 12
end if
-- convert these values to a date object for the end date.
set thestr to ((endmonth as string) & "/" & startday as string) & "/" & endyear as string
set enddate to date thestr
-- display dialog (response as string) & return & "Start on: " & short date string of startdate & return & "End on: " & short date string of enddate

-- ----------------------------------------------------------------------
--   2. list the dates in the period selected in the format: "mm/dd/yyyy, DOW" e.g., "09/05/2011, Mon"
set thedate to startdate
repeat while thedate is less than or equal to enddate
	set listitem to (month of thedate as integer) & "/" & day of thedate & "/" & year of thedate as string
	set newdate to (date (listitem)) as date
	set wd to weekday of newdate
	set wd to text items 1 through 3 of (weekday of newdate as string)
	set listitem to listitem & ", " & wd
	copy listitem to the end of datelist
	set thedate to thedate + 1 * days
	-- display dialog listitem
end repeat

-- ----------------------------------------------------------------------
--   3. return the date selected from a call to "choose from list" (if none selected, or Cancel button clicked, quit)
set selected to choose from list (datelist) with prompt "Choose a Due Date:" OK button name "Choose"
if selected is equal to false then
	-- cancelled with escape or clicking Cancel button
	return
end if
-- "choose from list" returns a list object, which we needed to trap as "false" if the selection was cancelled.
-- but, now we need a string object to work with, so cast the result "as string".
set selected to selected as string

--   3a. because a string is returned, not a date, prepare it in yyyy-mm-dd, dow format, so that these date strings will sort in proper ascending & descending order in text controls.
--   3b. also, format the string so that it is easy to read in text controls such as lists, and
--   3c. add a distinctive prefix to the selected date string, throw a couple of tabs onto the end to provide a fake form of justification, and copy it to the clipboard.
-- (i didn't pad the dates in the list because they are hard to distinguish if they all have the same length.)
set len to length of selected
if len is not equal to 15 then
	-- selected is not "mm/dd/yyyy, dow" (15 characters long), so split it into chunks and see if month, day, or both need to be padded.
	set delims to text item delimiters
	set text item delimiters to "/"
	set theitems to text items of selected
	set monthitem to text item 1 of theitems
	set dayitem to text item 2 of theitems
	if length of monthitem is equal to 1 then
		-- month is less than 10, so pad it
		set m to "0" & monthitem
	else
		-- month is at least 10, no padding necessary
		set m to monthitem
	end if
	if length of dayitem is equal to 1 then
		-- day is less than 10, so pad it
		set d to "0" & dayitem
	else
		-- day is at least 10, no padding necessary
		set d to dayitem
	end if
	-- month and day are both 2 digits long now, put the pieces back together.
	set selected to m & "/" & d & "/" & text item 3 of theitems
	set text item delimiters to delims
end if

-- replace "/" with " - " because it seems be easier to read in a list.
-- (dates seem easier to read in the "Choose from list" selection list with slashes.)
set olddelims to text item delimiters
set text item delimiters to "/"
set theitems to text items of selected

-- because a string is returned, not a date, prepare it in yyyy-mm-dd, dow format, so that these
-- date strings will sort in proper ascending & descending order in text controls.
set selected to items 1 through 4 of text item 3 of theitems & " - " & text item 1 of theitems & " - " & text item 2 of theitems & items 5 through 9 of text item 3 of theitems
--  & " - " & text item 2 of theitems & " - " & text item 3 of theitems
-- set selected to text item 1 of theitems & " - " & text item 2 of theitems & " - " & text item 3 of theitems
set text item delimiters to olddelims

-- add the date delimiter to the selected date as a prefix and a suffix, so that it stands out in the subject of a note,
-- and so that notes with due dates can be found by searching.
-- (i left the prefix out of the dates in the list because they are distracting, and not needed.)
-- also, add 2 tabs to the end, to help to justify the entries in a list.

set selected to magic & " " & selected & tab & tab
set the clipboard to selected as string

-- ==================================================

on finditeminlist(lookfor, inlist)
	repeat with ctr from 1 to (count of inlist)
		set tmp to item ctr of inlist
		-- display dialog "ctr=" & ctr & return & "find " & tab & "-" & lookfor & "-" & return & "- in -" & tab & "-" & tmp & "-"
		if (lookfor as string) is equal to (item ctr of inlist as string) then
			return ctr
		end if
	end repeat
	-- not found
	return 0
end finditeminlist

DEVONthink isn’t a task manager, and isn’t likely to become a very robust one. I use OmniFocus - other DEVONthink users prefer Things, ToDo, The HitList, Taskpaper, etc., or even iCal. For many users, DEVONthink integrates well with task applications. If you could consider approaches that aren’t dependent upon custom dates and fields, you might find integration with one of those apps words for you.

There are many fine scripts posted in the forums, bundled with DEVONthink (e.g., in the Support Assistant), and available on the Internet that help integrate various aspects of task tracking, GTD, or project management with DEVONthink. For example, scripts that create OmniFocus actions that link to documents in DEVONthink.

And, within DEVONthink, tags, labels, document names, groups, and smartgroups are, as you’ve discussed, useful tools for tasking. For example, it’s easy to work with tags to represent GTD contexts.

I do agree with you that custom metadata fields would be useful (you want a date field, I might want different fields - with custom fields this would be possible). Until the owner decides to do that (hint :bulb: ), we do have other solutions.

I like your script. Over here, I’ve been using TextExpander for a similar approach - which avoids crawling long date-selector lists.

My snippet is


%fill:Year%-%fill:Month%-%fill:Day%: 

Thank you, korm, for your helpful response.

I’m searching for one app that can keep work-in-progress, research and supporting material, and reminders, all together, so that the essentials of my materials for selected tasks are all in one place, and easy to manage on-the-fly. The idea is to have one place to keep these materials, so that I can get the big picture of my projects and related data, and be able to create drafts for finalizing and polishing in other apps. I’ve been using Journler for this purpose, but it simply crashes too often.

I’ve looked at: using Finder with aliases to other file system objects, task managers, outliners, project managers, notebook-style apps, calendars, mind maps, and brainstorming-style apps, but none meet my purpose. The Finder approach is unwieldy. Task managers generally fall into the simple list-keeping category, or the project-management or GTD category, neither of which meet my purpose. Outliners are generally document-based, not collections-based. I do not need the tracking and scheduling capabilities of a project management app. Notebook-style apps usually have poor or unwieldy support for todos and reminders. My organizational style uses my calendar for events only. Mind maps just don’t work for me. Brainstorming-style apps require too much setup and maintenance. And in any case, these categories of apps (save the notebook-style apps) all lack sufficiently robust document processing capabilities, devoted as they are to their specialized purposes (rather than researching, maintaining reference materials, and writing).

Integrating todo-type apps to provide what seems a simple “date due” function to DTP is overkill for my purpose. (But, as you suggest, I will look at the forums, for examples and enlightenment.)

Your TextExpander snippet caused me to abandon my clunky, ersatz AppleScript “data picker” in favor of a simpler approach. I remembered that I had a copy of Keyboard Maestro that I got in a bundle purchase, and worked out a macro that copies roughly-formatted text from a field, executes a simpler AppleScript that validates the text as a date, assembles the string I want, and passes it back to Maestro to paste into the originating field. The idea is to use this key sequence-triggered macro to add a “due date” prefix to the titles of selected DTP documents, and to use Smart Groups and tags to list them. (In lieu of a real due date attribute, and also a scriptable date picker that is callable from a script, this is more effective approach than my AppleScript pseudo-date picker, but maybe in future I’ll find or develop a scriptable Java date picker app…after I get the JDK, and Ant, and who knows what else I’d need).

Perhaps my new macro and script, along with tags and Smart Groups, will meet my need for due dates for selected documents.

I’m curious if other DTP users resort to overloading the titles of their documents to include information or provide additional functionality that DTP has trouble providing, and what the compromises are when doing this. Assembling these use cases might demonstrate the value of custom attributes to the Devon folks.

Thanks again for your help. I’m looking forward to examining DTP further.

You might also want to look at a post I made here a while back on creating a tickler file in DEVONthink. You’ll need to overlook the lack of images-I lost them once to TinyGrab, re-linked them when they changed the URL, and they broke the URLs one more time. I don’t have the original grabs to post them again.

Don’t overlook DEVONthink Pro and Pro Office ‘Reminder’ scripts.

For example, if I need to take an action on a document, I select it, invoke the Scripts > Reminders > Add as Event to iCal (there’s a similar script to add as to-do in iCal). I can set an iCal alarm to notify me when the action is coming due. Click in the iCal event and I’ll see a clickable link that will then take me back to the document in a DEVONthink database that requires action.