I have a smart rule that I want to execute a script on all new tags that are created regardless of how they are created. However, I cannot get the smart rule to run when a new tag is created on a document. If I create a new tag on a document and then go to the tag in the /Tags/ group and apply the smart rule, it works. However, the script doesn’t seem to run when the tag is created on the document.
The smart rule runs a script that does some filing based on things in the item name and I also want the script to run on document names (if the name is changed) AND on tag names when they are created. However, I would be willing to split this into two smart rules (one just for tags and one for documents) if I could find a configuration that works when a new tag is created on a document. I have tried removing Any Document from the smart rule, and it still does not work on new tags created on a document.
I think the issue is that the selected record (which I refer to in the script) is the document, not the new tag – so the script IS being triggered when a new tag is created, but when the script runs, it runs only on the selected document name and not the newly created tag name. However, since the trigger is the creation of the new tag, I wonder if there is a way to refer to the newly created tag in the script, even if the selected record is the document?
An alternative would be to have the script run on all of the tags of the selected record if the selected record is a document – however, I want to avoid having to run the script on tags that are not new. I only want to run the script on the newly created tags.
Yes, I can find out whether it is the document or the tag, but I don’t know how to refer to the newly created tag in the script. If a document is selected and a new tag is created, is there any way to refer to just the newly created tag without iterating through all of the tags?
function performsmartrule(records) {
var app = Application("DEVONthink 3");
app.includeStandardAdditions = true;
records.forEach (r => {
if (r.type() === "tag") {
app.logMessage(r.name());
} else {
app.logMessage(`triggered for document type ${r.type()}`);
}
})
}
And in DT’s log messages, I get “triggered for document type group”. Investigating further, the name property of this “group” is, in fact, what I entered as name for the new tag.
Is that what you were after?
The type of the new tag being “group” instead of “tag” seems a bit weird at first, but since the tag is shown to “contain” the record that has been tagged, that might be ok.
on PerformSmartRule(theRecords)
tell application id "DNtp"
local theResult
repeat with theRecord in theRecords
display dialog "Name of record: " & name of theRecord
end repeat
end tell
end PerformSmartRule
Now, when create a new tag on a document, it displays the name of the tag. This means I was wrong – the selected record IS the tag (I’m realizing that I shouldn’t use the term ‘selected record’ — I meant the record that the script is acting on in the smart rule, I just don’t know the terminology as well as I should). However, now, I can’t figure out why the script doesn’t do anything when I create a new tag, but it works when I apply the smart rule on demand to the tag in the /Tags/ group.
It seems the problem is actually because I am trying to trigger a python script using the PyDT3 repository. My actual smart rule script is:
on PerformSmartRule(theRecords)
do shell script "/Library/Frameworks/Python.framework/Versions/3.12/bin/python3 /Users/samrose/PycharmProjects/addPersonTag/addPersonTag.py"
end PerformSmartRule
and then in the python script, I am trying to access the record via dt3.selected_records:
dt3 = DEVONthink3()
#Gather selected Records
if len(dt3.selected_records) == 0:
try:
selected_records = [dt3.viewer_windows[0].root]
#if not (selected_records): dt3.display_dialog("No Records Selected")
if not(selected_records[0]):
print("No Records Selected")
sys.exit(0)
except:
#dt3.display_dialog("No Records Selected")
print("No Records Selected")
sys.exit(0)
else: selected_records = dt3.selected_records
I’m not proficient enough to know how to pass theRecords from the applescript wrapper into the python script and I don’t know if the PyDT3 API has a mechanism to access theRecords from the smart rule. I am trying to do this in python because I am trying to learn python and I’ve been frustrated with applescript, which becomes too bulky and gives me memory overload errors when I try to run my longer scripts. However, I know you don’t support the PyDT3 API. If anyone proficient can suggest a path to access theRecords within the python script, that would be great. However, that may be asking for too much… I didn’t realize this was the issue until now.
You’re not even running a python script “from the AppleScript wrapper” – you’re spawning a shell that runs Python which finally runs a python script. And you pass no parameters to neither the Shell nor Python nor your script. In the end, you have these processes
DT
shell
Python
all running in their own sandboxes, not being able to access the other’s data (fortunately) but through an API.
How would that script even know about the context you’re calling it from? All it could possibly see around it is Python. It doesn’t know about the Shell nor DT’s smart rule.
theRecords is a list of Object Specifiers passed into your performsmartrule handler. It remains there, it’s not going anywhere else unless you tell it to. And you can certainly not tell it to cross process boundaries. What you could do, is to pass the UUIDs of the records on to your Python code and then access the records by these UUIDs one by one. Which is akin to sending someone a PDF with text layer as a fax, having them scan and OCR it, only to get at the text again. Feasible. Not pretty.
If you want to run a script in your smart rule, use AppleScript or JavaScript. And frankly, using PyDT3 instead of JXA introduces another layer of complexity, without bringing any advantages (last time I looked at it, it was still missing some methods).
I agree on all points. I don’t understand this quite well enough. I can go back to applescript or Javascript. However, if there is an easy way to pass theRecords into the python script and anyone knows how to do that, that would be helpful.
There is, however, a difference between smart rule scripts and other scripts:
the latter can be run from inside and outside of DT, and they can ask DT for any kind of data it is prepared to talk about (aka “classes and methods specified in the scripting dictionary”).
But in your case, we’re talking about a function (“handler” in AppleScript parlance) running within DT that receives an Array (a “list” in AppleScript parlance) of records as parameter. This data belongs to DT, and you can’t transfer it out of DT from within the function. Or rather: The only way to do that would be serialization (for example, by passing the UUIDs as strings). But then the receiving program would have to repeat the work done by DT, namely retrieving all the records again from DT. Completely unnecessary.
Thanks everyone. I am targeting all of my open databases because I want the smart rule to apply to all of my open databases. I’m not sure I understand the question. It is a general application tool that I want to run for everything.
You’re essentially targeting EVERY document, group, and tag in EVERY open database (and database you open). That’s an incredibly broad target and generally not recommended.
Is there another better way to execute a script every time a name is changed or tag is created for all of my databases? What is the downside of targeting everything?
I’d guess a lot of overhead. How often do you change the name of a group? How often do you create new tags?
And what is your script doing?
And why this wide combination of triggers – do you have to run the script on group creation? On tag renaming? On “group importing” or even “tag importing”? Perhaps using several, more focused smart rules might be a better strategy.
I change the names of groups and add tags fairly often because I use tags to do a lot of things. The script looks for items in the name that start with @ and end with a delimiter (one of *, _, !). The end delimiter tells the script to search for a matching tag of a certain type – so my tags of people all start with “p.” and my tags of organizations all start with “o.”. If it finds a matching person, org, etc tag, the script tags the tag or document with the matched tag. If it doesn’t find a match, it prompts me to see if I want to create a new person or organization tag. I am basically using text in the name as a short cut for adding certain kinds of tags. I want it to check every document name and every new tag I create because they all might have the @ indicator in them. It isn’t an effective shortcut for me if it doesn’t check everything for an @ indicator.
It may be a lot of overhead, but so is tagging every document or new tag with a person or organization tag. It seems to work ok and I’m not sure how else to get the behavior I want. I would be open to splitting it up, but I don’t see a way around checking each document and tag name for @indicators and the best time to check seems to be whenever I change the name or create a new name. I’m open to suggestions for alternatives.
That sounds as if you’re replicating information from the record’s name in tags. That kind of redundancy is not something I’d advocate (coming from relational databases). And it obviously leads to the kind of additional work you’re facing. Why not stick with the names?