How to set up automated replication based on tags & naming conventions?

I’ve got a naming convention system for certain stock files that I used in DTP: ART_ for articles, DOC_ for documents, MMO_ for memos, etc.

In my DPT, I’ve created project-based groups, and then I’ve got a stock hierarchy of folders for the project articles, documents, and memos, etc.

The thing is, some articles, documents, and memos crossover into other projects, and so I’d replicate files so that they exist in each project articles, documents, and memos where that crossover exists.

But rather than replicating each of these many crossover files, I was wondering if there’s a way I could simply use my naming conventions for them (i.e., ART_ ; DOC_ ; MMO_), apply tags for the different project names (e.g., project names: “Zebras” “Safari” “Africa” would have tag names: “zebras” “safari” “africa”), and then set up a smart rule to automatically replicate files based on the naming conventions and tags.

Would this work? If so, how would I set up Smart Rule commands for this process?


It would be possible, either with a bunch of smart rules (since the move to operation can’t be parametrized) or with a single smart rule using a script that derives the target group from the tag name.

1 Like

Thank you! I think I’m seeking the latter, since it seems like a simpler approach for managing around a dozen naming conventions, and scores of project-tags.

But I know nothing about building such a script. What would you suggest for assembling such a thing? Thanks!

Give me a moment

For a script, something like

tell application id "DNtp"
	--set group-ART to get record at "/ART" in database "Devonthink"
	--set group-Zebras to get record at "/Zebras" in database "Devonthink"
	set theSelection to get selection
	repeat with theNote in theSelection
		set theName to name of theNote
		set theTags to tags of theNote
		if "ART_" is in theName then replicate theNote to group-ART
		if "Zebras" is in theTags then replicate theNote to group-Zebras
	end repeat
end tell

I hacked something together. Not tested at all though! Can be used in a smart rule as a JavaScript in the “Run script” action or as a standalone script. In the latter case, it works on the currently selected records.

Before trying it out, change the values in the mapping object!

/* Define the relationship between tags and groups - CHANGE these entries! */

const mapping = { "art": ["Database1", "ART"],
                  "zebra": ["Database2", "Zebra"],
                  /* and so on
                  "tag": ["Database Name", "Group Name"],

function onperformsmartrule(records) {
  const app = Application("DEVONthink 3");
  /* updateMapping replaces the human readable database/group definitions in mapping by just the group records */
  /* loop over all records selected manually or by the smart rule */
  records.forEach(r => {
    /* get the list of tags for this record */
    const tags = r.tags();
    /* check for every tag in 'mapping' if it is contained in the tags. 
      If so, replicate the record to the appropriate group
      Caveat: No check if record and target group are in the same database!
    for (let key in mapping) {
      if (tags.indexOf(key) >= 0) {
        app.replicate(r, mapping[key]);

/* Replace the human readable DB/group associations with the group itself */
function updateMapping(app) {
  for (let key in mapping) {
    const db = app.Databases[mapping[key[0]]]; /* get Database ID from DT */
    /* Search for a group with the given name in db. This returns a _list_, we want only the first element */
    const group =`name==${mapping[key[1]]} kind: group kind:!tag`, {in: db})[0];
    /* Replace the mapping for this key with the group */
    mapping[key] = group;

(() => {
  onperformsmartrule(Application("DEVONthink 3").selectedRecords());

Thank you, DTLow and chrillek for putting together scripts for me! It’s very generous of you, and I greatly appreciate it…

So…which script should I use? And, forgive me for asking, but can either of you clarify a bit more how I ought to use it vis-a-vis a smart rule? It’s something that I’d like to have continuously running, regardless of which databases are open.

Thanks again for your wonderful help…

As I already said:

1 Like

If you have a smart rule, the script can be triggered as an action
Screen Shot 2021-11-10 at 13.33.48

My example above retrieved selected records using “set theSelection to get selection”
Here’s sample code to retrieve the records from the smart rule

on performSmartRule(theRecords)
	tell application id "DNtp"
		repeat with theRecord in theRecords
		end repeat
	end tell
end performSmartRule

Got it. That’s very helpful. Thank you.

I’m still trying to evaluate which of the two scripts I ought to use. Again, I don’t know which would be most effective, and I’m wondering if one might be better in terms of limiting the scope of variables that I’d need to input (i.e., naming conventions and tag values).

Any thoughts?

Thanks so much again…

I doubt that any of those scripts is usable as it is for your case. So you’ll have to adopt it anyway in terms of groups, databases and tag names.

What do you mean? If one is faster than the other? Or if one solves the task while the other does not (which is the difference between efficient and effective).
Frankly, the code is there for you to see. Some of it is even heavily commented. So you can decide according to your personal preferences.

I’m quite certain that both are effective and sufficiently efficient, too. @DTLow’s is in AppleScript, mine in JavaScript. Theirs are taking names into account, mine doesn’t (left as an exercise to the user). That are the main differences.

1 Like

Right, I get that. And that gets to my previous question, in a way.

I’ve got about a dozen stock naming conventions, and many more tag names. So, I’m wondering which of the two scripts might be easier might be easier to use if I need to include all of that data – and change it periodically when I edit or add more tag names. Does that make sense?

Thanks again…

Either script (applescript or javascript) will work with modifications

I like @chrillek’s concept of defining a table at the beginning that identifies the tags, keywords and groups. I’d copy that for my code.

I understand that javascript might be a more useful platform
I intend to learn it someday
Today, whenever I look at the code … my brain hurts :frowning:

Speaking of head aches …

on display enhanced alert mainText message theExplanation as styleType : informational
alert buttons buttonsList suppression showSuppression : false giving up after giveUp : 0 
acc view width theWidth acc view height theHeight acc view controls controlsList

I love it how the real handler name is obfuscated here (it is “display enhanced alert”, in case anyone is wondering) and how one has to shovel their way through everything after that to figure out what is a parameter name, what is a default value and what is the “label” of the parameter (or whatever it’s called in AppleScript). giving up after give up : 0 is such a beauty…

Seriously: Any unknown language is prone to induce head aches, be it Mongolian or JavaScript. Especially if people are using slang or writing unnecessarily terse code, as I tend to do.

Fun fact re the afore mentioned AppleScript: As it is, it won’t even compile in Script Editor, but it runs perfectly fine if it is part of a library with a function dictionary. I gues this is one of the consequences of a language without a specified grammar.