In AppleScript, how do I get the back link from an annotation document to the original?

When creating and editing annotation documents, it’s possible to get the back link from the annotation document to the original using a couple of methods: one can use the %documentLink% placeholder in an annotation template, or insert a link via the pull-down menu.

Is there a way to get the link property from AppleScript? If I have selected annotation documents, and run a smart rule or script over the records in the selection, can I programmatically somehow find out the document that each annotation record annotates?

I know it’s possible to go from a document record to its annotation document, via the annotation propery of the record object class. I’ve been looking at the record object and I can’t find something that goes in the opposite direction.

If the annotations are in a markdown document, you can use its plain text property to extract the links from.

A content conversion to source handles this…

tell application id "DNtp"
	if not (exists (selected record 1)) then return
	set sel to (selected record 1)
	if (type of sel is in {markdown, formatted note, html, rtf, rtfd}) then
		set src to source of sel
		set allLinks to (get links of src)
		if allLinks is not {} then
			-- Process links. Here I grab the first link which is the initial backlink in the Annotation file templates.
			set docBackLink to item 1 of allLinks
			set referredDoc to (get record with uuid docBackLink)
             -->content id 393674 of database id 3
		end if
	end if
end tell

OK, that’s an approach, it’s true. I guess I was hoping for a direct way that didn’t require searching the text of the document :thinking:

Thank you for the extremely fast reply and code.

If I’m not mistaken, source will return an HTML version of the document (converting if needed), and get links of returns all the URLs of all the links in the HTML source. (Or at least, that’s what happens when I test it.) Is the hoped-for advantage in this approach that it provides a common way of handling many different document types? Otherwise, it seems like it would be computationally more expensive. The HTML of a document many be much larger than the Markdown source, if one is using custom CSS and extensions like MathJax.

Is there a technical reason why DEVONthink record objects don’t expose a property for the link from an annotation document to what it annotates? The record has an annotation property for the forward direction, and based on past discussions in this forum, it sounds like record objects do have an internal back pointer to the document to which an annotation refers. Could this be made accessible programmatically?

If there’s no technical reason why it can’t be done, I’d be happy to open a ticket to make the request.

For the time being, I found another approach that I feel may be more robust than trying to pull URLs out of the text of the document. The code below assumes that the selection is one or more annotation documents. It loops over each given annotation document, looks at its incoming links, reads the annotation property of each linking document, and compares the UUIDs of the linked annotation document to the current annotation document. This works because an annotation document only refers to a single source.

on performSmartRule(selected_records)
	tell application id "DNtp"
		repeat with this_record in selected_records
			-- We assume that these are annotation records already. We
			-- need to get the thing they're annotating.
			repeat with _incoming in incoming references of this_record
				if (exists annotation of _incoming) then
					set other_annot to get annotation of _incoming
					if uuid of other_annot = uuid of this_record then
						-- do stuff here...
					end if
				end if
			end repeat
		end repeat
	end tell
end performSmartRule

This information isn’t available in the database, there’s only a reference from the document to its annotation. See annotation property of records.

More expensive as? Since with HTML you get a DOM that can be queried efficiently, the conversion might result in a better performance than brute-force querying the plain text.

Custom CSS does not come into play here (sizewise) since that should be included with a link element, not verbatim.

Depends on how one goes about it, but for most normal ways that people would include custom CSS, you’re right. However, in DEVONthink 3.9.4, if you enable Prism support (in Preferences ▹ Files ▹ Markdown), the output of source of foo contains inline JavaScript code. Inspecting the output of source of foo on a simple markdown document shows the following near the top – this is just the start of the Prism part:

<style><!--
/* PrismJS 1.29.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+applescript+awk+bash+c+csharp+cpp+go+java+json+json5+jsonp+lisp+markup-templating+objectivec+perl+php+python+ruby+rust+sql+swift+typescript+yaml&plugins=line-numbers+highlight-keywords */
code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;fon .....

(Truncated here to save space.)

I wrote MathJax in my previous comment, but I see now that what I saw was the Prism code, not the MathJax code.

I think parsing the text would be more computationally significant than using an already established AppleScript function. And yes, i think running the raw text through python or the shell is overkill for your situation. My code already does what it needs to do and quickly. But to each their own.

Does this code assume that the first link in the document is the back link to the document being annotated?

As noted in my code…

So the code assumes a specific structure for the annotation document, which means that, as written, it may not work if the user creates annotations using custom annotation templates.

Can you think of a way to use get links of if the back link is not the first link in the annotation document?

You’d have to look at each link in turn and check if the current document is the link’s document’s annotation. Possibly quite slow.

Yes, because there was no information to the contrary in this instance.

OK, this is what I suspected.

The approach I took in the code posted upthread basically does that. A speedup is possible by not looking at all the links contained in the text of an annotation document: we can instead take advantage of the fact that DEVONthink provides a list of incoming links to each document in a database. The approach is to look at the incoming links of the annotation document and compare the source record’s annotation property to the annotation document, and figure out which incoming link document points to the annotation document. Hopefully the number of comparisons will be small in most cases. (In fact, it seems likely that most annotation documents have only one incoming link.)

I’d say few links are likely, but I wouldn’t imagine much of a performance hit unless someone was running some very unoptimized code, like getting all the items in a database and checking them one-by-one to see if they match :wink: