Tinderbox User-to-User Forum (for formal tech support please email: info@eastgate.com)
Tinderbox Users >> Agent, Actions, Rules & Automation >> Create action that demotes a note

Message started by Doug Johnson on Jan 21st, 2017, 4:20pm

Title: Create action that demotes a note
Post by Doug Johnson on Jan 21st, 2017, 4:20pm

As I attempt to migrate critical information from Circus Ponies Notebook to Tinderbox, I have been unable to find how to build an action that will demote selected notes after being imported and exploded in Tinderbox.

$OutlineDepth looked promising but is read-only so does not seem workable.

As part of the migration, I am placing a unique placeholder phrase at the beginning of every note that should eventually be demoted one level. My Query selects the notes to be demoted by finding the placeholder. The challenge is composing the Action to accomplish the demotion.

I'm demoting from the keyboard presently. That method works but I'm hoping for an automated solution given the quantity of my information.

I find discussions related to exports but have not found discussion on how to demote via an action. Have also seen references to the design of TBX being oriented to working within a note as opposed to the more broad focus of managing notes. Could this be the difficulty?

Still have much to learn but have realized great progress so far. The extensive resources contain great gold. The Manual, aTBRef, Tinderbox Way, Getting Started, Action and Export Cookbook, Mark Anderson's tutorials in Clarify-it, nuggets in this forum and more.

Title: Re: Create action that demotes a note
Post by Mark Anderson on Jan 21st, 2017, 5:45pm

If I understand correctly, you've an agent that finds the notes you want to demote but don't know how to carry that action.

The attribute you're after is $Container. It is read/write and holds the name of the (path to) the note's containing note, i.e its container. But, be aware an agent collects aliases of matched notes and an alias has it's own container (in this case always the agent that created it).

The trick is to get the agent action to set the $Container of the aliases original note. Let's assume you want the new container of the notes, aliased within the agent, to be the note "XYZ". The agent action would be:

$Container(original) = $Path("XYZ")

Title: Re: Create action that demotes a note
Post by Doug Johnson on Jan 21st, 2017, 9:57pm

Thank you, Mark, you have it correct: I have a working Query and need an Action.

Tried your approach and found in a single note test that it worked to move the note being demoted.

As i'm sure you find often, in my post, I failed to emphasize that my desired outcome is for the note to be demoted below the note immediately above it. It might help if I give more context. Consider this set as an illustration of the starting situation:

Site A
Detail for Site A
Site B
Site C
Detail for Site C
Site D

The object is to transform this into:

Site A
   Detail for Site A
Site B
Site C
   Detail for Site C
Site D

where each "Detail for Site X" is a child of the note above it in the original set.

I can easily make this happen using the keyboard. Perhaps the conclusion is that this is the most efficient method to accomplish the desired outcome.

Thank you for the suggestion. I had seen $Container and also the caution about avoiding aliases. The mystery is building a general solution to the $Path portion.

It needs to be a general expression because the $Name of each parent-to-be is different. Perhaps there is a way to code "go up one note, capture its $Name, place that as a string in the $Path, then execute while there are more children-to-be."

Thank you again for your effort to understand my inquiry. I apologize for not being more complete originally.

Title: Re: Create action that demotes a note
Post by Mark Anderson on Jan 22nd, 2017, 7:04am

There's no warning about aliases. Rather its important to know that some attributes are intrinsic, that is to say that for such attributes the original note and alias can have different values. This is most easily understood if you consider a note and an alias of it when on the same map: unless they are to plot on top of one another each needs to be able to store discrete X and Y co-ordinates ($Xpos, $Ypos).

As to the wider problem, if all to-be-child-notes have a prefix titled "Detail of ", then the new container's name is the detail notes $Name minus the first 10 characters. You can do this using the .substr() action code, telling it to return everything from character #11 onwards to the end of the title. For instance:

$MyString = $Name.substr(11);

In the above, if the note is called "Detail for Project X", $MyString would hold "Project X" which is, of course, the new container name we need. So now you have an agent:

Query: $Name.beginsWith("Detail for")
Action: $Container(original) = $Name.substr(11);

This works but the agent keeps detecting the 'detail' note after the move. No harm is done but you don't really want this to happen. Two suggestions:
  • set a (user) boolean attribute to true when as part of the action and add to the query to filter detail notes whose flag is true (i.e they've been moved).
  • (better): alter the query so it tests the whether the parent is a project - or some defined entity (pro tip: if your project notes/containers and only your projects use a particular prototype then it is easy to add to the query & $Prototype(parent(original))!="pProject". Thus once a detail note is moved into a project, its parent's prototype will be 'pProject' and the detail note will no longer match the query.
If we try the latter solution, the query for the agent above becomes:

$Name.beginsWith("Detail for") & $Prototype(parent(original))!="pProject"

This works for the scenario described, testing in v6.6.5. This does work even where there is more than one detail note per project.

As the latter requires having more than one note of the same title (e.g. "Detail for Project X") I suspect the actual case is more complex than described. I sense the real problem is the 'detail' pages don't necessarily take a consistent naming form but relate the closest previous non-detail note. In other words if 'Project X' has 3 detail notes, in your example outline they are the next 3 notes in the outline. For all 3 detail notes Project X is the closest previous not detail page. This is easy to pick by eye, less so by code. Some more clarification as to the real naming syntax you use may be needed.

Title: Re: Create action that demotes a note
Post by Doug Johnson on Jan 22nd, 2017, 1:59pm

Am very excited, Mark! I'm really on the way now. Will begin implementing immediately; the direction is crystal clear.

Your insight regarding the to-be-child notes all being unlikely to begin with "Detail of" is a good point. However, part of my import process involves opening the file in a text editor where I insert markers for child or grandchild where appropriate. These markers will now make good landmarks for the action.

Creating a prototype for projects which is then tested is genius for distinguishing children from parents.

I'm eager to get at it! Am also especially grateful as an underlying objective of mine is to improve skills in coding text transformations. This exchange has been a great leap forward in that endeavor.

Willl report back. Thanks again.


Title: Re: Create action that demotes a note
Post by Doug Johnson on Jan 22nd, 2017, 10:25pm

Yes! The Agent works perfectly with the sample data.

When I add a second detail under Site A, "Detail for Site A-prime" and apply the agent, instead of simply indenting this note one level, it is thrown into a new container at the bottom of the outline named "Site A-prime". I see that happens because there is no parent named Site A-prime. The varieties of wording of my child notes makes a code solution impractical, just as you observed.

The prototype is an effective method of filtering. The .substr() action is much faster than the .replace() code I began with.

Thus, I have I have taken a great deal of your time. I have learned a great deal about the component pieces you brought to the problem. However, I am thinking I will complete the task by picking children by eye and using the keyboard to indent. This will proceed quickly because of the flags designating Child or Grandchild placed at the front of each note.

Thank you again for your generous assistance.

Title: Re: Create action that demotes a note
Post by Mark Anderson on Jan 23rd, 2017, 3:57am

Thanks for your feedback. I think it's useful for others starting out to get a flavour of the experimentation process. The unexpected container effect is due to the fact that if you declare a $Container value (which is in essence the path to a note) that doesn't currently exist, then Tinderbox will create it for you. Another subtlety is that paths are stored with slashes as container delimiters in paths. So if you make a new container with a slash in the name you'll get two nested containers from the parts of the name either side of the slash. "Project X/Phase 2" would not make a container of that name, but rather a "Phase 2" container nested inside a "Project X"containers.

I'm glad you've taken to the idea of prototypes. Now you've discovered the limitations of a coding based on name alone, consider having a prototype for detail notes, which would include in its key attributes a custom String attribute "ProjectName". Then you'd make your new detail note, set the prototype type and set $ProjectName; once a project's name has been used once**, it will become an auto-correct value. This is a small amount more work than just typing your name, but now it's easier to make the agent (assumes 'pDetail' is the prototype detail notes):

Query: $Prototype=="pDetail" & $Name(parent(original))!=$ProjectName
Action: $Container(original)=$ProjectName

** Actually, if the list of projects will be small and little-changing you can even preset possible values for the value pop-up list - see suggested values in the Inspector.

But, we can do better with the agent to avoid 2 issues:
  • We don't want the agent to move notes with no project name. We'll get the agent to match those but not move them. So, if a detail note sticks in the agent we'll get the head-up we probably forgot to set $ProjectName.
  • If you look at an actual $Container value, you'll see it's an note's full $Path minus the note's $Name. The method of setting $Container by a note title alone is based on an unstated assumption that the title given is ambiguous (if not the first in $OutlineOrder is assumed to be the match). If we specify the full path to where the projects live (assuming they're all in one container) then we can happily the agent make new containers for detail notes whose parent project we forgot to make.
So, we'll assume projects are in the container at "/Work/Projects/":

Query: $Prototype=="pDetail" &  ($ProjectName=="" | $Name(parent(original))!=$ProjectName)
Action: if($ProjectName!=""){$Container(original)=("/Work/Projects/"+$ProjectName);}

The extra query clauses ensure we pick up detail notes at root level as well as those elsewhere with no $ProjectName.

Now if we also set the $OnAdd action via the inspector for the "Projects"container:


Now, any new agent-created project container will have the right prototype.

Tinderbox User-to-User Forum (for formal tech support please email: info@eastgate.com) » Powered by YaBB 2.2.1!
YaBB © 2000-2008. All Rights Reserved.