Tinderbox Forum

How to name $Title and move $Text for notes containing bracketed titles?

Last year, fellow Tinderbox users generously provided me with a stamp code to extract a few words from the top of a note’s text (in the body) to create note titles, and then move the notes’ text (which appears as the titles) into the notes’ text body.

That stamp code is:

$Text=$Name;
$Name=$Text.words(11)

It still works great! But I’m seeking advice for a process that would enable me to query and stamp notes whose title is bracketed with some kind of syntax, e.g., { } or ( ) or something else.

Here’s the background context…

I’ve created a process in which I annotate documents, render the annotation notes, export them into an OPLM format, and then import them into Tinderbox. When I import the notes into TBX, the notes’ text resides in the title – which is why I use that stamp to name the $Title and move the $Text.

But in some cases, I’m able to create a title for notes (e.g., notes for timelines, “1492: Columbus sailed the ocean blue”), and so I’m thinking it might make sense to query those notes with an agent, and then apply a different name $Title and move the $Text to them. So, here are specific questions…

  • I know that regular brackets are used for Tinderbox’s Ziplinks. So, what kind of bracket syntax I should use instead, that would be better to apply for note titles – and would work best for the process I’m seeking?

  • I assume I’m seeking an agent using regex query code for this purpose. Is that correct? If so, could I get some suggestion on the construction of that code for locating notes whose titled is bracketed in some way, e.g., {1492: Columbus sailed the ocean blue} or (1492: Columbus sailed the ocean blue) ?

  • Finally, what new stamp code should I use for notes with bracketed titles, so that I can remove the brackets (leaving the title intact w/o the brackets), and moving the text underneath it from the title area to the text body?

Thanks so much!

To restate your problem in order to ensure I understand it, you have some notes with titles like this:

Name: {1492: Columbus Sails} Columbus departs from Palos, Spain

and you want to transform them thus:

Name: 1492: Columbus sails
Text: Columbus sails

Tinderbox offers a bunch of ways to do this. You’re thinking of a stamp, but let’s use an agent instead. I think we’re looking at three steps:

  1. Gather the notes we want — notes with brackets
  2. Extract the title, which is inside the brackets
  3. Extract the text, which follows the brackets

I think for now we will ignore edge cases, such as notes with unpaired brackets, or nested brackets, and so forth. We can handle those later, or just fix any mistakes by hand.

step 1

The agent query finds the notes in which we’re interested. I think those are notes with brackets {}. Depending on your use, you might want to look for () or [] or some other delimiters. You might want to restrict the agent to looking in a specific part of the document

inside(/inbox) & ....

or to looking for specific kinds of notes

$Prototype=="Task" & ....

But I think we can leave that aside for now.

Our query, then, can be simple:

$Name.contains("}")

We could check for the opening bracket, too, but that might be more work than it’s worth.

$Name.contains("}") & $Name.contains("{")

#step 2

Now, what does the agent do to the notes it finds? You’re right: we could use a regular expression in the query and extract back references that way. I’m going to do it a little differently.

I want to make an agent action that splits the text at the closing bracket “}”.

$MyList=$Name.split("}");

This makes a list of substrings, divided at the “}” character, and stores that list in $MyList. That does almost all the work we need to do! All that’s left is to move the parts where they belong:

$MyList=$Name.split("}");
   $Text=$MyList[1];
   $Name=$MyList[0];

Almost there! We have one glitch left: the opening “{” remains in the name. We’ll delete it:

$MyList=$Name.split("}");
   $Text=$MyList[1];
   $Name=$MyList[0].replace("{", "");

Again, you could do this with regular expressions just as well, and that’s what most people would reach for first. I think this approach is a little cleaner, especially since regular expressions think that characters such as “( ) [ ]” have special meanings and so require a special signal to remove their specialness.

One warning: A perfectionist might be tempted to try to write a regular expression that ensures that braces are properly paired and that handles nested braces. That’s won’t work, because that’s a problem beyond the capacity of regular expressions. It’s easy to forget that! But you aren’t going to encounter such stuff in the wild, so just don’t do it.

2 Likes

Hey there, great sample of the use of split and back-references. Once crucial point. Curly brackets are a RegEx operator. For @eastgate example to work you need to escape the brackets with a backslaph \.

I’ve created a sample file with three approaches to the problem. Also, it includes a logging example. I will go over things today in today’s meetup, as I think it holds a ton of valuable lessons.

This lesson is in response to a post by @jprint714

How to name $Title and move $Text for notes containing bracketed titles?. This is a wonderful tutorial that gets at the root of action code, logging, text transformation, note name path management and more

Description

Parse the text in an attribute and place the different parts in different attributes.

Learning Outcome

  1. Know how to use .split operator

  2. Appreciate the retention of your source data until you’re ready to destroy it

  3. Missed typed operators can lead to data being inexplicably destroyed (see LO2)

  4. Logging is your friend, helps you see what’s going on.

  5. Commenting code is your friends, it helps your future-self

  6. There is more than one way to do something

  7. Understand RegEx operational characters and how to escape them

  8. Tinderbox’s syntax for finding keys in a list or dictionary, e.g. [0], [1].

  9. User of designators, e.g. a note name, path, to enhance action code.

  10. The use of variables

  11. Appreciate the value of cleaning up after yourself

  12. Bonus: writing in markdown gives a ton of flexibility

  13. Bonus: some visual affordance really help, e.g., views, separators, and badges

  14. Bonus: replacing tricky characters, e.g., “;”, in operations with a different delimiter to complete a process and then later refers back to the original.

  15. Bonus: Triggering functions.

Objective:

Parse this text “{1492: Columbus Sails} Columbus departs from Palos, Spain” that is in the $Name of a note.

  1. Place the text between the{} in the $Name

  2. Place the remaining text in $Text

  3. Do it in a way that leaves no lingering data

The Solution

See the stamps. I’ve produced several for the exercise:

  1. Reset

  2. Parse Name Original 0, the stamp that does not work

  3. Parse Name Original 1, simple stamp

  4. Parse Name Original 2 Modified, handles “;” in text.

  5. Parse =Name Refined Eg 1 Use Attribute Placeholder , uses attributes as placeholder

  6. Parse Name Refined Eg 2 Use Variable uses variables instead of attributes.

  7. Tigger Parse Note, triggers a function to parse the note

See the functions, I’ve created two functions.

  1. fParseNote, parses the note on a boolean trigger

  2. fReset, resets the note on a boolean trigger

TBX L - Meetup 23OCT22 Using Split to Parse Data.tbx (303.1 KB)

1 Like

Oh wow. Thank you so much, @eastgate and @satikusala – this looks amazing! I’m grateful for your generous help…

I’m excited to put this to work, but first wanted to further clarify the way the text appears so that I’m applying the right code to fix it. Using the same example, the lines of text would appear like…

{1492: Columbus Sails}
Columbus departs from Palos, Spain (near Huelva) in southern Spain, on August 3, 1492, in command of three ships: the Niña, the Pinta and the Santa Maria. His crew mostly came from surrounding towns such as Lepe and Moguer.

In order words, whenever I create a bracketed title, the text is in the first line, and the remaining text falls directly underneath it – but all of the text still appears in the title section of the note.

BTW, I’ve been using this process with stamps (as I noted in our exchange on the other post), but I’m happy to do this through another process. I’m still learning Tinderbox, so I’m not clear on the pros/cons of doing it through ways other than stamps.

Here is the meetup from today: Tinderbox Meetup- Meetup 23OCT22 Working with Action Code, a Step-by-Step Walkthrough

1 Like

This is tremendous… Thank you, guys! I’ve been watching the the meet up recap, and trying to absorb all of the lessons.

Now I’m going to throw you a curve! @satikusala and a few others know that I’m using this process for annotated notes that I’m importing into Tinderbox.

You’ve just provided me with an elegant solution for preserving titles for notes with { } (curly brackets). (Again, I’m happy to use < >'s, parentheses or anything else for bracketing these titles.) As I said, I’m mostly using this syntax around note titles with dates, for notes that are part of my timelines.

However, I still need to apply a stamp to name the $Title and move the $Text for the notes without the curly brackets as well.

Sure, I could manually locate those notes and apply the old stamp to them. But is there another way that I could do this, that would also integrate some of the automated functions you created for the notes with curly brackets?

I’m curious to hear your answer, and thank you again for all of your help!

I don’t remember. your old code. However, this should be simple enough. You can string action code with functional or stamp() actions. I assume you have a prototype for the imported notes. Add a boolean that shows the default is true, e.g. $IsNeedsProcessing. Add your modified function to the prototype and have the function turn $IsNeedsProcessing to false once it is complete. Doing this will automatically trigger the action code when you import the notes. Or, you bring the important notes into a simple folder that has an OnAdd to trigger your function. You can then have another function move them to wherever you want them to go in your file after they’ve been imported. I can’t be more specific without knowing your context

But, IIRC, in the demo above, .replace() is used. So, notes with no curly brackets won’t be affected. IOW, the code works for notes whether or not_ they contain the problematic characters. The same applies at the reverse stage.

If you are getting something different, could you post a small TBX showing the problem as I suspect there may be another factor at play.

Thanks! I’ll answer your question first, and then walk you through my process for importing notes.

The stamp code I’ve used extract a few words from the top of a note’s text (in the body) to create note titles, and then move the notes’ text (which appears as the titles) into the notes’ text body is simply:

$Text=$Name;
$Name=$Text.words(11)

I’ve created three prototypes that are nearly identical in every way but their appearance: pArticles; pDocuments; pInterviews. These prototypes have the same attributes; they just have different names (of course, for the source data) and colors and badges for quick identification.

After I’ve annotated documents (in a PDF format) in MarginNote, I render them in OmniOutliner (through a process I’ve shared with @satikusala and @mwra), and then export them with OmniOutliner in an OPML format. During the OPML export process, I change the prefix so that any files will include the following file naming conventions: REN_ART_2022_10_25-Columbus sailing (REN for rendered; ART for article) or REN_DOC_2022_10_25-Columbus sailing historical data (DOC for document) or REN_INT_2022_10_25-Interview with august historian about Columbus sailing (INT for interview).

Then I create and name a folder (container) in Tinderbox using the OPML file name, e.g., REN_ART_2022_10_25-Columbus sailing. And then I open the OPML file in Tinderbox, select all notes, copy them, and then paste them into the target TBX container.

I believe I once set up an approach in which Tinderbox would apply the right prototype to notes inside of a container, depending on the parent containers prefix (i.e., REN_ART / REN_DOC / REN_INT).

As I said, I’m open to suggested approaches by fellow users for applying stamps and selecting the appropriate prototypes or creating an automated process that selects the appropriate prototype for the notes, then applies the stamp code to the notes so that titles in the curly brackets remain as titles, while the remaining text outside the right curly bracket is moved to the text body and the notes without bracketed titles undergo the original title-shortening-text-moving process (for which the code above was designed).

I hope this makes sense. I’m happy to clarify this further and answer any questions.

Thank you again for all of your help!

One thought. Make a prototype which you apply to the new containers you make to subsequently hold the pasted-in OPML-generated notes. Give the prototype’s $OnAdd this code:

if($Name.beginsWith("REN_ART"){$Prototype="pArticles"};
if($Name.beginsWith("REN_Doc"){$Prototype="pDocuments"};
if($Name.beginsWith("REN_ART"){$Prototype="pInterviews"};

Some notes on the choices above:

  • Setting the prototype via OnAdd means the test runs only one.
  • Putting the ONAdd code in a prototype means you only need create the code once
  • Why not use .contains()? .beginsWith is a simple literal string value check, i.e. not a regex, so simpler faster. You could use .contains() if you prefer.

So the process, once you’ve made the new prototype:

  • make a new prototype, e.g. “pFolder” - choose a name that makes sense to you
  • make your new import container and set it to your new prototype
  • paste your new notes into the container and their prototypes are automatically set.
1 Like

This is brilliant. Thank you so much, @mwra !

I realize this is a somewhat elementary question, so apologies in advance… In describing my annotated notes import process I failed to mention how I’m creating folders (containers) for these notes under a parent folder titled “I M P O R T S.” So, I’m wondering if it’s possible – and advisable – to simply include $OnAdd code, and the if($Name.beginsWith sequence you suggested, within that parent folder’s Action, Rule or Edict section?

Is there any other functional difference between inserting the code into one of those sections versus creating a prototype with the $OnAdd code, and applying it to the parent “I M P O R T S” folder?

I want to first nail that down that phase of work. But the next question is: would it be possible also include code for the Parse name stamp that @satikusala created – so that the $OnAdd can first apply the appropriate prototype to the notes and then parse the names, and move the text? Or would combining those two functions be problematic?

Like said, I’m aiming to first work on the first phase of the $OnAdd before I test the Parse name functions; just want to determine if it’s viable to combine those functions within a single $OnAdd sequence.

Thanks again!

No difference at all; if the note has no $OnAdd, it will inherit from its prototype.

Yes. If, the ‘I M P O R T S’ container contains a container per import into which the OPML-generated notes are added, the ‘I M P O R T S’ OnAdd doesn’t fire as the OPML imports are not children of that container but grandchildren. My solution was designed—using the information you gave—to be applied to the container holding the new OPML-generated notes, not their grandparent.

But, if you set OnAdd for ‘I M P O R T S’ to:

$Prototype="pFolder";

Then any container created a child of ‘I M P O R T S’ will inherit pFolder’s ONAdd so when the OPML-generated notes are added to it the prototype’s ONAdd will set the right prototype for the new children.

Tip, if struggling to imagine the process, just actually test it in a small TBX. Otherwise you drown in imagined hypothetical questions about problems that likely won’t arise.

Sadly, you’ve not told us what those are so an answer would be a complete guess. Why not take a step back and post a small TBX with your starting problem and the prototypes and functions etc., that you want to use? It’s hard to give a helpful answer if part of the question is missing. Don’t forget we can’t see your file, nor do we know your design assumptions unless they are stated. :slight_smile:

You’re right, you’re right… I’ll first try experimenting a bit more, using the process you’ve suggested, and then I’ll circle back with more concrete follow-up questions.

Thanks again.

1 Like