Grouping items by date opened or like a daily journal

Wow! Thanks for this! I’ve got it set up and running smoothly.

Now to start tweaking it….
I already added an additional trigger, so that it runs not just when an item is opened but also when a new item is imported.
I’d like to prevent it from adding duplicates. Maybe a custom metadata flag could assist with that.

Each record is only added once.

There are some exceptions, I haven’t figured it out yet. Here’s an example. I think this may have happened when I used space to quick-look before opening? or maybe I opened it in with DTP3 and with an external editor? not sure but it’s happened a few times, although not every time.

* [181899847-Subculture](x-devonthink-item://5C9191B8-43CC-470D-B800-9F37472FEA9E)  
* [181899847-Subculture](x-devonthink-item://5C9191B8-43CC-470D-B800-9F37472FEA9E)  

No, it was a silly mistake. I didn’t use a variable and instead wrote out the string that should be replaced and of course did it wrong … one time with the linefeed at the end, one time at the start. The problem only shows if one opens the last added record immediately again, i.e. if no other record was opened in between. Fixed. Thanks!

Hi pete31,
This is an awesome script. Super helpful. Thanks for sharing.

1 Like

Updated the script. Improvements:

  • Escape link names for Markdown.
  • Use only the Reference URL to sort records by last opened.
    (Previously renaming and opening a record again yielded unwanted entries)
  • Added property that can be used to exclude databases.
  • Mark Record History as read.

Edit: Updated script again.


Thank you for posting this. Very nifty.

1 Like

Changed my setup: Adding openingDate:Today and trigger On Rename covers cases where a record was opened before renaming it and not opened again afterwards. Without it the Record History still shows the old name.

  • Criteria:
    • kind:any
    • openingDate:Today
  • Event:
    • On Open
    • On Rename

If property sortByLastOpened is set to true then renaming a record also moves it to the bottom of the list which is not 100% correct but better than still showing the old name, I think.

1 Like

Seriously cool idea and script. I had no idea that I needed this until I tried it. So very hepful.

1 Like

For your next trick … is it possible to create a Smart Group containing all the items linked from a Record History note that this rule creates?

For instance, I’m thinking of a case where I read five different papers two weeks ago, and now need to confirm exactly which one contains a specific fact.

Same here! Thanks a lot @aaaaaaaaaaaaaaaa !!!!!!

1 Like

Yes, but unfortunately not easily. The only way I can think of is to tag the entries. But one can’t create a global Smart Group via AppleScript which means you’ll have to create it manually.

-- Assign tag to selected History Record's entries

property copyTag : true -- Copy tag (i.e. name of the History Record) for easier creation of global Smart Group

tell application id "DNtp"
		set theRecords to selected records
		if theRecords = {} then error "Please select a History Record"
		set theRecord to item 1 of theRecords
		set theRecord_Source to source of theRecord
		set theReferenceURLs to get links of theRecord_Source
		set theRecord_NameWithoutExtension to name without extension of theRecord
		repeat with thisReferenceURL in theReferenceURLs
			set thisReferenceURL to thisReferenceURL as string
			set thisLinkedRecord to (get record with uuid thisReferenceURL)
			if thisLinkedRecord ≠ missing value then
				set tags of thisLinkedRecord to (tags of thisLinkedRecord) & {theRecord_NameWithoutExtension}
			end if
		end repeat
		-- It's not possible to create a global Smart Group via AppleScript so this needs to be done manually
		display dialog "Please create a global Smart Goup with query:" & linefeed & linefeed & "tags:" & theRecord_NameWithoutExtension
		if copyTag then set the clipboard to theRecord_NameWithoutExtension
	on error error_message number error_number
		if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
	end try
end tell

1 Like

It would probably be far better to use custom meta data. I guess you don’t want the tags to stay around so you’ll have to delete them at some point (and this in each database). Custom meta data could stay attached to the records without spoiling the UI much.

Custom meta data could be attached while adding the record to the History Record. However, it would still be necessary to manually run a script as it might be that not all entries’ databases are open all the time.

Easiest one for me, create a Smart Group with all items (“Kind” equals “Any Document”). Open it, then sort by date.

The moment you open a record you’ll lose any indication about when it was opened in the past.

This could get you started Script: Add reference URL to aliases and create Smart Group.

Hi @pete31 - this is really useful.

I get too busy to record my chargeable time and this is a really useful way of telling me what I’ve worked on when it comes time to log billable time entries at the end of the day.

I tweaked it to put in the current time and database name (which tells me the matter name the record relates to) before each record.

Thankyou! Outstanding. :grinning: :clap:

1 Like

I love this script, and I’d like to add the timestamp to the list that you’ve mentioned. Please can you show me how you did this? I thought I’d be able to figure it out myself but I am new to scripts and couldn’t see what to do.

Hi @MsLogica

Sure - here’s the script with my additions. It’s probably a bit hacky (what little I know about scripting I’ve cobbled together, mostly from this forum) but it works with no problem for me. :slightly_smiling_face:

-- adapted from script posted on @pete31 Nov 2021:

property sortByLastOpened : true
property theExcludedDatabaseNames : {"_temp"}

on performSmartRule(theRecords)
	tell application id "DNtp"
			repeat with thisRecord in theRecords
				if (name of database of thisRecord) is not in theExcludedDatabaseNames then
					set theHistoryRecord_Name to my formatDate(current date) & space & "Activity Record"
					set theResults to search "kind:markdown {any: name==" & theHistoryRecord_Name & " name==" & theHistoryRecord_Name & ".md}"
					if theResults ≠ {} then
						set theHistoryRecord to item 1 of theResults
						set theHistoryRecord_Text to plain text of theHistoryRecord
						set theGroup to create location "/Daily Activity Record" in inbox
						set theHistoryRecord_Text to "<style> a {text-decoration: none;} </style>" & linefeed & linefeed & "# " & theHistoryRecord_Name & linefeed
						set theHistoryRecord to create record with {name:theHistoryRecord_Name, type:markdown, plain text:theHistoryRecord_Text, exclude from see also:true, unread:false} in theGroup
					end if
					set thisRecord_ReferenceURL to reference URL of thisRecord
					if thisRecord_ReferenceURL ≠ (reference URL of theHistoryRecord) then
						--added to original script:
						set {year:y, month:m, day:d, time:t} to (current date)
						set theTime to my secsToHMS(t as integer)
						set theDatabase to the name of current database
						--end addition

						set thisMarkdownLink to (theTime & ": " & "**" & theDatabase & "** : " & "[" & my escapeLinkName(name of thisRecord) & "](" & thisRecord_ReferenceURL & ")") as string
						if theHistoryRecord_Text does not contain thisRecord_ReferenceURL then
							set plain text of theHistoryRecord to theHistoryRecord_Text & linefeed & "* " & thisMarkdownLink & space & space
						else if sortByLastOpened then
							set plain text of theHistoryRecord to my updateOrder(theHistoryRecord_Text, thisRecord_ReferenceURL, thisMarkdownLink)
						end if
					end if
				end if
			end repeat
		on error error_message number error_number
			if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
		end try
	end tell
end performSmartRule

on formatDate(theDate)
	set theYear to year of theDate as string
	set theMonth to ((month of theDate) as integer) as string
	if (count theMonth) < 2 then set theMonth to "0" & theMonth
	set theDay to day of theDate as string
	if (count theDay) < 2 then set theDay to "0" & theDay
	return theYear & "-" & theMonth & "-" & theDay
end formatDate

on escapeLinkName(theText)
	set d to AppleScript's text item delimiters
	repeat with thisCharacter in {"[", "]", "(", ")", ">"}
		set AppleScript's text item delimiters to {"\\" & thisCharacter, thisCharacter}
		set theTextItems to text items of theText
		set AppleScript's text item delimiters to "\\" & thisCharacter
		set theText to theTextItems as text
	end repeat
	set AppleScript's text item delimiters to d
	return theText
end escapeLinkName

--added to original script: this gets the current time
on secsToHMS(secs)
	--from <>
	tell (1000000 + secs div hours * 10000 + secs mod hours div minutes * 100 + secs mod minutes) as string ¬
		to return text 2 thru 3 & ":" & text 4 thru 5 & ":" & text 6 thru 7
end secsToHMS

on updateOrder(theText, theReferenceURL, theMarkdownLink)
	set theDelimiters to {"(" & theReferenceURL & ")" & space & space & linefeed, "(" & theReferenceURL & ")" & space & space}
	set theTextItems to my tid(theText, theDelimiters)
	set theTextItems_modified to {}
	repeat with i from 1 to (count of theTextItems)
		set thisTextItem to item i of theTextItems
		set thisTextItem_Paragraphs to paragraphs of thisTextItem
		set thisTextItem_Paragraphs_Count to (count of thisTextItem_Paragraphs)
		if thisTextItem_Paragraphs_Count > 0 then
			if item -1 in thisTextItem_Paragraphs contains "x-devonthink" then
				set theTextItems_modified to theTextItems_modified & thisTextItem_Paragraphs
			else if thisTextItem_Paragraphs_Count > 1 then
				set theTextItems_modified to theTextItems_modified & items 1 thru -2 in thisTextItem_Paragraphs
			end if
		end if
	end repeat
	return my tid(theTextItems_modified, linefeed) & linefeed & "* " & theMarkdownLink & space & space
end updateOrder

on tid(theInput, theDelimiter)
	set d to AppleScript's text item delimiters
	set AppleScript's text item delimiters to theDelimiter
	if class of theInput = text then
		set theOutput to text items of theInput
	else if class of theInput = list then
		set theOutput to theInput as text
	end if
	set AppleScript's text item delimiters to d
	return theOutput
end tid

Thank you so much for sharing this. Because I like to make myself suffer needlessly, I went through your code and @pete31’s code line by line to learn what was different (I am new to AppleScript).

I thought I’d implemented my code the same as yours, but there was a bit of a glitch yesterday where I managed to create 200 individual logs whilst I was doing some file amends :joy::rofl: I’ve corrected the code today and it’s all running smoothly now (as far as I can tell). And I’ve learnt that one tiny bit of code can have big consequences :grimacing: