Create a Smart Rule using AppleScript?

Is there any way to essentially create a Smart Rule using AppleScript?

I have rules like the one below, but will end up having to build like 200 Smart Rules for vendors & customers to send the files to their respective folders (not even sure how many Smart Rules DEVONthink can handle?).

This is taking some time, and I’m sure there’s a better way to do it.

@DTlow has suggested AppleScript as a good solution to a lot of problems, and lately I’ve really started to realize the power of custom solutions using AppleScript.

So, can anyone tell me:

  1. Whether it would be possible to build essentially a Smart Rule using AppleScript
  2. How one might go about finding an how to build a solution like that?

Thanks,
JC

1 Like

Something like this
I modified your smart rule action to run a script
Screen Shot 2022-06-24 at 19.47.57

and here is a sample script (not tested)
The boilerplate code is generated by clicking Edit Script

on performSmartRule(theRecords)
	tell application id "DNtp"
		repeat with theRecord in theRecords
			if "CEPA" is in name of theRecord then
				set theDestination to get record with uuid "C7C8988A-2CFB-4C50-88F5-ED9BDE2FF948"
			else if "AAAA" is in name of theRecord then
				set theDestination to get record with uuid "C7C8988A-2CFB-4C50-88F5-ED9BDE2FF948"
			else if "BBBB" is in name of theRecord then
				set theDestination to get record with uuid "C7C8988A-2CFB-4C50-88F5-ED9BDE2FF948"
			else if "CCCC" is in name of theRecord then
				set theDestination to get record with uuid "C7C8988A-2CFB-4C50-88F5-ED9BDE2FF948"
...
			else
				set theDestination to get record with uuid "C7C8988A-2CFB-4C50-88F5-ED9BDE2FF948"
			end if
			
			move record theRecord to theDestination

		end repeat
	end tell
end performSmartRule
2 Likes

Depending on the group hierarchy a simple smart rule like this one might be also sufficient:

2 Likes

Having 200 company names in a regular expression in this Scan Name field might be a bit challenging :wink:

@DTLow Instead of building a large if … else if … statement, I’d rather set up a record (in AppleScript) – wait, scrap that. AppleScript `record’s can’t iterate over their own keys. What a mess.

So, in JavaScript (untested, just to show the idea):

const companyMap = {
  'CEPA': 'C7C8988A-…',
  'AAAA': 'D7D9099B-…',
…
}; 
function performsmartrule(records) {
  const app = Application("DEVONthink 3");
  records.forEach(r => { // loop over all records
     const name = rec.name();
    /* Get all the keys from mymap with "Object.keys", 
      then use "filter" to return only those keys that are contained in the record's name 
       (using "name.indexOf(key)" as a test) */
     const groupKeys = Object.keys(companyMap).filter(key => name.indexOf(key) > - 1);
    /* now groupKeys 
      - is empty if no key matched the name of the record 
      - contains one element if only one key matched 
            the name of the record
      - contains more than one element if more than one key matched 
            the name of the record 
     The first and last case will throw an error. 
     */ 
     if (groupKeys.length > 1) {
       app.logMessage(`More than one matching group found for "${name}".`);
     } else if (groupKeys.length === 0) {
      app.logMessage(`No matching group found for "${name}".`);
     } else {
       /* Only one key matched the record's name. 
           Get the UUID of the group with "companyMap[groupKeys[0]]
           Then get the group itself with this UUID */
       const group = app.getRecordWithUuid(companyMap[groupKeys[0]]);
       app.move({record: r, to: group});
  })
}

Why? Well, first off, it seems to be clearer to have all the mappings spelled out at the beginning of the function. Second, it’s a lot easier to type just two strings in an otherwise unstructured part of code than adding a new if ... else section. And third, there’s no point to have all these get record with uuid statements inside the if ... else ... anyway - just get the group ID there and then do a single get record with ... outside it.

Also, it would be straightforward to use regular expressions instead of keys here, so one could match a record’s name to “IBM|International Business Machines” inside the filter call.

2 Likes

Do you have a standard structure for the group/folders?
I’m thinking it would be simpler if we just need to identify the search text (like CEPA) and can easily derive the destination

@DTLow thank you so much for making this!! Wow, this is incredibly helpful. I have ~30 in there so far and it seems to be working just fine.

I did try to separate them out and make one rule for customer, one for vendors, and a couple other misc ones, but for some weird reason, the other rules did not work, even though I duplicated the first one that WAS working. I tried to disable all the other ones to see if for some reason they were conflicting with each other, but still didn’t work :man_shrugging:t3:.

Totally fine though, I just added them all to the same script, and I am so appreciative for the working solution. Thank you :beers:

What do you mean by standard structure? Yeah pretty much all the same.

I’m thinking it would be simpler if we just need to identify the search text (like CEPA) and can easily derive the destination

And yes this is basically all it needs to accomplish

@cgrunenberg Thank you Christian! I was not aware of this option, and am going to play around with this to figure out how to implement.

@chrillek really appreciate you writing this up. My coding skills are pretty rudimentary right now (working to get better), but I understand high-level the benefits you’re describing re: why to use the JS instead of AppleScript. I’m going to dive deeper into the script and your explanation, and thank you in advance for the learnings

1 Like